Spring Boot + JPA: аннотация имени столбца игнорируется


122

У меня есть приложение Spring Boot с зависимостью spring-boot-starter-data-jpa. В моем классе сущности есть аннотация столбца с именем столбца. Например:

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

SQL, сгенерированный этим, создается test_nameкак имя столбца. После поиска решения я обнаружил, что spring.jpa.hibernate.naming_strategy=org.hibernate.cfg.EJB3NamingStrategyрешает проблему (имя столбца взято из аннотации столбца).

Тем не менее, мой вопрос в том, почему без naming_strategy, установленного в EJB3NamingStrategyJPA, игнорируется аннотация столбца? Может диалект гибернации как-то здесь? Я подключаюсь к MS SQL 2014 Express, и мои журналы содержат:

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

1
Этот вопрос о явном виде при условии , имя столбца быть изменен , а не игнорировали . Все сводится к тому, что этот вариант выполняется вместо ожидаемого прозрачного варианта . Hibernate может фактически игнорировать @Column(name="...")аннотацию, например, когда вы используете тип доступа, отличный от ожидаемого, но здесь это не так.
Vlastimil Ovčáčík

Ответы:


164

Для hibernate5 я решил эту проблему, поместив следующие строки в свой файл 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

30
spring.jpa.hibernate.naming.physical-strategy = org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl, только это свойство требуется для сохранения имени как есть.
abhishek Ringsia 09

1
У меня была такая же проблема, и добавление этих двух свойств решило ее для меня. Я использую Spring Boot 1.4.3
Йохан,

1
Это единственное решение, которое сработало и для меня. Я использую Spring Boot 1.4.2
Санджив Дживан

Я использую Spring Boot 1.5.9.RELEASE, этот пост у меня работает
IcyBrk

Потрясающе .. Мне было интересно, почему он игнорирует мою аннотацию @Column. Наконец это помогло мне. Мне кажется, что это либо ошибка, либо отсутствует функциональность.
Raju Penumatsa

86

По умолчанию Spring использует org.springframework.boot.orm.jpa.SpringNamingStrategyдля генерации имен таблиц. Это очень тонкое расширение org.hibernate.cfg.ImprovedNamingStrategy. tableNameМетод в этом классе передается исходное Stringзначение , но не знает , если она исходит от @Column.nameатрибута или если оно неявно генерируется из имени поля.

Преобразование ImprovedNamingStrategyбудет происходить CamelCaseв том месте, SNAKE_CASEгде EJB3NamingStrategyпросто используется имя таблицы без изменений.

Если вы не хотите менять стратегию именования, вы всегда можете просто указать имя столбца в нижнем регистре:

@Column(name="testname")

1
Привет, Фил. Используя весеннюю загрузку, я добавил spring.jpa.hibernate.naming.strategy: org.hibernate.cfg.EJB3NamingStrategy. Но, похоже, у меня не работает. Вы можете помочь мне?
BeeNoisy

Важная часть ответа - ввести имя в нижнем регистре! Я советую не изменять состояние, а указывать имя в нижнем регистре, так как имя столбца нечувствительно к регистру!
loicmathieu

31

Кажется, что

@Column (имя = "..")

полностью игнорируется, если нет

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

указано, так что для меня это ошибка.

Я потратил несколько часов, пытаясь понять, почему @Column (name = "..") игнорируется.


4
У меня такая же проблема. Я добавил отчет о проблеме здесь: github.com/spring-projects/spring-boot/issues/2129
Kacper86

Большое спасибо. Потеряли около дня, чтобы указать моему приложению на существующую базу данных.
Дмитрий Ерохин

На самом деле это не игнорируется, просто к заданному атрибуту name применяется стратегия именования Spring по умолчанию. Прочтите ответ @PhilWebb
Мишель

16

Стратегия по умолчанию для @Column(name="TestName") будет test_name, это правильное поведение!

Если у вас есть столбец с именем TestName в вашей базе данных, вы должны изменить аннотацию столбца на @Column(name="testname").

Это работает, потому что базе данных безразлично, назовете ли вы свой столбец TestName или testname ( имена столбцов нечувствительны к регистру !! ).

Но будьте осторожны, то же самое не относится к имени базы данных и именам таблиц, которые чувствительны к регистру в системах Unix, но чувствительны к регистру в системах Windows (тот факт, что, вероятно, не давал уснуть по ночам, работая с Windows, но развертываясь в Linux. :))


3
1. На самом деле это не так, имена столбцов могут быть чувствительны к регистру в зависимости от конфигурации базы данных, которую вы используете ... 2. Имя @Column - как следует из названия, должно быть место для указания имени столбца базы данных, а не какого-либо идентификатора, который фреймворк будет меняться во время выполнения ..
Камил

1. Спасибо, вы можете привести пример базы данных, в которой имена столбцов по умолчанию чувствительны к регистру? 2. На самом деле @Column дает нам логические имена, которые преобразуются в физические имена с помощью PhysicalNamingStrategy, по крайней мере, это похоже на то, что говорится в документе: docs.jboss.org/hibernate/orm/5.1/userguide/html_single/chapters/…
Orhan

2
1. Извините, я не могу, так как мне все равно, у кого он установлен по умолчанию, мне все равно, какие настройки установлены администратором баз данных на том, который я использую. 2. Это, к сожалению, правда - это просто мое личное мнение, что этот подход неверен, поскольку он заставляет меня думать о том, как имя будет отображаться в столбец в конце, или какую стратегию именования использовать, которая не затрагивает предоставленные имена.
Камил

1
Конечно, это было бы наиболее интуитивно понятным решением, и, конечно, лучшая документация по нему не помешала бы.
Orhan

явно заданное имя столбца должно при всех условиях иметь приоритет над неявно созданным. Если нет, то это ошибка в реализации JPA.
jwent

13

Единственное решение, которое сработало для меня, было опубликовано teteArg выше. Я использую Spring Boot 1.4.2 с Hibernate 5. А именно

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

Для дополнительной информации я публикую трассировку вызовов, чтобы было понятно, какие вызовы Spring делает в Hibernate для настройки стратегии именования.

      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 , большое вам спасибо. Просто дополнительная информация, чтобы каждый, кто столкнулся с этим вопросом, мог понять, почему.

То, что сказал teteArg, указано в общих свойствах Spring Boot: http://docs.spring.io/spring-boot/docs/current/reference/html/common-application-properties.html

По-видимому, spring.jpa.hibernate.naming.strategy не является поддерживаемым свойством для реализации Spring JPA с использованием Hibernate 5.


Я рад помочь вам
teteArg

4

Оказывается, мне просто нужно преобразовать @columnимя testName во все маленькие буквы, так как изначально оно было в верблюжьем регистре.

Хотя я не смог воспользоваться официальным ответом, этот вопрос помог мне решить мою проблему, дав мне понять, что нужно исследовать.

Изменить:

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

Для того, чтобы:

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

3

Если вы хотите использовать @Column (...), всегда используйте маленькие буквы, даже если ваш фактический столбец БД находится в верблюжьем регистре.

Пример: если ваше фактическое имя столбца БД, TestNameтогда используйте:

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

Если вам это не нравится, просто измените фактическое имя столбца БД на: test_name


1

В моем случае аннотация была в методе getter (), а не в самом поле (перенесенном из устаревшего приложения).

Spring игнорирует аннотацию и в этом случае, но не жалуется. Решением было переместить его в поле вместо геттера.


1
Спасибо за обновление. Действительно ценная информация.
jwent
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.