Java 改进的命名策略不再适用于 Hibernate 5
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/32437202/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ImprovedNamingStrategy no longer working in Hibernate 5
提问by Anup
I have simple spring-jpa configuration where I have configured Hibernate's ImprovedNamingStrategy
. This means if my entity class has a variable userName
, then Hibernate should convert it to user_name
for querying the database. But this naming conversion stopped working after I upgraded to Hibernate 5. I am getting the error:
我有简单的 spring-jpa 配置,我在其中配置了 Hibernate 的ImprovedNamingStrategy
. 这意味着如果我的实体类有一个变量userName
,那么 Hibernate 应该将它转换user_name
为查询数据库。但是这个命名转换在我升级到 Hibernate 5 后停止工作。我收到错误:
ERROR: Unknown column 'user0_.userName' in 'field list'
错误:“字段列表”中的未知列“user0_.userName”
This is my Hibernate config:
这是我的休眠配置:
@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();
}
}
This is my Entity 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;
}
I don't want to explicitly name my database fields within the @Column annotations. I want my configuration which can implicitly convert camel case to underscore.
我不想在 @Column 注释中明确命名我的数据库字段。我希望我的配置可以将驼峰式大小写隐式转换为下划线。
Please guide.
请指导。
采纳答案by Anup
Just figured out the issue, the configuration is absolutely fine when using a Hibernate version < 5.0 but not for Hibernate >= 5.0.
刚刚发现问题,使用 Hibernate 版本 < 5.0 时配置绝对没问题,但不适用于 Hibernate >= 5.0。
I was using Hibernate 5.0.0.Final with Spring 4.2.0.RELEASE. I guess the Hibernate 5 is not fully compatible with Spring 4.2 . I just downgraded Hibernate to 4.2.1.Final and things started working fine.
我使用的是 Hibernate 5.0.0.Final 和 Spring 4.2.0.RELEASE。我猜 Hibernate 5 与 Spring 4.2 不完全兼容。我刚刚将 Hibernate 降级到 4.2.1.Final 并且一切开始正常。
Hibernate's NamingStrategy
class is deprecated in Hibernate 5.
Hibernate 的NamingStrategy
类在 Hibernate 5 中已被弃用。
回答by Samuel Andrés
Thanks for posting your own solution. It helps me so much to set Hibernate 5 naming strategy!
感谢您发布您自己的解决方案。设置 Hibernate 5 命名策略对我有很大帮助!
The hibernate.ejb.naming_strategy
property of pre-Hibernate 5.0 seems split into two parts:
hibernate.ejb.naming_strategy
Pre-Hibernate 5.0的属性似乎分为两部分:
hibernate.physical_naming_strategy
hibernate.implicit_naming_strategy
hibernate.physical_naming_strategy
hibernate.implicit_naming_strategy
The values of these properties do not implement the NamingStrategy
interface as did hibernate.ejb.naming_strategy
. There are two new interfaces for these purposes:
这些属性的值NamingStrategy
不像以前那样实现接口hibernate.ejb.naming_strategy
。有两个用于这些目的的新接口:
org.hibernate.boot.model.naming.PhysicalNamingStrategy
org.hibernate.boot.model.naming.ImplicitNamingStrategy
org.hibernate.boot.model.naming.PhysicalNamingStrategy
org.hibernate.boot.model.naming.ImplicitNamingStrategy
Hibernate 5 provides only one implementation of PhysicalNamingStrategy
(PhysicalNamingStrategyStandardImpl
) that assumes physical identifier names are the same as logical ones.
Hibernate 5 只提供了PhysicalNamingStrategy
( PhysicalNamingStrategyStandardImpl
) 的一种实现,它假定物理标识符名称与逻辑标识符名称相同。
There are several implementations of ImplicitNamingStrategy
but I found none equivalent to the old ImprovedNamingStrategy
. (See: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl
)
有几种实现,ImplicitNamingStrategy
但我发现没有一个等同于旧的ImprovedNamingStrategy
. (参见:org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyHbmImpl
)
So, I implemented my own PhysicalNamingStrategy
which is very simple:
所以,我实现了我自己的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);
}
}
Note that the addUnderscores()
method is from the original org.hibernate.cfg.ImprovedNamingStrategy
.
请注意,该addUnderscores()
方法来自原始org.hibernate.cfg.ImprovedNamingStrategy
.
Then, I set this physical strategy into the persistence.xml file :
然后,我将此物理策略设置到persistence.xml 文件中:
<property name="hibernate.physical_naming_strategy" value="my.package.PhysicalNamingStrategyImpl" />
It is a trap to set Hibernate 5 naming strategy as previous version settings.
将 Hibernate 5 命名策略设置为以前版本设置是一个陷阱。
回答by Ignacio
hope this helps:
希望这可以帮助:
hibernate.implicit_naming_strategy=....ImplicitNamingStrategy hibernate.physical_naming_strategy=....PhysicalNamingStrategyImpl
hibernate.implicit_naming_strategy=..ImplicitNamingStrategy hibernate.physical_naming_strategy=..PhysicalNamingStrategyImpl
and here is the code (just re-arrenged from existing code):
这是代码(只是从现有代码重新排列):
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);
}
}
回答by davnicwil
Thanks and +1 to Samuel Andrés for the very helpful answer, however it's probably a good idea to avoid the hand-written snake-casing logic. Here is the same solution, using Guava.
感谢塞缪尔·安德烈斯 (Samuel Andrés) 的 +1 非常有用的答案,但是避免手写蛇套逻辑可能是个好主意。这是使用 Guava 的相同解决方案。
It assumes your entity names are written in the StandardJavaClassFormat
and column names in the standardJavaFieldFormat
它假定你的实体名称都写在StandardJavaClassFormat
和列名在standardJavaFieldFormat
Hopefully this will save some people coming here in future some 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()
);
}
}
回答by Michael Hegner
thanks for that post. Little anoying that upgrade breaks the table and column name strategy. Instead of copying the logic from ImprovedNamingStrategy
you could also use delegation.
谢谢你的帖子。升级打破了表和列名策略的小烦恼。除了从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);
}
}
回答by SoBeRich
No Guava and Apache utils
没有 Guava 和 Apache utils
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()
);
}
}
回答by ThreeDots
Every answer posts solution by implementing PhysicalNamingStrategy
, but all you need (and should do) is to implement ImplicitNamingStrategy
.
每个答案都通过实现来发布解决方案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.
当实体没有显式命名它映射到的数据库表时,我们需要隐式确定该表名。或者当特定属性没有明确命名它映射到的数据库列时,我们需要隐式确定该列名。有一些例子说明了 org.hibernate.boot.model.naming.ImplicitNamingStrategy 合同在映射未提供显式名称时确定逻辑名称的作用。
And the code can be as easy as this (using the original addUnderscores
as in other answers):
代码可以像这样简单(使用原始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);
}
}