spring+mybatis使用interceptor(plugin)实现数据库读写别离ITeye - 超凡娱乐

spring+mybatis使用interceptor(plugin)实现数据库读写别离ITeye

2019-01-13 14:12:59 | 作者: 绮烟 | 标签: 数据库,数据,读写 | 浏览: 1224

 

体系中存在的多台服务器是“位置适当”的,不过,同一时刻他们都处于活动(Active)状况,处于负载均衡等要素考虑,数据拜访恳求需求在这几台数据库服务器之间进行合理分配, 这个时分,经过一致的一个DataSource来屏蔽这种恳求分配的需求,然后屏蔽数据拜访类与详细DataSource的耦合;

体系中存在的多台数据库服务器现在位置或许适当也或许不适当,但数据拜访类在体系启动时刻无法清晰究竟应该运用哪一个数据源进行数据拜访,而有必要在体系运转期间经过某种条件来断定究竟应该运用哪一个数据源,这个时分,咱们也得运用这种“合纵连横”的办法向数据拜访类露出一个一致的DataSource,由该DataSource来免除数据拜访类与详细数据源之间的过紧耦合;
更多场景需求读者依据详细的运用来断定,不过,并非一切的运用要做这样的处理,假如能够坚持简略,那尽量坚持简略.要完成这种“合纵连横”的多数据源办理办法,总的辅导准则就是完成一个自定义的DataSource,让该DataSource来办理体系中存在的多个与详细数据库挂钩的数据源, 数据拜访类只跟这个自定义的DataSource打交道即可。在spring2.0.1发布之前,各个项目中或许存在多种针对这种情况下的多数据源办理办法, 不过,spring2.0.1发布之后,引入了AbstractRoutingDataSource,运用该类能够完成遍及意义上的多数据源办理功用。

假定咱们有三台数据库用来完成负载均衡,一切的数据拜访恳求终究需求均匀的分配到这三台数据库服务器之上,那么,咱们能够经过承继AbstractRoutingDataSource来快速完成一个满意这样场景的原型(Prototype):

