ImprovedNamingStrategy больше не работает в Hibernate 5
У меня есть простая конфигурация spring -jpa, где я настроил Hibernate ImprovedNamingStrategy
. Это означает, что если класс объекта имеет переменную userName
, то Hibernate должен преобразовать его в user_name
для запроса базы данных. Но это преобразование имен перестало работать после того, как я обновился до Hibernate 5. Я получаю сообщение об ошибке:
ОШИБКА: Неизвестный столбец 'user0_.userName' в 'списке полей'
Это моя конфигурация Hibernate:
@Configuration
@EnableJpaRepositories("com.springJpa.repository")
@EnableTransactionManagement
public class DataConfig {
@Bean
public DataSource dataSource(){
DriverManagerDataSource ds = new DriverManagerDataSource();
ds.setDriverClassName("com.mysql.jdbc.Driver");
ds.setUrl("jdbc:mysql://localhost:3306/test");
ds.setUsername("root");
ds.setPassword("admin");
return ds;
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(){
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setShowSql(Boolean.TRUE);
vendorAdapter.setDatabase(Database.MYSQL);
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
factory.setJpaVendorAdapter(vendorAdapter);
factory.setDataSource(dataSource());
factory.setPackagesToScan("com.springJpa.entity");
Properties jpaProperties = new Properties();
jpaProperties.put("hibernate.ejb.naming_strategy","org.hibernate.cfg.ImprovedNamingStrategy");
jpaProperties.put("hibernate.dialect","org.hibernate.dialect.MySQL5InnoDBDialect");
factory.setJpaProperties(jpaProperties);
factory.afterPropertiesSet();
return factory;
}
@Bean
public SharedEntityManagerBean entityManager() {
SharedEntityManagerBean entityManager = new SharedEntityManagerBean();
entityManager.setEntityManagerFactory(entityManagerFactory().getObject());
return entityManager;
}
@Bean
public PlatformTransactionManager transactionManager() {
JpaTransactionManager txManager = new JpaTransactionManager();
txManager.setEntityManagerFactory(entityManagerFactory().getObject());
return txManager;
}
@Bean
public ImprovedNamingStrategy namingStrategy(){
return new ImprovedNamingStrategy();
}
}
Это мой класс Entity:
@Getter
@Setter
@Entity
@Table(name="user")
public class User{
@Id
@GeneratedValue
private Long id;
private String userName;
private String email;
private String password;
private String role;
}
Я не хочу явно указывать поля моей базы данных в аннотациях @Column. Мне нужна моя конфигурация, которая может неявно преобразовывать регистр верблюда в подчеркивание.
Пожалуйста, направляйте.
Ответы
Ответ 1
Просто выяснив проблему, конфигурация абсолютно прекрасна при использовании версии Hibernate < 5.0, но не для Hibernate >= 5.0.
Я использовал Hibernate 5.0.0.Final с Spring 4.2.0.RELEASE. Я думаю, что Hibernate 5 не полностью совместим с Spring 4.2. Я просто понизил Hibernate до 4.2.1. Final, и все стало нормально работать.
Класс Hibernate NamingStrategy
устарел в Hibernate 5.
Ответ 2
Спасибо, что разместили свое собственное решение. Мне очень помогает установить стратегию именования Hibernate 5!
Свойство hibernate.ejb.naming_strategy
для pre-Hibernate 5.0 кажется разделенным на две части:
-
hibernate.physical_naming_strategy
-
hibernate.implicit_naming_strategy
Значения этих свойств не реализуют интерфейс NamingStrategy
, как и hibernate.ejb.naming_strategy
. Для этих целей есть два новых интерфейса:
-
org.hibernate.boot.model.naming.PhysicalNamingStrategy
-
org.hibernate.boot.model.naming.ImplicitNamingStrategy
Hibernate 5 предоставляет только одну реализацию PhysicalNamingStrategy
(PhysicalNamingStrategyStandardImpl
), которая предполагает, что имена физических идентификаторов совпадают с логическими.
Существует несколько реализаций ImplicitNamingStrategy
, но я не нашел ни одного, эквивалентного старому ImprovedNamingStrategy
. (См.: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl
)
Итак, я внедрил свой собственный PhysicalNamingStrategy
, который очень прост:
public class PhysicalNamingStrategyImpl extends PhysicalNamingStrategyStandardImpl implements Serializable {
public static final PhysicalNamingStrategyImpl INSTANCE = new PhysicalNamingStrategyImpl();
@Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
return new Identifier(addUnderscores(name.getText()), name.isQuoted());
}
@Override
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
return new Identifier(addUnderscores(name.getText()), name.isQuoted());
}
protected static String addUnderscores(String name) {
final StringBuilder buf = new StringBuilder( name.replace('.', '_') );
for (int i=1; i<buf.length()-1; i++) {
if (
Character.isLowerCase( buf.charAt(i-1) ) &&
Character.isUpperCase( buf.charAt(i) ) &&
Character.isLowerCase( buf.charAt(i+1) )
) {
buf.insert(i++, '_');
}
}
return buf.toString().toLowerCase(Locale.ROOT);
}
}
Обратите внимание, что метод addUnderscores()
относится к оригиналу org.hibernate.cfg.ImprovedNamingStrategy
.
Затем я установил эту физическую стратегию в файл persistence.xml:
<property name="hibernate.physical_naming_strategy" value="my.package.PhysicalNamingStrategyImpl" />
Это ловушка для установки стратегии именования Hibernate 5 в качестве настроек предыдущей версии.
Ответ 3
Спасибо и +1 Самуэлю Андресу за очень полезный ответ, однако, вероятно, это хорошая идея избежать рукописной логики змеиного корпуса. Вот такое же решение, используя Guava.
Предполагается, что имена ваших сущностей записываются в именах StandardJavaClassFormat
и столбцов в standardJavaFieldFormat
Надеюсь, это спасет некоторых людей, пришедших сюда в будущем, в какой-то googling: -)
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
import static com.google.common.base.CaseFormat.*;
public class SnakeCaseNamingStrategy extends PhysicalNamingStrategyStandardImpl {
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
return new Identifier(
UPPER_CAMEL.to(LOWER_UNDERSCORE, name.getText()),
name.isQuoted()
);
}
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
return new Identifier(
LOWER_CAMEL.to(LOWER_UNDERSCORE, name.getText()),
name.isQuoted()
);
}
}
Ответ 4
надеюсь, что это поможет:
hibernate.implicit_naming_strategy =.... ImplicitNamingStrategy
hibernate.physical_naming_strategy =.... PhysicalNamingStrategyImpl
и вот код (просто переустановленный из существующего кода):
import java.io.Serializable;
import java.util.Locale;
import org.hibernate.boot.model.naming.Identifier;
import org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl;
import org.hibernate.engine.jdbc.env.spi.JdbcEnvironment;
public class PhysicalNamingStrategyImpl extends PhysicalNamingStrategyStandardImpl implements Serializable {
public static final PhysicalNamingStrategyImpl INSTANCE = new PhysicalNamingStrategyImpl();
@Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
return new Identifier(addUnderscores(name.getText()), name.isQuoted());
}
@Override
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
return new Identifier(addUnderscores(name.getText()), name.isQuoted());
}
protected static String addUnderscores(String name) {
final StringBuilder buf = new StringBuilder( name.replace('.', '_') );
for (int i=1; i<buf.length()-1; i++) {
if (
Character.isLowerCase( buf.charAt(i-1) ) &&
Character.isUpperCase( buf.charAt(i) ) &&
Character.isLowerCase( buf.charAt(i+1) )
) {
buf.insert(i++, '_');
}
}
return buf.toString().toLowerCase(Locale.ROOT);
}
}
Ответ 5
спасибо за этот пост. Мало того, что обновление разрушает стратегию имен таблиц и столбцов. Вместо копирования логики из ImprovedNamingStrategy
вы также можете использовать делегирование.
public class TableNamingStrategy extends PhysicalNamingStrategyStandardImpl {
private static final String TABLE_PREFIX = "APP_";
private static final long serialVersionUID = 1L;
private static final ImprovedNamingStrategy STRATEGY_INSTANCE = new ImprovedNamingStrategy();
@Override
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
return new Identifier(classToTableName(name.getText()), name.isQuoted());
}
@Override
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
return new Identifier(STRATEGY_INSTANCE.classToTableName(name.getText()), name.isQuoted());
}
private String classToTableName(String className) {
return STRATEGY_INSTANCE.classToTableName(TABLE_PREFIX + className);
}
}