springboot+mybatisplus+sharding-jdbc分庫分表實例

項目實踐

如今Java項目使用mybatis多一些,因此我也作了一個springboot+mybatisplus+sharding-jdbc分庫分表項目例子分享給你們。html

要是用的springboot+jpa能夠看這篇文章:http://www.javashuo.com/article/p-wfymhrfk-ba.html前端

其它的框架內容不作贅述,直接上代碼。java

數據準備

裝備兩個數據庫。並在兩個庫中建表,建表sql以下:mysql

DROP TABLE IF EXISTS `t_user_0`;
CREATE TABLE `t_user_0` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
  `order_id` BIGINT(20) DEFAULT '0' COMMENT '順序編號',
  `user_id` BIGINT(20) DEFAULT '0' COMMENT '用戶編號',
  `user_name` varchar(32) DEFAULT NULL COMMENT '用戶名',
  `pass_word` varchar(32) DEFAULT NULL COMMENT '密碼',
  `nick_name` varchar(32) DEFAULT NULL COMMENT '倪名',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

DROP TABLE IF EXISTS `t_user_1`;
CREATE TABLE `t_user_1` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '主鍵id',
  `order_id` BIGINT(20) DEFAULT '0' COMMENT '順序編號',
  `user_id` BIGINT(20) DEFAULT '0' COMMENT '用戶編號',
  `user_name` varchar(32) DEFAULT NULL COMMENT '用戶名',
  `pass_word` varchar(32) DEFAULT NULL COMMENT '密碼',
  `nick_name` varchar(32) DEFAULT NULL COMMENT '倪名',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;

POM配置

 <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>com.dangdang</groupId>
            <artifactId>sharding-jdbc-core</artifactId>
            <version>1.5.4</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>druid</artifactId>
            <version>1.1.3</version>
        </dependency>
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.44</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- mybatis-plus begin -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatisplus-spring-boot-starter</artifactId>
            <version>${mybatisplus-spring-boot-starter.version}</version>
        </dependency>
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus</artifactId>
            <version>${mybatisplus.version}</version>
        </dependency>
        <dependency>
            <groupId>org.apache.velocity</groupId>
            <artifactId>velocity</artifactId>
            <version>${velocity.version}</version>
        </dependency>
        <!-- mybatis-plus end -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.51</version>
        </dependency>
    </dependencies>

application.properties配置

spring.devtools.remote.restart.enabled=false

spring.jdbc1.type=com.alibaba.druid.pool.DruidDataSource
spring.jdbc1.driverClassName=com.mysql.jdbc.Driver
spring.jdbc1.url=jdbc:mysql://localhost:3306/mazhq?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.jdbc1.username=root
spring.jdbc1.password=123456
spring.jdbc1.connectionProperties=config.decrypt=true;druid.stat.slowSqlMillis=3000;druid.stat.logSlowSql=true;druid.stat.mergeSql=true
spring.jdbc1.filters=stat
spring.jdbc1.maxActive=100
spring.jdbc1.initialSize=1
spring.jdbc1.maxWait=15000
spring.jdbc1.minIdle=1
spring.jdbc1.timeBetweenEvictionRunsMillis=30000
spring.jdbc1.minEvictableIdleTimeMillis=180000
spring.jdbc1.validationQuery=SELECT 'x'
spring.jdbc1.testWhileIdle=true
spring.jdbc1.testOnBorrow=false
spring.jdbc1.testOnReturn=false
spring.jdbc1.poolPreparedStatements=false
spring.jdbc1.maxPoolPreparedStatementPerConnectionSize=20
spring.jdbc1.removeAbandoned=true
spring.jdbc1.removeAbandonedTimeout=600
spring.jdbc1.logAbandoned=false
spring.jdbc1.connectionInitSqls=

spring.jdbc2.type=com.alibaba.druid.pool.DruidDataSource
spring.jdbc2.driverClassName=com.mysql.jdbc.Driver
spring.jdbc2.url=jdbc:mysql://localhost:3306/liugh?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
spring.jdbc2.username=root
spring.jdbc2.password=123456
spring.jdbc2.connectionProperties=config.decrypt=true;druid.stat.slowSqlMillis=3000;druid.stat.logSlowSql=true;druid.stat.mergeSql=true
spring.jdbc2.filters=stat
spring.jdbc2.maxActive=100
spring.jdbc2.initialSize=1
spring.jdbc2.maxWait=15000
spring.jdbc2.minIdle=1
spring.jdbc2.timeBetweenEvictionRunsMillis=30000
spring.jdbc2.minEvictableIdleTimeMillis=180000
spring.jdbc2.validationQuery=SELECT 'x'
spring.jdbc2.testWhileIdle=true
spring.jdbc2.testOnBorrow=false
spring.jdbc2.testOnReturn=false
spring.jdbc2.poolPreparedStatements=false
spring.jdbc2.maxPoolPreparedStatementPerConnectionSize=20
spring.jdbc2.removeAbandoned=true
spring.jdbc2.removeAbandonedTimeout=600
spring.jdbc2.logAbandoned=false
spring.jdbc2.connectionInitSqls=

mybatis-plus.mapper-locations=classpath:/com/mazhq/web/mapper/xml/*Mapper.xml
mybatis-plus.type-aliases-package=com.mazhq.web.entity
#1:數據庫ID自增   2:用戶輸入id   3:全局惟一id(IdWorker)   4:全局惟一ID(uuid)
mybatis-plus.global-config.id-type=3
mybatis-plus.global-config.db-column-underline=true
mybatis-plus.global-config.refresh-mapper=true
mybatis-plus.configuration.map-underscore-to-camel-case=true
#配置的緩存的全局開關
mybatis-plus.configuration.cache-enabled=true
#延時加載的開關
mybatis-plus.configuration.lazy-loading-enabled=true
#開啓的話,延時加載一個屬性時會加載該對象所有屬性,不然按需加載屬性
mybatis-plus.configuration.multiple-result-sets-enabled=true
#打印sql語句,調試用
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl

分庫分表最主要有幾個配置:

 1. 有多少個數據源 (2個:database0和database1)

@Data
@ConfigurationProperties(prefix = "spring.jdbc1")
public class ShardDataSource1 {
    private String driverClassName;
    private String url;
    private String username;
    private String password;
    private String filters;
    private int maxActive;
    private int initialSize;
    private int maxWait;
    private int minIdle;
    private int timeBetweenEvictionRunsMillis;
    private int minEvictableIdleTimeMillis;
    private String validationQuery;
    private boolean testWhileIdle;
    private boolean testOnBorrow;
    private boolean testOnReturn;
    private boolean poolPreparedStatements;
    private int maxPoolPreparedStatementPerConnectionSize;
    private boolean removeAbandoned;
    private int removeAbandonedTimeout;
    private boolean logAbandoned;
    private List<String> connectionInitSqls;
    private String connectionProperties;
}

  2. 用什麼列進行分庫以及分庫算法 

/**
 * @author mazhq
 * @date 2019/8/7 17:23
 */
public class DataBaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm<Long> {

    @Override
    public String doEqualSharding(Collection<String> databaseNames, ShardingValue<Long> shardingValue) {
        for (String each : databaseNames) {
            if (each.endsWith(Long.parseLong(shardingValue.getValue().toString()) % 2 + "")) {
                return each;
            }
        }
        throw new IllegalArgumentException();
    }

    @Override
    public Collection<String> doInSharding(Collection<String> databaseNames, ShardingValue<Long> shardingValue) {
        Collection<String> result = new LinkedHashSet<>(databaseNames.size());
        for (Long value : shardingValue.getValues()) {
            for (String tableName : databaseNames) {
                if (tableName.endsWith(value % 2 + "")) {
                    result.add(tableName);
                }
            }
        }
        return result;
    }

    @Override
    public Collection<String> doBetweenSharding(Collection<String> databaseNames, ShardingValue<Long> shardingValue) {
        Collection<String> result = new LinkedHashSet<>(databaseNames.size());
        Range<Long> range = (Range<Long>) shardingValue.getValueRange();
        for (Long i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
            for (String each : databaseNames) {
                if (each.endsWith(i % 2 + "")) {
                    result.add(each);
                }
            }
        }
        return result;
    }
}

  3. 用什麼列進行分表以及分表算法

/**
 * @author mazhq
 * @Title: TableShardingAlgorithm
 * @date 2019/8/12 16:40
 */
public class TableShardingAlgorithm implements SingleKeyTableShardingAlgorithm<Long> {
    @Override
    public String doEqualSharding(Collection<String> tableNames, ShardingValue<Long> shardingValue) {
        for (String each : tableNames) {
            if (each.endsWith(shardingValue.getValue() % 2 + "")) {
                return each;
            }
        }
        throw new IllegalArgumentException();
    }

    @Override
    public Collection<String> doInSharding(Collection<String> tableNames, ShardingValue<Long> shardingValue) {
        Collection<String> result = new LinkedHashSet<>(tableNames.size());
        for (Long value : shardingValue.getValues()) {
            for (String tableName : tableNames) {
                if (tableName.endsWith(value % 2 + "")) {
                    result.add(tableName);
                }
            }
        }
        return result;
    }

    @Override
    public Collection<String> doBetweenSharding(Collection<String> tableNames, ShardingValue<Long> shardingValue) {
        Collection<String> result = new LinkedHashSet<>(tableNames.size());
        Range<Long> range = (Range<Long>) shardingValue.getValueRange();
        for (Long i = range.lowerEndpoint(); i <= range.upperEndpoint(); i++) {
            for (String each : tableNames) {
                if (each.endsWith(i % 2 + "")) {
                    result.add(each);
                }
            }
        }
        return result;
    }
}

  4. 每張表的邏輯表名和全部物理表名和集成調用

/**
 * @author mazhq
 * @Title: DataSourceConfig
 * @date 2019/8/7 17:05
 */
@Configuration
@EnableTransactionManagement
@ConditionalOnClass(DruidDataSource.class)
@EnableConfigurationProperties({ShardDataSource1.class, ShardDataSource2.class})
public class DataSourceConfig {
    @Autowired
    private ShardDataSource1 dataSource1;
    @Autowired
    private ShardDataSource2 dataSource2;

    /**
     * 配置數據源0,數據源的名稱最好要有必定的規則,方便配置分庫的計算規則
     * @return
     */
    private DataSource db1() throws SQLException {
        return this.getDB1(dataSource1);
    }
    /**
     * 配置數據源1,數據源的名稱最好要有必定的規則,方便配置分庫的計算規則
     * @return
     */
    private DataSource db2() throws SQLException {
        return this.getDB2(dataSource2);
    }

    /**
     * 配置數據源規則,即將多個數據源交給sharding-jdbc管理,而且能夠設置默認的數據源,
     * 當表沒有配置分庫規則時會使用默認的數據源
     * @return
     */
    @Bean
    public DataSourceRule dataSourceRule() throws SQLException {
        Map<String, DataSource> dataSourceMap = new HashMap<>();
        dataSourceMap.put("dataSource0", this.db1());
        dataSourceMap.put("dataSource1", this.db2());
        return new DataSourceRule(dataSourceMap, "dataSource0");
    }

    /**
     * 配置數據源策略和表策略,具體策略須要本身實現
     * @param dataSourceRule
     * @return
     */
    @Bean
    public ShardingRule shardingRule(@Qualifier("dataSourceRule") DataSourceRule dataSourceRule){
        //具體分庫分表策略
        TableRule orderTableRule = TableRule.builder("t_user")
                .actualTables(Arrays.asList("t_user_0", "t_user_1"))
                .tableShardingStrategy(new TableShardingStrategy("order_id", new TableShardingAlgorithm()))
                .dataSourceRule(dataSourceRule)
                .build();

        //綁定表策略,在查詢時會使用主表策略計算路由的數據源,所以須要約定綁定表策略的表的規則須要一致,能夠必定程度提升效率
        List<BindingTableRule> bindingTableRuleList = new ArrayList<BindingTableRule>();
        bindingTableRuleList.add(new BindingTableRule(Arrays.asList(orderTableRule)));
        return ShardingRule.builder().dataSourceRule(dataSourceRule)
                .tableRules(Arrays.asList(orderTableRule))
                .bindingTableRules(bindingTableRuleList)
                .databaseShardingStrategy(new DatabaseShardingStrategy("user_id", new DataBaseShardingAlgorithm()))
                .tableShardingStrategy(new TableShardingStrategy("order_id", new TableShardingAlgorithm()))
                .build();
    }

