作者简介:
🌹 作者:暗夜91
🤟 个人主页:暗夜91的主页
📝 微信公众号: 程序员的知识站 ,关注本人公众号,学习更多前沿知识。
具体问题:
最近项目遇到了一个因为Spring Data JPA命名策略的原因导致的数据库表与实体类映射错误的问题,具体问题如下:
数据库中已经有一批业务表了,字段命名方式是用 “_” 连接,然后其他组的人员创建数据表的时候,使用的是驼峰方式直接在数据库中创建的表,没有使用统一的命名方式。数据表中已经跑入了大量的数据,如果修改表结构的话,需要联系其他组人员修改代码,重新跑入数据,成本太高,不现实,只能在java端通过其他技术解决。
在java代码中,创建该表对应的实体类时,在对应的驼峰字段上使用@Column注解进行标注,结果发现启动项目的时候,@Column的注解没有生效,在表中创建了大量使用 “_” 连接的冗余字段。
原因分析:
数据库中的业务表是使用JPA自动在数据库中创建,所以表中 “_” 连接的字段是JPA生成的,查询发现JPA默认的命名策略是默认的隐式策略,会把所有的字段名称转为小写,并且在大写字母前面加上横线,比如testId映射到数据库中是test_id。
解决方法:
元凶找到了,接下来就是解决方法了。
Spring Data JPA是在 Hibernate 的基础之上的封装实现,所以我们可以使用Hibernate的命名规则,在properties文件中加入一下配置:
spring.jpa.hibernate.naming.physical-strategy=org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
重新启动服务,然后对数据表与实体类的映射比对,发现这次映射成功,可以在代码中使用jpa操作数据表了。
解决了数据表的映射以后,本以为万事大吉,结果查看启动日志时发现了问题,日志中显示在原本存在的业务表中添加了好多驼峰形式的字段,瞬间就麻瓜了,发现这种处理方式虽然解决了数据表的映射问题,但是对原来已经创建的业务表并没有兼容,只好苦逼的一个个业务表处理,把多余的字段删除掉。
分析发现,如果在配置文件中加入命名策略配置后,会对整个项目全部生效,这明显不符合需求。
经过一番实验,最后找到了一种迂回的解决办法,利用多数据源的方式,具体实现如下:
1)增加Datasource配置文件
@Configuration
public class DataSourceConfig {
@Bean(name = "primaryDataSource")
@Qualifier("primaryDataSource")
@Primary
@ConfigurationProperties(prefix="spring.datasource.primary")
public DataSource dvDataSource() {
return DataSourceBuilder.create().build();
}
}
2)增加主数据源JPA配置文件
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef="entityManagerFactoryPrimary",
transactionManagerRef="transactionManagerPrimary",
basePackages= { "com.test.business" })
public class PrimaryJPAConfig {
@Autowired
private HibernateProperties hibernateProperties;
@Autowired
private JpaProperties jpaProperties;
@Autowired
@Qualifier("primaryDataSource")
private DataSource dvDataSource;
@Primary
@Bean(name="entityManagerPrimary")
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactoryPrimary(builder).getObject().createEntityManager();
}
@Primary
@Bean(name="entityManagerFactoryPrimary")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryPrimary(EntityManagerFactoryBuilder builder) {
Map<String,Object> properties = hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(),new HibernateSettings());
return builder.dataSource(dvDataSource)
.properties(properties)
.packages("com.test.business") //次数据源实体所在位置
.persistenceUnit("primaryPersistenceUnit")
.build();
}
@Primary
@Bean(name="transactionManagerPrimary")
public PlatformTransactionManager transactionManagerDv(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactoryPrimary(builder).getObject());
}
}
3)增加数据表使用的数据源
与主数据源配置相比,需要为数据源对应的数据源添加命名策略的配置如下
properties.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
具体配置文件如下:
@Configuration
@EnableTransactionManagement
@EnableJpaRepositories(
entityManagerFactoryRef="entityManagerFactoryData",
transactionManagerRef="transactionManagerData",
basePackages= { "com.test.data" })
public class DataJPAConfig {
@Autowired
private HibernateProperties hibernateProperties;
@Autowired
private JpaProperties jpaProperties;
@Autowired
@Qualifier("primaryDataSource")
private DataSource dvDataSource;
@Bean(name="entityManagerData")
public EntityManager entityManager(EntityManagerFactoryBuilder builder) {
return entityManagerFactoryData(builder).getObject().createEntityManager();
}
@Bean(name="entityManagerFactoryData")
public LocalContainerEntityManagerFactoryBean entityManagerFactoryData(EntityManagerFactoryBuilder builder) {
Map<String,Object> properties = hibernateProperties.determineHibernateProperties(jpaProperties.getProperties(),new HibernateSettings());
properties.put("hibernate.physical_naming_strategy", "org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy");
return builder.dataSource(dvDataSource)
.properties(properties)
.packages("com.test.data") //次数据源实体所在位置
.persistenceUnit("dataPersistenceUnit")
.build();
}
@Bean(name="transactionManagerData")
public PlatformTransactionManager transactionManagerData(EntityManagerFactoryBuilder builder) {
return new JpaTransactionManager(entityManagerFactoryData(builder).getObject());
}
}
4)创建数据表实体类所在package
在和业务表package路径同级的位置创建数据表的package路径,在上面的代码中,业务表package为com.test.business,数据表package为com.test.data
之后所有数据表的操作都放在com.test.data路径下,这样就可以实现在不影响原有业务表的情况下,解决数据表的@Column注解不生效的问题。