mybatis+mysql分库分表_springboot+mybatisplus+sharding-jdbc分库分表实例

项目实践

现在Java项目使用mybatis多一些,所以我也做了一个springboot+mybatisplus+sharding-jdbc分库分表项目例子分享给大家。

其它的框架内容不做赘述,直接上代码。

数据准备

装备两个数据库。并在两个库中建表,建表sql如下:

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配置

org.springframework.boot

spring-boot-starter

org.springframework.boot

spring-boot-devtools

runtime

true

org.projectlombok

lombok

true

org.springframework.boot

spring-boot-starter-test

test

com.dangdang

sharding-jdbc-core

1.5.4

com.alibaba

druid

1.1.3

commons-dbcp

commons-dbcp

1.4

mysql

mysql-connector-java

5.1.44

org.springframework.boot

spring-boot-starter-web

com.baomidou

mybatisplus-spring-boot-starter

${mybatisplus-spring-boot-starter.version}

com.baomidou

mybatis-plus

${mybatisplus.version}

org.apache.velocity

velocity

${velocity.version}

com.alibaba

fastjson

1.2.51

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 connectionInitSqls;

private String connectionProperties;

}

2. 用什么列进行分库以及分库算法

/**

* @author mazhq

* @date 2019/8/7 17:23

*/

public class DataBaseShardingAlgorithm implements SingleKeyDatabaseShardingAlgorithm {

@Override

public String doEqualSharding(Collection databaseNames, ShardingValue shardingValue) {

for (String each : databaseNames) {

if (each.endsWith(Long.parseLong(shardingValue.getValue().toString()) % 2 + "")) {

return each;

}

}

throw new IllegalArgumentException();

}

@Override

public Collection doInSharding(Collection databaseNames, ShardingValue shardingValue) {

Collection 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 doBetweenSharding(Collection databaseNames, ShardingValue shardingValue) {

Collection result = new LinkedHashSet<>(databaseNames.size());

Range range = (Range) 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 {

@Override

public String doEqualSharding(Collection tableNames, ShardingValue shardingValue) {

for (String each : tableNames) {

if (each.endsWith(shardingValue.getValue() % 2 + "")) {

return each;

}

}

throw new IllegalArgumentException();

}

@Override

public Collection doInSharding(Collection tableNames, ShardingValue shardingValue) {

Collection 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 doBetweenSharding(Collection tableNames, ShardingValue shardingValue) {

Collection result = new LinkedHashSet<>(tableNames.size());

Range range = (Range) 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 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 bindingTableRuleList = new ArrayList();

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.java

@Data

@TableName("t_user")

public class User extends Model {

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

id, order_id AS orderId, user_id AS userId, user_name AS userName, pass_word AS passWord, nick_name AS nickName

UserMapper.java

/**

*

* Mapper 接口

*

*

* @author mazhq123

* @since 2019-08-20

*/

public interface UserMapper extends BaseMapper {

}

service层

IUserService.java

/**

*

* 服务类

*

*

* @author mazhq123

* @since 2019-08-20

*/

public interface IUserService extends IService {

}

UserServiceImpl.java

/**

*

* 服务实现类

*

*

* @author mazhq123

* @since 2019-08-20

*/

@Service

public class UserServiceImpl extends ServiceImpl implements IUserService {

}

controller层

UserController.java

/**

*

* 前端控制器

*

* @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 userWrapper = new Wrapper() {

@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

你可能感兴趣的:(mybatis+mysql分库分表_springboot+mybatisplus+sharding-jdbc分库分表实例)