Hibernate4迁移至Hibernate5:命名策略引发映射异常求助
问题背景
将应用从Hibernate 4迁移到Hibernate 5时,在命名策略配置上遇到瓶颈,尝试多种调整后仍出现映射错误。
现有配置与代码
hibernate.cfg.xml映射配置
<mapping resource="com/ubs/investor/cpss/domain/BonusHis.hbm.xml"/> <mapping resource="com/ubs/investor/cpss/domain/Instrument.hbm.xml"/>
SessionFactory初始化代码
已将AnnotationConfiguration替换为Configuration,当前实现:
private void reloadHibernateSessionFactoryFromConf(Map<String, String> prop) { try { sessionFactory = null; Configuration config = new Configuration(); //hibernate5 for(Map.Entry<String, String> entry : prop.entrySet()) { config.setProperty(entry.getKey(), entry.getValue()); } if(namingStrategy!=null){ configuration.setNamingStrategy(namingStrategy); //怀疑是这段代码导致问题 } if(annotatedEntityPackages!=null){ addAnnotatedClasses(config); } sessionFactory = config.configure().buildSessionFactory(); } catch (final HibernateException e) { LOGGER.error("SessionFactory creation failed", e); } }
原命名策略注入配置
<property name="namingStrategy"> <bean class="org.hibernate.cfg.ImprovedNamingStrategy"/> </property>
尝试过的解决方案
- 注释
configuration.setNamingStrategy(namingStrategy);代码 - 设置
configuration.setPhysicalNamingStrategy(PhysicalNamingStrategyImpl.INSTANCE); - 自定义多种命名策略类(如大写转换、驼峰转下划线)
示例自定义大写命名策略:
public class UpperCaseNamingStrategy extends PhysicalNamingStrategyStandardImpl { @Override public Identifier toPhysicalColumnName(Identifier name, JdbcEnvironment context) { return context.getIdentifierHelper().toIdentifier( StringUtils.upperCase(name.getText(), Locale.ENGLISH)); } @Override public Identifier toPhysicalCatalogName(final Identifier name, final JdbcEnvironment context) { return context.getIdentifierHelper().toIdentifier( StringUtils.upperCase(name.getText(), Locale.ENGLISH)); } @Override public Identifier toPhysicalSchemaName(final Identifier name, final JdbcEnvironment context) { return context.getIdentifierHelper().toIdentifier( StringUtils.upperCase(name.getText(), Locale.ENGLISH)); } @Override public Identifier toPhysicalSequenceName(final Identifier name, final JdbcEnvironment context) { return context.getIdentifierHelper().toIdentifier( StringUtils.upperCase(name.getText(), Locale.ENGLISH)); } @Override public Identifier toPhysicalTableName(final Identifier name, final JdbcEnvironment context) { return context.getIdentifierHelper().toIdentifier( StringUtils.upperCase(name.getText(), Locale.ENGLISH)); } }
编辑补充:后续尝试
使用自定义驼峰转下划线的物理命名策略:
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()); } 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); } }
并完全移除原namingStrategy注入配置,但仍出现相同错误。
报错信息
org.hibernate.MappingException: An association from the table BONUS_HIS refers to an unmapped class: domain.Instrument at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:709) at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:746) at repository.legacy.HibernateSessionManager.reloadHibernateSessionFactoryFromConf
核心疑问
Hibernate 5中org.hibernate.cfg.ImprovedNamingStrategy的正确实现方式是什么?
1. Hibernate 5命名策略的核心变化
Hibernate 5抛弃了旧版单一的NamingStrategy,拆分为ImplicitNamingStrategy(处理未显式指定名称的映射逻辑)和PhysicalNamingStrategy(负责将逻辑名称转换为数据库物理名称),原ImprovedNamingStrategy属于废弃API,不再建议直接使用。
2. 先解决映射错误核心问题
碰到的BONUS_HIS关联未映射类domain.Instrument错误,本质和命名策略无关,先排查以下几点:
- 检查
BonusHis.hbm.xml里关联Instrument的配置,class属性必须写全限定类名:com.ubs.investor.cpss.domain.Instrument,而非报错里的domain.Instrument - 确认
Instrument.hbm.xml被正确加载:核对hibernate.cfg.xml中的映射路径是否准确,或代码中是否遗漏加载该文件 - 验证命名策略是否篡改了实体逻辑名称,导致关联匹配失败
3. 实现类似ImprovedNamingStrategy的效果
如果要保留原ImprovedNamingStrategy的驼峰转下划线、小写转换行为,需按新API配置:
自定义PhysicalNamingStrategy(驼峰转下划线)
可以直接用你编辑后的PhysicalNamingStrategyImpl,它的逻辑和原ImprovedNamingStrategy的表名转换一致。
配置命名策略到SessionFactory
替换原setNamingStrategy的代码,改用新API:
// 移除旧版setNamingStrategy代码,替换为: config.setImplicitNamingStrategy(ImplicitNamingStrategyJpaCompliantImpl.INSTANCE); config.setPhysicalNamingStrategy(PhysicalNamingStrategyImpl.INSTANCE);
也可以通过配置文件设置(无需代码硬编码):
hibernate.implicit_naming_strategy=org.hibernate.boot.model.naming.ImplicitNamingStrategyJpaCompliantImpl hibernate.physical_naming_strategy=com.yourpackage.PhysicalNamingStrategyImpl
旧版NamingStrategy兼容方案(不推荐)
如果要兼容旧的ImprovedNamingStrategy,可以用适配器转换:
config.setPhysicalNamingStrategy(new PhysicalNamingStrategyAdapter(new ImprovedNamingStrategy())); config.setImplicitNamingStrategy(ImplicitNamingStrategyLegacyHbmImpl.INSTANCE);
4. 映射错误排查技巧
- 开启Hibernate DEBUG日志,查看映射文件加载过程,确认
Instrument.hbm.xml是否被成功解析 - 检查
BonusHis.hbm.xml中的关联标签(如<many-to-one>)的class属性是否正确 - 验证
Instrument的表名转换结果,是否和BonusHis关联中引用的表名一致
内容的提问来源于stack exchange,提问作者Michu93

