分庫分表在小型公司不多能遇到也不多使用,畢竟數據量沒有那麼大,當數據量大,全部數據都壓在一張表時,若是單從數據庫的角度考慮是能夠分庫分表處理來存儲數據。分庫分表 顧名思義就是根據查詢條件動態的去獲取數據所在的庫和表的位置.例如一個系統有惟一的標識userNum,全部路由規則均可以根據userNum作庫表的定位工做.本文測試用3個庫5個表作測試.html
三個庫book_00,book_01,book_02 每一個庫裏的表 t_user_0000,t_user_0001,t_user_0002,t_user_0003,t_user_0004, 根據userNum尋找匹配庫的_00後綴,匹配表的_0000 後綴 便可定位到是幾庫幾表.
本人會創建一個新的項目,一步一步講解若是簡單的搭建一個分庫分表的系統而且用頁面展現從庫裏查詢出來的數據.java
1,首先須要建立一個新的maven的webApp項目mysql
使用框架版本:web
Spring 4.0.2 RELEASE算法
Spring MVC 4.0.2 RELEASEspring
MyBatis 3.2.6sql
2,pom裏引入jar包數據庫
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
apache
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
json
<modelVersion>4.0.0</modelVersion>
<groupId>com.subdt</groupId>
<artifactId>subDbTable</artifactId>
<packaging>war</packaging>
<version>1.0-SNAPSHOT</version>
<name>subDbTable Maven Webapp</name>
<url>http://maven.apache.org</url>
<properties>
<!-- spring版本號 -->
<spring.version>4.0.2.RELEASE</spring.version>
<!-- mybatis版本號 -->
<mybatis.version>3.2.6</mybatis.version>
<!-- log4j日誌文件管理包版本 -->
<slf4j.version>1.7.7</slf4j.version>
<log4j.version>1.2.17</log4j.version>
</properties>
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<!-- 表示開發的時候引入,發佈的時候不會加載此包 -->
<scope>test</scope>
</dependency>
<!-- spring核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-oxm</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.9</version>
</dependency>
<dependency>
<groupId>commons-beanutils</groupId>
<artifactId>commons-beanutils</artifactId>
<version>1.8.3</version>
</dependency>
<dependency>
<groupId>commons-lang</groupId>
<artifactId>commons-lang</artifactId>
<version>2.6</version>
</dependency>
<!-- mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>${mybatis.version}</version>
</dependency>
<!-- mybatis/spring包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.2.2</version>
</dependency>
<!-- 導入java ee jar 包 -->
<dependency>
<groupId>javax</groupId>
<artifactId>javaee-api</artifactId>
<version>7.0</version>
</dependency>
<!-- 導入Mysql數據庫連接jar包 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.30</version>
</dependency>
<!-- 導入dbcp的jar包,用來在applicationContext.xml中配置數據庫 -->
<dependency>
<groupId>commons-dbcp</groupId>
<artifactId>commons-dbcp</artifactId>
<version>1.2.2</version>
</dependency>
<!-- JSTL標籤類 -->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 日誌文件管理包 -->
<!-- log start -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>${log4j.version}</version>
</dependency>
<!-- 格式化對象,方便輸出日誌 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.1.41</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${slf4j.version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>${slf4j.version}</version>
</dependency>
<!-- log end -->
<!-- 映入JSON -->
<dependency>
<groupId>org.codehaus.jackson</groupId>
<artifactId>jackson-mapper-asl</artifactId>
<version>1.9.13</version>
</dependency>
<!-- 上傳組件包 -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.9</version>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>1.7.1</version>
</dependency>
</dependencies>
<build>
<finalName>subDbTable</finalName>
<resources>
<!--編譯以後包含xml-->
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-resources-plugin</artifactId>
<configuration>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
</plugins>
</build>
</project>
首先看下目錄結構
首先建立 jdbc.properties 配置數據庫信息
driver=com.mysql.jdbc.Driver
#定義初始鏈接數
initialSize=0
#定義最大鏈接數
maxActive=20
#定義最大空閒
maxIdle=20
#定義最小空閒
minIdle=1
#定義最長等待時間
maxWait=60000
jdbc.mysql.url0=jdbc:mysql://localhost:3306/book_02?createDatabaseIfNotExist=true&characterEncoding=utf-8&useUnicode=true
jdbc.mysql.username0=root
jdbc.mysql.password0=123456
jdbc.mysql.url1=jdbc:mysql://localhost:3306/book_00?createDatabaseIfNotExist=true&characterEncoding=utf-8&useUnicode=true
jdbc.mysql.username1=root
jdbc.mysql.password1=123456
jdbc.mysql.url2=jdbc:mysql://localhost:3306/book_01?createDatabaseIfNotExist=true&characterEncoding=utf-8&useUnicode=true
jdbc.mysql.username2=root
jdbc.mysql.password2=123456
建立Spring配置文件 spring-config.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:context="http://www.springframework.org/schema/context"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-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"
default-autowire="byName">
<context:component-scan base-package="com.sub.dt"/>
<aop:aspectj-autoproxy/>
<!-- 屬性文件讀入 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:props/*.properties</value>
</list>
</property>
</bean>
<import resource="classpath:spring/spring-*.xml"/>
</beans>
建立spring與mybatis結合的整合配置文件. spring-config-datasource-dbcp.xml
這個文件主要是多數據源配置,自動掃描mybatis的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"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<!-- 引入配置文件 -->
<bean id="propertyConfigurer"
class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location" value="classpath:props/jdbc.properties"/>
</bean>
<bean id="dataSource0" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${jdbc.mysql.url0}"/>
<property name="username" value="${jdbc.mysql.username0}"/>
<property name="password" value="${jdbc.mysql.password0}"/>
<!-- 初始化鏈接大小 -->
<property name="initialSize" value="${initialSize}"></property>
<!-- 鏈接池最大數量 -->
<property name="maxActive" value="${maxActive}"></property>
<!-- 鏈接池最大空閒 -->
<property name="maxIdle" value="${maxIdle}"></property>
<!-- 鏈接池最小空閒 -->
<property name="minIdle" value="${minIdle}"></property>
<!-- 獲取鏈接最大等待時間 -->
<property name="maxWait" value="${maxWait}"></property>
</bean>
<bean id="dataSource1" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${jdbc.mysql.url1}"/>
<property name="username" value="${jdbc.mysql.username1}"/>
<property name="password" value="${jdbc.mysql.password1}"/>
<!-- 初始化鏈接大小 -->
<property name="initialSize" value="${initialSize}"></property>
<!-- 鏈接池最大數量 -->
<property name="maxActive" value="${maxActive}"></property>
<!-- 鏈接池最大空閒 -->
<property name="maxIdle" value="${maxIdle}"></property>
<!-- 鏈接池最小空閒 -->
<property name="minIdle" value="${minIdle}"></property>
<!-- 獲取鏈接最大等待時間 -->
<property name="maxWait" value="${maxWait}"></property>
</bean>
<bean id="dataSource2" class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${driver}"/>
<property name="url" value="${jdbc.mysql.url2}"/>
<property name="username" value="${jdbc.mysql.username2}"/>
<property name="password" value="${jdbc.mysql.password2}"/>
<!-- 初始化鏈接大小 -->
<property name="initialSize" value="${initialSize}"></property>
<!-- 鏈接池最大數量 -->
<property name="maxActive" value="${maxActive}"></property>
<!-- 鏈接池最大空閒 -->
<property name="maxIdle" value="${maxIdle}"></property>
<!-- 鏈接池最小空閒 -->
<property name="minIdle" value="${minIdle}"></property>
<!-- 獲取鏈接最大等待時間 -->
<property name="maxWait" value="${maxWait}"></property>
</bean>
<bean id="mysqlDynamicDataSource" class="com.sub.dt.dbRouting.db.DynamicDataSource">
<property name="targetDataSources">
<!-- 標識符類型 -->
<map>
<entry key="db0" value-ref="dataSource0"/>
<entry key="db1" value-ref="dataSource1"/>
<entry key="db2" value-ref="dataSource2"/>
</map>
</property>
</bean>
<bean id="dbRuleSet" class="com.sub.dt.dbRouting.bean.RouterSet">
<property name="routeFieldStart" value="0"></property>
<property name="routeFieldEnd" value="9200000000000000000"></property>
<property name="dbNumber" value="3"></property>
<property name="routeType" value="2"></property>
<property name="ruleType" value="3"></property>
<property name="tableNumber" value="5"></property>
<property name="dbKeyArray">
<list>
<value>db0</value>
<value>db1</value>
<value>db2</value>
</list>
</property>
</bean>
<bean id="dBRouter" class="com.sub.dt.dbRouting.router.DBRouterImpl">
<property name="routerSetList">
<!-- 標識符類型 -->
<list>
<ref bean="dbRuleSet"/>
</list>
</property>
</bean>
<!--事務-->
<bean id="baiTiaoTransactionManager"
class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="mysqlDynamicDataSource"></property>
</bean>
<bean id="btTransactionTemplate" class="org.springframework.transaction.support.TransactionTemplate">
<property name="transactionManager" ref="baiTiaoTransactionManager"></property>
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED"></property>
</bean>
<!-- spring和MyBatis完美整合,不須要mybatis的配置映射文件 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<property name="dataSource" ref="mysqlDynamicDataSource"/>
<!-- 自動掃描mapping.xml文件 -->
<property name="mapperLocations" value="classpath:com/sub/dt/mapping/*.xml"></property>
</bean>
<!-- DAO接口所在包名,Spring會自動查找其下的類 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<property name="basePackage" value="com.sub.dt.dao"/>
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"></property>
</bean>
</beans>
程序使用spring aop切面+自定義註解的方式去在每一個方法調用時根據userNum定位庫表, 關鍵類已標紅(代碼會上傳附件自行下載查看)
spring-config-datasource-dbcp.xml中配置的com.sub.dt.dbRouting.db.DynamicDataSource 繼承 AbstractRoutingDataSource方法,單表配置mybatis是使用的是BasicDataSource
BasicDataSourBasicDataSource
BasicDataSource 自定義的AbstractRoutingDataSource與spring的BasicDataSource都是一個道理 管理數據源 實現了DataSource接口.
package com.sub.dt.dbRouting.db;
import com.sub.dt.dbRouting.DbContextHolder;
import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;
import java.util.logging.Logger;
/**
* @Description SPring 的動態數據源的實現
* @Autohr supers【weChat:13031016567】
*/
public class DynamicDataSource extends AbstractRoutingDataSource {
public static final Logger logger = Logger.getLogger(DynamicDataSource.class.toString());
protected Object determineCurrentLookupKey() {
return DbContextHolder.getDbKey();//獲取當前數據源
}
}
UserMapper.xml建立庫表對應的xml文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sub.dt.dao.IUserDao">
<resultMap id="BaseResultMap" type="com.sub.dt.pojo.User">
<id column="id" property="id" jdbcType="INTEGER"/>
<result column="user_name" property="userName" jdbcType="VARCHAR"/>
<result column="password" property="password" jdbcType="VARCHAR"/>
<result column="age" property="age" jdbcType="INTEGER"/>
<result column="user_num" property="userNum" jdbcType="VARCHAR"/>
</resultMap>
<sql id="Base_Column_List">
id,user_num, user_name, password, age
</sql>
<insert id="insertUser" parameterType="com.sub.dt.pojo.User">
insert into t_user${tableIndex} (id,user_num,user_name, password,age)
values (#{id,jdbcType=INTEGER},#{userNum,jdbcType=VARCHAR}, #{userName,jdbcType=VARCHAR}, #{password,jdbcType=VARCHAR},#{age,jdbcType=INTEGER})
</insert>
<insert id="deleteByuserNum" parameterType="com.sub.dt.pojo.User">
delete from t_user${tableIndex}
where user_num = #{userNum,jdbcType=VARCHAR}
</insert>
<update id="updateByUserNum" parameterType="com.sub.dt.pojo.User">
update t_user${tableIndex}
<set>
<if test="userName != null">
user_name = #{userName,jdbcType=VARCHAR},
</if>
<if test="password != null">
password = #{password,jdbcType=VARCHAR},
</if>
<if test="age != null">
age = #{age,jdbcType=INTEGER},
</if>
</set>
where user_num = #{userNum,jdbcType=VARCHAR}
</update>
<select id="selectByUserNum" resultMap="BaseResultMap" parameterType="com.sub.dt.pojo.User">
select
<include refid="Base_Column_List"/>
from t_user${tableIndex}
where user_num = #{userNum,jdbcType=VARCHAR}
</select>
</mapper>
IUserDao.java
package com.sub.dt.dao;
import com.sub.dt.pojo.User;
public interface IUserDao {
/**
* @Description
* @Autohr supers【weChat:13031016567】
*/
int insertUser(User user);
/**
* @Description
* @Autohr supers【weChat:13031016567】
*/
int deleteByuserNum(User user);
/**
* @Description
* @Autohr supers【weChat:13031016567】
*/
int updateByUserNum(User user);
/**
* @Description
* @Autohr supers【weChat:13031016567】
*/
User selectByUserNum(User user);
}
IUserService.java
package com.sub.dt.service;
import com.sub.dt.pojo.User;
/**
* @Description
* @Autohr supers【weChat:13031016567】
*/
public interface IUserService {
/**
* @Description
* @Autohr supers【weChat:13031016567】
*/
public int insertUser(User user);
/**
* @Description
* @Autohr supers【weChat:13031016567】
*/
public int deleteByuserNum(User user);
/**
* @Description
* @Autohr supers【weChat:13031016567】
*/
public int updateByUserNum(User user);
/**
* @Description
* @Autohr supers【weChat:13031016567】
*/
public User selectByUserNum(User user);
}
UserServiceImpl.java
package com.sub.dt.service.impl;
import com.sub.dt.dao.IUserDao;
import com.sub.dt.dbRouting.annotation.Router;
import com.sub.dt.pojo.User;
import com.sub.dt.service.IUserService;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
/**
* @Description
* @Autohr supers【weChat:13031016567】
*/
@Service("userService")
public class UserServiceImpl implements IUserService {
@Resource
private IUserDao userDao;
@Router
public int insertUser(User user) {
return this.userDao.insertUser(user);
}
@Router
public int deleteByuserNum(User user) {
return this.userDao.deleteByuserNum(user);
}
@Router
public int updateByUserNum(User user) {
return this.userDao.updateByUserNum(user);
}
@Router
public User selectByUserNum(User user) {
return this.userDao.selectByUserNum(user);
}
}
UserController.java
package com.sub.dt.controller;
import com.sub.dt.pojo.User;
import com.sub.dt.service.IUserService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
@Controller
@RequestMapping("/user")
public class UserController {
@Resource
private IUserService userService;
@RequestMapping("/queryUser")
public String toIndex(HttpServletRequest request,Model model,User user){
User userDb = this.userService.selectByUserNum(user);
model.addAttribute("user", userDb);
return "queryUser";
}
}
4,核心分庫分表包都在dbRouting文件夾下
Router.java 自定義註解,此註解做用是當作一個切點,在方法上添加此註解,執行方法前會執行DBRouterInterceptor的doRoute方法.詳情可參考:http://blog.csdn.net/buchengbugui/article/details/60875401
package com.sub.dt.dbRouting.annotation;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* @Description
* @Autohr supers【weChat:13031016567】
*/
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Router {
String routerField() default RouterConstants.ROUTER_FIELD_DEFAULT;
String tableStyle() default RouterConstants.ROUTER_TABLE_SUFFIX_DEFAULT;
}
DBRouterInterceptor.java 定義切點爲方法上使用,@Router註解的方法,執行註解方法前前執行doRoute方法,根據參數中的userNum設置是幾庫幾表
package com.sub.dt.dbRouting;
import com.sub.dt.dbRouting.annotation.Router;
import com.sub.dt.dbRouting.annotation.RouterConstants;
import com.sub.dt.dbRouting.router.RouterUtils;
import org.apache.commons.beanutils.BeanUtils;
import org.apache.commons.lang.StringUtils;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import java.lang.reflect.Method;
/**
* @Description 切面切點 在Router註解的方法執行前執行 切點織入
* @Autohr supers【weChat:13031016567】
*/
@Aspect
@Component
public class DBRouterInterceptor {
private static final Logger log = LoggerFactory.getLogger(DBRouterInterceptor.class);
private DBRouter dBRouter;
@Pointcut("@annotation( com.sub.dt.dbRouting.annotation.Router)")
public void aopPoint() {
}
@Before("aopPoint()")
public Object doRoute(JoinPoint jp) throws Throwable {
long t1 = System.currentTimeMillis();
boolean result = true;
Method method = getMethod(jp);
Router router = method.getAnnotation(Router.class);
String routeField = router.routerField();
Object[] args = jp.getArgs();
if (args != null && args.length > 0) {
for (int i = 0; i < args.length; i++) {
long t2 = System.currentTimeMillis();
String routeFieldValue = BeanUtils.getProperty(args[i],
routeField);
log.debug("routeFieldValue{}" + (System.currentTimeMillis() - t2));
if (StringUtils.isNotEmpty(routeFieldValue)) {
if (RouterConstants.ROUTER_FIELD_DEFAULT.equals(routeField)) {
dBRouter.doRouteByResource("" + RouterUtils.getResourceCode(routeFieldValue));
break;
} else {
this.searchParamCheck(routeFieldValue);
String resource = routeFieldValue.substring(routeFieldValue.length() - 4);
dBRouter.doRouteByResource(resource);
break;
}
}
}
}
log.debug("doRouteTime{}" + (System.currentTimeMillis() - t1));
return result;
}
private Method getMethod(JoinPoint jp) throws NoSuchMethodException {
Signature sig = jp.getSignature();
MethodSignature msig = (MethodSignature) sig;
return getClass(jp).getMethod(msig.getName(), msig.getParameterTypes());
}
private Class<? extends Object> getClass(JoinPoint jp)
throws NoSuchMethodException {
return jp.getTarget().getClass();
}
/**
* 查詢支付結構參數檢查
*
* @param payId
*/
private void searchParamCheck(String payId) {
if (payId.trim().equals("")) {
throw new IllegalArgumentException("payId is empty");
}
}
public DBRouter getdBRouter() {
return dBRouter;
}
public void setdBRouter(DBRouter dBRouter) {
this.dBRouter = dBRouter;
}
}
RouterSet.java 主要是 配置在spring-config-datasource-dbcp.xml中 存儲庫的數量,表的數量,庫表相關信息
package com.sub.dt.dbRouting.bean;
import java.util.List;
/**
* @Description
* @Autohr supers【weChat:13031016567】
*/
public class RouterSet {
/**根據字符串*/
public final static int RULE_TYPE_STR=3;
public final static int ROUTER_TYPE_DB=0;
public final static int ROUTER_TYPE_TABLE =1;
public final static int ROUTER_TYPE_DBANDTABLE=2;
/**數據庫表的邏輯KEY,與數據源MAP配置中的key一致*/
private List<String> dbKeyArray;
/**數據庫數量*/
private int dbNumber;
/**數據表數量*/
private int tableNumber;
/**數據表index樣式*/
private String tableIndexStyle;
/**Id開始*/
private String routeFieldStart;
/**Id結束*/
private String routeFieldEnd;
/**規則類型*/
private int ruleType;
/**路由類型類型*/
private int routeType;
public static int getRULE_TYPE_STR() {
return RULE_TYPE_STR;
}
public static int getROUTER_TYPE_DB() {
return ROUTER_TYPE_DB;
}
public static int getROUTER_TYPE_TABLE() {
return ROUTER_TYPE_TABLE;
}
public static int getROUTER_TYPE_DBANDTABLE() {
return ROUTER_TYPE_DBANDTABLE;
}
public List<String> getDbKeyArray() {
return dbKeyArray;
}
public void setDbKeyArray(List<String> dbKeyArray) {
this.dbKeyArray = dbKeyArray;
}
public int getDbNumber() {
return dbNumber;
}
public void setDbNumber(int dbNumber) {
this.dbNumber = dbNumber;
}
public int getTableNumber() {
return tableNumber;
}
public void setTableNumber(int tableNumber) {
this.tableNumber = tableNumber;
}
public String getTableIndexStyle() {
return tableIndexStyle;
}
public void setTableIndexStyle(String tableIndexStyle) {
this.tableIndexStyle = tableIndexStyle;
}
public String getRouteFieldStart() {
return routeFieldStart;
}
public void setRouteFieldStart(String routeFieldStart) {
this.routeFieldStart = routeFieldStart;
}
public String getRouteFieldEnd() {
return routeFieldEnd;
}
public void setRouteFieldEnd(String routeFieldEnd) {
this.routeFieldEnd = routeFieldEnd;
}
public int getRuleType() {
return ruleType;
}
public void setRuleType(int ruleType) {
this.ruleType = ruleType;
}
public int getRouteType() {
return routeType;
}
public void setRouteType(int routeType) {
this.routeType = routeType;
}
}
DBRouterImpl.java getDbKey方法根據userNum定位庫表算法
package com.sub.dt.dbRouting.router;
import com.sub.dt.dbRouting.DBRouter;
import com.sub.dt.dbRouting.DbContextHolder;
import com.sub.dt.dbRouting.annotation.RouterConstants;
import com.sub.dt.dbRouting.bean.RouterSet;
import org.apache.commons.lang.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.text.DecimalFormat;
import java.util.List;
/**
* @Description 根據指定變量動態切 庫和表
* @Autohr supers【weChat:13031016567】
*/
public class DBRouterImpl implements DBRouter {
private static final Logger log = LoggerFactory.getLogger(DBRouterImpl.class);
/**
* 配置列表
*/
private List<RouterSet> routerSetList;
@Override
public String doRoute(String fieldId) {
if (StringUtils.isEmpty(fieldId)) {
throw new IllegalArgumentException("dbsCount and tablesCount must be both positive!");
}
int routeFieldInt = RouterUtils.getResourceCode(fieldId);
String dbKey = getDbKey(routerSetList, routeFieldInt);
return dbKey;
}
@Override
public String doRouteByResource(String resourceCode) {
if (StringUtils.isEmpty(resourceCode)) {
throw new IllegalArgumentException("dbsCount and tablesCount must be both positive!");
}
int routeFieldInt = Integer.valueOf(resourceCode);
String dbKey = getDbKey(routerSetList, routeFieldInt);
return dbKey;
}
/**
* @Description 根據數據字段來判斷屬於哪一個段的規則,得到數據庫key
* @Autohr supers【weChat:13031016567】
*/
private String getDbKey(List<RouterSet> routerSets, int routeFieldInt) {
RouterSet routerSet = null;
if (routerSets == null || routerSets.size() <= 0) {
throw new IllegalArgumentException("dbsCount and tablesCount must be both positive!");
}
String dbKey = null;
for (RouterSet item : routerSets) {
if (item.getRuleType() == routerSet.RULE_TYPE_STR) {
routerSet = item;
if (routerSet.getDbKeyArray() != null && routerSet.getDbNumber() != 0) {
long dbIndex = 0;
long tbIndex = 0;
//默認按照分庫進行計算
long mode = routerSet.getDbNumber();
//若是是按照分庫分表的話,計算
if (item.getRouteType() == RouterSet.ROUTER_TYPE_DBANDTABLE && item.getTableNumber() != 0) {
mode = routerSet.getDbNumber() * item.getTableNumber();
dbIndex = routeFieldInt % mode / item.getTableNumber();
tbIndex = routeFieldInt % item.getTableNumber();
String tableIndex = getFormateTableIndex(item.getTableIndexStyle(), tbIndex);
DbContextHolder.setTableIndex(tableIndex);
} else if (item.getRouteType() == RouterSet.ROUTER_TYPE_DB) {
mode = routerSet.getDbNumber();
dbIndex = routeFieldInt % mode;
} else if (item.getRouteType() == RouterSet.ROUTER_TYPE_TABLE) {
tbIndex = routeFieldInt % item.getTableNumber();
String tableIndex = getFormateTableIndex(item.getTableIndexStyle(), tbIndex);
DbContextHolder.setTableIndex(tableIndex);
}
dbKey = routerSet.getDbKeyArray().get(Long.valueOf(dbIndex).intValue());
log.debug("getDbKey resource:{}------->dbkey:{},tableIndex:{},", new Object[]{routeFieldInt, dbKey, tbIndex});
DbContextHolder.setDbKey(dbKey);
}
break;
}
}
return dbKey;
}
/**
* @Description 此方法是將例如+++0000根式的字符串替換成傳參數字例如44 變成+++0044
* @Autohr supers【weChat:13031016567】
*/
private static String getFormateTableIndex(String style, long tbIndex) {
String tableIndex = null;
DecimalFormat df = new DecimalFormat();
if (StringUtils.isEmpty(style)) {
style = RouterConstants.ROUTER_TABLE_SUFFIX_DEFAULT;//在格式後添加諸如單位等字符
}
df.applyPattern(style);
tableIndex = df.format(tbIndex);
return tableIndex;
}
public List<RouterSet> getRouterSetList() {
return routerSetList;
}
public void setRouterSetList(List<RouterSet> routerSetList) {
this.routerSetList = routerSetList;
}
}
spring-mvc.xml這個很簡單了 就是前段與後端controller層的結合,很少說.
<?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:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<!-- 自動掃描該包,使SpringMVC認爲包下用了@controller註解的類是控制器 -->
<context:component-scan base-package="com.sub.dt.controller"/>
<!--避免IE執行AJAX時,返回JSON出現下載文件 -->
<bean id="mappingJacksonHttpMessageConverter"
class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<value>text/html;charset=UTF-8</value>
</list>
</property>
</bean>
<!-- 啓動SpringMVC的註解功能,完成請求和註解POJO的映射 -->
<bean
class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
<property name="messageConverters">
<list>
<ref bean="mappingJacksonHttpMessageConverter"/>
<!-- JSON轉換器 -->
</list>
</property>
</bean>
<!-- 定義跳轉的文件的先後綴 ,視圖模式配置-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 這裏的配置個人理解是自動給後面action的方法return的字符串加上前綴和後綴,變成一個 可用的url地址 -->
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 配置文件上傳,若是沒有使用文件上傳能夠不用配置,固然若是不配,那麼配置文件中也沒必要引入上傳組件包 -->
<bean id="multipartResolver"
class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 默認編碼 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 文件大小最大值 -->
<property name="maxUploadSize" value="10485760000"/>
<!-- 內存中的最大值 -->
<property name="maxInMemorySize" value="40960"/>
</bean>
</beans>
web.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
version="3.0">
<display-name>Archetype Created Web Application</display-name>
<!-- Spring和mybatis的配置文件 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-config.xml</param-value>
</context-param>
<!-- 編碼過濾器 -->
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<async-supported>true</async-supported>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Spring監聽器 -->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 防止Spring內存溢出監聽器 -->
<listener>
<listener-class>org.springframework.web.util.IntrospectorCleanupListener</listener-class>
</listener>
<!-- Spring MVC servlet -->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring-mvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
<async-supported>true</async-supported>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<!-- 此處能夠能夠配置成*.do,對應struts的後綴習慣 -->
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>/index.jsp</welcome-file>
</welcome-file-list>
</web-app>
奉上 mysql建立表的sql語句,首先自行建立三個庫book_00,book_01,book_02 每一個庫都執行如下sql便可
/*
Navicat MySQL Data Transfer
Source Server : local
Source Server Version : 50173
Source Host : localhost:3306
Source Database : book_00
Target Server Type : MYSQL
Target Server Version : 50173
File Encoding : 65001
Date: 2017-03-10 09:48:12
*/
SET FOREIGN_KEY_CHECKS=0;
-- ----------------------------
-- Table structure for `t_user_0000`
-- ----------------------------
DROP TABLE IF EXISTS `t_user_0000`;
CREATE TABLE `t_user_0000` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_num` varchar(64) NOT NULL,
`user_name` varchar(16) NOT NULL,
`password` varchar(64) NOT NULL,
`age` int(4) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_user_num` (`user_num`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_user_0000
-- ----------------------------
-- ----------------------------
-- Table structure for `t_user_0001`
-- ----------------------------
DROP TABLE IF EXISTS `t_user_0001`;
CREATE TABLE `t_user_0001` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_num` varchar(64) NOT NULL,
`user_name` varchar(16) NOT NULL,
`password` varchar(64) NOT NULL,
`age` int(4) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_user_num` (`user_num`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_user_0001
-- ----------------------------
-- ----------------------------
-- Table structure for `t_user_0002`
-- ----------------------------
DROP TABLE IF EXISTS `t_user_0002`;
CREATE TABLE `t_user_0002` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_num` varchar(64) NOT NULL,
`user_name` varchar(16) NOT NULL,
`password` varchar(64) NOT NULL,
`age` int(4) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_user_num` (`user_num`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_user_0002
-- ----------------------------
-- ----------------------------
-- Table structure for `t_user_0003`
-- ----------------------------
DROP TABLE IF EXISTS `t_user_0003`;
CREATE TABLE `t_user_0003` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_num` varchar(64) NOT NULL,
`user_name` varchar(16) NOT NULL,
`password` varchar(64) NOT NULL,
`age` int(4) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_user_num` (`user_num`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_user_0003
-- ----------------------------
-- ----------------------------
-- Table structure for `t_user_0004`
-- ----------------------------
DROP TABLE IF EXISTS `t_user_0004`;
CREATE TABLE `t_user_0004` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`user_num` varchar(64) NOT NULL,
`user_name` varchar(16) NOT NULL,
`password` varchar(64) NOT NULL,
`age` int(4) NOT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `idx_user_num` (`user_num`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;
-- ----------------------------
-- Records of t_user_0004
全部代碼建立完畢
測試類 TestUserService.java
package com.sub.dt.service;
import com.sub.common.GsonUtils;
import com.sub.dt.dbRouting.DbContextHolder;
import com.sub.dt.pojo.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
/**
* Created by supers on 2017/3/9.
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/spring-config.xml")
public class TestUserService {
@Autowired
private IUserService userService;
/**
* @Description 測試分庫分表插入
* @Autohr supers【weChat:13031016567】
*/
@Test
public void testInsertUser(){
User user = new User();
user.setUserNum("wergsgdf3243");
user.setUserName("admin");
user.setAge(23);
user.setPassword("adf23");
int re = userService.insertUser(user);
System.out.println(DbContextHolder.getDbKey()+"庫 "+DbContextHolder.getTableIndex()+"表 的插入結果:"+GsonUtils.toJson(re));
}
/**
* @Description 測試分庫分表刪除
* @Autohr supers【weChat:13031016567】
*/
@Test
public void testDeleteByuserNum(){
User user = new User();
user.setUserNum("wergsgdf3243");
int re = userService.deleteByuserNum(user);
System.out.println(DbContextHolder.getDbKey()+"庫 "+DbContextHolder.getTableIndex()+"表 的刪除結果:"+GsonUtils.toJson(re));
}
/**
* @Description 測試分庫分表修改
* @Autohr supers【weChat:13031016567】
*/
@Test
public void testupdateByUserNum(){
User user = new User();
user.setUserNum("wergsgdf3243");
user.setAge(34);
int re = userService.updateByUserNum(user);
System.out.println(DbContextHolder.getDbKey()+"庫 "+DbContextHolder.getTableIndex()+"表 的更新結果:"+GsonUtils.toJson(re));
}
/**
* @Description 測試分庫分表查詢
* @Autohr supers【weChat:13031016567】
*/
@Test
public void testQueryUserByNum(){
User user = new User();
user.setId(1);
user.setUserNum("wergsgdf3243");
User userDb = userService.selectByUserNum(user);
System.out.println(DbContextHolder.getDbKey()+"庫 "+DbContextHolder.getTableIndex()+"表 的查詢結果:"+GsonUtils.toJson(userDb));
}
}
最後奉上 log4j.properties配置
#定義LOG輸出級別
log4j.rootLogger=DEBUG,Console,File
#定義日誌輸出目的地爲控制檯
log4j.appender.Console=org.apache.log4j.ConsoleAppender
log4j.appender.Console.Target=System.out
#能夠靈活地指定日誌輸出格式,下面一行是指定具體的格式
log4j.appender.Console.layout = org.apache.log4j.PatternLayout
log4j.appender.Console.layout.ConversionPattern=[%c] - %m%n
#文件大小到達指定尺寸的時候產生一個新的文件
log4j.appender.File = org.apache.log4j.RollingFileAppender
#指定輸出目錄
log4j.appender.File.File = logs/ssm.log
#定義文件最大大小
log4j.appender.File.MaxFileSize = 10MB
# 輸出因此日誌,若是換成DEBUG表示輸出DEBUG以上級別日誌
log4j.appender.File.Threshold = ALL
log4j.appender.File.layout = org.apache.log4j.PatternLayout
log4j.appender.File.layout.ConversionPattern =[%p] [%d{yyyy-MM-dd HH\:mm\:ss}][%c]%m%n
至此,SSM三大框架的整合以及分庫分表策略完畢,在此基礎上可再添加其餘功能,進行開發。本文講解略顯粗糙,本人語言表達水平有限,盡力了.