spring jpa 主从分离 AbstractRoutingDataSource 无效

liu2510865 2017-12-15 07:50:03
主要代码如下
@Configuration
@EnableJpaRepositories(entityManagerFactoryRef = "entityManagerFactory")
public class JpaDbConfig {
@Value("${druid.type}")
private Class<? extends DataSource> dataSourceType;
@Bean(name = "writeDataSource")
@ConfigurationProperties(prefix = "druid.winpos.master")
public DataSource winposMasterDataSource() {
return DataSourceBuilder.create().type(dataSourceType).build();
}

@Bean(name = "readDataSource")
@ConfigurationProperties(prefix = "druid.winpos.slave")
public DataSource winposSlaveDataSource1() {
return DataSourceBuilder.create().type(dataSourceType).build();
}
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(@Qualifier("dataSource") DataSource dataSource){
LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
vendorAdapter.setDatabase(Database.MYSQL);
vendorAdapter.setGenerateDdl(true);
vendorAdapter.setShowSql(false);
factory.setJpaVendorAdapter(vendorAdapter);

factory.setPackagesToScan("mw.jpa");
factory.setDataSource(dataSource);

Map<String, Object> jpaProperties = new HashMap<String, Object>();
jpaProperties.put("hibernate.ejb.interceptor",new MyInterceptor());
jpaProperties.put("hibernate.cache.use.query_cache",false);
jpaProperties.put("hibernate.cache.use_second_level_cache",false);
jpaProperties.put("javax.persistence.cache.storeMode", CacheStoreMode.BYPASS);
jpaProperties.put("javax.persistence.cache.retrieveMode", CacheRetrieveMode.BYPASS);
factory.setJpaPropertyMap(jpaProperties);
factory.setSharedCacheMode(SharedCacheMode.DISABLE_SELECTIVE);
factory.afterPropertiesSet();
return factory;
}
@Bean
public ReplicationRoutingDataSource dataSource(@Qualifier("writeDataSource") DataSource writeDataSource, @Qualifier("readDataSource") DataSource readDataSource){
ReplicationRoutingDataSource routingDataSource=new ReplicationRoutingDataSource();
Map<Object, Object> dataSourceMap = new HashMap<Object, Object>();
dataSourceMap.put("write", writeDataSource);
dataSourceMap.put("read", readDataSource);
routingDataSource.setTargetDataSources(dataSourceMap);
routingDataSource.setDefaultTargetDataSource(writeDataSource);
return routingDataSource;
}

@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
DynamicDataSourceTransactionManager transactionManager = new DynamicDataSourceTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
// transactionManager.setDataSource(dataSource());
return transactionManager;
}
}

ReplicationRoutingDataSource
public class ReplicationRoutingDataSource extends AbstractRoutingDataSource {
private Logger log = LoggerFactory.getLogger(ReplicationRoutingDataSource.class);

@Override
protected Object determineCurrentLookupKey() {
// String dataSourceType = TransactionSynchronizationManager.isCurrentTransactionReadOnly() ? "read" : "write";
log.info("current dataSourceType : {}", DynamicDataSourceHolder.getDataSource().name());
return DynamicDataSourceHolder.getDataSource().name().toLowerCase();
}
}

public class DynamicDataSourceTransactionManager extends JpaTransactionManager
{

/**
* 只读事务到读库,读写事务到写库
* @param transaction
* @param definition
*/
@Override
protected void doBegin(Object transaction, TransactionDefinition definition) {
//设置数据源
boolean readOnly = definition.isReadOnly();
if(readOnly) {
logger.info("doBegin"+DynamicDataSourceGlobal.READ.name());
DynamicDataSourceHolder.putDataSource(DynamicDataSourceGlobal.READ);
} else {
DynamicDataSourceHolder.putDataSource(DynamicDataSourceGlobal.WRITE);
logger.info("doBegin"+DynamicDataSourceGlobal.WRITE.name());
}

super.doBegin(transaction, definition);
}

/**
* 清理本地线程的数据源
* @param transaction
*/
@Override
protected void doCleanupAfterCompletion(Object transaction) {
super.doCleanupAfterCompletion(transaction);
DynamicDataSourceHolder.clearDataSource();
}
}

使用代码
    @GetMapping("test")
public void saveWrite() throws Exception {
User newUserFromRead = userOuterService.findByIdRead(1);
User newUser = new User();
newUser.setName("New User");

userOuterService.save(newUser);
log.info("User saved : {}", newUser);

// User newUserFromRead = userOuterService.findByIdRead(1);
//// assertThat(newUserFromRead).as("New user is saved to write db. So read db must not have the user.").isNull();
//
// User newUserFromWrite = userOuterService.findByIdWrite(1);
// assertThat(newUserFromWrite).as("New user is saved to write db. So write db must have the user.").isNotNull().isEqualTo(newUser);
}

输出日志
DEBUG 19:44:24.925 [http-nio-8080-exec-8] m.j.r.DynamicDataSourceTransactionManager[doGetTransaction:334] - Found thread-bound EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@6db997e4] for JPA transaction
DEBUG 19:44:24.925 [http-nio-8080-exec-8] m.j.r.DynamicDataSourceTransactionManager[getTransaction:367] - Creating new transaction with name [mw.jpa.entiy.UserOuterService.findByIdRead]: PROPAGATION_REQUIRES_NEW,ISOLATION_DEFAULT,readOnly; ''
INFO 19:44:24.926 [http-nio-8080-exec-8] m.j.r.DynamicDataSourceTransactionManager[doBegin:29] - doBeginREAD
INFO 19:44:24.926 [http-nio-8080-exec-8] m.j.r.ReplicationRoutingDataSource[determineCurrentLookupKey:14] - current dataSourceType : READ
DEBUG 19:44:24.955 [http-nio-8080-exec-8] m.j.r.DynamicDataSourceTransactionManager[doBegin:403] - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@6e1b7]
DEBUG 19:44:26.774 [http-nio-8080-exec-8] org.hibernate.SQL[logStatement:92] - select user0_.id as id1_4_0_, user0_.name as name2_4_0_ from users user0_ where user0_.id=?
TRACE 19:44:26.775 [http-nio-8080-exec-8] o.h.type.descriptor.sql.BasicBinder[bind:65] - binding parameter [1] as [INTEGER] - [1]
TRACE 19:44:26.786 [http-nio-8080-exec-8] o.h.t.descriptor.sql.BasicExtractor[extract:61] - extracted value ([name2_4_0_] : [VARCHAR]) - [read_1]
DEBUG 19:44:26.787 [http-nio-8080-exec-8] m.j.r.DynamicDataSourceTransactionManager[processCommit:759] - Initiating transaction commit
DEBUG 19:44:26.788 [http-nio-8080-exec-8] m.j.r.DynamicDataSourceTransactionManager[doCommit:512] - Committing JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@6db997e4]
DEBUG 19:44:26.816 [http-nio-8080-exec-8] m.j.r.DynamicDataSourceTransactionManager[doCleanupAfterCompletion:605] - Not closing pre-bound JPA EntityManager after transaction
DEBUG 19:44:26.816 [http-nio-8080-exec-8] m.j.r.DynamicDataSourceTransactionManager[doGetTransaction:334] - Found thread-bound EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@6db997e4] for JPA transaction
DEBUG 19:44:26.817 [http-nio-8080-exec-8] m.j.r.DynamicDataSourceTransactionManager[getTransaction:367] - Creating new transaction with name [mw.jpa.entiy.UserOuterService.save]: PROPAGATION_REQUIRED,ISOLATION_DEFAULT; ''
INFO 19:44:26.817 [http-nio-8080-exec-8] m.j.r.DynamicDataSourceTransactionManager[doBegin:33] - doBeginWRITE
DEBUG 19:44:26.824 [http-nio-8080-exec-8] m.j.r.DynamicDataSourceTransactionManager[doBegin:403] - Exposing JPA transaction as JDBC transaction [org.springframework.orm.jpa.vendor.HibernateJpaDialect$HibernateConnectionHandle@264b5c0d]
DEBUG 19:44:27.574 [http-nio-8080-exec-8] org.hibernate.SQL[logStatement:92] - insert into users (name) values (?)
TRACE 19:44:27.576 [http-nio-8080-exec-8] o.h.type.descriptor.sql.BasicBinder[bind:65] - binding parameter [1] as [VARCHAR] - [New User]
WARN 19:44:27.585 [http-nio-8080-exec-8] o.h.e.jdbc.spi.SqlExceptionHelper[logExceptions:127] - SQL Error: 1290, SQLState: HY000

ERROR 19:44:27.586 [http-nio-8080-exec-8] o.h.e.jdbc.spi.SqlExceptionHelper[logExceptions:129] - The MySQL server is running with the --read-only option so it cannot execute this statement
DEBUG 19:44:27.587 [http-nio-8080-exec-8] m.j.r.DynamicDataSourceTransactionManager[processRollback:851] - Initiating transaction rollback
DEBUG 19:44:27.587 [http-nio-8080-exec-8] m.j.r.DynamicDataSourceTransactionManager[doRollback:538] - Rolling back JPA transaction on EntityManager [org.hibernate.jpa.internal.EntityManagerImpl@6db997e4]
DEBUG 19:44:27.602 [http-nio-8080-exec-8] m.j.r.DynamicDataSourceTransactionManager[doCleanupAfterCompletion:605] - Not closing pre-bound JPA EntityManager after transaction
ERROR 19:44:27.605 [http-nio-8080-exec-8] o.a.c.c.C.[.[.[.[dispatcherServlet][log:181] - Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.orm.jpa.JpaSystemException: could not execute statement; nested exception is org.hibernate.exception.GenericJDBCException: could not execute statement] with root cause

可以看到在调用第一个findByIdRead 时数据源切到从库
但是 执行userOuterService.save(newUser); 数据源并没有切换到主库,
ReplicationRoutingDataSource determineCurrentLookupKey在执行第二个方法时没有被调用。
是我的配置的有问题么?
...全文
355 回复 打赏 收藏 转发到动态 举报
写回复
用AI写文章
回复
切换为时间正序
请发表友善的回复…
发表回复

81,092

社区成员

发帖
与我相关
我的任务
社区描述
Java Web 开发
社区管理员
  • Web 开发社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