Spring Boot + JPA: Chú thích tên cột bị bỏ qua


121

Tôi có một ứng dụng Spring Boot với sự phụ thuộc spring-boot-starter-data-jpa. Lớp thực thể của tôi có chú thích cột với tên cột. Ví dụ:

@Column(name="TestName")
private String testName;

SQL được tạo bởi điều này được tạo ra test_namedưới dạng tên cột. Sau khi tìm kiếm giải pháp, tôi đã tìm thấy giải pháp đó đã spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.EJB3NamingStrategygiải quyết được vấn đề (tên cột được lấy từ chú thích cột).

Tuy nhiên, câu hỏi của tôi là tại sao không đặt tên_strategy thành EJB3NamingStrategyJPA lại bỏ qua chú thích cột? Có lẽ phương ngữ ngủ đông có một cái gì đó để làm với nó? Tôi đang kết nối với MS SQL 2014 Express và nhật ký của tôi chứa:

Unknown Microsoft SQL Server major version [12] using SQL Server 2000 dialect
Using dialect: org.hibernate.dialect.SQLServerDialect 

1
Câu hỏi này là về việc thay đổi tên cột được cung cấp rõ ràng thaybị bỏ qua . Nó dẫn đến việc điều này được thực thi thay vì biến thể trong suốt như mong đợi . Hibernate thực sự có thể bỏ qua @Column(name="...")chú thích, chẳng hạn như khi bạn sử dụng loại truy cập khác với loại truy cập mong đợi, nhưng đó không phải là trường hợp ở đây.
Vlastimil Ovčáčík

Câu trả lời:


163

Đối với hibernate5, tôi đã giải quyết vấn đề này bằng cách đặt các dòng tiếp theo trong tệp application.properties của mình:

spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

30
spring.jpa.hibernate.naming.physical-strategy = org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl, chỉ có thuộc tính này là bắt buộc để giữ nguyên tên.
abhishek ringia

1
Tôi đã gặp vấn đề tương tự và việc thêm 2 thuộc tính đó đã giải quyết được vấn đề đó cho tôi. Tôi đang chạy Spring Boot 1.4.3
Johan

1
Đây là giải pháp duy nhất làm việc cho tôi. Tôi đang sử dụng Spring Boot 1.4.2
Sanjiv Jivan

Tôi đang sử dụng Spring Boot 1.5.9.RELEASE, bài này làm việc cho tôi
IcyBrk

Tuyệt vời .. Tôi đã tự hỏi tại sao nó lại bỏ qua chú thích @Column của tôi. Cuối cùng điều này đã giúp tôi. Đối với tôi, tôi cảm thấy đây là một lỗi hoặc thiếu chức năng.
Raju Penumatsa

86

Theo mặc định, Spring sử dụng org.springframework.boot.orm.jpa.SpringNamingStrategyđể tạo tên bảng. Đây là một phần mở rộng rất mỏng của org.hibernate.cfg.ImprovedNamingStrategy. Các tableNamephương pháp trong lớp đó được truyền một nguồn Stringgiá trị nhưng nó là không biết nếu nó xuất phát từ một @Column.namethuộc tính hoặc nếu nó đã được mặc nhiên tạo ra từ các tên trường.

Các ImprovedNamingStrategysẽ chuyển đổi CamelCaseđể SNAKE_CASEnơi như EJB3NamingStrategychỉ sử dụng tên bảng không thay đổi.

Nếu không muốn thay đổi chiến lược đặt tên, bạn luôn có thể chỉ định tên cột của mình bằng chữ thường:

@Column(name="testname")

1
Chào, Phil. Bằng cách sử dụng khởi động mùa xuân, tôi đã thêm spring.jpa.hibernate.naming.strategy: org.hibernate.cfg.EJB3NamingStrategy. Nhưng nó có vẻ như không làm việc cho tôi. Bạn có thể giúp tôi được không?
BeeNoisy

Phần quan trọng của câu trả lời là đặt tên bằng chữ thường! Tôi khuyên không nên thay đổi trạng thái nhưng nên đặt tên là chữ thường vì tên cột không phân biệt chữ hoa chữ thường!
loicmathieu.

