赞
踩
近期给团队小伙伴做技术支持时,其从网上copy的jpa多数据源配置项目遇到了各种数据库连接异常抑或事务失效的情况,故专门新建了一个纯净对jpa多数据源配置进行了整理。本文主要展示Jpa多数据源不使用jta和使用jta分布式事务的项目配置,并编写了一些示例验证事务特性。本文演示不详尽之处,文末会附上整个项目源码下载链接,并欢迎批评指正。
springboot选择了2.3.2.RELEASE版本,在较低版本(测试了2.1.4)下会出现jta配置异常找不到jtaPlatform的错误。
spring: main: allow-bean-definition-overriding: true # 使用springboot默认的hikari数据库连接池,配置比较简单明了 datasource: common: &ds_common minimun-idle: 5 maximum-pool-size: 15 max-life-time: 600000 idle-timeout: 300000 validation-timeout: 5000 connection-timeout: 5000 #门架原始数据查询数据库 main: <<: *ds_common pool-name: mainDataSource jdbc-url: jdbc:mysql:///main?useUnicode=true&characterEncoding=UTF8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai username: root password: root connection-test-query: select 1 slave: <<: *ds_common pool-name: slaveDataSource jdbc-url: jdbc:mysql:///slave?useUnicode=true&characterEncoding=UTF8&autoReconnect=true&useSSL=false&serverTimezone=Asia/Shanghai username: root password: root connection-test-query: select 1 jpa: common: &jpa_common show-sql: true generate-ddl: true properties: hibernate: hbm2ddl: auto: update mysql: <<: *jpa_common database: mysql database-platform: org.hibernate.dialect.MySQL57Dialect # 非jta事务 jta: enabled: false logging: file: path: D:\demo\logs\multi-datasource # 调试所需 # level: # org: # hibernate: trace server: port: 8888
此处有些配置是默认也列出来重复配置了一遍,方便后期项目调优调试。
@Configuration @EnableTransactionManagement @EnableJpaRepositories(entityManagerFactoryRef = "mainEntityManagerFactory", transactionManagerRef = "mainTransactionManager", // 此处的packages用于repository扫描管理 basePackages = {"com.kyq.multids.modules.main"}) public class MainDataSourceConfig { @Value("${spring.jta.enabled: false}") private boolean jta; @Primary @Bean("mysqlJpaProperties") @ConfigurationProperties(prefix = "spring.jpa.mysql") public JpaProperties mysqlJpaProperties() { return new JpaProperties(); } @Primary @Bean("mainDataSource") @ConfigurationProperties(prefix = "spring.datasource.main") public DataSource mainDataSource(){ return DataSourceBuilder.create().build(); } @Primary @Bean("mainEntityManagerFactory") public LocalContainerEntityManagerFactoryBean mainFactoryBean(@Qualifier("mainDataSource") DataSource dataSource, @Qualifier("mysqlJpaProperties") JpaProperties jpaProperties){ HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter(); jpaVendorAdapter.setGenerateDdl(jpaProperties.isGenerateDdl()); jpaVendorAdapter.setDatabasePlatform(jpaProperties.getDatabasePlatform()); jpaVendorAdapter.setShowSql(jpaProperties.isShowSql()); jpaVendorAdapter.setDatabase(jpaProperties.getDatabase()); return new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.getProperties(), null) .dataSource(dataSource) .jta(jta) .persistenceUnit("mainEntityManagerFactory") // 此处的packages用于domain扫描管理 .packages("com.kyq.multids.modules.main") .build(); } @Primary @Bean public JdbcTemplate mainJdbcTemplate(@Qualifier("mainDataSource") DataSource mainDataSource){ JdbcTemplate jdbcTemplate = new JdbcTemplate(mainDataSource); return jdbcTemplate; } @Primary @Bean("mainTransactionManager") public PlatformTransactionManager mainTransactionManager(@Qualifier("mainEntityManagerFactory") EntityManagerFactory entityManagerFactory){ return new JpaTransactionManager(entityManagerFactory); } // @Primary // @Bean // public EntityManager mainEntityManager(@Qualifier("mainEntityManagerFactory") LocalContainerEntityManagerFactoryBean entityManagerFactoryBean){ // return entityManagerFactoryBean.getObject().createEntityManager(); // }
@Configuration @EnableTransactionManagement @EnableJpaRepositories(entityManagerFactoryRef = "slaveEntityManagerFactory", transactionManagerRef = "slaveTransactionManager", basePackages = {"com.kyq.multids.modules.slave"}) public class SlaveDataSourceConfig { @Value("${spring.jta.enabled: false}") private boolean jta; @Bean("slaveDataSource") @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource(){ return DataSourceBuilder.create().build(); } @Bean("slaveEntityManagerFactory") public LocalContainerEntityManagerFactoryBean slaveFactoryBean(@Qualifier("slaveDataSource") DataSource dataSource, @Qualifier("mysqlJpaProperties") JpaProperties jpaProperties){ HibernateJpaVendorAdapter jpaVendorAdapter = new HibernateJpaVendorAdapter(); jpaVendorAdapter.setGenerateDdl(jpaProperties.isGenerateDdl()); jpaVendorAdapter.setDatabasePlatform(jpaProperties.getDatabasePlatform()); jpaVendorAdapter.setShowSql(jpaProperties.isShowSql()); jpaVendorAdapter.setDatabase(jpaProperties.getDatabase()); return new EntityManagerFactoryBuilder(jpaVendorAdapter, jpaProperties.getProperties(), null) .dataSource(dataSource) .jta(jta) .persistenceUnit("slaveEntityManagerFactory") .packages("com.kyq.multids.modules.slave") .build(); } @Bean("slaveTransactionManager") public PlatformTransactionManager slaveTransactionManager(@Qualifier("slaveEntityManagerFactory")EntityManagerFactory entityManagerFactory){ return new JpaTransactionManager(entityManagerFactory); } @Bean public JdbcTemplate slaveJdbcTemplate(@Qualifier("slaveDataSource")DataSource slaveDataSource){ return new JdbcTemplate(slaveDataSource); } }
//MultiDataSourceTestServiceImpl.java /** * Description: com.kyq.multids.modules.index.service.impl * 注意,因为本项目有着多个transactionManager各自管理各自的数据库, * nonjta项目的MultiDataSourceTestServiceImpl是没有在class上添加@Transactional的。 * * CopyRight: © 2015 CSTC. All rights reserved. * Company: cstc * * @author kyq1024 * @version 1.0 * @timestamp 2021-08-18 14:27 */ @Service public class MultiDataSourceTestServiceImpl implements MultiDataSourceTestService { ...... /** * 此处Qualifier注解指向的是一个beanFactory对象而不是一个entityManger对象,对此有兴趣可参见我的另一篇博客对此有详细说明。 * */ @Autowired @Qualifier("mainEntityManagerFactory") EntityManager mainEntityManager; @PersistenceContext(unitName = "slaveEntityManagerFactory") EntityManager slaveEntityManager; /** * 测试使用原生SQL语句跨数据库查询数据 * * @return*/ public Map<String, Object> findDataAcrossDs(){ Map<String, Object> ret = new HashMap<String, Object>(4); Query userQuery = mainEntityManager.createNativeQuery("select * from base_user") .unwrap(NativeQuery.class) .setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE); List userList = userQuery.getResultList(); Query dictQuery = slaveEntityManager.createNativeQuery("select * from base_dict") .unwrap(NativeQuery.class) .setResultTransformer(AliasToEntityMapResultTransformer.INSTANCE); List dictList = dictQuery.getResultList(); ret.put("user", userList); ret.put("dict", dictList); return ret; } /** * 测试跨数据库保存数据 * case1: 使用jdbcTemplate进行数据库修改操作,指定txManager为transactionManager时,user和dict都不会保存,回滚正常; * case2: 指定txManager为slaveTransactionManager时,user会保存成功; * * 触发了两次txInfo,分别是addDictByJdbcOnError和addByTemplate * */ // @Transactional(rollbackFor = RuntimeException.class) @Transactional(transactionManager = "slaveTransactionManager", rollbackFor = RuntimeException.class) public void addByTemplate(){ userService.addUserByJdbc(); dictService.addDictByJdbcOnError(); } ...... }
经过上述简单配置,已经可以实现jpa的多数据源操作,且在有需要也可使用jdbcTemplate进行原生sql操作并保证单数据源事务回滚。如果需要实现多数据源的分布式事务,则需要引入jta进行事务管理即可实现,本文就不再详述,有兴趣可以下载附件Demo查看完整的配置源码(含本文演示的源码和事务测试示例)。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。