1、读写分离:可以通过Spring提供的AbstractRoutingDataSource类,重写determineCurrentLookupKey方法,实现动态切换数据源的功能;读写分离可以有效减轻写库的压力,又可以把查询数据的请求分发到不同读库;MySql主从同步传送门

2、写数据库:当调用insert、update、delete及一些实时数据用到的库;

3、读数据库:当调用select查询数据用到的库;

4、JaveWeb工程通过AbstractRoutingDataSource类实现读写分离;

a、jdbc.properties文件配置读写数据源

datasource.type=mysql
datasource.driverClassName=com.mysql.jdbc.Driver
datasource.username=root
#写库
w.datasource.url=jdbc\:mysql\://127.0.0.1\:3306/ddt?characterEncoding\=utf-8
w.datasource.password=write123
#读库
r.datasource.url=jdbc\:mysql\://IP\:3306/ddt?characterEncoding\=utf-8
r.datasource.password=read123 #连接池配置
c3p0.acquireIncrement=3
c3p0.acquireRetryAttempts=10
c3p0.acquireRetryDelay=1000
c3p0.initialPoolSize=20
c3p0.idleConnectionTestPeriod=3600
c3p0.testConnectionOnCheckout=true
c3p0.minPoolSize=10
c3p0.maxPoolSize=80
c3p0.maxStatements=100
c3p0.numHelperThreads=10
c3p0.maxIdleTime=10800

