Spring Boot + JPA: annotation du nom de colonne ignorée

121

J'ai une application Spring Boot avec dépendance spring-boot-starter-data-jpa. Ma classe d'entité a une annotation de colonne avec un nom de colonne. Par exemple:

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

SQL généré par cela créé test_namecomme nom de colonne. Après avoir cherché une solution, j'ai trouvé qui a spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.EJB3NamingStrategyrésolu le problème (le nom de la colonne provient de l'annotation de la colonne).

Pourtant, ma question est de savoir pourquoi sans naming_strategy défini sur EJB3NamingStrategyJPA ignore l'annotation de colonne? Peut-être que le dialecte d'hibernation a quelque chose à voir avec ça? Je me connecte à MS SQL 2014 Express et mes journaux contiennent:

Unknown Microsoft SQL Server major version [12] using SQL Server 2000 dialect
Using dialect: org.hibernate.dialect.SQLServerDialect 
Kamil
la source
1
Cette question concerne le nom de colonne fourni explicitement qui est modifié plutôt que ignoré . Il se résume à cet être exécuté au lieu de prévu variante transparente . Hibernate peut en fait ignorer les @Column(name="...")annotations, par exemple lorsque vous utilisez un type d'accès autre que prévu, mais ce n'est pas le cas ici.
Vlastimil Ovčáčík

Réponses:

163

Pour hibernate5, j'ai résolu ce problème en mettant les lignes suivantes dans mon fichier application.properties:

spring.jpa.hibernate.naming.implicit-strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
teteArg
la source
30
spring.jpa.hibernate.naming.physical-strategy = org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl, seule cette propriété est requise pour conserver le nom tel quel.
abhishek ringsia
1
J'ai eu le même problème et l'ajout de ces 2 propriétés l'a résolu pour moi. J'utilise Spring Boot 1.4.3
Johan
1
C'est la seule solution qui a fonctionné pour moi aussi. J'utilise Spring Boot 1.4.2
Sanjiv Jivan
J'utilise Spring Boot 1.5.9.RELEASE, cet article fonctionne pour moi
IcyBrk
Génial ... Je me demandais pourquoi il ignorait mon annotation @Column. Finalement, cela m'a aidé. Pour moi, j'ai l'impression que c'est soit un bogue, soit une fonctionnalité manquante.
Raju Penumatsa
86

Par défaut, Spring utilise org.springframework.boot.orm.jpa.SpringNamingStrategypour générer les noms de table. Il s'agit d'une extension très fine de org.hibernate.cfg.ImprovedNamingStrategy. La tableNameméthode de cette classe reçoit une Stringvaleur source , mais elle ne sait pas si elle provient d'un @Column.nameattribut ou si elle a été générée implicitement à partir du nom de champ.

Le ImprovedNamingStrategysera converti CamelCaseen SNAKE_CASEoù car EJB3NamingStrategyil utilise simplement le nom de la table inchangé.

Si vous ne souhaitez pas modifier la stratégie de dénomination, vous pouvez toujours simplement spécifier le nom de votre colonne en minuscules:

@Column(name="testname")
Phil Webb
la source
1
Salut, Phil. En utilisant Spring Boot, j'ai ajouté spring.jpa.hibernate.naming.strategy: org.hibernate.cfg.EJB3NamingStrategy. Mais ça ne marche pas pour moi. Pouvez-vous m'aider?
BeeNoisy
La partie importante de la réponse est de mettre le nom en minuscules! Je conseille de ne pas changer de stratégie mais de mettre le nom en minuscules car les noms de colonnes sont insensibles à la casse!
loicmathieu
31

Il paraît que

@Column (nom = "..")

est complètement ignoré à moins qu'il n'y ait

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

spécifié, donc pour moi, c'est un bug.

J'ai passé quelques heures à essayer de comprendre pourquoi @Column (name = "..") était ignoré.

ncaralicea
la source
4
J'ai eu le même problème. J'ai ajouté un rapport de problème ici: github.com/spring-projects/spring-boot/issues/2129
Kacper86
Merci beaucoup. J'ai perdu environ une journée pour diriger mon application vers la base de données existante.
Dmitry Erokhin
Ce n'est en fait pas ignoré, seule la stratégie de dénomination de ressort par défaut est appliquée à l'attribut de nom donné. Lire la réponse @PhilWebb
Michel Feldheim
16

