一、数据库规划
192.128.127.129和192.128.127.134
192.128.127.129 创建数据库,数据库名为sharding_test
192.128.127.134 创建数据库,数据库名为sharding_test2
如下图所示

在每个分库下建立一样的分表,用户表,用户地址表,订单表,订单明细表和商品表。同一个用户的用户表数据和订单数据在一个库中。除了商品表不需要分表以外,其他表都需要分表(商品表作为全局表放在每个库里)以提高查询性能。建表语句如下:
SET NAMES utf8mb4; -- ---------------------------- -- Table structure for t_order_0 -- ---------------------------- DROP TABLE IF EXISTS `t_order_0`; CREATE TABLE `t_order_0` ( `order_id` bigint(32) NOT NULL COMMENT '主键', `order_no` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '订单号', `user_id` bigint(32) NOT NULL COMMENT '用户id', `order_amount` decimal(20, 2) NOT NULL DEFAULT 0.00 COMMENT '订单总额', `order_status` int(4) NOT NULL DEFAULT 1 COMMENT '订单状态,1-进行中,2-已完成,3-已取消', `remark` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '留言', PRIMARY KEY (`order_id`), INDEX `idx_order_user_id`(`user_id`), INDEX `idx_order_order_no`(`order_no`) ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '订单表'; -- ---------------------------- -- Table structure for t_order_1 -- ---------------------------- DROP TABLE IF EXISTS `t_order_1`; CREATE TABLE `t_order_1` ( `order_id` bigint(32) NOT NULL COMMENT '主键', `order_no` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '订单号', `user_id` bigint(32) NOT NULL COMMENT '用户id', `order_amount` decimal(20, 2) NOT NULL DEFAULT 0.00 COMMENT '订单总额', `order_status` int(4) NOT NULL DEFAULT 1 COMMENT '订单状态,1-进行中,2-已完成,3-已取消', `remark` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '留言', PRIMARY KEY (`order_id`), INDEX `idx_order_user_id`(`user_id`), INDEX `idx_order_order_no`(`order_no`) ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '订单表'; -- ---------------------------- -- Table structure for t_order_item_0 -- ---------------------------- DROP TABLE IF EXISTS `t_order_item_0`; CREATE TABLE `t_order_item_0` ( `order_item_id` bigint(32) NOT NULL COMMENT '主键', `order_id` bigint(32) NOT NULL COMMENT '订单id', `product_id` bigint(32) NOT NULL COMMENT '商品id', `item_price` decimal(20, 2) NOT NULL DEFAULT 0.00 COMMENT '单价', `total_num` int(4) NOT NULL DEFAULT 1 COMMENT '数量', `total_price` decimal(20, 2) NOT NULL DEFAULT 0.00 COMMENT '订单总额', `order_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '下单时间', `user_id` bigint(32) NOT NULL COMMENT '用户id', PRIMARY KEY (`order_item_id`), INDEX `idx_order_item_order_id`(`order_id`), INDEX `idx_order_item_user_id`(`user_id`), INDEX `idx_order_item_product_id`(`product_id`), INDEX `idx_order_item_order_time`(`order_time`) ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '订单明细表'; -- ---------------------------- -- Table structure for t_order_item_1 -- ---------------------------- DROP TABLE IF EXISTS `t_order_item_1`; CREATE TABLE `t_order_item_1` ( `order_item_id` bigint(32) NOT NULL COMMENT '主键', `order_id` bigint(32) NOT NULL COMMENT '订单id', `product_id` bigint(32) NOT NULL COMMENT '商品id', `item_price` decimal(20, 2) NOT NULL DEFAULT 0.00 COMMENT '单价', `total_num` int(4) NOT NULL DEFAULT 1 COMMENT '数量', `total_price` decimal(20, 2) NOT NULL DEFAULT 0.00 COMMENT '订单总额', `order_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '下单时间', `user_id` bigint(32) NOT NULL COMMENT '用户id', PRIMARY KEY (`order_item_id`), INDEX `idx_order_item_order_id`(`order_id`), INDEX `idx_order_item_user_id`(`user_id`), INDEX `idx_order_item_product_id`(`product_id`), INDEX `idx_order_item_order_time`(`order_time`) ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '订单明细表'; -- ---------------------------- -- Table structure for t_product -- ---------------------------- DROP TABLE IF EXISTS `t_product`; CREATE TABLE `t_product` ( `product_id` bigint(32) NOT NULL COMMENT '主键', `code` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '商品编码', `name` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '商品名称', `desc` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '商品描述', PRIMARY KEY (`product_id`), INDEX `idx_user_product_code`(`code`) ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '商品表'; -- ---------------------------- -- Table structure for t_user_0 -- ---------------------------- DROP TABLE IF EXISTS `t_user_0`; CREATE TABLE `t_user_0` ( `user_id` bigint(32) NOT NULL COMMENT '主键', `id_number` varchar(18) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '身份证号码', `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '姓名', `age` int(4) DEFAULT NULL COMMENT '年龄', `gender` int(2) DEFAULT 1 COMMENT '性别:1-男;2-女', `birth_date` date DEFAULT NULL COMMENT '出生日期', PRIMARY KEY (`user_id`) ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表'; -- ---------------------------- -- Table structure for t_user_1 -- ---------------------------- DROP TABLE IF EXISTS `t_user_1`; CREATE TABLE `t_user_1` ( `user_id` bigint(32) NOT NULL COMMENT '主键', `id_number` varchar(18) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '身份证号码', `name` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '姓名', `age` int(4) DEFAULT NULL COMMENT '年龄', `gender` int(2) DEFAULT 1 COMMENT '性别:1-男;2-女', `birth_date` date DEFAULT NULL COMMENT '出生日期', PRIMARY KEY (`user_id`) ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '用户表'; -- ---------------------------- -- Table structure for t_user_address_0 -- ---------------------------- DROP TABLE IF EXISTS `t_user_address_0`; CREATE TABLE `t_user_address_0` ( `address_id` bigint(32) NOT NULL COMMENT '主键', `user_id` bigint(32) NOT NULL COMMENT '用户id', `province` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '省', `city` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '市', `district` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '区', `detail` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '详细地址', `sort` int(4) DEFAULT 1 COMMENT '排序', `gender` int(2) DEFAULT 1 COMMENT '性别:1-男;2-女', PRIMARY KEY (`address_id`), INDEX `idx_user_address_user_id`(`user_id`) ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '地址表'; -- ---------------------------- -- Table structure for t_user_address_1 -- ---------------------------- DROP TABLE IF EXISTS `t_user_address_1`; CREATE TABLE `t_user_address_1` ( `address_id` bigint(32) NOT NULL COMMENT '主键', `user_id` bigint(32) NOT NULL COMMENT '用户id', `province` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '省', `city` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL COMMENT '市', `district` varchar(20) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '区', `detail` varchar(100) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL COMMENT '详细地址', `sort` int(4) DEFAULT 1 COMMENT '排序', `gender` int(2) DEFAULT 1 COMMENT '性别:1-男;2-女', PRIMARY KEY (`address_id`), INDEX `idx_user_address_user_id`(`user_id`) ) ENGINE = InnoDB CHARACTER SET = utf8mb4 COLLATE = utf8mb4_general_ci COMMENT = '地址表';
二、工程创建
整个工程结构如下下图所示

1、创建Spring Boot工程

工程名为my-sharding-jdbc-demo

Spring Boot 版本为2.2.11,选择的依赖如下图所示

2、MyBatis-Generator生成相关代码
(1) mapping文件

OrderItemMapping.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.example.myshardingjdbcdemo.dao.OrderItemMapper" >
<resultMap id="BaseResultMap" type="com.example.myshardingjdbcdemo.model.OrderItem" >
<id column="order_item_id" property="orderItemId" jdbcType="BIGINT" />
<result column="order_id" property="orderId" jdbcType="BIGINT" />
<result column="product_id" property="productId" jdbcType="BIGINT" />
<result column="item_price" property="itemPrice" jdbcType="DECIMAL" />
<result column="total_num" property="totalNum" jdbcType="INTEGER" />
<result column="total_price" property="totalPrice" jdbcType="DECIMAL" />
<result column="order_time" property="orderTime" jdbcType="TIMESTAMP" />
<result column="user_id" property="userId" jdbcType="BIGINT" />
</resultMap>
<sql id="Base_Column_List" >
order_item_id, order_id, product_id, item_price, total_num, total_price, order_time,
user_id
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
select
<include refid="Base_Column_List" />
from t_order_item
where order_item_id = #{orderItemId,jdbcType=BIGINT}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
delete from t_order_item
where order_item_id = #{orderItemId,jdbcType=BIGINT}
</delete>
<insert id="insert" parameterType="com.example.myshardingjdbcdemo.model.OrderItem" >
insert into t_order_item (order_item_id, order_id, product_id,
item_price, total_num, total_price,
order_time, user_id)
values (#{orderItemId,jdbcType=BIGINT}, #{orderId,jdbcType=BIGINT}, #{productId,jdbcType=BIGINT},
#{itemPrice,jdbcType=DECIMAL}, #{totalNum,jdbcType=INTEGER}, #{totalPrice,jdbcType=DECIMAL},
#{orderTime,jdbcType=TIMESTAMP}, #{userId,jdbcType=BIGINT})
</insert>
<insert id="insertSelective" parameterType="com.example.myshardingjdbcdemo.model.OrderItem" >
insert into t_order_item
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="orderItemId != null" >
order_item_id,
</if>
<if test="orderId != null" >
order_id,
</if>
<if test="productId != null" >
product_id,
</if>
<if test="itemPrice != null" >
item_price,
</if>
<if test="totalNum != null" >
total_num,
</if>
<if test="totalPrice != null" >
total_price,
</if>
<if test="orderTime != null" >
order_time,
</if>
<if test="userId != null" >
user_id,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="orderItemId != null" >
#{orderItemId,jdbcType=BIGINT},
</if>
<if test="orderId != null" >
#{orderId,jdbcType=BIGINT},
</if>
<if test="productId != null" >
#{productId,jdbcType=BIGINT},
</if>
<if test="itemPrice != null" >
#{itemPrice,jdbcType=DECIMAL},
</if>
<if test="totalNum != null" >
#{totalNum,jdbcType=INTEGER},
</if>
<if test="totalPrice != null" >
#{totalPrice,jdbcType=DECIMAL},
</if>
<if test="orderTime != null" >
#{orderTime,jdbcType=TIMESTAMP},
</if>
<if test="userId != null" >
#{userId,jdbcType=BIGINT},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.example.myshardingjdbcdemo.model.OrderItem" >
update t_order_item
<set >
<if test="orderId != null" >
order_id = #{orderId,jdbcType=BIGINT},
</if>
<if test="productId != null" >
product_id = #{productId,jdbcType=BIGINT},
</if>
<if test="itemPrice != null" >
item_price = #{itemPrice,jdbcType=DECIMAL},
</if>
<if test="totalNum != null" >
total_num = #{totalNum,jdbcType=INTEGER},
</if>
<if test="totalPrice != null" >
total_price = #{totalPrice,jdbcType=DECIMAL},
</if>
<if test="orderTime != null" >
order_time = #{orderTime,jdbcType=TIMESTAMP},
</if>
<if test="userId != null" >
user_id = #{userId,jdbcType=BIGINT},
</if>
</set>
where order_item_id = #{orderItemId,jdbcType=BIGINT}
</update>
<update id="updateByPrimaryKey" parameterType="com.example.myshardingjdbcdemo.model.OrderItem" >
update t_order_item
set order_id = #{orderId,jdbcType=BIGINT},
product_id = #{productId,jdbcType=BIGINT},
item_price = #{itemPrice,jdbcType=DECIMAL},
total_num = #{totalNum,jdbcType=INTEGER},
total_price = #{totalPrice,jdbcType=DECIMAL},
order_time = #{orderTime,jdbcType=TIMESTAMP},
user_id = #{userId,jdbcType=BIGINT}
where order_item_id = #{orderItemId,jdbcType=BIGINT}
</update>
</mapper>
OrderMapping.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.example.myshardingjdbcdemo.dao.OrderMapper" >
<resultMap id="BaseResultMap" type="com.example.myshardingjdbcdemo.model.Order" >
<id column="order_id" property="orderId" jdbcType="BIGINT" />
<result column="order_no" property="orderNo" jdbcType="VARCHAR" />
<result column="user_id" property="userId" jdbcType="BIGINT" />
<result column="order_amount" property="orderAmount" jdbcType="DECIMAL" />
<result column="order_status" property="orderStatus" jdbcType="INTEGER" />
<result column="remark" property="remark" jdbcType="VARCHAR" />
</resultMap>
<sql id="Base_Column_List" >
order_id, order_no, user_id, order_amount, order_status, remark
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
select
<include refid="Base_Column_List" />
from t_order
where order_id = #{orderId,jdbcType=BIGINT}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
delete from t_order
where order_id = #{orderId,jdbcType=BIGINT}
</delete>
<insert id="insert" parameterType="com.example.myshardingjdbcdemo.model.Order" >
insert into t_order (order_id, order_no, user_id,
order_amount, order_status, remark
)
values (#{orderId,jdbcType=BIGINT}, #{orderNo,jdbcType=VARCHAR}, #{userId,jdbcType=BIGINT},
#{orderAmount,jdbcType=DECIMAL}, #{orderStatus,jdbcType=INTEGER}, #{remark,jdbcType=VARCHAR}
)
</insert>
<insert id="insertSelective" parameterType="com.example.myshardingjdbcdemo.model.Order" >
insert into t_order
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="orderId != null" >
order_id,
</if>
<if test="orderNo != null" >
order_no,
</if>
<if test="userId != null" >
user_id,
</if>
<if test="orderAmount != null" >
order_amount,
</if>
<if test="orderStatus != null" >
order_status,
</if>
<if test="remark != null" >
remark,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="orderId != null" >
#{orderId,jdbcType=BIGINT},
</if>
<if test="orderNo != null" >
#{orderNo,jdbcType=VARCHAR},
</if>
<if test="userId != null" >
#{userId,jdbcType=BIGINT},
</if>
<if test="orderAmount != null" >
#{orderAmount,jdbcType=DECIMAL},
</if>
<if test="orderStatus != null" >
#{orderStatus,jdbcType=INTEGER},
</if>
<if test="remark != null" >
#{remark,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.example.myshardingjdbcdemo.model.Order" >
update t_order
<set >
<if test="orderNo != null" >
order_no = #{orderNo,jdbcType=VARCHAR},
</if>
<if test="userId != null" >
user_id = #{userId,jdbcType=BIGINT},
</if>
<if test="orderAmount != null" >
order_amount = #{orderAmount,jdbcType=DECIMAL},
</if>
<if test="orderStatus != null" >
order_status = #{orderStatus,jdbcType=INTEGER},
</if>
<if test="remark != null" >
remark = #{remark,jdbcType=VARCHAR},
</if>
</set>
where order_id = #{orderId,jdbcType=BIGINT}
</update>
<update id="updateByPrimaryKey" parameterType="com.example.myshardingjdbcdemo.model.Order" >
update t_order
set order_no = #{orderNo,jdbcType=VARCHAR},
user_id = #{userId,jdbcType=BIGINT},
order_amount = #{orderAmount,jdbcType=DECIMAL},
order_status = #{orderStatus,jdbcType=INTEGER},
remark = #{remark,jdbcType=VARCHAR}
where order_id = #{orderId,jdbcType=BIGINT}
</update>
</mapper>
ProductMapper.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.example.myshardingjdbcdemo.dao.ProductMapper" >
<resultMap id="BaseResultMap" type="com.example.myshardingjdbcdemo.model.Product" >
<id column="product_id" property="productId" jdbcType="BIGINT" />
<result column="code" property="code" jdbcType="VARCHAR" />
<result column="name" property="name" jdbcType="VARCHAR" />
<result column="remark" property="remark" jdbcType="VARCHAR" />
</resultMap>
<sql id="Base_Column_List" >
product_id, code, name, remark
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
select
<include refid="Base_Column_List" />
from t_product
where product_id = #{productId,jdbcType=BIGINT}
</select>
<select id="selectProductList" resultMap="BaseResultMap" >
select
<include refid="Base_Column_List" />
from t_product
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
delete from t_product
where product_id = #{productId,jdbcType=BIGINT}
</delete>
<insert id="insert" parameterType="com.example.myshardingjdbcdemo.model.Product" >
insert into t_product (product_id, code, name,
remark)
values (#{productId,jdbcType=BIGINT}, #{code,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
#{remark,jdbcType=VARCHAR})
</insert>
<insert id="insertSelective" parameterType="com.example.myshardingjdbcdemo.model.Product" >
insert into t_product
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="productId != null" >
product_id,
</if>
<if test="code != null" >
code,
</if>
<if test="name != null" >
name,
</if>
<if test="remark != null" >
remark,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="productId != null" >
#{productId,jdbcType=BIGINT},
</if>
<if test="code != null" >
#{code,jdbcType=VARCHAR},
</if>
<if test="name != null" >
#{name,jdbcType=VARCHAR},
</if>
<if test="remark != null" >
#{remark,jdbcType=VARCHAR},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.example.myshardingjdbcdemo.model.Product" >
update t_product
<set >
<if test="code != null" >
code = #{code,jdbcType=VARCHAR},
</if>
<if test="name != null" >
name = #{name,jdbcType=VARCHAR},
</if>
<if test="remark != null" >
remark = #{remark,jdbcType=VARCHAR},
</if>
</set>
where product_id = #{productId,jdbcType=BIGINT}
</update>
<update id="updateByPrimaryKey" parameterType="com.example.myshardingjdbcdemo.model.Product" >
update t_product
set code = #{code,jdbcType=VARCHAR},
name = #{name,jdbcType=VARCHAR},
remark = #{remark,jdbcType=VARCHAR}
where product_id = #{productId,jdbcType=BIGINT}
</update>
</mapper>
UserAddressMapper.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.example.myshardingjdbcdemo.dao.UserAddressMapper" >
<resultMap id="BaseResultMap" type="com.example.myshardingjdbcdemo.model.UserAddress" >
<id column="address_id" property="addressId" jdbcType="BIGINT" />
<result column="user_id" property="userId" jdbcType="BIGINT" />
<result column="province" property="province" jdbcType="VARCHAR" />
<result column="city" property="city" jdbcType="VARCHAR" />
<result column="district" property="district" jdbcType="VARCHAR" />
<result column="detail" property="detail" jdbcType="VARCHAR" />
<result column="sort" property="sort" jdbcType="INTEGER" />
<result column="gender" property="gender" jdbcType="INTEGER" />
</resultMap>
<sql id="Base_Column_List" >
address_id, user_id, province, city, district, detail, sort, gender
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
select
<include refid="Base_Column_List" />
from t_user_address
where address_id = #{addressId,jdbcType=BIGINT}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
delete from t_user_address
where address_id = #{addressId,jdbcType=BIGINT}
</delete>
<insert id="insert" parameterType="com.example.myshardingjdbcdemo.model.UserAddress" >
insert into t_user_address (address_id, user_id, province,
city, district, detail,
sort, gender)
values (#{addressId,jdbcType=BIGINT}, #{userId,jdbcType=BIGINT}, #{province,jdbcType=VARCHAR},
#{city,jdbcType=VARCHAR}, #{district,jdbcType=VARCHAR}, #{detail,jdbcType=VARCHAR},
#{sort,jdbcType=INTEGER}, #{gender,jdbcType=INTEGER})
</insert>
<insert id="insertSelective" parameterType="com.example.myshardingjdbcdemo.model.UserAddress" >
insert into t_user_address
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="addressId != null" >
address_id,
</if>
<if test="userId != null" >
user_id,
</if>
<if test="province != null" >
province,
</if>
<if test="city != null" >
city,
</if>
<if test="district != null" >
district,
</if>
<if test="detail != null" >
detail,
</if>
<if test="sort != null" >
sort,
</if>
<if test="gender != null" >
gender,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="addressId != null" >
#{addressId,jdbcType=BIGINT},
</if>
<if test="userId != null" >
#{userId,jdbcType=BIGINT},
</if>
<if test="province != null" >
#{province,jdbcType=VARCHAR},
</if>
<if test="city != null" >
#{city,jdbcType=VARCHAR},
</if>
<if test="district != null" >
#{district,jdbcType=VARCHAR},
</if>
<if test="detail != null" >
#{detail,jdbcType=VARCHAR},
</if>
<if test="sort != null" >
#{sort,jdbcType=INTEGER},
</if>
<if test="gender != null" >
#{gender,jdbcType=INTEGER},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.example.myshardingjdbcdemo.model.UserAddress" >
update t_user_address
<set >
<if test="userId != null" >
user_id = #{userId,jdbcType=BIGINT},
</if>
<if test="province != null" >
province = #{province,jdbcType=VARCHAR},
</if>
<if test="city != null" >
city = #{city,jdbcType=VARCHAR},
</if>
<if test="district != null" >
district = #{district,jdbcType=VARCHAR},
</if>
<if test="detail != null" >
detail = #{detail,jdbcType=VARCHAR},
</if>
<if test="sort != null" >
sort = #{sort,jdbcType=INTEGER},
</if>
<if test="gender != null" >
gender = #{gender,jdbcType=INTEGER},
</if>
</set>
where address_id = #{addressId,jdbcType=BIGINT}
</update>
<update id="updateByPrimaryKey" parameterType="com.example.myshardingjdbcdemo.model.UserAddress" >
update t_user_address
set user_id = #{userId,jdbcType=BIGINT},
province = #{province,jdbcType=VARCHAR},
city = #{city,jdbcType=VARCHAR},
district = #{district,jdbcType=VARCHAR},
detail = #{detail,jdbcType=VARCHAR},
sort = #{sort,jdbcType=INTEGER},
gender = #{gender,jdbcType=INTEGER}
where address_id = #{addressId,jdbcType=BIGINT}
</update>
</mapper>
UserMapper.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.example.myshardingjdbcdemo.dao.UserMapper" >
<resultMap id="BaseResultMap" type="com.example.myshardingjdbcdemo.model.User" >
<id column="user_id" property="userId" jdbcType="BIGINT" />
<result column="id_number" property="idNumber" jdbcType="VARCHAR" />
<result column="name" property="name" jdbcType="VARCHAR" />
<result column="age" property="age" jdbcType="INTEGER" />
<result column="gender" property="gender" jdbcType="INTEGER" />
<result column="birth_date" property="birthDate" jdbcType="DATE" />
</resultMap>
<sql id="Base_Column_List" >
user_id, id_number, name, age, gender, birth_date
</sql>
<select id="selectByPrimaryKey" resultMap="BaseResultMap" parameterType="java.lang.Long" >
select
<include refid="Base_Column_List" />
from t_user
where user_id = #{userId,jdbcType=BIGINT}
</select>
<select id="selectByUserIdGender" resultMap="BaseResultMap" >
select
<include refid="Base_Column_List" />
from t_user
where user_id = #{userId,jdbcType=BIGINT} and gender = #{gender,jdbcType=INTEGER}
</select>
<delete id="deleteByPrimaryKey" parameterType="java.lang.Long" >
delete from t_user
where user_id = #{userId,jdbcType=BIGINT}
</delete>
<insert id="insert" parameterType="com.example.myshardingjdbcdemo.model.User" >
insert into t_user (user_id, id_number, name,
age, gender, birth_date
)
values (#{userId,jdbcType=BIGINT}, #{idNumber,jdbcType=VARCHAR}, #{name,jdbcType=VARCHAR},
#{age,jdbcType=INTEGER}, #{gender,jdbcType=INTEGER}, #{birthDate,jdbcType=DATE}
)
</insert>
<insert id="insertSelective" parameterType="com.example.myshardingjdbcdemo.model.User" >
insert into t_user
<trim prefix="(" suffix=")" suffixOverrides="," >
<if test="userId != null" >
user_id,
</if>
<if test="idNumber != null" >
id_number,
</if>
<if test="name != null" >
name,
</if>
<if test="age != null" >
age,
</if>
<if test="gender != null" >
gender,
</if>
<if test="birthDate != null" >
birth_date,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides="," >
<if test="userId != null" >
#{userId,jdbcType=BIGINT},
</if>
<if test="idNumber != null" >
#{idNumber,jdbcType=VARCHAR},
</if>
<if test="name != null" >
#{name,jdbcType=VARCHAR},
</if>
<if test="age != null" >
#{age,jdbcType=INTEGER},
</if>
<if test="gender != null" >
#{gender,jdbcType=INTEGER},
</if>
<if test="birthDate != null" >
#{birthDate,jdbcType=DATE},
</if>
</trim>
</insert>
<update id="updateByPrimaryKeySelective" parameterType="com.example.myshardingjdbcdemo.model.User" >
update t_user
<set >
<if test="idNumber != null" >
id_number = #{idNumber,jdbcType=VARCHAR},
</if>
<if test="name != null" >
name = #{name,jdbcType=VARCHAR},
</if>
<if test="age != null" >
age = #{age,jdbcType=INTEGER},
</if>
<if test="gender != null" >
gender = #{gender,jdbcType=INTEGER},
</if>
<if test="birthDate != null" >
birth_date = #{birthDate,jdbcType=DATE},
</if>
</set>
where user_id = #{userId,jdbcType=BIGINT}
</update>
<update id="updateByPrimaryKey" parameterType="com.example.myshardingjdbcdemo.model.User" >
update t_user
set id_number = #{idNumber,jdbcType=VARCHAR},
name = #{name,jdbcType=VARCHAR},
age = #{age,jdbcType=INTEGER},
gender = #{gender,jdbcType=INTEGER},
birth_date = #{birthDate,jdbcType=DATE}
where user_id = #{userId,jdbcType=BIGINT}
</update>
</mapper>
(2) dao层

(3) Mode层

3、写分库和分表的算法,分库分表时必然会根据表里的字段设计分表分库的算法,对于用户表和用户地址表用user_id取模来分库,使用gender来分表。对于订单和订单明细表,使用user_id取模来分库,order_id取模来分表。因此需要用到两个分片算法,根据性别分片和根据id分片
package com.example.myshardingjdbcdemo.shardingalgorithm;
import com.example.myshardingjdbcdemo.enums.GenderEnum;
import com.google.common.collect.Range;
import io.shardingsphere.api.algorithm.sharding.PreciseShardingValue;
import io.shardingsphere.api.algorithm.sharding.RangeShardingValue;
import io.shardingsphere.api.algorithm.sharding.standard.PreciseShardingAlgorithm;
import io.shardingsphere.api.algorithm.sharding.standard.RangeShardingAlgorithm;
import java.util.Collection;
import java.util.LinkedHashSet;
/**
* @description: 按性别分表
* @author: think
* @create: 2020-11-16 10:08
*/
public class GenderShardingAlgorithm implements PreciseShardingAlgorithm<Integer>, RangeShardingAlgorithm<Integer> {
/**
* Sharding.
*
* @param availableTargetNames available data sources or tables's names
* @param shardingValue sharding value
* @return sharding result for data source or table's name
*/
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Integer> shardingValue) {
String databaseName = availableTargetNames.stream().findFirst().get();
for (String dbName : availableTargetNames) {
if (dbName.endsWith(genderToTableSuffix(shardingValue.getValue()))) {
databaseName = dbName;
}
}
return databaseName;
}
/**
* Sharding.
*
* @param availableTargetNames available data sources or tables's names
* @param shardingValue sharding value
* @return sharding results for data sources or tables's names
*/
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Integer> shardingValue) {
Collection<String> dbs = new LinkedHashSet<>(availableTargetNames.size());
Range<Integer> range = (Range<Integer>) shardingValue.getValueRange();
for (Integer i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
for (String dbName : availableTargetNames) {
if (dbName.endsWith(genderToTableSuffix(i))) {
dbs.add(dbName);
}
}
}
return dbs;
}
/**
* 字段与分库的映射关系
*
* @param gender
* @return
*/
private String genderToTableSuffix(Integer gender) {
return gender.equals(GenderEnum.MALE.getCode()) ? "0" : "1";
}
}
package com.example.myshardingjdbcdemo.shardingalgorithm;
import com.google.common.collect.Range;
import io.shardingsphere.api.algorithm.sharding.PreciseShardingValue;
import io.shardingsphere.api.algorithm.sharding.RangeShardingValue;
import io.shardingsphere.api.algorithm.sharding.standard.PreciseShardingAlgorithm;
import io.shardingsphere.api.algorithm.sharding.standard.RangeShardingAlgorithm;
import java.util.Collection;
import java.util.LinkedHashSet;
/**
* @description: 根据分库分表
* @author:
* @create: 2020-11-16 10:16
*/
public class IdShardingAlgorithm implements PreciseShardingAlgorithm<Long>, RangeShardingAlgorithm<Long> {
/**
* Sharding.
*
* @param availableTargetNames available data sources or tables's names
* @param shardingValue sharding value
* @return sharding result for data source or table's name
*/
@Override
public String doSharding(Collection<String> availableTargetNames, PreciseShardingValue<Long> shardingValue) {
String table = availableTargetNames.stream().findFirst().get();
for (String tableName : availableTargetNames) {
if (tableName.endsWith(idToTableSuffix(shardingValue.getValue()))) {
table = tableName;
}
}
return table;
}
/**
* Sharding.
*
* @param availableTargetNames available data sources or tables's names
* @param shardingValue sharding value
* @return sharding results for data sources or tables's names
*/
@Override
public Collection<String> doSharding(Collection<String> availableTargetNames, RangeShardingValue<Long> shardingValue) {
Collection<String> dbs = new LinkedHashSet<>(availableTargetNames.size());
Range<Long> range = (Range<Long>) shardingValue.getValueRange();
for (long i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
for (String dbName : availableTargetNames) {
if (dbName.endsWith(idToTableSuffix(i))) {
dbs.add(dbName);
}
}
}
return dbs;
}
/**
* 字段与分表的映射关系
*
* @param id
* @return 表后缀(201906、201907等)
*/
private String idToTableSuffix(Long id) {
return String.valueOf(id % 2);
}
}
编写ID生成算法,在数据分片的场景中使用MySQL主键自增就不太合适了,因此我使用了snowflake算法来生成主键
@Data
@ConfigurationProperties(prefix = "sharding.ds1")
public class SecondDsProp {
private String jdbcUrl;
private String username;
private String password;
private String type;
}
@Component
public class Sequence {
private static final Log logger = LogFactory.getLog(Sequence.class);
/* 时间起始标记点,作为基准,一般取系统的最近时间(一旦确定不能变动) */
private final long twepoch = 1288834974657L;
private final long workerIdBits = 5L;/* 机器标识位数 */
private final long datacenterIdBits = 5L;
private final long maxWorkerId = -1L ^ (-1L << workerIdBits);
private final long maxDatacenterId = -1L ^ (-1L << datacenterIdBits);
private final long sequenceBits = 12L;/* 毫秒内自增位 */
private final long workerIdShift = sequenceBits;
private final long datacenterIdShift = sequenceBits + workerIdBits;
/* 时间戳左移动位 */
private final long timestampLeftShift = sequenceBits + workerIdBits + datacenterIdBits;
private final long sequenceMask = -1L ^ (-1L << sequenceBits);
private long workerId;
/* 数据标识id部分 */
private long datacenterId;
private long sequence = 0L;/* 0,并发控制 */
private long lastTimestamp = -1L;/* 上次生产id时间戳 */
public Sequence() {
this.datacenterId = getDatacenterId(maxDatacenterId);
this.workerId = getMaxWorkerId(datacenterId, maxWorkerId);
}
/**
* @param workerId 工作机器ID
* @param datacenterId 序列号
*/
public Sequence(long workerId, long datacenterId) {
if (workerId > maxWorkerId || workerId < 0) {
throw new IllegalArgumentException(String.format("worker Id can't be greater than %d or less than 0", maxWorkerId));
}
if (datacenterId > maxDatacenterId || datacenterId < 0) {
throw new IllegalArgumentException(
String.format("datacenter Id can't be greater than %d or less than 0", maxDatacenterId));
}
this.workerId = workerId;
this.datacenterId = datacenterId;
}
/**
* <p>
* 获取 maxWorkerId
* </p>
*/
protected static long getMaxWorkerId(long datacenterId, long maxWorkerId) {
StringBuilder mpid = new StringBuilder();
mpid.append(datacenterId);
String name = ManagementFactory.getRuntimeMXBean().getName();
if (StringUtils.isNotEmpty(name)) {
/*
* GET jvmPid
*/
mpid.append(name.split("@")[0]);
}
/*
* MAC + PID 的 hashcode 获取16个低位
*/
return (mpid.toString().hashCode() & 0xffff) % (maxWorkerId + 1);
}
/**
* <p>
* 数据标识id部分
* </p>
*/
protected static long getDatacenterId(long maxDatacenterId) {
long id = 0L;
try {
InetAddress ip = InetAddress.getLocalHost();
NetworkInterface network = NetworkInterface.getByInetAddress(ip);
if (network == null) {
id = 1L;
} else {
byte[] mac = network.getHardwareAddress();
if (null != mac) {
id = ((0x000000FF & (long) mac[mac.length - 1]) | (0x0000FF00 & (((long) mac[mac.length - 2]) << 8))) >> 6;
id = id % (maxDatacenterId + 1);
}
}
} catch (Exception e) {
logger.warn(" getDatacenterId: " + e.getMessage());
}
return id;
}
/**
* 获取下一个ID
*
* @return
*/
public synchronized long nextId() {
long timestamp = timeGen();
if (timestamp < lastTimestamp) {//闰秒
long offset = lastTimestamp - timestamp;
if (offset <= 5) {
try {
wait(offset << 1);
timestamp = timeGen();
if (timestamp < lastTimestamp) {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", offset));
}
} catch (Exception e) {
throw new RuntimeException(e);
}
} else {
throw new RuntimeException(String.format("Clock moved backwards. Refusing to generate id for %d milliseconds", offset));
}
}
if (lastTimestamp == timestamp) {
// 相同毫秒内,序列号自增
sequence = (sequence + 1) & sequenceMask;
if (sequence == 0) {
// 同一毫秒的序列数已经达到最大
timestamp = tilNextMillis(lastTimestamp);
}
} else {
// 不同毫秒内,序列号置为 1 - 3 随机数
sequence = ThreadLocalRandom.current().nextLong(1, 3);
}
lastTimestamp = timestamp;
return ((timestamp - twepoch) << timestampLeftShift) // 时间戳部分
| (datacenterId << datacenterIdShift) // 数据中心部分
| (workerId << workerIdShift) // 机器标识部分
| sequence; // 序列号部分
}
protected long tilNextMillis(long lastTimestamp) {
long timestamp = timeGen();
while (timestamp <= lastTimestamp) {
timestamp = timeGen();
}
return timestamp;
}
protected long timeGen() {
return SystemClock.now();
}
}
4、配置数据库连接、MyBatis集成和编写属性文件等等
1) application.properties
#data source0 sharding.ds0.type=com.alibaba.druid.pool.DruidDataSource sharding.ds0.jdbcUrl=jdbc:mysql://192.168.127.129:3306/sharding_test?serverTimezone=Asia/Shanghai&useSSL=false&useUnicode=true&characterEncoding=utf8 sharding.ds0.username=root sharding.ds0.password=123456 #data source1 sharding.ds1.type=com.alibaba.druid.pool.DruidDataSource sharding.ds1.jdbcUrl=jdbc:mysql://192.168.127.134:3306/sharding_test2?serverTimezone=Asia/Shanghai&useSSL=false&useUnicode=true&characterEncoding=utf8 sharding.ds1.username=root sharding.ds1.password=123456 snow.work.id=1 snow.datacenter.id=2 mybatis.configuration.map-underscore-to-camel-case=true mybatis.type-aliases-package=com.example.myshardingjdbcdemo mybatis.mapper-locations=classpath:mapping/*.xml
2) FirstDsProp.java
@Data
@ConfigurationProperties(prefix = "sharding.ds0")
public class FirstDsProp {
private String jdbcUrl;
private String username;
private String password;
private String type;
}
3) SecondDsProp.java
@Data
@ConfigurationProperties(prefix = "sharding.ds1")
public class SecondDsProp {
private String jdbcUrl;
private String username;
private String password;
private String type;
}
4)数据分片相关配置,需要配置的东西很多,包括:各个表的配置规则TableRuleConfiguration、数据源DataSource
package com.example.myshardingjdbcdemo.config;
import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.example.myshardingjdbcdemo.shardingalgorithm.GenderShardingAlgorithm;
import com.example.myshardingjdbcdemo.shardingalgorithm.IdShardingAlgorithm;
import com.example.myshardingjdbcdemo.shardingalgorithm.SnowflakeShardingKeyGenerator;
import com.google.common.collect.Lists;
import io.shardingsphere.api.config.rule.ShardingRuleConfiguration;
import io.shardingsphere.api.config.rule.TableRuleConfiguration;
import io.shardingsphere.api.config.strategy.StandardShardingStrategyConfiguration;
import io.shardingsphere.shardingjdbc.api.ShardingDataSourceFactory;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.core.env.Environment;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;
import javax.sql.DataSource;
import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
/**
* @description:
* @author:
* @create: 2020-11-16 10:26
*/
@Configuration
@EnableConfigurationProperties({FirstDsProp.class, SecondDsProp.class})
@EnableTransactionManagement(proxyTargetClass = true)
@MapperScan(basePackages = "com.example.myshardingjdbcdemo.dao", sqlSessionTemplateRef = "sqlSessionTemplate")
public class DataSourceConfig {
@Value("${snow.work.id:0}")
private Long workId;
@Value("${snow.datacenter.id:0}")
private Long datacenterId;
@Autowired
private Environment env;
/**
* druid数据源1
*
* @param firstDSProp
* @return
*/
@Bean("ds0")
public DataSource ds0(FirstDsProp firstDSProp) {
Map<String, Object> dsMap = new HashMap<>();
dsMap.put("type", firstDSProp.getType());
dsMap.put("url", firstDSProp.getJdbcUrl());
dsMap.put("username", firstDSProp.getUsername());
dsMap.put("password", firstDSProp.getPassword());
DruidDataSource ds = (DruidDataSource) DataSourceUtil.buildDataSource(dsMap);
ds.setProxyFilters(Lists.newArrayList(statFilter()));
// 每个分区最大的连接数
ds.setMaxActive(20);
// 每个分区最小的连接数
ds.setMinIdle(5);
return ds;
}
/**
* druid数据源2
*
* @param secondDsProp
* @return
*/
@Bean("ds1")
public DataSource ds1(SecondDsProp secondDsProp) {
Map<String, Object> dsMap = new HashMap<>();
dsMap.put("type", secondDsProp.getType());
dsMap.put("url", secondDsProp.getJdbcUrl());
dsMap.put("username", secondDsProp.getUsername());
dsMap.put("password", secondDsProp.getPassword());
DruidDataSource ds = (DruidDataSource) DataSourceUtil.buildDataSource(dsMap);
ds.setProxyFilters(Lists.newArrayList(statFilter()));
// 每个分区最大的连接数
ds.setMaxActive(20);
// 每个分区最小的连接数
ds.setMinIdle(5);
return ds;
}
@Bean
public Filter statFilter() {
StatFilter filter = new StatFilter();
filter.setSlowSqlMillis(5000);
filter.setLogSlowSql(true);
filter.setMergeSql(true);
return filter;
}
@Bean
public ServletRegistrationBean statViewServlet() {
//创建servlet注册实体
ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
//设置ip白名单
servletRegistrationBean.addInitParameter("allow", "127.0.0.1");
//设置控制台管理用户
servletRegistrationBean.addInitParameter("loginUsername", "admin");
servletRegistrationBean.addInitParameter("loginPassword", "123456");
//是否可以重置数据
servletRegistrationBean.addInitParameter("resetEnable", "false");
return servletRegistrationBean;
}
/**
* shardingjdbc数据源
*
* @return
* @throws SQLException
*/
@Bean("dataSource")
public DataSource dataSource(@Qualifier("ds0") DataSource ds0, @Qualifier("ds1") DataSource ds1) throws SQLException {
// 配置真实数据源
Map<String, DataSource> dataSourceMap = new HashMap<>();
dataSourceMap.put("ds0", ds0);
dataSourceMap.put("ds1", ds1);
// 配置分片规则
ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
shardingRuleConfig.getTableRuleConfigs().add(userRuleConfig());
shardingRuleConfig.getTableRuleConfigs().add(addressRuleConfig());
shardingRuleConfig.getTableRuleConfigs().add(orderRuleConfig());
shardingRuleConfig.getTableRuleConfigs().add(orderItemRuleConfig());
shardingRuleConfig.setDefaultDatabaseShardingStrategyConfig(
new StandardShardingStrategyConfiguration("user_id",
new IdShardingAlgorithm(), new IdShardingAlgorithm()));
shardingRuleConfig.getBindingTableGroups().add("t_user, t_user_address");
shardingRuleConfig.getBindingTableGroups().add("t_order, t_order_item");
//t_product为全局表
shardingRuleConfig.getBroadcastTables().add("t_product");
Properties p = new Properties();
p.setProperty("sql.show",Boolean.TRUE.toString());
// 获取数据源对象
DataSource dataSource = ShardingDataSourceFactory.createDataSource(dataSourceMap, shardingRuleConfig, new ConcurrentHashMap(), p);
return dataSource;
}
/**
* 需要手动配置事务管理器
* @param dataSource
* @return
*/
@Bean
public DataSourceTransactionManager transactitonManager(@Qualifier("dataSource") DataSource dataSource){
return new DataSourceTransactionManager(dataSource);
}
@Bean("sqlSessionFactory")
@Primary
public SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath:mapping/*.xml"));
return bean.getObject();
}
@Bean("sqlSessionTemplate")
@Primary
public SqlSessionTemplate sqlSessionTemplate(@Qualifier("sqlSessionFactory") SqlSessionFactory sqlSessionFactory) throws Exception {
return new SqlSessionTemplate(sqlSessionFactory);
}
/**
* 用户表切分。根据userId,切分到数据库0和数据库1,然后根据gender切分为t_user_1 和 t_user_2
*/
private TableRuleConfiguration userRuleConfig() {
TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration();
tableRuleConfig.setLogicTable("t_user");
tableRuleConfig.setActualDataNodes("ds${0..1}.t_user_${0..1}");
tableRuleConfig.setKeyGeneratorColumnName("user_id");
tableRuleConfig.setKeyGenerator(new SnowflakeShardingKeyGenerator(workId, datacenterId));
tableRuleConfig.setDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id", new IdShardingAlgorithm(), new IdShardingAlgorithm()));
tableRuleConfig.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("gender", new GenderShardingAlgorithm(), new GenderShardingAlgorithm()));
return tableRuleConfig;
}
/**
* 用户地址表切分。根据userId,切分到数据库0和数据库1,然后根据gender切分为t_user_address_1 和 t_user_address_2
*/
private TableRuleConfiguration addressRuleConfig() {
TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration();
tableRuleConfig.setLogicTable("t_user_address");
tableRuleConfig.setActualDataNodes("ds${0..1}.t_user_address_${0..1}");
tableRuleConfig.setKeyGeneratorColumnName("address_id");
tableRuleConfig.setKeyGenerator(new SnowflakeShardingKeyGenerator(workId, datacenterId));
tableRuleConfig.setDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id", new IdShardingAlgorithm(), new IdShardingAlgorithm()));
tableRuleConfig.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("gender", new GenderShardingAlgorithm(), new GenderShardingAlgorithm()));
return tableRuleConfig;
}
/**
* 订单表切分。根据userId,切分到数据库0和数据库1,然后根据order_id切分为t_order_0 和 t_order_1
*/
private TableRuleConfiguration orderRuleConfig() {
TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration();
tableRuleConfig.setLogicTable("t_order");
tableRuleConfig.setActualDataNodes("ds${0..1}.t_order_${0..1}");
tableRuleConfig.setKeyGeneratorColumnName("order_id");
tableRuleConfig.setKeyGenerator(new SnowflakeShardingKeyGenerator(workId, datacenterId));
tableRuleConfig.setDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id", new IdShardingAlgorithm(), new IdShardingAlgorithm()));
tableRuleConfig.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("order_id", new IdShardingAlgorithm(), new IdShardingAlgorithm()));
return tableRuleConfig;
}
/**
* 订单Item表切分。根据userId,切分到数据库0和数据库1,然后根据order_id切分为t_order_item_0 和 t_order_item_1
*/
private TableRuleConfiguration orderItemRuleConfig() {
TableRuleConfiguration tableRuleConfig = new TableRuleConfiguration();
tableRuleConfig.setLogicTable("t_order_item");
tableRuleConfig.setActualDataNodes("ds${0..1}.t_order_item_${0..1}");
tableRuleConfig.setKeyGeneratorColumnName("order_item_id");
tableRuleConfig.setKeyGenerator(new SnowflakeShardingKeyGenerator(workId, datacenterId));
tableRuleConfig.setDatabaseShardingStrategyConfig(new StandardShardingStrategyConfiguration("user_id", new IdShardingAlgorithm(), new IdShardingAlgorithm()));
tableRuleConfig.setTableShardingStrategyConfig(new StandardShardingStrategyConfiguration("order_id", new IdShardingAlgorithm(), new IdShardingAlgorithm()));
return tableRuleConfig;
}
}
DataSourceUtil
package com.example.myshardingjdbcdemo.config;
/**
* @description:
* @author:
* @create: 2020-11-16 10:30
*/
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.jdbc.DataSourceBuilder;
import javax.sql.DataSource;
import java.util.Map;
@Slf4j
public class DataSourceUtil {
private static final String DATASOURCE_TYPE_DEFAULT = "com.zaxxer.hikari.HikariDataSource";
public static DataSource buildDataSource(Map<String, Object> dataSourceMap) {
Object type = dataSourceMap.get("type");
if (type == null) {
type = DATASOURCE_TYPE_DEFAULT;
}
try {
Class<? extends DataSource> dataSourceType;
dataSourceType = (Class<? extends DataSource>) Class.forName((String) type);
//String driverClassName = dataSourceMap.get("driver").toString();
String url = dataSourceMap.get("url").toString();
String username = dataSourceMap.get("username").toString();
String password = dataSourceMap.get("password").toString();
// 自定义DataSource配置
DataSourceBuilder factory = DataSourceBuilder.create().url(url).username(username).password(password).type(dataSourceType);
return factory.build();
} catch (Exception e) {
log.error("构建数据源" + type + "出错", e);
}
return null;
}
}
5、启动类
@SpringBootApplication(exclude={DataSourceAutoConfiguration.class})
public class MyShardingJdbcDemoApplication {
public static void main(String[] args) {
SpringApplication.run(MyShardingJdbcDemoApplication.class, args);
}
}
6、service层
@Service
public class BussinessService {
@Autowired
private UserMapper userMapper;
@Autowired
private UserAddressMapper addressMapper;
@Autowired
private OrderMapper orderMapper;
@Autowired
private OrderItemMapper orderItemMapper;
@Autowired
private ProductMapper productMapper;
/**
* 创建用户
*/
@Transactional
public void saveUser(User user, UserAddress address) {
userMapper.insertSelective(user);
address.setUserId(user.getUserId());
addressMapper.insertSelective(address);
}
/**
* 查询用户信息
*/
public User queryUser(Long userId) {
return userMapper.selectByPrimaryKey(userId);
}
/**
* 根据用户Id和性别查询
*/
public User queryUser(Long userId, int gender) {
User user = new User();
user.setUserId( userId);
user.setGender(gender);
return userMapper.selectByUserIdGender(userId, gender);
}
@Transactional
public void saveOrder(User user, Order order, OrderItem orderItem) {
order.setUserId(user.getUserId());
orderMapper.insertSelective(order);
orderItem.setOrderId(order.getOrderId());
orderItem.setUserId(user.getUserId());
orderItemMapper.insertSelective(orderItem);
}
public Order queryOrder(Long orderId) {
return orderMapper.selectByPrimaryKey(orderId);
}
@Transactional
public void saveAll(User user, UserAddress address, Order order, OrderItem orderItem) {
order.setUserId(user.getUserId());
orderMapper.insertSelective(order);
orderItem.setOrderId(order.getOrderId());
orderItem.setUserId(user.getUserId());
orderItemMapper.insertSelective(orderItem);
}
@Transactional
public void saveProduct(Product product) {
productMapper.insertSelective(product);
}
/**
* 查询产品列表
*/
public List<Product> queryProduct() {
return productMapper.selectProductList();
}
}
7、Controller层
@Slf4j
@RestController
public class BussinessController {
@Autowired
private BussinessService bussinessService;
//@Autowired
//private SnowflakeIdGenerator snowflakeIdGenerator;
@Autowired
private Sequence sequence;
@InitBinder
public void initBinder(WebDataBinder binder, WebRequest request) {
//转换日期
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
@GetMapping("/createProduct")
public String createProduct() {
for (int i = 1; i < 10; i++) {
Product product = new Product();
//product.setProductId(snowflakeIdGenerator.nextId());
product.setProductId(sequence.nextId());
product.setCode("P00" + i);
product.setName("产品名称" + i);
product.setRemark("产品介绍" + i);
bussinessService.saveProduct(product);
}
return "成功";
}
@GetMapping("/queryProduct")
public String queryProduct() {
List<Product> list = bussinessService.queryProduct();
if(list == null || list.isEmpty()){
return "未查询到产品信息";
}
return JSON.toJSONString(list);
}
@GetMapping("/createUser")
public String createUser() {
for (int i = 1; i <= 21; i++) {
User user = new User();
user.setUserId(Long.parseLong(i + ""));
user.setName("张无忌" + i);
Random r = new Random();
int gender = r.nextInt(100) % 2 ;
user.setGender(gender);
user.setAge(20 + i);
user.setBirthDate(DateUtil.parseDate("1989-08-16"));
user.setIdNumber("4101231989691" + i);
UserAddress address = new UserAddress();
address.setCity("某某市");
address.setDetail("某某街道");
address.setDistrict("某某区");
address.setProvince("浙江省");
address.setSort(1);
address.setGender(user.getGender());
bussinessService.saveUser(user, address);
}
return "成功";
}
@GetMapping("/queryUser")
public String queryUser() {
Long userId = 2L;
User user = bussinessService.queryUser(userId);
if(user == null){
return "未查询到相关用户信息";
}
return JSON.toJSONString(user);
}
//http://localhost:8080/queryUserByUserIdGender?userId=2&gender=0
@GetMapping("/queryUserByUserIdGender")
public String queryUserByUserIdGender(Long userId , int gender) {
User user = bussinessService.queryUser(userId, gender);
if(user == null){
return "未查询到相关用户信息";
}
return JSON.toJSONString(user);
}
//http://localhost:8080/queryOrder?orderId=1328224735909232641
@GetMapping("/queryOrder")
public String queryOrder(Long orderId ) {
Order order = bussinessService.queryOrder(orderId);
if(order == null){
return "未查询到相关信息";
}
return JSON.toJSONString(order);
}
@GetMapping("/createOrder")
public String createOrder() {
for (int i = 1; i <= 21; i++) {
User user = new User();
user.setUserId(Long.parseLong(i + ""));
Order order = new Order();
order.setOrderId(sequence.nextId());
order.setOrderAmount(new BigDecimal(100));
order.setOrderNo("ORDER_00" + i);
order.setOrderStatus(OrderStatusEnum.PROCESSING.getCode());
order.setRemark("测试");
OrderItem orderItem = new OrderItem();
orderItem.setItemPrice(new BigDecimal(5));
orderItem.setOrderTime(DateUtil.parseDate("2019-06-27 17:50:05"));
orderItem.setProductId(1328207116648960001L);
orderItem.setTotalNum(20);
orderItem.setTotalPrice(new BigDecimal(100));
bussinessService.saveOrder(user, order, orderItem);
}
return "成功";
}
@GetMapping("/buss/all")
public String findAll(){
Map<String,Object> result = new HashMap<>();
result = bussinessService.findAll();
return JSON.toJSONString(result);
}
}
参考:https://blog.csdn.net/hyc2zbj/article/details/94005745