b、application.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:jee="http://www.springframework.org/schema/jee"
xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:task="http://www.springframework.org/schema/task"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx-3.0.xsd
http://www.springframework.org/schema/jee
http://www.springframework.org/schema/jee/spring-jee-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop-3.0.xsd
http://www.springframework.org/schema/task
http://www.springframework.org/schema/task/spring-task-3.0.xsd">
<!-- 使用annotation 自动注册bean,并保证@Required,@Autowired的属性被注入 -->
<context:component-scan base-package="com.eb3">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Service" />
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
</context:component-scan>
<bean class="org.springframework.beans.factory.config.PreferencesPlaceholderConfigurer" >
<property name="ignoreResourceNotFound" value="true" />
<property name="properties" ref="configProperties" />
</bean>
<bean id="configProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<list>
<value>classpath*:jdbc.properties</value>
</list>
</property>
</bean> <context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 定义Hibernate读数据源 -->
<bean id="dataSourceRead" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass">
<value>${datasource.driverClassName}</value>
</property>
<property name="jdbcUrl">
<value>${r.datasource.url}</value>
</property>
<property name="user">
<value>${datasource.username}</value>
</property>
<property name="password">
<value>${r.datasource.password}</value>
</property>
<!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。-->
<property name="acquireIncrement">
<value>${c3p0.acquireIncrement}</value>
</property>
<!--定义在从数据库获取新连接失败后重复尝试的次数。-->
<property name="acquireRetryAttempts">
<value>${c3p0.acquireRetryAttempts}</value>
</property>
<!--两次连接中间隔时间,单位毫秒。-->
<property name="acquireRetryDelay">
<value>${c3p0.acquireRetryDelay}</value>
</property>
<property name="initialPoolSize">
<value>${c3p0.initialPoolSize}</value>
</property>
<property name="testConnectionOnCheckout">
<value>${c3p0.testConnectionOnCheckout}</value>
</property>
<property name="minPoolSize">
<value>${c3p0.minPoolSize}</value>
</property>
<property name="maxPoolSize">
<value>${c3p0.maxPoolSize}</value>
</property>
<property name="maxIdleTime">
<value>${c3p0.maxIdleTime}</value>
</property>
<property name="idleConnectionTestPeriod">
<value>${c3p0.idleConnectionTestPeriod}</value>
</property>
<property name="maxStatements">
<value>${c3p0.maxStatements}</value>
</property>
<property name="numHelperThreads">
<value>${c3p0.numHelperThreads}</value>
</property>
</bean>
<!-- 定义Hibernate写数据源 -->
<bean id="dataSourceWrite" class="com.mchange.v2.c3p0.ComboPooledDataSource"
destroy-method="close">
<property name="driverClass">
<value>${datasource.driverClassName}</value>
</property>
<property name="jdbcUrl">
<value>${w.datasource.url}</value>
</property>
<property name="user">
<value>${datasource.username}</value>
</property>
<property name="password">
<value>${w.datasource.password}</value>
</property>
<!--当连接池中的连接耗尽的时候c3p0一次同时获取的连接数。-->
<property name="acquireIncrement">
<value>${c3p0.acquireIncrement}</value>
</property>
<!--定义在从数据库获取新连接失败后重复尝试的次数。-->
<property name="acquireRetryAttempts">
<value>${c3p0.acquireRetryAttempts}</value>
</property>
<!--两次连接中间隔时间,单位毫秒。-->
<property name="acquireRetryDelay">
<value>${c3p0.acquireRetryDelay}</value>
</property>
<property name="initialPoolSize">
<value>${c3p0.initialPoolSize}</value>
</property>
<property name="testConnectionOnCheckout">
<value>${c3p0.testConnectionOnCheckout}</value>
</property>
<property name="minPoolSize">
<value>${c3p0.minPoolSize}</value>
</property>
<property name="maxPoolSize">
<value>${c3p0.maxPoolSize}</value>
</property>
<property name="maxIdleTime">
<value>${c3p0.maxIdleTime}</value>
</property>
<property name="idleConnectionTestPeriod">
<value>${c3p0.idleConnectionTestPeriod}</value>
</property>
<property name="maxStatements">
<value>${c3p0.maxStatements}</value>
</property>
<property name="numHelperThreads">
<value>${c3p0.numHelperThreads}</value>
</property>
</bean>
<!-- 动态数据源 -->
<bean id="dynamicDataSource" class="com.eb3.ddt.DynamicDataSource">
<!-- 通过key-value关联数据源 -->
<property name="targetDataSources">
<map>
<entry value-ref="dataSourceWrite" key="dataSourceWrite"></entry>
<entry value-ref="dataSourceRead" key="dataSourceRead"></entry>
</map>
</property>
<property name="defaultTargetDataSource" ref="dataSourceWrite" />
</bean> <!-- 设置sessionFactory -->
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
<!-- 依赖注入数据源,注入正是上文定义的dataSource -->
<property name="dataSource" ref="dynamicDataSource" />
<property name="packagesToScan" value="com.eb3.ddt.pojo,com.eb3.loan.pojo"/>
<!--定义Hibernate的SessionFactory的属性 -->
<property name="hibernateProperties">
<props>
<!-- 指定Hibernate的连接方言-->
<prop key="hibernate.dialect">
${hibernate.dialect}
</prop>
<prop key="hibernate.connection.autocommit">${hibernate.connection.autocommit}</prop>
<!-- 制定Hibernate是否打印SQL语句 -->
<prop key="hibernate.show_sql">${hibernate.show_sql}</prop>
<prop key="hibernate.format_sql">${hibernate.format_sql}</prop>
<!-- 设create(启动创建),create-drop(启动创建,退出删除),update(启动更新),validate(启动验证) -->
<prop key="hibernate.hbm2ddl.auto">${hibernate.hbm2ddl.auto}</prop>
<prop key="connection.characterEncoding">utf-8</prop>
<!-- 设置二级缓存 -->
<prop key="hibernate.cache.user_query_cache">${hibernate.cache.user_query_cache}</prop>
<prop key="hibernate.user_second_level_cache">${hibernate.user_second_level_cache}</prop>
<prop key="hibernate.cache.provider_class">${hibernate.cache.class}</prop>
<prop key="hibernate.cache.provider_configuration_file_resource_path">${hibernate.ehcache_config_file}</prop>
</props>
</property>
</bean>
<!-- 事务管理器配置,单数据源事务 -->
<bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
<property name="sessionFactory" ref="sessionFactory"/>
</bean>
    <tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="save*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="add*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="delete*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="update*" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="merge*" isolation="READ_COMMITTED" propagation="REQUIRED" rollback-for="Exception"/>