La stratégie par défaut pour @Column(name="TestName")sera test_name, c'est un comportement correct!

Si vous avez une colonne nommée TestNamedans votre base de données, vous devez remplacer l'annotation de colonne par @Column(name="testname").

Cela fonctionne parce que la base de données ne se soucie pas si vous nommez votre colonne TestName ou testname (les noms de colonne sont insensibles à la casse !! ).

Mais attention, il n'en va pas de même pour le nom de la base de données et les noms de table, qui sont sensibles à la casse sur les systèmes Unix mais sensibles à la casse sur les systèmes Windows (le fait que beaucoup de gens restaient probablement éveillés la nuit, travaillant sous Windows mais déployant sous Linux :))

Orhan
la source
3
1. En fait, ce n'est pas vrai, les noms de colonne peuvent être sensibles à la casse en fonction de la configuration de la base de données que vous utilisez ... 2. @Column name - comme son nom l'indique, devrait être un endroit pour fournir le nom de la colonne de la base de données, pas un identifiant qui constitue un cadre changera pendant l'exécution ..
Kamil
1. Merci, pouvez-vous donner un exemple de db où les noms de colonne sont sensibles à la casse par défaut? 2. En fait, @Column nous donne des noms logiques qui sont résolus en noms physiques par PhysicalNamingStrategy, du moins cela semble être ce que dit le document: docs.jboss.org/hibernate/orm/5.1/userguide/html_single/chapters/…
Orhan
2
1.Désolé, je ne peux pas car je me fiche de celui qui l'a par défaut, je me soucie des paramètres définis par DBA sur celui que j'utilise. 2. C'est malheureusement vrai - c'est juste mon opinion personnelle que cette approche est erronée car elle m'oblige à réfléchir à la façon dont le nom serait mappé à la colonne à la fin, ou à la stratégie de dénomination à utiliser qui ne touche pas les noms fournis.
Kamil
1
Certes, ce serait la solution la plus intuitive, et bien sûr, une meilleure documentation à ce sujet ne ferait pas de mal.
Orhan
un nom de colonne explicitement défini doit dans toutes les conditions remplacer un nom généré implicitement. Si ce n'est pas le cas, c'est un bogue dans l'implémentation JPA.
jwenting
13

La seule solution qui a fonctionné pour moi était celle publiée par teteArg ci-dessus. Je suis sur Spring Boot 1.4.2 avec Hibernate 5. À savoir

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

Pour plus d'informations, je publie la trace des appels afin que ce que Spring appelle clairement dans Hibernate pour configurer la stratégie de dénomination.

      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)
Sanjiv Jivan
la source
6

teteArg , merci beaucoup. Juste une information supplémentaire, donc tout le monde se heurtant à cette question sera en mesure de comprendre pourquoi.

Ce que teteArg a dit est indiqué sur les propriétés communes de Spring Boot: http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html

Apparemment, spring.jpa.hibernate.naming.strategy n'est pas une propriété prise en charge pour l'implémentation Spring JPA utilisant Hibernate 5.

Roméo Jr Maranan
la source
Je suis heureux de vous aider
teteArg
4

Il s'avère que je dois juste convertir le @columnnom testName en toutes les petites lettres, car il était initialement dans le cas du chameau.

Bien que je n'ai pas pu utiliser la réponse officielle, la question a pu m'aider à résoudre mon problème en me laissant savoir sur quoi enquêter.

Changement:

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

À:

@Column(name="testname")
private String testName;
Mohammad Cali
la source
3

Si vous voulez utiliser @Column (...), utilisez toujours des lettres minuscules même si votre colonne DB réelle est en cas de chameau.

Exemple: si votre nom de colonne DB réel est TestNamealors utilisez:

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

Si vous n'aimez pas cela, changez simplement le nom réel de la colonne DB en: test_name

doyen
la source
1

Dans mon cas, l'annotation était sur la méthode getter () au lieu du champ lui-même (porté à partir d'une application héritée).

Spring ignore également l'annotation dans ce cas, mais ne se plaint pas. La solution était de le déplacer sur le terrain au lieu du getter.

java-addict301
la source
1
Merci pour la mise à jour. Des informations précieuses en effet.
jwenting