spring jpa 读写分离

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring 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. }  

 

 


以上是关于spring jpa 读写分离的主要内容,如果未能解决你的问题,请参考以下文章

基于Spring读写分离

spring实现读写分离aop注解方式

spring-data-redis读写分离

Spring AOP 实现读写分离

Spring Boot+MyBatis实现读写分离

Spring 实现数据库读写分离