<tx:method name="get*" read-only="true"/>
<tx:method name="find*" read-only="true"/>
<tx:method name="list*" read-only="true"/>
<tx:method name="select*" read-only="true"/>
<tx:method name="*" propagation="REQUIRED" />
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="interceptorPointCuts" expression="execution(* com.eb3.*.service.*.*(..))" />
<aop:advisor advice-ref="txAdvice" pointcut-ref="interceptorPointCuts" />
</aop:config> <aop:aspectj-autoproxy proxy-target-class="true" /> <!-- 定时器配置 task:scheduler@pool-size调度线程池的大小,调度线程在被调度任务完成前不会空闲 task:executor/@pool-size:可以指定执行线程池的初始大小、最大大小
task:executor/@queue-capacity:等待执行的任务队列的容量 task:executor/@rejection-policy:当等待队已满时的策略,分为丢弃、由任务执行器直接运行等方式
@Async 异步任务时 task任务执行线程数 task:scheduler 和 task:executor 两个线程池同样起作用 没有异步注解时
task任务执行线程数只受task:scheduler的线程池大小影响 -->
<!-- 声明一个具有10个线程的池,每一个对象将获取同样的运行机会 -->
<task:scheduler id="scheduler" pool-size="10" />
<task:executor id="executor" keep-alive="3600" pool-size="100-300" queue-capacity="500" rejection-policy="CALLER_RUNS" />
<task:annotation-driven executor="executor" scheduler="scheduler" /> </beans>

c、继承AbstractRoutingDataSource类的动态数据源类DynamicDataSource

package com.eb3.ddt;

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

public class DynamicDataSource extends AbstractRoutingDataSource {
/**
* 重写determineCurrentLookupKey方法
*/
@Override
protected Object determineCurrentLookupKey() {
Object obj = DBHelper.getDbType();
return obj;
}
}

d、DBHelper工具类

package com.eb3.ddt;

import org.apache.commons.lang.StringUtils;

public class DBHelper {
private static ThreadLocal<String> dbContext = new ThreadLocal<String>(); // 写数据源标识
public final static String DB_WRITE = "dataSourceWrite";
// 读数据源标识
public final static String DB_READ = "dataSourceRead"; /**
* 获取数据源类型,即是写数据源,还是读数据源
*
* @return
*/
public static String getDbType() {
String db_type = dbContext.get();
if (StringUtils.isEmpty(db_type)) {
// 默认是写数据源
db_type = DB_WRITE;
}
return db_type;
} /**
* 设置该线程的数据源类型
*
* @param str
*/
public static void setDbType(String str) {
dbContext.set(str);
}
}

e、服务层调用

/*@Aspect 此注解会影响数据源切换,运行代码得知不加的话会先执行DynamicDataSource里的determineCurrentLookupKey方法,后执行Service层里DBHelper.setDbType()方法,导致数据源切换失败!
*/
@Aspect
@Component("userService")
public class UserServiceImpl extends BaseServiceImpl<User, User, Integer> implements UserService {
@Resource
private UserDao userDao; @Override
protected BaseDao<User, Integer> getDao() {
return this.userDao;
} @Override
public void save(User user) {
DBHelper.setDbType(DBHelper.DB_WRITE); // 写库
this.userDao.save(user);
} @Override
public User findByUserName(String username) {
DBHelper.setDbType(DBHelper.DB_READ); // 读库
List<User> userList = this.userDao.findBy("username", username);
return CollectionUtils.isNotEmpty(userList) ? userList.get(0) : null;
}
}