31

Có vẻ như

@Column (name = "..")

hoàn toàn bị bỏ qua trừ khi có

spring.jpa.hibernate.naming_strategy = org.hibernate.cfg.EJB3NamingStrategy

được chỉ định, vì vậy với tôi đây là một lỗi.

Tôi đã dành vài giờ để tìm hiểu lý do tại sao @Column (name = "..") bị bỏ qua.


4
Tôi đã từng gặp vấn đề tương tự. Tôi đã thêm báo cáo sự cố tại đây: github.com/spring-projects/spring-boot/issues/2129
Kacper86

Cảm ơn rất nhiều. Mất khoảng một ngày để trỏ ứng dụng của tôi đến db hiện có.
Dmitry Erokhin

Nó thực sự không bị bỏ qua, chỉ là chiến lược đặt tên mùa xuân mặc định được áp dụng trên thuộc tính tên đã cho. Đọc câu trả lời của @PhilWebb
Michel Feldheim

16

Chiến lược mặc định cho @Column(name="TestName")sẽ là test_name, đây là hành vi đúng!

Nếu bạn có một cột được đặt tên TestNametrong cơ sở dữ liệu của mình, bạn nên thay đổi chú thích Cột thành @Column(name="testname").

Điều này hoạt động vì cơ sở dữ liệu không quan tâm đến việc bạn đặt tên cho cột là TestName hay tên thử nghiệm ( tên cột không phân biệt chữ hoa chữ thường !! ).

Nhưng hãy cẩn thận, điều tương tự không áp dụng cho tên cơ sở dữ liệu và tên bảng, phân biệt chữ hoa chữ thường trên hệ thống Unix mà phân biệt chữ hoa chữ thường trên hệ thống Windows (thực tế là có lẽ rất nhiều người thức đêm, làm việc trên windows nhưng triển khai trên linux :))


3
1. Thực ra điều đó không đúng, tên cột có thể phân biệt chữ hoa chữ thường tùy thuộc vào cấu hình của cơ sở dữ liệu bạn đang sử dụng ... 2. @ Tên cột - như tên cho thấy phải là nơi cung cấp tên cột cơ sở dữ liệu chứ không phải một số định danh mà một khuôn khổ sẽ thay đổi trong thời gian chạy ..
Kamil

1. Cảm ơn, bạn có thể cho một ví dụ về db ​​trong đó tên cột có phân biệt chữ hoa chữ thường theo mặc định không? 2. Trên thực tế, @Column cung cấp cho chúng ta các tên hợp lý được giải quyết thành tên thực bởi PhysicalNamingStrategy, ít nhất đó có vẻ là những gì tài liệu nói: docs.jboss.org/hibernate/orm/5.1/userguide/html_single/cha Chương/
Orhan

2
1. Xin lỗi, tôi không thể vì tôi không quan tâm cái nào có nó theo mặc định, tôi quan tâm những cài đặt nào được thiết lập bởi DBA trên cái tôi đang sử dụng. 2. Thật không may, điều đó đúng - đó chỉ là ý kiến ​​cá nhân của tôi rằng cách tiếp cận này là sai vì nó buộc tôi phải suy nghĩ hoặc về cách tên sẽ được ánh xạ vào cột ở cuối hoặc sử dụng chiến lược đặt tên nào không liên quan đến tên được cung cấp.
Kamil

1
Đúng, đó sẽ là giải pháp trực quan nhất và tất nhiên tài liệu tốt hơn về điều này sẽ không gây hại.
Orhan

tên cột được đặt rõ ràng trong tất cả các điều kiện phải ghi đè tên cột được tạo ngầm định. Nếu không, đó là một lỗi trong việc triển khai JPA.
jwenting

13

Giải pháp duy nhất phù hợp với tôi là giải pháp được đăng bởi teteArg ở trên. Tôi đang sử dụng Spring Boot 1.4.2 w / Hibernate 5. Cụ thể là

spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl

