`
lee1177
  • 浏览: 117757 次
  • 性别: Icon_minigender_1
  • 来自: 北京
社区版块
存档分类
最新评论

spring jpa 读写分离

阅读更多

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

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

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

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

先声明EntityManager

 

 

<!-- Jpa Entity Manager 配置 -->
	<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean"> 
		<property name="dataSource" ref="dataSource"/>
		<property name="jpaVendorAdapter">
            <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
                <property name="showSql" value="false" />
                <property name="generateDdl" value="true" />
                <property name="database" value="MYSQL" />
            </bean>
        </property>
		<property name="packagesToScan" value="com.lee"/>
		<property name="jpaProperties">
			<props>
				<!-- 命名规则 My_NAME->MyName -->
				<prop key="hibernate.ejb.naming_strategy">org.hibernate.cfg.ImprovedNamingStrategy</prop>
			</props>
		</property>
	</bean>
    
    <!-- 动态dataSource -->
    <bean id="dataSource" class="com.lee.spring.core.jpa.rws.RwDataSource">
        <property name="targetDataSources">  
              <map key-type="java.lang.String">  
                  <!-- write -->
                 <entry key="master" value-ref="masterDataSource"/>  
                 <!-- read -->
                 <entry key="slave" value-ref="slaveDataSource"/>  
              </map>  
        </property>  
        <property name="defaultTargetDataSource" ref="masterDataSource"/>  
    </bean>
    
    <!-- 主(写)数据源 -->
    <bean id="masterDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="driverClass" value="${jdbc.master.driver}" />
		<property name="jdbcUrl" value="${jdbc.master.url}" />
		<property name="user" value="${jdbc.master.username}" />
		<property name="password" value="${jdbc.master.password}" />
		<property name="maxPoolSize" value="30" />
		<property name="minPoolSize" value="10" />
		<property name="initialPoolSize" value="1" />
		<property name="maxIdleTime" value="0" />
		<property name="acquireIncrement" value="3" />
		<property name="acquireRetryAttempts" value="30" />
		<property name="checkoutTimeout" value="0" />
		<property name="idleConnectionTestPeriod" value="60" />
	</bean>
	
	<!-- 从(读)数据源 -->
    <bean id="slaveDataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
		<property name="driverClass" value="${jdbc.slave.driver}" />
		<property name="jdbcUrl" value="${jdbc.slave.url}" />
		<property name="user" value="${jdbc.slave.username}" />
		<property name="password" value="${jdbc.slave.password}" />
		<property name="maxPoolSize" value="30" />
		<property name="minPoolSize" value="10" />
		<property name="initialPoolSize" value="1" />
		<property name="maxIdleTime" value="0" />
		<property name="acquireIncrement" value="3" />
		<property name="acquireRetryAttempts" value="30" />
		<property name="checkoutTimeout" value="0" />
		<property name="idleConnectionTestPeriod" value="60" />
	</bean>

 用java声明的jpa设置

 

 

import javax.annotation.Resource;
import javax.persistence.EntityManagerFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.lee.spring.core.jpa.rws.MyJpaTransactionManager;

/**
 * spring-jpa设置
 * @author lee
 *
 */
@Configuration
@PropertySource("classpath:/application.properties")
@EnableTransactionManagement
@EnableJpaRepositories(basePackages = {"com.lee.**.dao"})
public class SpringDaoConfig {
	private static final Logger logger = LoggerFactory.getLogger(SpringDaoConfig.class);
	@Resource(name="entityManagerFactory")
	private EntityManagerFactory entityManagerFactory;  
    
    /**
     * 描述 : 负责解析资源文件
     * 这个类必须有,而且必须声明为static,否则不能正常解析
     * @return
     */
    @Bean  
    public static PropertySourcesPlaceholderConfigurer placehodlerConfigurer() {  
        logger.info("PropertySourcesPlaceholderConfigurer");  
        return new PropertySourcesPlaceholderConfigurer();  
    }  
    
    @Bean(name="entityManagerFactory")
    public EntityManagerFactory entityManagerFactory() {
    	return entityManagerFactory;
    } 
  
    @Bean(name="transactionManager")
    public MyJpaTransactionManager transactionManager() {
    	MyJpaTransactionManager transactionManager = new MyJpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }
    
}

 

 

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

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

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

 

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.orm.jpa.JpaTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.support.DefaultTransactionStatus;

@SuppressWarnings("serial")
public class MyJpaTransactionManager extends JpaTransactionManager{
	private static final Logger logger = LoggerFactory.getLogger(MyJpaTransactionManager.class);
	@Override
	protected void doBegin(Object transaction, TransactionDefinition definition) {
		if(definition.isReadOnly()){
			RwDataSourceHolder.localSlave();
		}else{
			RwDataSourceHolder.localMaster();
		}
		logger.info("jpa-transaction:begin-----now dataSource is ["+RwDataSourceHolder.getDataSouce()+"]");
		super.doBegin(transaction, definition);
	}
	@Override
	protected void doCommit(DefaultTransactionStatus status) {
		logger.info("jpa-transaction:commit-----now dataSource is ["+RwDataSourceHolder.getDataSouce()+"]");
		super.doCommit(status);
	}
}

 

 

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

public class RwDataSourceHolder {
	public static final String MASTER = "master";	//主(写)连接池
	public static final String SLAVE = "slave";		//从(读)连接池
	
	public static final ThreadLocal<String> holder = new ThreadLocal<String>();

    public static void localMaster() {
        holder.set(MASTER);
    }
    
    public static void localSlave() {
        holder.set(SLAVE);
    }

    public static String getDataSouce() {
        return holder.get();
    }
}

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

import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

public class RwDataSource extends AbstractRoutingDataSource {

	@Override
	protected Object determineCurrentLookupKey() {
        return RwDataSourceHolder.getDataSouce();
	}

}

 

 

分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics