转 spring jpa 读写分离

pring jpa 读写分离

博客分类:
 

本文主要解决基于spring data jpa读写分离。

思想:在dataSource做路由,根据事务判断使用主从数据源。

背景:spring+spring data jpa(hibernate jpa)

首先是jpa配置,时间有限在原基础上该的,既有java配置也有xml配置,见谅。

先声明EntityManager

 

 

Xml代码   收藏代码
  1. <!-- Jpa Entity Manager 配置 -->  
  2.     <bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">   
  3.         <property name="dataSource" ref="dataSource"/>  
  4.         <property name="jpaVendorAdapter">  
  5.             <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">  
  6.                 <property name="showSql" value="false" />  
  7.                 <property name="generateDdl" value="true" />  
  8.                 <property name="database" value="MYSQL" />  
  9.             </bean>  
  10.         </property>  
  11.         <property name="packagesToScan" value="com.lee"/>  
  12.         <property name="jpaProperties">  
  13.             <props>  
  14.                 <!-- 命名规则 My_NAME->MyName -->  
  15.                 <prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>  
  16.             </props>  
  17.         </property>  
  18.     </bean>  
  19.       
  20.     <!-- 动态dataSource -->  
  21.     <bean id="dataSource" class="com.lee.spring.core.jpa.rws.RwDataSource">  
  22.         <property name="targetDataSources">    
  23.               <map key-type="java.lang.String">    
  24.                   <!-- write -->  
  25.                  <entry key="master" value-ref="masterDataSource"/>    
  26.                  <!-- read -->  
  27.                  <entry key="slave" value-ref="slaveDataSource"/>    
  28.               </map>    
  29.         </property>    
  30.         <property name="defaultTargetDataSource" ref="masterDataSource"/>    
  31.     </bean>  
  32.       
  33.     <!-- 主(写)数据源 -->  
  34.     <bean id="masterDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  
  35.         <property name="driverClass" value="${jdbc.master.driver}" />  
  36.         <property name="jdbcUrl" value="${jdbc.master.url}" />  
  37.         <property name="user" value="${jdbc.master.username}" />  
  38.         <property name="password" value="${jdbc.master.password}" />  
  39.         <property name="maxPoolSize" value="30" />  
  40.         <property name="minPoolSize" value="10" />  
  41.         <property name="initialPoolSize" value="1" />  
  42.         <property name="maxIdleTime" value="0" />  
  43.         <property name="acquireIncrement" value="3" />  
  44.         <property name="acquireRetryAttempts" value="30" />  
  45.         <property name="checkoutTimeout" value="0" />  
  46.         <property name="idleConnectionTestPeriod" value="60" />  
  47.     </bean>  
  48.       
  49.     <!-- 从(读)数据源 -->  
  50.     <bean id="slaveDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">  
  51.         <property name="driverClass" value="${jdbc.slave.driver}" />  
  52.         <property name="jdbcUrl" value="${jdbc.slave.url}" />  
  53.         <property name="user" value="${jdbc.slave.username}" />  
  54.         <property name="password" value="${jdbc.slave.password}" />  
  55.         <property name="maxPoolSize" value="30" />  
  56.         <property name="minPoolSize" value="10" />  
  57.         <property name="initialPoolSize" value="1" />  
  58.         <property name="maxIdleTime" value="0" />  
  59.         <property name="acquireIncrement" value="3" />  
  60.         <property name="acquireRetryAttempts" value="30" />  
  61.         <property name="checkoutTimeout" value="0" />  
  62.         <property name="idleConnectionTestPeriod" value="60" />  
  63.     </bean>  

 用java声明的jpa设置

 

 

