使用spring动态路由切换主从库
2015-12-02 11:20
344 查看
通过集成
配置如下: datasource-config.xml:
com.common.bean.RoutingDataSource类:
测试main方法如下:
运行结果是分别向数据库1和数据库2中插入了1调记录。
注意:
由于对DbInfoService配置了事物,如果将切换数据源的代码ThreadInfoHolder.addCurrentThreadShard(shard);放在在DbInfoServiceImpl类的getUserInfo方法中,如下:
main方法改为:
则运行结果将是向数据库1中插入2条相同的记录,而不是分别想数据库1,2各插一条记录,产生该结果的原因是应为由于DbInfoServiceImpl配置了事物,所以在getUserInfo方法中的第一次连数据库会新建一个连接,而后将该连接绑定在线程的本地变量即ThreadLoad中,当以后在需要访问数据库时不在新建连接而是使用这个绑定了老连接,在本例子中,即第一次的数据库连接是连数据库1,当第二次访问数据库时,使用的还是这个数据库1的连接,即切换数据源设置代码shard.setDbId(2);ThreadInfoHolder.addCurrentThreadShard(shard);失效。同理我们可以退出,如果将切换数据源代码放在Test类的getInfo方法中,即:
这样是能正确运行的,应为在调用事物前我们已经切换了数据源。
通过集成
org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource类,自定义动态数据源。
配置如下: datasource-config.xml:
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-2.5.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"> <!-- 数据源配置 --> <bean id="dataSourceFirst" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@10.20.151.4:1521:ptdev" /> <property name="username" value="pt" /> <property name="password" value="pt" /> <property name="maxActive" value="200" /> <property name="maxIdle" value="5" /> <property name="poolPreparedStatements" value="true" /> <property name="removeAbandoned" value="true" /> <property name="removeAbandonedTimeout" value="300" /> </bean> <bean id="dataSourceSecond" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver" /> <property name="url" value="jdbc:oracle:thin:@10.20.151.12:1521:pt10g" /> <property name="username" value="pt" /> <property name="password" value="pt" /> <property name="maxActive" value="200" /> <property name="maxIdle" value="5" /> <property name="poolPreparedStatements" value="true" /> <property name="removeAbandoned" value="true" /> <property name="removeAbandonedTimeout" value="300" /> </bean> <bean id="dataSource" class="com.common.bean.RoutingDataSource"> <property name="targetDataSources"> <map> <entry key="1" value-ref="dataSourceFirst" /> <entry key="2" value-ref="dataSourceSecond" /> </map> </property> <property name="defaultTargetDataSource"> <ref local="dataSourceFirst" /> </property> </bean> <!--配置事物--> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource"> <ref local="dataSource" /> </property> </bean> <bean id="lobHandler" class="org.springframework.jdbc.support.lob.DefaultLobHandler" lazy-init="true" /> <bean id="sqlMapClient" class="org.springframework.orm.ibatis.SqlMapClientFactoryBean"> <property name="dataSource" ref="dataSource" /> <property name="lobHandler" ref="lobHandler" /> <property name="configLocations" value="classpath*:/ibatis/config/sql-map.xml" /> </bean> <bean id="txAttributeSource" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource"> <property name="properties"> <props> <prop key="add*">PROPAGATION_REQUIRED,-PtServiceException</prop> <prop key="update*">PROPAGATION_REQUIRED,-PtServiceException</prop> <prop key="delete*">PROPAGATION_REQUIRED,-PtServiceException</prop> <prop key="batch*">PROPAGATION_REQUIRED,-PtServiceException</prop> <prop key="get*">PROPAGATION_REQUIRED,-PtServiceException</prop> </props> </property> </bean> <bean id="transactionManagerProxy" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="proxyTargetClass"> <value>true</value> </property> <property name="target"> <ref bean="transactionManager" /> </property> </bean> <bean id="transactionDefinition" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean" abstract="true"> <property name="transactionManager"> <ref bean="transactionManagerProxy" /> </property> <property name="transactionAttributeSource"> <ref bean="txAttributeSource" /> </property> </bean> <bean id="baseDao" class="com.common.dao.impl.BaseDaoImpl"> <property name="sqlMapClient"> <ref bean="sqlMapClient" /> </property> <property name="dataSource"> <ref bean="dataSource" /> </property> </bean> <bean id="dbInfoService" parent="transactionDefinition"> <property name="target"> <bean class="com.service.impl.DbInfoServiceImpl"> <property name="baseDao" ref="baseDao" /> </bean> </property> </bean> <bean id="test" class="com.common.bean.Test"> <property name="dbInfoService" ref="dbInfoService"></property> </bean> </beans>
com.common.bean.RoutingDataSource类:
package com.common.bean; import java.sql.Connection; import java.sql.SQLException; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; public class RoutingDataSource extends AbstractRoutingDataSource { protected Object determineCurrentLookupKey() { //获取当前线程处理的账号对应分片信息 Shard shard = ThreadInfoHolder.getCurrentThreadShard(); //动态选定DataSource String dbId = shard == null ? null : String.valueOf(shard.getDbId()); return dbId; } @Override public String toString() { //获取当前线程处理的账号对应分片信息 Shard shard = ThreadInfoHolder.getCurrentThreadShard(); //动态选定DataSource String dbId = shard == null ? null : String.valueOf(shard.getDbId()); return "DB ID " + dbId + ":" + super.toString(); } public String getTargetDbId() throws SQLException { Connection conn = null; try { //jdbc:oracle:thin:@10.20.151.4:1521:ptdev, UserName=xx, Oracle JDBC driver conn = determineTargetDataSource().getConnection(); if (conn != null) { String connectionDesc = conn.getMetaData().getURL(); int beginIdx = connectionDesc.indexOf("@") + 1; int endIdx = connectionDesc.indexOf(":", beginIdx); return connectionDesc.substring(beginIdx, endIdx); } } finally { if (conn != null) { conn.close(); } } return null; } } com.common.bean.ThreadInfoHolder类如下: package com.common.bean; public class ThreadInfoHolder { // thread local, 获取、存储本线程处理的账号对应分片信息 private static final ThreadLocal<Shard> shardLocal = new ThreadLocal<Shard>(); /** * 获取当前线程处理的账号对应分片信息 * * @return */ public static Shard getCurrentThreadShard() { return ThreadInfoHolder.shardLocal.get(); } /** * 在当前线程存储账号对应分片信息 * * @param shard */ public static void addCurrentThreadShard(Shard shard) { ThreadInfoHolder.shardLocal.set(shard); } /** * 清空前线程存储分片信息 * * @param shard */ public static void cleanCurrentThreadShard() { ThreadInfoHolder.shardLocal.remove(); } } com.common.bean.Shard类如下: public class Shard { //存放Account数据的DB_ID private Integer dbId; /** * @return the dbId */ public Integer getDbId() { return dbId; } /** * @param dbId the dbId to set */ public void setDbId(Integer dbId) { this.dbId = dbId; } } com.service.DbInfoService接口: package com.service; import java.util.List; import java.util.Map; import com.common.dao.model.User; public interface DbInfoService { public List<Map<String,Object>> getUserInfo(User user); } com.service.impl.DbInfoServiceImpl实现类: package com.service.impl; import java.util.List; import java.util.Map; import com.common.bean.Shard; import com.common.bean.ThreadInfoHolder; import com.common.dao.BaseDao; import com.common.dao.model.User; import com.service.DbInfoService; public class DbInfoServiceImpl implements DbInfoService{ public BaseDao baseDao; public void setBaseDao(BaseDao baseDao) { this.baseDao = baseDao; } public List<Map<String,Object>> getUserInfo(User user) { baseDao.add("login.addUser", user); List<Map<String,Object>> result=baseDao.getList("login.getUserInfo",user.getName()); return result; } } com.common.bean.Test类: package com.common.bean; import java.util.List; import java.util.Map; import com.common.dao.model.User; import com.service.DbInfoService; public class Test { public DbInfoService dbInfoService; public void setDbInfoService(DbInfoService dbInfoService) { this.dbInfoService = dbInfoService; } public List<Map<String,Object>> getInfo(User user) { List<Map<String,Object>> result=dbInfoService.getUserInfo(user); return result; } }
测试main方法如下:
package com.transaction; import java.util.List; import java.util.Map; import org.springframework.beans.factory.xml.XmlBeanFactory; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.core.io.ClassPathResource; import com.common.bean.Shard; import com.common.bean.Test; import com.common.bean.ThreadInfoHolder; import com.common.dao.model.User; import com.service.impl.DbInfoServiceImpl; public class TransactionTest { public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:spring/datasource-config.xml"); Test t=(Test)ctx.getBean("test"); User user=new User(); user.setName("xj"); user.setPassword("123"); Shard shard=new Shard(); shard.setDbId(1);//使用datasource1 ThreadInfoHolder.addCurrentThreadShard(shard); List<Map<String,Object>> result =t.getInfo(user); shard.setDbId(2);//使用datasource2 ThreadInfoHolder.addCurrentThreadShard(shard); List<Map<String,Object>> result1 =t.getInfo(user); System.out.println(result); } }
运行结果是分别向数据库1和数据库2中插入了1调记录。
注意:
由于对DbInfoService配置了事物,如果将切换数据源的代码ThreadInfoHolder.addCurrentThreadShard(shard);放在在DbInfoServiceImpl类的getUserInfo方法中,如下:
public List<Map<String,Object>> getUserInfo(User user) { Shard shard=new Shard(); shard.setDbId(1); ThreadInfoHolder.addCurrentThreadShard(shard); baseDao.add("login.addUser", user); shard.setDbId(2); ThreadInfoHolder.addCurrentThreadShard(shard); baseDao.add("login.addUser", user); List<Map<String,Object>> result=baseDao.getList("login.getUserInfo",user.getName()); return result; }
main方法改为:
public static void main(String[] args) { ApplicationContext ctx = new ClassPathXmlApplicationContext("classpath*:spring/datasource-config.xml"); Test t=(Test)ctx.getBean("test"); User user=new User(); user.setName("xj"); user.setPassword("123"); List<Map<String,Object>> result =t.getInfo(user); System.out.println(result); }
则运行结果将是向数据库1中插入2条相同的记录,而不是分别想数据库1,2各插一条记录,产生该结果的原因是应为由于DbInfoServiceImpl配置了事物,所以在getUserInfo方法中的第一次连数据库会新建一个连接,而后将该连接绑定在线程的本地变量即ThreadLoad中,当以后在需要访问数据库时不在新建连接而是使用这个绑定了老连接,在本例子中,即第一次的数据库连接是连数据库1,当第二次访问数据库时,使用的还是这个数据库1的连接,即切换数据源设置代码shard.setDbId(2);ThreadInfoHolder.addCurrentThreadShard(shard);失效。同理我们可以退出,如果将切换数据源代码放在Test类的getInfo方法中,即:
public List<Map<String,Object>> getInfo(User user) { Shard shard=new Shard(); shard.setDbId(1); ThreadInfoHolder.addCurrentThreadShard(shard); List<Map<String,Object>> result=dbInfoService.getUserInfo(user); shard.setDbId(2); ThreadInfoHolder.addCurrentThreadShard(shard); List<Map<String,Object>> result1=dbInfoService.getUserInfo(user); return result; }
这样是能正确运行的,应为在调用事物前我们已经切换了数据源。
相关文章推荐
- 一个jar包里的网站
- 一个jar包里的网站之文件上传
- 一个jar包里的网站之返回对媒体类型
- 模拟Spring的简单实现
- spring+html5实现安全传输随机数字密码键盘
- Spring中属性注入详解
- struts2 spring整合fieldError问题
- spring的jdbctemplate的crud的基类dao
- 读取spring配置文件的方法(spring读取资源文件)
- Spring Bean基本管理实例详解
- java实现简单美女拼图游戏
- java基本教程之线程休眠 java多线程教程
- JSP开发中在spring mvc项目中实现登录账号单浏览器登录
- spring boot实战之内嵌容器tomcat配置
- 基于Spring框架的Shiro配置方法
- Spring MVC中上传文件实例
- spring aop两种配置方式
- 实例讲解Java的Spring框架中的AOP实现
- Java的Spring框架的三种连接池的基本用法示例
- 解析Java中如何获取Spring中配置的bean