    /**
     * 建立sharding-jdbc的數據源DataSource,MybatisAutoConfiguration會使用此數據源
     * @param shardingRule
     * @return
     * @throws SQLException
     */
    @Bean
    public DataSource shardingDataSource(@Qualifier("shardingRule") ShardingRule shardingRule) throws SQLException {
        return ShardingDataSourceFactory.createDataSource(shardingRule);
    }

    private DruidDataSource getDB1(ShardDataSource1 shardDataSource1) throws SQLException {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(shardDataSource1.getDriverClassName());
        ds.setUrl(shardDataSource1.getUrl());
        ds.setUsername(shardDataSource1.getUsername());
        ds.setPassword(shardDataSource1.getPassword());
        ds.setFilters(shardDataSource1.getFilters());
        ds.setMaxActive(shardDataSource1.getMaxActive());
        ds.setInitialSize(shardDataSource1.getInitialSize());
        ds.setMaxWait(shardDataSource1.getMaxWait());
        ds.setMinIdle(shardDataSource1.getMinIdle());
        ds.setTimeBetweenEvictionRunsMillis(shardDataSource1.getTimeBetweenEvictionRunsMillis());
        ds.setMinEvictableIdleTimeMillis(shardDataSource1.getMinEvictableIdleTimeMillis());
        ds.setValidationQuery(shardDataSource1.getValidationQuery());
        ds.setTestWhileIdle(shardDataSource1.isTestWhileIdle());
        ds.setTestOnBorrow(shardDataSource1.isTestOnBorrow());
        ds.setTestOnReturn(shardDataSource1.isTestOnReturn());
        ds.setPoolPreparedStatements(shardDataSource1.isPoolPreparedStatements());
        ds.setMaxPoolPreparedStatementPerConnectionSize(
                shardDataSource1.getMaxPoolPreparedStatementPerConnectionSize());
        ds.setRemoveAbandoned(shardDataSource1.isRemoveAbandoned());
        ds.setRemoveAbandonedTimeout(shardDataSource1.getRemoveAbandonedTimeout());
        ds.setLogAbandoned(shardDataSource1.isLogAbandoned());
        ds.setConnectionInitSqls(shardDataSource1.getConnectionInitSqls());
        ds.setConnectionProperties(shardDataSource1.getConnectionProperties());
        return ds;
    }

    private DruidDataSource getDB2(ShardDataSource2 shardDataSource2) throws SQLException {
        DruidDataSource ds = new DruidDataSource();
        ds.setDriverClassName(shardDataSource2.getDriverClassName());
        ds.setUrl(shardDataSource2.getUrl());
        ds.setUsername(shardDataSource2.getUsername());
        ds.setPassword(shardDataSource2.getPassword());
        ds.setFilters(shardDataSource2.getFilters());
        ds.setMaxActive(shardDataSource2.getMaxActive());
        ds.setInitialSize(shardDataSource2.getInitialSize());
        ds.setMaxWait(shardDataSource2.getMaxWait());
        ds.setMinIdle(shardDataSource2.getMinIdle());
        ds.setTimeBetweenEvictionRunsMillis(shardDataSource2.getTimeBetweenEvictionRunsMillis());
        ds.setMinEvictableIdleTimeMillis(shardDataSource2.getMinEvictableIdleTimeMillis());
        ds.setValidationQuery(shardDataSource2.getValidationQuery());
        ds.setTestWhileIdle(shardDataSource2.isTestWhileIdle());
        ds.setTestOnBorrow(shardDataSource2.isTestOnBorrow());
        ds.setTestOnReturn(shardDataSource2.isTestOnReturn());
        ds.setPoolPreparedStatements(shardDataSource2.isPoolPreparedStatements());
        ds.setMaxPoolPreparedStatementPerConnectionSize(
                shardDataSource2.getMaxPoolPreparedStatementPerConnectionSize());
        ds.setRemoveAbandoned(shardDataSource2.isRemoveAbandoned());
        ds.setRemoveAbandonedTimeout(shardDataSource2.getRemoveAbandonedTimeout());
        ds.setLogAbandoned(shardDataSource2.isLogAbandoned());
        ds.setConnectionInitSqls(shardDataSource2.getConnectionInitSqls());
        ds.setConnectionProperties(shardDataSource2.getConnectionProperties());
        return ds;
    }

}

  

