java - hibernate naming strategy
La estrategia de nombres mejorada ya no funciona en Hibernate 5 (7)
Tengo una configuración simple de spring-jpa donde configuré
ImprovedNamingStrategy
de Hibernate.
Esto significa que si mi clase de entidad tiene una variable
userName
, Hibernate debería convertirla en
user_name
para consultar la base de datos.
Pero esta conversión de nombres dejó de funcionar después de actualizar a Hibernate 5. Recibo el error:
ERROR: columna desconocida ''user0_.userName'' en ''lista de campos''
Esta es mi configuración de 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();
}
}
Esta es mi clase de entidad:
@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;
}
No quiero nombrar explícitamente los campos de mi base de datos dentro de las anotaciones @Column. Quiero mi configuración que implícitamente puede convertir el caso de camello en subrayado.
Por favor guía.
Acabo de resolver el problema, la configuración es absolutamente buena cuando se usa una versión de Hibernate <5.0 pero no para Hibernate> = 5.0.
Estaba usando Hibernate 5.0.0.Final con Spring 4.2.0.RELEASE. Supongo que el Hibernate 5 no es totalmente compatible con Spring 4.2. Acabo de bajar Hibernate a 4.2.1. Final y las cosas comenzaron a funcionar bien.
La clase
NamingStrategy
de Hibernate está en desuso en Hibernate 5.
Cada respuesta publica una solución implementando
PhysicalNamingStrategy
, pero todo lo que necesita (y debe hacer) es implementar
ImplicitNamingStrategy
.
Cuando una entidad no nombra explícitamente la tabla de la base de datos a la que se asigna, necesitamos determinar implícitamente ese nombre de tabla. O cuando un atributo particular no nombra explícitamente la columna de la base de datos a la que se asigna, necesitamos determinar implícitamente ese nombre de columna. Hay ejemplos del rol del contrato org.hibernate.boot.model.naming.ImplicitNamingStrategy para determinar un nombre lógico cuando la asignación no proporcionó un nombre explícito.
Y el código puede ser tan fácil como esto (usando los
addUnderscores
originales como en otras respuestas):
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);
}
}
Gracias por publicar su propia solución. ¡Me ayuda mucho establecer la estrategia de nombres de Hibernate 5!
La propiedad
hibernate.ejb.naming_strategy
de pre-Hibernate 5.0 parece dividida en dos partes:
-
hibernate.physical_naming_strategy
-
hibernate.implicit_naming_strategy
Los valores de estas propiedades no implementan la interfaz
NamingStrategy
como
hibernate.ejb.naming_strategy
.
Hay dos nuevas interfaces para estos fines:
-
org.hibernate.boot.model.naming.PhysicalNamingStrategy
-
org.hibernate.boot.model.naming.ImplicitNamingStrategy
Hibernate 5 proporciona solo una implementación de
PhysicalNamingStrategy
(
PhysicalNamingStrategyStandardImpl
) que supone que los nombres de los identificadores físicos son los mismos que los lógicos.
Hay varias implementaciones de
ImplicitNamingStrategy
pero no encontré ninguna equivalente a la antigua
ImprovedNamingStrategy
.
(Ver:
org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl
)
Entonces, implementé mi propia
PhysicalNamingStrategy
que es muy simple:
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);
}
}
Tenga en cuenta que el método
addUnderscores()
es del
org.hibernate.cfg.ImprovedNamingStrategy
original.
Luego, configuro esta estrategia física en el archivo persistence.xml:
<property name="hibernate.physical_naming_strategy" value="my.package.PhysicalNamingStrategyImpl" />
Es una trampa establecer la estrategia de nombres de Hibernate 5 como configuración de la versión anterior.
Sin utilidades de guayaba y 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()
);
}
}
espero que esto ayude:
hibernate.implicit_naming_strategy = .... ImplicitNamingStrategy hibernate.physical_naming_strategy = .... PhysicalNamingStrategyImpl
y aquí está el código (solo reordenado del código existente):
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);
}
}
gracias por ese post
Poco molesto que la actualización rompe la tabla y la estrategia de nombre de columna.
En lugar de copiar la lógica de
ImprovedNamingStrategy
, también podría usar la delegación.
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);
}
}
Gracias y +1 a Samuel Andrés por la útil respuesta , sin embargo, probablemente sea una buena idea evitar la lógica de la carcasa de la serpiente escrita a mano. Aquí está la misma solución, usando guayaba.
Asume que los nombres de sus entidades están escritos en
StandardJavaClassFormat
y los nombres de columna en
standardJavaFieldFormat
Espero que esto ahorre a algunas personas que vienen aquí en el futuro a buscar en Google :-)
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()
);
}
}