本文主要解决基于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;
}
}
由上可以看出跟平常不同的有两点
自定义动态连接池RwDataSource
自定义事务管理器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();
}
}
原文地址:http://11701009.blog.51cto.com/11691009/1787066