Java代码   收藏代码
  1. import javax.annotation.Resource;  
  2. import javax.persistence.EntityManagerFactory;  
  3.   
  4. import org.slf4j.Logger;  
  5. import org.slf4j.LoggerFactory;  
  6. import org.springframework.context.annotation.Bean;  
  7. import org.springframework.context.annotation.Configuration;  
  8. import org.springframework.context.annotation.PropertySource;  
  9. import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;  
  10. import org.springframework.data.jpa.repository.config.EnableJpaRepositories;  
  11. import org.springframework.transaction.annotation.EnableTransactionManagement;  
  12.   
  13. import com.lee.spring.core.jpa.rws.MyJpaTransactionManager;  
  14.   
  15. /** 
  16.  * spring-jpa设置 
  17.  * @author lee 
  18.  * 
  19.  */  
  20. @Configuration  
  21. @PropertySource("classpath:/application.properties")  
  22. @EnableTransactionManagement  
  23. @EnableJpaRepositories(basePackages = {"com.lee.**.dao"})  
  24. public class SpringDaoConfig {  
  25.     private static final Logger logger = LoggerFactory.getLogger(SpringDaoConfig.class);  
  26.     @Resource(name="entityManagerFactory")  
  27.     private EntityManagerFactory entityManagerFactory;    
  28.       
  29.     /** 
  30.      * 描述 : 负责解析资源文件 
  31.      * 这个类必须有,而且必须声明为static,否则不能正常解析 
  32.      * @return 
  33.      */  
  34.     @Bean    
  35.     public static PropertySourcesPlaceholderConfigurer placehodlerConfigurer() {    
  36.         logger.info("PropertySourcesPlaceholderConfigurer");    
  37.         return new PropertySourcesPlaceholderConfigurer();    
  38.     }    
  39.       
  40.     @Bean(name="entityManagerFactory")  
  41.     public EntityManagerFactory entityManagerFactory() {  
  42.         return entityManagerFactory;  
  43.     }   
  44.     
  45.     @Bean(name="transactionManager")  
  46.     public MyJpaTransactionManager transactionManager() {  
  47.         MyJpaTransactionManager transactionManager = new MyJpaTransactionManager();  
  48.         transactionManager.setEntityManagerFactory(entityManagerFactory);  
  49.         return transactionManager;  
  50.     }  
  51.       
  52. }  

 

 

由上可以看出跟平常不同的有两点

  1. 自定义动态连接池RwDataSource
  2. 自定义事务管理器MyJpaTransactionManager

其中MyJpaTransactionManager主要作用在于判断事务类别。因为我是使用注解@Transactional来声明事务,所以该类做了如下调整

 

Java代码   收藏代码
  1. import org.slf4j.Logger;  
  2. import org.slf4j.LoggerFactory;  
  3. import org.springframework.orm.jpa.JpaTransactionManager;  
  4. import org.springframework.transaction.TransactionDefinition;  
  5. import org.springframework.transaction.support.DefaultTransactionStatus;  
  6.   
  7. @SuppressWarnings("serial")  
  8. public class MyJpaTransactionManager extends JpaTransactionManager{  
  9.     private static final Logger logger = LoggerFactory.getLogger(MyJpaTransactionManager.class);  
  10.     @Override  
  11.     protected void doBegin(Object transaction, TransactionDefinition definition) {  
  12.         if(definition.isReadOnly()){  
  13.             RwDataSourceHolder.localSlave();  
  14.         }else{  
  15.             RwDataSourceHolder.localMaster();  
  16.         }  
  17.         logger.info("jpa-transaction:begin-----now dataSource is ["+RwDataSourceHolder.getDataSouce()+"]");  
  18.         super.doBegin(transaction, definition);  
  19.     }  
  20.     @Override  
  21.     protected void doCommit(DefaultTransactionStatus status) {  
  22.         logger.info("jpa-transaction:commit-----now dataSource is ["+RwDataSourceHolder.getDataSouce()+"]");  
  23.         super.doCommit(status);  
  24.     }  
  25. }  

 

 

上面涉及到definition.isReadOnly()来判断我的注解声明,依此来决定使用哪个dataSource。

Java代码   收藏代码
  1. public class RwDataSourceHolder {  
  2.     public static final String MASTER = "master";   //主(写)连接池  
  3.     public static final String SLAVE = "slave";     //从(读)连接池  
  4.       
  5.     public static final ThreadLocal<String> holder = new ThreadLocal<String>();  
  6.   
  7.     public static void localMaster() {  
  8.         holder.set(MASTER);  
  9.     }  
  10.       
  11.     public static void localSlave() {  
  12.         holder.set(SLAVE);  
  13.     }  
  14.   
  15.     public static String getDataSouce() {  
  16.         return holder.get();  
  17.     }  
  18. }  

 最后是RwDataSource,这个完全基于spring提供的AbstractRoutingDataSource

Java代码   收藏代码
  1. import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;  
  2.   
  3. public class RwDataSource extends AbstractRoutingDataSource {  
  4.   
  5.     @Override  
  6.     protected Object determineCurrentLookupKey() {  
  7.         return RwDataSourceHolder.getDataSouce();  
  8.     }  
  9.   
  10. }  

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值