Để có thêm thông tin chi tiết, tôi sẽ đăng theo dõi cuộc gọi để nó rõ ràng những gì mà Spring đang thực hiện thành Hibernate để thiết lập chiến lược đặt tên.

      at org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl.toPhysicalColumnName(PhysicalNamingStrategyStandardImpl.java:46)
  at org.hibernate.cfg.Ejb3Column.redefineColumnName(Ejb3Column.java:309)
  at org.hibernate.cfg.Ejb3Column.initMappingColumn(Ejb3Column.java:234)
  at org.hibernate.cfg.Ejb3Column.bind(Ejb3Column.java:206)
  at org.hibernate.cfg.Ejb3DiscriminatorColumn.buildDiscriminatorColumn(Ejb3DiscriminatorColumn.java:82)
  at org.hibernate.cfg.AnnotationBinder.processSingleTableDiscriminatorProperties(AnnotationBinder.java:797)
  at org.hibernate.cfg.AnnotationBinder.bindClass(AnnotationBinder.java:561)
  at org.hibernate.boot.model.source.internal.annotations.AnnotationMetadataSourceProcessorImpl.processEntityHierarchies(AnnotationMetadataSourceProcessorImpl.java:245)
  at org.hibernate.boot.model.process.spi.MetadataBuildingProcess$1.processEntityHierarchies(MetadataBuildingProcess.java:222)
  at org.hibernate.boot.model.process.spi.MetadataBuildingProcess.complete(MetadataBuildingProcess.java:265)
  at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.metadata(EntityManagerFactoryBuilderImpl.java:847)
  at org.hibernate.jpa.boot.internal.EntityManagerFactoryBuilderImpl.build(EntityManagerFactoryBuilderImpl.java:874)
  at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60)
  at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:353)
  at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.buildNativeEntityManagerFactory(AbstractEntityManagerFactoryBean.java:373)
  at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:362)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1642)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1579)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
  at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
  at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
  at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
  - locked <0x1687> (a java.util.concurrent.ConcurrentHashMap)
  at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
  at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
  at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1081)
  at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:856)
  at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:542)
  - locked <0x1688> (a java.lang.Object)
  at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:761)
  at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:371)
  at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
  at org.springframework.boot.SpringApplication.run(SpringApplication.java:1186)
  at org.springframework.boot.SpringApplication.run(SpringApplication.java:1175)

6

teteArg , cảm ơn bạn rất nhiều. Chỉ cần một thông tin bổ sung như vậy, mọi người chạm vào câu hỏi này sẽ có thể hiểu tại sao.

Những gì teteArg đã nói được chỉ ra trên Thuộc tính chung của Spring Boot: http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html

Rõ ràng, spring.jpa.hibernate.naming.strategy không phải là thuộc tính được hỗ trợ để triển khai Spring JPA bằng Hibernate 5.


Tôi rất vui khi được giúp bạn
teteArg 7/12/17

4

Hóa ra tôi chỉ phải chuyển đổi @columntên testName thành tất cả các chữ cái nhỏ, vì ban đầu nó nằm trong trường hợp lạc đà.

Mặc dù tôi không thể sử dụng câu trả lời chính thức, nhưng câu hỏi có thể giúp tôi giải quyết vấn đề của mình bằng cách cho tôi biết những gì cần điều tra.

Thay đổi:

@Column(name="testName")
private String testName;

Đến:

@Column(name="testname")
private String testName;

3

Nếu bạn muốn sử dụng @Column (...), thì hãy luôn sử dụng các chữ cái viết thường ngay cả khi cột DB thực của bạn ở dạng camel-case.

Ví dụ: Nếu tên cột DB thực của bạn TestNamethì hãy sử dụng:

  @Column(name="testname") //all small-case

Nếu bạn không thích điều đó, thì chỉ cần thay đổi tên cột DB thực tế thành: test_name


1

Trong trường hợp của tôi, chú thích nằm trên phương thức getter () thay vì chính trường (được chuyển từ ứng dụng kế thừa).

Spring cũng bỏ qua chú thích trong trường hợp này nhưng không phàn nàn. Giải pháp là chuyển nó ra cánh đồng thay vì getter.


1
Cảm ơn các cập nhật. Thông tin có giá trị thực sự.
jwenting
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.