前言
本工程基于Spring提供的AbstractRoutingDataSource,实现了一个动态数据源的功能,即可以做到,当往数据库里面写数据时,则将数据写到一个数据库当中,一般称为写数据库;当要查询数据时,则获取另一个数据库中的信息,这个数据库一般称为读数据库。这样做,有利于提高网站的性能,特别是在数据库这一层。本工程就是实现了这样一个功能,当然对于写数据库如何跟读数据库如何同步的问题,这章不讲,下次文章再来解决。以下将关键代码附上。
相关配置文件
spring-beans.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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xmlns:jdbc="http://www.springframework.org/schema/jdbc" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:jpa="http://www.springframework.org/schema/data/jpa" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.1.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.1.xsd http://www.springframework.org/schema/jdbc http://www.springframework.org/schema/jdbc/spring-jdbc-3.1.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.1.xsd http://www.springframework.org/schema/data/jpa http://www.springframework.org/schema/data/jpa/spring-jpa.xsd" default-lazy-init="true"> <!-- 自动扫描与装配bean --> <context:component-scan base-package="com.tongtongxue"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" /> </context:component-scan> <!-- 数据源连接配置文件 --> <context:property-placeholder location="classpath:jdbc.properties"/> <!-- 写数据源配置 --> <bean id="dataSourceWrite" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${w.jdbc.driver}" /> <property name="url" value="${w.jdbc.url}" /> <property name="username" value="${w.jdbc.username}" /> <property name="password" value="${w.jdbc.password}" /> <property name="initialSize" value="${w.jdbc.initialSize}" /> <property name="maxActive" value="${w.jdbc.maxActive}" /> <property name="maxIdle" value="${w.jdbc.maxIdle}" /> <property name="maxWait" value="${w.jdbc.maxWait}" /> </bean> <!-- 读数据源配置 --> <bean id="dataSourceRead" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <property name="driverClassName" value="${r.jdbc.driver}" /> <property name="url" value="${r.jdbc.url}" /> <property name="username" value="${r.jdbc.username}" /> <property name="password" value="${r.jdbc.password}" /> <property name="initialSize" value="${r.jdbc.initialSize}" /> <property name="maxActive" value="${r.jdbc.maxActive}" /> <property name="maxIdle" value="${r.jdbc.maxIdle}" /> <property name="maxWait" value="${r.jdbc.maxWait}" /> </bean> <!-- 动态数据源 --> <bean id="dynamicDataSource" class="com.tongtongxue.rw.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> <!-- spring和mybatis整合,不需要mybatis的配置映射文件 --> <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dynamicDataSource"/> <property name="typeAliasesPackage" value="com.tongtongxue.rw.model"/> <property name="mapperLocations" value="classpath:com/tongtongxue/rw/model/mapper/*Mapper.xml" /> </bean> <!-- 扫描basePackage下所有以@Repository标识的 接口进行自动生成代理的Dao --> <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="basePackage" value="com.tongtongxue"/> <property name="annotationClass" value="org.springframework.stereotype.Repository"/> </bean> <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSourceWrite" /> </bean> <!-- 注解式事务管理,需要在Service类上标注@Transactional --> <tx:annotation-driven transaction-manager="transactionManager" /> <!-- 处理包含文件上传的MultipartRequest --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 单位:字节, 1048576=1M,5242880=5M,10485760=10M,20971520=20M,52428800=50M --> <property name="maxUploadSize"> <value>20971520</value> </property> <property name="maxInMemorySize"> <value>4096</value> </property> <property name="defaultEncoding"> <value>UTF-8</value> </property> </bean> </beans>
动态数据源类DynamicDataSource
package com.tongtongxue.rw; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; /** * 动态数据源 * * @author lzj * */ public class DynamicDataSource extends AbstractRoutingDataSource { @Override protected Object determineCurrentLookupKey() { return DBHelper.getDbType(); } }
于设置及获取每个线程访问的哪个数据源
基于ThreadLocal来实现的。
package com.tongtongxue.rw; import org.apache.commons.lang.StringUtils; /** * 用于设置及获取每个线程访问的哪个数据源 * * @author lzj * */ 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); } }
用户服务类
package com.tongtongxue.rw.service.impl; import java.util.List; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; import com.tongtongxue.rw.DBHelper; import com.tongtongxue.rw.dao.UserDao; import com.tongtongxue.rw.model.User; import com.tongtongxue.rw.service.IUserService; /** * 用户服务类 * * @author lzj * */ @Service @Transactional public class UserService implements IUserService { @Autowired private UserDao userDao; @Override public void create(User user) throws RuntimeException { // 创建用户是写数据,则是将数据放到“写数据源”中 DBHelper.setDbType(DBHelper.DB_WRITE); // 保存用户信息 userDao.create(user); } @Transactional(propagation = Propagation.NOT_SUPPORTED) @Override public List<User> queryForList() throws RuntimeException { // 查询用户数据,则是获取“读数据源”中的数据 DBHelper.setDbType(DBHelper.DB_READ); return userDao.queryForList(); } }