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. Я хочу свою конфигурацию, которая может неявно преобразовать случай верблюда в подчеркивание.
Пожалуйста, руководство.
8 ответов
Только что выяснили проблему, конфигурация абсолютно подходит при использовании версии 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. Наконец-то все заработало нормально.
в Hibernate NamingStrategy
класс устарел в Hibernate 5.
Спасибо за размещение вашего собственного решения. Мне очень помогает установить стратегию именования 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 в качестве настроек предыдущей версии.
Вместо
jpaProperties.put("hibernate.ejb.naming_strategy",
"org.hibernate.cfg.ImprovedNamingStrategy");
переход на новую стратегию физического именования и новую реализацию
CamelCaseToUnderscoresNamingStrategy
, который ведет себя как старый
ImprovedNamingStrategy
:
jpaProperties.put("hibernate.physical_naming_strategy",
"org.hibernate.boot.model.naming.CamelCaseToUnderscoresNamingStrategy");
Доступно в Hibernate 5.6.1.FINAL
Спасибо и +1 Самуэлю Андресу за очень полезный ответ, однако, вероятно, будет хорошей идеей избегать написанной от руки логики с изображением змеиной оболочки. Вот то же решение, использующее гуаву.
Предполагается, что имена вашей сущности записаны в StandardJavaClassFormat
и имена столбцов в standardJavaFieldFormat
Надеюсь, это спасет некоторых людей, приходящих сюда в будущем, погуглить:-)
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()
);
}
}
Спасибо за этот пост. Немного раздражает, что обновление нарушает стратегию имен таблиц и столбцов. Вместо того, чтобы копировать логику из 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);
}
}
Каждый ответ публикует решение путем реализации PhysicalNamingStrategy
, но все, что вам нужно (и нужно сделать), это реализовать ImplicitNamingStrategy
,
Когда сущность не называет явно таблицу базы данных, с которой она сопоставляется, нам необходимо неявно определить это имя таблицы. Или когда конкретный атрибут не указывает явно столбец базы данных, в который он отображается, нам нужно неявно определить имя этого столбца. Существуют примеры роли контракта org.hibernate.boot.model.naming.ImplicitNamingStrategy для определения логического имени, когда сопоставление не предоставило явного имени.
И код может быть так просто, как это (используя оригинальный addUnderscores
как и в других ответах):
public class ImplicitNamingStrategyImpl extends ImplicitNamingStrategyJpaCompliantImpl {
@Override
protected Identifier toIdentifier(String stringForm, MetadataBuildingContext buildingContext) {
return super.toIdentifier(addUnderscores(stringForm), buildingContext);
}
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);
}
}
Надеюсь это поможет:
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);
}
}
Нет утилит Guava и Apache
public class PhysicalNamingStrategyImpl extends PhysicalNamingStrategyStandardImpl {
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
return context.getIdentifierHelper().toIdentifier(
name.getText().replaceAll("((?!^)[^_])([A-Z])", "$1_$2").toLowerCase(),
name.isQuoted()
);
}
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
return context.getIdentifierHelper().toIdentifier(
name.getText().replaceAll("((?!^)[^_])([A-Z])", "$1_$2").toLowerCase(),
name.isQuoted()
);
}
}