实现读写分离的Web工程

前言

    本工程基于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();
	}
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值