Java知识点梳理——读写分离的更多相关文章

  1. java知识点梳理

    网络搜索结果,出处不详,仅供参考 对于刚刚接触Java的人,java基础知识技术点繁多,那么gkstk小编为大家汇总最全java知识点如下,仅供大家参考学习! 1. JVM相关(包括了各个版本的特性) ...

  2. Java知识点梳理——集合

    1.定义:Java集合类存放于java.util包,是存放对象的容器,长度可变,只能存放对象,可以存放不同的数据类型: 2.常用集合接口: a.Collection接口:最基本的集合接口,存储不唯一, ...

  3. Java知识点梳理——装箱和拆箱

    1.前言:Java是典型的面向对象编程语言,但其中有8种基本数据类型不支持面向对象编程,基本数据类型不具备对象的特性,没有属性和方法:Java为此8种基本数据类型设计了对应的类(包装类),使之相互转换 ...

  4. Java知识点梳理——继承

    1.定义:继承允许创建分等级层次的类,就是子类继承父类的特征行为,使得子类对象具有父类实例的方法,   使得子类具有父类相同的行为. 2.继承的特性: a.子类拥有父类非priavte的属性.方法: ...

  5. Java知识点梳理——多态

    1.定义:多态是同一个行为具有多个不同表现形式或形态的能力,即一个接口不同的实例执行不同的操作: 2.优点:消除类型之间的耦合关系.可替换性.可扩展性.接口性.灵活性.简化性: 3.多态存在的3个必要 ...

  6. Java知识点梳理——抽象类和接口

    抽象类 1.定义:没有包含足够的信息来描绘一个具体对象的类,不能被实例化,必须被继承: 2.abstract关键字:abstract class定义抽象类,普通类的其它功能依然存在,如变量.方法等: ...

  7. Java知识点梳理——常用方法总结

    1.查找字符串最后一次出现的位置 String str = "my name is zzw"; int lastIndex = str.lastIndexOf("zzw& ...

  8. Java知识点梳理——泛型

    1.定义:泛型的本质是参数化类型,就是将类型由原来的具体的类型参数化,这种参数类型可以用在类.接口.方法中,分别称为泛型类.泛型接口.泛型方法: 2.泛型类:泛型类的声明和非泛型类的声明类似,除了在类 ...

  9. java 知识点梳理

    1.ArrayList与linkedList 区别 ArrayList 采用的是数组形式来保存对象的,这种方式将对象放在连续的位置中,所以最大的缺点就是插入删除时非常麻烦; 优点是查找比较快. Lin ...

随机推荐

  1. ASP.NET MVC 实现 AJAX 跨域请求

    ASP.NET MVC 实现AJAX跨域请求的两种方法 和大家分享下Ajax 跨域的经验,之前也找了好多资料,但是都不行,后来看到个可行的修改了并测试下 果然OK了   希望对大家有所帮助! 通常发送 ...

  2. Codeforces Gym100814 B.Unlucky Teacher (ACM International Collegiate Programming Contest, Egyptian Collegiate Programming Contest (2015) Arab Academy for Science and Technology)

    今日份的训练题解,今天写出来的题没有昨天多,可能是因为有些事吧... 这个题就是老师改卷子,忘带标准答案了,但是他改了一部分卷子,并且确定自己改的卷子没出错,他想从改过的卷子里把标准答案推出来. 因为 ...

  3. 洛谷——1508 Likecloud-吃、吃、吃

    题目背景 问世间,青春期为何物? 答曰:“甲亢,甲亢,再甲亢:挨饿,挨饿,再挨饿!” 题目描述 正处在某一特定时期之中的李大水牛由于消化系统比较发达,最近一直处在饥饿的状态中.某日上课,正当他饿得头昏 ...

  4. POJ 3249 Test for Job (dfs + dp)

    题目链接:http://poj.org/problem?id=3249 题意: 给你一个DAG图,问你入度为0的点到出度为0的点的最长路是多少 思路: 记忆化搜索,注意v[i]可以是负的,所以初始值要 ...

  5. mariadb 启动方法

    通用启动方法 /etc/init.d/mariadb status #查看状态 /etc/init.d/mariadb start #启动 /etc/init.d/mariadb restart #重 ...

  6. Unity -- 材质-Material和预设体-Prefabs

    材质(Materials)用来把网格(Mesh)或粒子渲染器(Particle Renderers)贴到游戏对象上.他们在定义对象怎么被显示发挥重要组成部分.材质包括用于呈现网状或颗粒着色器的参考,所 ...

  7. android 内存泄漏出现的情况

    非静态内部类的静态实例由于内部类默认持有外部类的引用,而静态实例属于类.所以,当外部类被销毁时,内部类仍然持有外部类的引用,致使外部类无法被GC回收.因此造成内存泄露. 类的静态变量持有大数据对象静态 ...

  8. C#面试基础题1

    1.简述 private. protected. public. internal 修饰符的访问权限.(C++中没有internal) private : 私有成员, 在类的内部才可以访问 ,也就是类 ...

  9. (转)python装饰器二

    Python装饰器进阶之二 保存被装饰方法的元数据 什么是方法的元数据 举个栗子 def hello(): print('Hello, World.') print(dir(hello)) 结果如下: ...

  10. df、du、fdisk:Linux磁盘管理

    磁盘是Linux系统中一项非常重要的资源,如何对其进行有效的管理直接关系到整个系统的性能问题.对Linux磁盘管理稍微有一些学习和经验的朋 友们应该都知道df.du和fdisk这三个常用命令:df用于 ...