接口測試代碼

entity層

User.javaweb

@Data
@TableName("t_user")
public class User extends Model<User> {

    private static final long serialVersionUID = 1L;

    /**
     * 主鍵id
     */
    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    /**
     * 順序編號
     */
    @TableField("order_id")
    private Long orderId;
    /**
     * 用戶編號
     */
    @TableField("user_id")
    private Long userId;
    /**
     * 用戶名
     */
    @TableField("user_name")
    private String userName;
    /**
     * 密碼
     */
    @TableField("pass_word")
    private String passWord;
    /**
     * 倪名
     */
    @TableField("nick_name")
    private String nickName;

    @Override
    protected Serializable pkVal() {
        return this.id;
    }

    @Override
    public String toString() {
        return "User{" +
        "id=" + id +
        ", orderId=" + orderId +
        ", userId=" + userId +
        ", userName=" + userName +
        ", passWord=" + passWord +
        ", nickName=" + nickName +
        "}";
    }
}

mapper層

User.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.mazhq.web.mapper.UserMapper">

    <!-- 通用查詢映射結果 -->
    <resultMap id="BaseResultMap" type="com.mazhq.web.entity.User">
        <id column="id" property="id" />
        <result column="order_id" property="orderId" />
        <result column="user_id" property="userId" />
        <result column="user_name" property="userName" />
        <result column="pass_word" property="passWord" />
        <result column="nick_name" property="nickName" />
    </resultMap>

    <!-- 通用查詢結果列 -->
    <sql id="Base_Column_List">
        id, order_id AS orderId, user_id AS userId, user_name AS userName, pass_word AS passWord, nick_name AS nickName
    </sql>

</mapper>

UserMapper.javaspring

/**
 * <p>
 *  Mapper 接口
 * </p>
 *
 * @author mazhq123
 * @since 2019-08-20
 */
public interface UserMapper extends BaseMapper<User> {

}

service層

IUserService.javasql

/**
 * <p>
 *  服務類
 * </p>
 *
 * @author mazhq123
 * @since 2019-08-20
 */
public interface IUserService extends IService<User> {

}

UserServiceImpl.java數據庫

/**
 * <p>
 *  服務實現類
 * </p>
 *
 * @author mazhq123
 * @since 2019-08-20
 */
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {

}

controller層

UserController.javaapache

/**
 * <p>
 *  前端控制器
 * </p>
 * @author mazhq123
 * @since 2019-08-20
 */
@RestController
@RequestMapping("/web/user")
public class UserController {
    @Autowired
    IUserService userService;

    @RequestMapping("/save")
    public String save(){
        User user2 = new User();
        for (int i = 0; i < 40; i++) {
            user2.setId((long)i);
            user2.setUserId((long)i);
            Random r = new Random();
            user2.setOrderId((long)r.nextInt(100));
            user2.setNickName("owenma"+i);
            user2.setPassWord("password"+i);
            user2.setUserName("userName"+i);
            userService.insert(user2);
        }
        return "success";
    }

    @RequestMapping("/findAll")
    public String findAll(){
        Wrapper<User> userWrapper = new Wrapper<User>() {
            @Override
            public String getSqlSegment() {
                return "order by order_id desc";
            }
        };
        return JSONObject.toJSONString(userService.selectList(userWrapper));
    }
}

測試方式:

 先新增: http://localhost:8080/web/user/save

 再查詢: http://localhost:8080/web/user/findAll

相關文章
相關標籤/搜索