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.