public class PrototypeLoadBalanceDataSource extends AbstractRoutingDataSource { 
 private Lock lock = new ReentrantLock(); 
 private int counter = 0; 
 private int dataSourceNumber = 3; 
 @Override 
 protected Object determineCurrentLookupKey() { 
 lock.lock(); 
 try{ 
 counter++; 
 int lookupKey = counter % getDataSourceNumber(); 
 return new Integer(lookupKey); 
 }finally{ 
 lock.unlock(); 
 // ... 

 

 

 

咱们在介绍AbstractRoutingDataSource的时分说过,要承继该类,一般只需求给出determineCurrentLookupKey()办法的逻辑即可。 下面是针对PrototypeLoadBalanceDataSource的装备:

 bean id="dataSourc1" destroy-method="close" 
02. property name="url" value=".."/ 
03. property name="driverClassName" value=".."/ 
04. property name="username" value=".."/ 
05. property name="password" value=".."/ 
06. !-- other property settings -- 
07. /bean 
08. bean id="dataSource2" destroy-method="close" 
09. property name="url" value=".."/ 
10. property name="driverClassName" value=".."/ 
11. property name="username" value=".."/ 
12. property name="password" value=".."/ 
13. !-- other property settings -- 
14. /bean 
15. bean id="dataSource3" destroy-method="close" 
16. property name="url" value=".."/ 
17. property name="driverClassName" value=".."/ 
18. property name="username" value=".."/ 
19. property name="password" value=".."/ 
20. !-- other property settings -- 
21. /bean 
22. util:map id="dataSources" 
23. entry key="0" value-ref="dataSource1"/ 
24. entry key="1" value-ref="dataSource2"/ 
25. entry key="2" value-ref="dataSource3"/ 
26. /util:map 
27. bean id="dataSourceLookup" 
28. constructor-arg 
29. ref bean="dataSources"/ 
30. /constructor-arg 
31. /bean 
32. bean id="dataSource" 
33. property name="defaultTargetDataSource" ref="dataSourc1"/ 
34. property name="targetDataSources" ref="dataSources"/ 
35. property name="dataSourceLookup" ref=""/ 
36. /bean 
37. bean id="jdbcTemplate" 
38. property name="dataSource" ref="dataSource"/ 
39. /bean 
40. bean id="someDao" 
41. property name=""jdbcTemplate"" ref=""jdbcTemplate""/ 
42. !-- other property settings -- 
43. /bean 

 运用spring的动态路由完成数据库读写别离

Spring2.0.1今后的版别现已支撑装备多数据源,而且能够在运转的时分动态加载不同的数据源。经过承继AbstractRoutingDataSource就能够完成多数据源的动态转化。现在做的项目就是需求拜访2个数据源,每个数据源的表结构都是相同的,所以要求数据源的变化关于编码人员来说是通明,也就是说相同SQL句子在不同的环境下操作的数据库是不相同的。详细的流程如下:

 

1.树立一个取得和设置上下文的类

package com.lvye.base.dao.impl.jdbc; 
02./** 
03. *衔接哪个数据源的环境变量 
04. */ 
05.public class JdbcContextHolder { 
06. private static final ThreadLocal String contextHolder = new ThreadLocal String 
07. public static void setJdbcType(String jdbcType) { 
08. contextHolder.set(jdbcType); 
09. } 
10. public static void setSlave(){ 
11. setJdbcType("slave"); 
12. } 
13. public static void setMaster(){ 
14. clearJdbcType(); 
15. } 
16. public static String getJdbcType(){ 
17. return (String) contextHolder.get(); 
18. } 
19. public static void clearJdbcType() { 
20. contextHolder.remove(); 
21. } 
22.} 

 2.树立动态数据源类,这个类有必要承继AbstractRoutingDataSource

package com.lvye.base.dao.impl.jdbc; 
02.import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; 
03.public class DynamicDataSource extends AbstractRoutingDataSource{ 
04. /*(non-Javadoc) 
05. *@see org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource#determineCurrentLookupKey() 
06. *@author wenc 
07. */ 
08. @Override 
09. protected Object determineCurrentLookupKey() { 
10. return JdbcContextHolder.getJdbcType(); 
11. } 
12.} 

 这个类完成了determineCurrentLookupKey办法,该办法回来一个Object,一般是回来字符串。该办法中直接运用了JdbcContextHolder.getJdbcType();办法取得上下文环境并直接回来。

 

 

3.编写spring的装备文件装备数据源

 beans 
02. bean id="master" destroy-method="close" 
03. property name="driverClass" 
04. value com.mysql.jdbc.Driver /value 
05. /property 
06. property name="jdbcUrl" 
07. value jdbc:mysql://192.168.18.143:3306/wenhq?useUnicode=true characterEncoding=utf-8 /value 
08. /property 
09. property name="user" 
10. value root /value 
11. /property 
12. property name="password" 
13. value /value 
14. /property 
15. /bean 
16. bean id="slave" destroy-method="close" 
17. property name="driverClass" 
18. value com.mysql.jdbc.Driver /value 
19. /property 
20. property name="jdbcUrl" 
21. value jdbc:mysql://192.168.18.144:3306/ wenhq?useUnicode=true characterEncoding=utf-8 /value 
22. /property 
23. property name="user" 
24. value root /value 
25. /property 
26. property name="password" 
27. value /value 
28. /property 
29. /bean 
30. bean id="mySqlDataSource" 
31. property name="targetDataSources" 
32. map 
33. entry key="slave" value-ref="slave"/ 
34. /map 
35. /property 
36. property name="defaultTargetDataSource" ref="master"/ 
37. /bean 
38. bean id="jdbcTemplate" 
39. property name="dataSource" ref="mySqlDataSource" / 
40. /bean 
41. bean id="transactionManager" 
42. property name="dataSource" ref="mySqlDataSource" / 
43. /bean 
44. /beans 

 在这个装备中能够看到首要装备两个实在的数据库衔接,运用的msyql数据库;master和slave是依照mysql装备的主从关系的数据库,数据会主动实时同步mySqlDataSource会依据上下文挑选不同的数据源。在这个装备中第一个property特点装备方针数据源, entry key="slave" value-ref=" slave"/ 中key的值有必要要和JdbcContextHolder类中设置的参数值相同,假如有多个值,能够装备多个 entry 标签。第二个property特点装备默许的数据源,咱们一般默许为主数据库。有些朋友喜爱运用hibernate,只需求把上面的jdbcTemplate替换为hibernate的就能够了。

4.多数据库衔接装备结束,简略测验

public void testSave() throws Exception{ 
02. jdbcContextHolder.setSlave();//设置从数据源 
03. Test test = new Test(); 
04. test.setTest("www.wenhq.com.cn"); 
05. mydao.save(test);//运用dao保存实体 
06. jdbcContextHolder.setMaster();//设置主数据源 
07. mydao.save(test);//运用dao保存实体到另一个库中 
08.} 

 5.完成读写别离,上面的测验经过了,现在就简略了我的程序是运用jdbc完成的保存数据,仅仅运用了c3p0的数据库衔接池罢了。把一切拜访数据库的办法包装一下,一致调用。把履行更新的sql发送到主数据库了

public void execute(String sql) { 
02. JdbcContextHolder.setMaster(); 
03. log.debug("execute-sql:" + sql); 
04. jdbcTemplate.execute(sql); 
05.} 

 把查询的发送到从数据库,需求留意的是像LAST_INSERT_ID这类的查询需求特别处理,有必要发送到主数据库,主张添加专门的办法,用于获取自增加的主键。

public List findObject(String queryString, Class clazz) { 
02. JdbcContextHolder.setSlave(); 
03. log.debug("findObject-sql:" + queryString); 
04. List list = jdbcTemplate.queryForList(queryString); 
05. try { 
06. list = StringBase.convertList(list, clazz);// 将List转化为List clazz 
07. } catch (Exception e) { 
08. log.error("List convert List Object error:" + e); 
09. } 
10. AbstractRoutingDataSourcereturn list; 
11.} 

 

 

spring+mybatis运用interceptor(plugin)完成数据库读写别离

 

1. 条件

    好长时刻不写博客了,应该吐槽,写点什么东西了!最近在研讨数据库读写别离,分表分库的一些东西。其实这个问题好早之前就想好,仅仅曾经运用hibernate,难点是欠好判别什么样的sql走读库,什么样的sql走主库?用正则匹配最初或许能够,/^select 没想出什么好的解决办法,mybatis就不相同了,mappedstatement有commandtype特点,象select,update,delete等类型,为完成读写别离打下来杰出的根底。

2. 解决办法

    LazyConnectionProxy + RoutingDataSource +   Plugin

在SqlSessionTemplate,创立DefaultSqlSession的时分,运用connection proxy的署理,这时并没有实在的获取connection,由于咱们不知道是要取读仍是写的数据源。待到StatementHandler的prepare()运用connection创立PreparedStatement的时分再依据mappedstatement的commandType去路由获取实在的connection。

   RoutingDataSource支撑一主一从,或许一主多从并选用round robin的办法简略负载均衡,预留接口路由和负载均衡策略可自定义。

   不支撑业务,合适auto commit为true的场景。表述才能

01. ?xml version="1.0" encoding="UTF-8"? 
02. beans xmlns="http://www.springframework.org/schema/beans" 
03. xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 
04. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" 
05. xsi:schemaLocation="http://www.springframework.org/schema/beans 
06. http://www.springframework.org/schema/beans/spring-beans-3.0.xsd 
07. http://www.springframework.org/schema/aop 
08. http://www.springframework.org/schema/aop/spring-aop-3.0.xsd 
09. http://www.springframework.org/schema/tx 
10. http://www.springframework.org/schema/tx/spring-tx-3.0.xsd 
11. http://www.springframework.org/schema/context 
12. http://www.springframework.org/schema/context/spring-context-3.0.xsd" 
14. !-- 导入特点装备文件 -- 
15. context:property-placeholder location="classpath*:*.properties" / 
17. bean id="abstractDataSource" abstract="true" 
19. destroy-method="close" 
20. property name="driverClass" value="com.mysql.jdbc.Driver" / 
21. property name="user" value="root" / 
22. property name="password" value="" / 
23. /bean 
25. bean id="readDS" parent="abstractDataSource" 
26. property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test" / 
27. /bean 
29. bean id="writeDS" parent="abstractDataSource" 
30. property name="jdbcUrl" value="jdbc:mysql://localhost:3306/test" / 
31. /bean 
33. !--简略的一个master和一个slaver 读写别离的数据源 -- 
34. bean id="routingDS" 
35. property name="targetDataSources" 
36. map key-type="java.lang.String" 
37. entry key="read" value-ref="readDS" /entry 
38. entry key="write" value-ref="writeDS" /entry 
39. /map 
40. /property 
41. property name="defaultTargetDataSource" ref="writeDS" /property 
42. /bean 
44. !-- 适用于一个master和多个slaver的场景,并用roundrobin做负载均衡 -- 
45. bean id="roundRobinDs" 
46. property name="writeDataSource" ref="writeDS" /property 
47. property name="readDataSoures" 
48. list 
49. ref bean="readDS"/ 
50. ref bean="readDS"/ 
51. ref bean="readDS"/ 
52. /list 
53. /property 
54. property name="readKey" value="READ" /property 
55. property name="writeKey" value="WRITE" /property 
56. /bean 
58. bean id="sqlSessionFactory" 
59. property name="dataSource" ref="routingDS" / 
60. property name="configLocation" value="classpath:mybatis-config.xml" / 
61. !-- mapper和resultmap装备途径 -- 
62. property name="mapperLocations" 
63. list 
64. value classpath:com/test/rwmybatis/mapper/**/*-Mapper.xml 
65. /value 
66. /list 
67. /property 
68. /bean 
70. bean id="sqlSessionTemplate" 
71. constructor-arg ref="sqlSessionFactory" / 
72. /bean 
73. !-- 经过扫描的形式,扫描目录下一切的mapper, 依据对应的mapper.xml为其生成署理类-- 
74. bean id="mapper" 
75. property name="basePackage" value="com.test.rwmybatis.mapper" / 
76. property name="sqlSessionTemplate" ref="sqlSessionTemplate" /property 
77. /bean 
79. !-- bean id="monitor" /bean -- 
80. !-- aop:config -- 
81. !-- aop:pointcut expression="execution(* com.taofang.smc.persistence..*.*(..))" id="my_pc"/ -- 
82. !-- aop:advisor advice-ref="monitor" pointcut-ref="my_pc"/ -- 
83. !-- /aop:config -- 
84. /beans                          
			
版权声明
本文来源于网络,版权归原作者所有,其内容与观点不代表超凡娱乐立场。转载文章仅为传播更有价值的信息,如采编人员采编有误或者版权原因,请与我们联系,我们核实后立即修改或删除。

猜您喜欢的文章

阅读排行

  • 1

    PHP多态ITeye

    多态,目标,不同
  • 2
  • 3
  • 4

    调用体系程序(转)ITeye

    进程,咱们,程序
  • 5
  • 6

    puttyITeye

    保存,用户名,暗码
  • 7

    1001ITeye

    小数点,个数,位数
  • 8
  • 9
  • 10

    vim装备ITeye

    文件,设置,状况