ImprovedNamingStrategy 不再适用于 Hibernate 5
ImprovedNamingStrategy no longer working in Hibernate 5
我有简单的 spring-jpa 配置,其中配置了 Hibernate 的 ImprovedNamingStrategy
。这意味着如果我的实体 class 有一个变量 userName
,那么 Hibernate 应该将它转换为 user_name
以便查询数据库。但是在我升级到 Hibernate 5 之后,这个命名转换停止工作了。我收到错误:
ERROR: Unknown column 'user0_.userName' in 'field list'
这是我的 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();
}
}
这是我的实体 class:
@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 注释中明确命名我的数据库字段。我想要我的配置可以隐式地将驼峰大小写转换为下划线。
请指导。
刚刚解决了这个问题,当使用 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
class 在 Hibernate 5 中已弃用。
感谢您发布自己的解决方案。设置 Hibernate 5 命名策略对我帮助很大!
pre-Hibernate 5.0 的 hibernate.ejb.naming_strategy
属性 似乎分为两部分:
hibernate.physical_naming_strategy
hibernate.implicit_naming_strategy
这些属性的值没有像 hibernate.ejb.naming_strategy
那样实现 NamingStrategy
接口。有两个用于这些目的的新界面:
org.hibernate.boot.model.naming.PhysicalNamingStrategy
org.hibernate.boot.model.naming.ImplicitNamingStrategy
Hibernate 5 仅提供了一种 PhysicalNamingStrategy
(PhysicalNamingStrategyStandardImpl
) 的实现,它假定物理标识符名称与逻辑名称相同。
ImplicitNamingStrategy
有多种实现,但我发现 none 等同于旧的 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命名策略设置为以前的版本设置是一个陷阱。
希望这对您有所帮助:
hibernate.implicit_naming_strategy=....隐式命名策略
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);
}
}
感谢并 +1 Samuel Andrés 的非常有用的答案,但是避免手写蛇套逻辑可能是个好主意。这是相同的解决方案,使用番石榴。
它假定您的实体名称写在 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()
);
}
}
感谢 post。升级会破坏 table 和列名策略,这有点让人恼火。除了从 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);
}
}
没有 Guava 和 Apache 实用程序
public class PhysicalNamingStrategyImpl extends PhysicalNamingStrategyStandardImpl {
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
return context.getIdentifierHelper().toIdentifier(
name.getText().replaceAll("((?!^)[^_])([A-Z])", "_").toLowerCase(),
name.isQuoted()
);
}
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
return context.getIdentifierHelper().toIdentifier(
name.getText().replaceAll("((?!^)[^_])([A-Z])", "_").toLowerCase(),
name.isQuoted()
);
}
}
每个答案都通过实施 PhysicalNamingStrategy
来发布解决方案,但您需要(并且应该做的)就是实施 ImplicitNamingStrategy
。
When an entity does not explicitly name the database table that it maps to, we need to implicitly determine that table name. Or when a particular attribute does not explicitly name the database column that it maps to, we need to implicitly determine that column name. There are examples of the role of the org.hibernate.boot.model.naming.ImplicitNamingStrategy contract to determine a logical name when the mapping did not provide an explicit name.
代码可以像这样简单(使用其他答案中的原始 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);
}
}
而不是
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
我有简单的 spring-jpa 配置,其中配置了 Hibernate 的 ImprovedNamingStrategy
。这意味着如果我的实体 class 有一个变量 userName
,那么 Hibernate 应该将它转换为 user_name
以便查询数据库。但是在我升级到 Hibernate 5 之后,这个命名转换停止工作了。我收到错误:
ERROR: Unknown column 'user0_.userName' in 'field list'
这是我的 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();
}
}
这是我的实体 class:
@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 注释中明确命名我的数据库字段。我想要我的配置可以隐式地将驼峰大小写转换为下划线。
请指导。
刚刚解决了这个问题,当使用 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
class 在 Hibernate 5 中已弃用。
感谢您发布自己的解决方案。设置 Hibernate 5 命名策略对我帮助很大!
pre-Hibernate 5.0 的 hibernate.ejb.naming_strategy
属性 似乎分为两部分:
hibernate.physical_naming_strategy
hibernate.implicit_naming_strategy
这些属性的值没有像 hibernate.ejb.naming_strategy
那样实现 NamingStrategy
接口。有两个用于这些目的的新界面:
org.hibernate.boot.model.naming.PhysicalNamingStrategy
org.hibernate.boot.model.naming.ImplicitNamingStrategy
Hibernate 5 仅提供了一种 PhysicalNamingStrategy
(PhysicalNamingStrategyStandardImpl
) 的实现,它假定物理标识符名称与逻辑名称相同。
ImplicitNamingStrategy
有多种实现,但我发现 none 等同于旧的 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命名策略设置为以前的版本设置是一个陷阱。
希望这对您有所帮助:
hibernate.implicit_naming_strategy=....隐式命名策略 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);
}
}
感谢并 +1 Samuel Andrés 的非常有用的答案,但是避免手写蛇套逻辑可能是个好主意。这是相同的解决方案,使用番石榴。
它假定您的实体名称写在 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()
);
}
}
感谢 post。升级会破坏 table 和列名策略,这有点让人恼火。除了从 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);
}
}
没有 Guava 和 Apache 实用程序
public class PhysicalNamingStrategyImpl extends PhysicalNamingStrategyStandardImpl {
public Identifier toPhysicalTableName(Identifier name, JdbcEnvironment context) {
return context.getIdentifierHelper().toIdentifier(
name.getText().replaceAll("((?!^)[^_])([A-Z])", "_").toLowerCase(),
name.isQuoted()
);
}
public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) {
return context.getIdentifierHelper().toIdentifier(
name.getText().replaceAll("((?!^)[^_])([A-Z])", "_").toLowerCase(),
name.isQuoted()
);
}
}
每个答案都通过实施 PhysicalNamingStrategy
来发布解决方案,但您需要(并且应该做的)就是实施 ImplicitNamingStrategy
。
When an entity does not explicitly name the database table that it maps to, we need to implicitly determine that table name. Or when a particular attribute does not explicitly name the database column that it maps to, we need to implicitly determine that column name. There are examples of the role of the org.hibernate.boot.model.naming.ImplicitNamingStrategy contract to determine a logical name when the mapping did not provide an explicit name.
代码可以像这样简单(使用其他答案中的原始 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);
}
}
而不是
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