SpringBoot 整合 Sharding-JDBC 分库分表
导读
分库分表的技术有:数据库中间件Mycat(点我直达),当当网开源的Sharding-JDBC;我们公司用的也是sharding-jdbc,自己也搭建一个完整的项目,直接可以拿来用。下面附源码(CRUD,分页,事务等都已测试过)
技术栈
- SpringBoot 2.3.9
- sharding-jdbc-core 2.0.3 (官网地址:点我直达)
- druid
- mybatis-plus
- lombok
- mybatis | mybatisplus 分页功能
- 统一异常处理器
项目结构
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.9.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.ybchen</groupId>
<artifactId>springboot-sharding</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-sharding</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
<maven.compiler.target>1.8</maven.compiler.target>
<maven.compiler.source>1.8</maven.compiler.source>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency> <dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
<!--sharding-->
<dependency>
<groupId>io.shardingjdbc</groupId>
<artifactId>sharding-jdbc-core</artifactId>
<version>2.0.3</version>
</dependency>
<!--mysql-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--druid-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.5</version>
</dependency>
<!--mybatisplus-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.1</version>
</dependency>
<!--mybatis pagehelper分页插件-->
<dependency>
<groupId>com.github.pagehelper</groupId>
<artifactId>pagehelper</artifactId>
<version>5.2.0</version>
</dependency>
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.20</version>
</dependency>
</dependencies> <build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xml</include>
</includes>
</resource>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
</build>
</project>
logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- 日志级别从低到高分为TRACE < DEBUG < INFO < WARN < ERROR < FATAL,如果设置为WARN,则低于WARN的信息都不会输出 -->
<!-- scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true -->
<!-- scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒。当scan为true时,此属性生效。默认的时间间隔为1分钟。 -->
<!-- debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。 -->
<configuration scan="true" scanPeriod="10 seconds"> <contextName>logback</contextName>
<!-- name的值是变量的名称,value的值时变量定义的值。通过定义的值会被插入到logger上下文中。定义变量后,可以使“${}”来使用变量。 -->
<property name="log.path" value="applog/" />
<property name="log.name" value="springboot-sharding"/>
<!--控制台打印格式-->
<property name="CONSOLE_LOG_PATTERN_FILE" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %C:%M:%L [%thread] %-5level %msg%n"/>
<!--debug文件打印格式-->
<property name="DEBUG_LOG_PATTERN_FILE" value="%d{yyyy-MM-dd HH:mm:ss} [%c]-[%p] %m%n"/> <!-- 彩色日志 -->
<!-- 彩色日志依赖的渲染类 -->
<conversionRule conversionWord="clr" converterClass="org.springframework.boot.logging.logback.ColorConverter" />
<conversionRule conversionWord="wex" converterClass="org.springframework.boot.logging.logback.WhitespaceThrowableProxyConverter" />
<conversionRule conversionWord="wEx" converterClass="org.springframework.boot.logging.logback.ExtendedWhitespaceThrowableProxyConverter" />
<!-- 彩色日志格式 -->
<property name="CONSOLE_LOG_PATTERN" value="${CONSOLE_LOG_PATTERN:-%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(${LOG_LEVEL_PATTERN:-%5p}) %clr(${PID:- }){magenta} %clr(---){faint} %clr([%15.15t]){faint} %clr(%-40.40logger{39}){cyan} %clr(:){faint} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}}"/> <!--输出到控制台-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<!--此日志appender是为开发使用,只配置最底级别,控制台输出的日志级别是大于或等于此级别的日志信息-->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>debug</level>
</filter>
<encoder>
<Pattern>${CONSOLE_LOG_PATTERN}</Pattern>
<!-- 设置字符集 -->
<charset>UTF-8</charset>
</encoder>
</appender> <!--输出到文件-->
<!-- 时间滚动输出 level为 INFO 日志 -->
<appender name="INFO_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/${log.name}/${log.name}_info.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>${CONSOLE_LOG_PATTERN_FILE}</pattern>
<charset>UTF-8</charset>
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<!-- 每天日志归档路径以及格式 -->
<fileNamePattern>${log.path}/${log.name}/info/${log.name}-info-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录info级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>info</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender> <!-- 时间滚动输出 level为 debug 日志 -->
<appender name="DEBUG_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/${log.name}/${log.name}_debug.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>${DEBUG_LOG_PATTERN_FILE}</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/${log.name}/debug/${log.name}-debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录DEBUG级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender> <!-- 时间滚动输出 level为 ERROR 日志 -->
<appender name="ERROR_FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 正在记录的日志文件的路径及文件名 -->
<file>${log.path}/${log.name}/${log.name}_error.log</file>
<!--日志文件输出格式-->
<encoder>
<pattern>${CONSOLE_LOG_PATTERN_FILE}</pattern>
<charset>UTF-8</charset> <!-- 此处设置字符集 -->
</encoder>
<!-- 日志记录器的滚动策略,按日期,按大小记录 -->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${log.path}/${log.name}/error/${log.name}-error-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
<!--日志文件保留天数-->
<maxHistory>15</maxHistory>
</rollingPolicy>
<!-- 此日志文件只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
</appender> <root level="info">
<appender-ref ref="CONSOLE" />
<appender-ref ref="INFO_FILE" />
<appender-ref ref="DEBUG_FILE" />
<appender-ref ref="ERROR_FILE" />
</root>
<logger name="com.ybchen.mapper" level="DEBUG"/>
<logger name="com.ybchen" level="DEBUG"/>
</configuration>
application.properties
server.port=9999
# ds0
ds0.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
ds0.datasource.url=jdbc:mysql://localhost:3306/online_education?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
ds0.datasource.username=root
ds0.datasource.password=root
# ds1
ds1.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
ds1.datasource.url=jdbc:mysql://localhost:3306/online_education1?useUnicode=true&characterEncoding=UTF-8&serverTimezone=Asia/Shanghai
ds1.datasource.username=root
ds1.datasource.password=root
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.ybchen.mapper.UserMapper">
<select id="all" resultType="com.ybchen.domain.UserDO">
SELECT * FROM t_user
</select>
<insert id="add" parameterType="com.ybchen.domain.UserDO">
INSERT INTO `t_user` (`id`, `user_name`, `age`, `create_time`, `tags`) VALUES (#{id}, #{userName}, #{age}, #{createTime}, #{tags})
</insert>
<update id="update" parameterType="com.ybchen.domain.UserDO">
update t_user set user_name=#{userName} where id=#{id}
</update>
<delete id="delete">
delete from t_user where id=#{id}
</delete>
</mapper>
SpringBootShardingApplication.java
package com.ybchen; import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.transaction.annotation.EnableTransactionManagement; //忽略自动装配DataSource
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class})
//扫描Mapper
@MapperScan("com.ybchen.mapper")
//开启事务
@EnableTransactionManagement
public class SpringbootShardingApplication { public static void main(String[] args) {
SpringApplication.run(SpringbootShardingApplication.class, args);
} }
DataSourceConfig.java
package com.ybchen; import com.alibaba.druid.filter.Filter;
import com.alibaba.druid.filter.stat.StatFilter;
import com.alibaba.druid.pool.DruidDataSource;
import com.baomidou.mybatisplus.annotation.DbType;
import com.baomidou.mybatisplus.extension.plugins.MybatisPlusInterceptor;
import com.baomidou.mybatisplus.extension.plugins.inner.PaginationInnerInterceptor;
import com.baomidou.mybatisplus.extension.spring.MybatisSqlSessionFactoryBean;
import com.github.pagehelper.PageInterceptor;
import com.google.common.collect.Lists;
import groovy.util.logging.Slf4j;
import io.shardingjdbc.core.api.ShardingDataSourceFactory;
import io.shardingjdbc.core.api.config.ShardingRuleConfiguration;
import io.shardingjdbc.core.api.config.TableRuleConfiguration;
import io.shardingjdbc.core.api.config.strategy.InlineShardingStrategyConfiguration;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.session.SqlSessionFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.Environment;
import org.springframework.core.io.Resource;
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.*; /**
* @Description:配置参考链接:https://shardingsphere.apache.org/document/legacy/3.x/document/cn/manual/sharding-jdbc/configuration/config-java/
* @Author:chenyanbin
* @Date:2021/4/15 上午11:44
* @Versiion:1.0
*/
@Configuration
@EnableTransactionManagement
@Slf4j
public class DataSourceConfig {
@Autowired
private Environment env; @Bean
public Filter statFilter() {
StatFilter filter = new StatFilter();
filter.setSlowSqlMillis(5000);
filter.setLogSlowSql(true);
filter.setMergeSql(true);
return filter;
} @Bean("sqlSessionFactory")
SqlSessionFactory sqlSessionFactory(
) throws Exception {
final MybatisSqlSessionFactoryBean sessionFactory = new MybatisSqlSessionFactoryBean();
//设置数据源
sessionFactory.setDataSource(dataSource());
//设置分页
sessionFactory.setPlugins(new Interceptor[]{mybatisPlusInterceptor(), pageInterceptor()});
//mapper扫描路径
Resource[] r1 = new PathMatchingResourcePatternResolver()
.getResources("classpath*:com/ybchen/mapper/xml/*.xml");
Resource[] r2 = new PathMatchingResourcePatternResolver()
.getResources("classpath*:mapper/*.xml");
List<Resource> list = new ArrayList<>();
list.addAll(Arrays.asList(r1));
list.addAll(Arrays.asList(r2));
sessionFactory.setMapperLocations(list.toArray(new Resource[list.size()]));
return sessionFactory.getObject();
} //事务管理
@Bean
public DataSourceTransactionManager transactitonManager(@Autowired DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
} //mybatis 分页
public PageInterceptor pageInterceptor() {
PageInterceptor pi = new PageInterceptor();
Properties p = new Properties();
//当该参数设置为 true 时,pageNum<=0 时会查询第一页, pageNum>pages(超过总数时),会查询最后一页
p.setProperty("reasonable", "true");
pi.setProperties(p);
return pi;
} //mybatis plus分页
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return mybatisPlusInterceptor;
} @Bean
public DataSource dataSource() throws SQLException {
ShardingRuleConfiguration shardingRuleConfig = new ShardingRuleConfiguration();
//添加表
shardingRuleConfig.getTableRuleConfigs().add(tUserTableRuleConfiguration());
Properties properties = new Properties();
//是否开启SQL显示,默认值: false
// properties.setProperty("sql.show", "true");
return ShardingDataSourceFactory.createDataSource(createDataSourceMap(), shardingRuleConfig, new HashMap<>(), properties);
} /**
* 表分片规则配置对象,表:t_user
* 参考链接:https://shardingsphere.apache.org/document/legacy/3.x/document/cn/manual/sharding-jdbc/configuration/config-java/
*
* @return
*/
TableRuleConfiguration tUserTableRuleConfiguration() {
TableRuleConfiguration result = new TableRuleConfiguration();
//逻辑表名称
result.setLogicTable("t_user");
//由数据源名 + 表名组成,以小数点分隔。多个表以逗号分隔,支持inline表达式。缺省表示使用已知数据源与逻辑表名称生成数据节点。用于广播表(即每个库中都需要一个同样的表用于关联查询,多为字典表)或只分库不分表且所有库的表结构完全一致的情况
result.setActualDataNodes("ds${0..1}.t_user");
//分片列名称
final String shardingColumn = "tags";
//分片算法行表达式,需符合groovy语法,表达式参考:https://shardingsphere.apache.org/document/legacy/3.x/document/cn/features/sharding/other-features/inline-expression/
final String algorithmExpression = "ds${tags%2}";
//ShardingStrategyConfiguration的实现类,用于配置行表达式分片策略。
result.setDatabaseShardingStrategyConfig(new InlineShardingStrategyConfiguration(shardingColumn, algorithmExpression));
//自增列名称,缺省表示不适用自增主键生成器
// result.setKeyGeneratorColumnName("id");
return result;
} Map<String, DataSource> createDataSourceMap() {
Map<String, DataSource> result = new HashMap<>();
result.put("ds0", dataSource_0());
result.put("ds1", dataSource_1());
return result;
} /**
* 数据源-0
*
* @return
*/
public DataSource dataSource_0() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(env.getProperty("ds0.datasource.driver-class-name"));
dataSource.setUrl(env.getProperty("ds0.datasource.url"));
dataSource.setUsername(env.getProperty("ds0.datasource.username"));
dataSource.setPassword(env.getProperty("ds0.datasource.password"));
dataSource.setProxyFilters(Lists.newArrayList(statFilter()));
//每个分区最大的连接数
dataSource.setMaxActive(20);
//每个分区最小的连接数
dataSource.setMinIdle(5);
return dataSource;
} /**
* 数据源-1
*
* @return
*/
public DataSource dataSource_1() {
DruidDataSource dataSource = new DruidDataSource();
dataSource.setDriverClassName(env.getProperty("ds1.datasource.driver-class-name"));
dataSource.setUrl(env.getProperty("ds1.datasource.url"));
dataSource.setUsername(env.getProperty("ds1.datasource.username"));
dataSource.setPassword(env.getProperty("ds1.datasource.password"));
dataSource.setProxyFilters(Lists.newArrayList(statFilter()));
//每个分区最大的连接数
dataSource.setMaxActive(20);
//每个分区最小的连接数
dataSource.setMinIdle(5);
return dataSource;
}
}
GlobalExceptions.java
package com.ybchen.exception; import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody; /**
* @ClassName:GlobalExceptiions
* @Description:全局异常
* @Author:chenyb
* @Date:2021/4/15 上午11:44
* @Versiion:1.0
*/
@ControllerAdvice
public class GlobalExceptions {
private final Logger logger = LoggerFactory.getLogger(getClass()); @ExceptionHandler(value = Exception.class)
@ResponseBody
public Object handle(Exception ex) {
logger.error("「 全局异常 」 ===============》{}", ex);
return "「 全局异常 」错误信息:"+ex.getMessage();
}
}
UserDO.java
package com.ybchen.domain; import java.util.Date; /**
* @Description:mybatis方式实体类
* @Author:chenyanbin
* @Date:2021/4/16 上午9:47
* @Versiion:1.0
*/
public class UserDO {
private String id;
private String userName;
private Integer age;
private Date createTime;
private Integer tags; public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
} public Integer getAge() {
return age;
} public void setAge(Integer age) {
this.age = age;
} public Date getCreateTime() {
return createTime;
} public void setCreateTime(Date createTime) {
this.createTime = createTime;
} public Integer getTags() {
return tags;
} public void setTags(Integer tags) {
this.tags = tags;
} @Override
public String toString() {
return "UserDO{" +
"id='" + id + '\'' +
", userName='" + userName + '\'' +
", age=" + age +
", createTime=" + createTime +
", tags=" + tags +
'}';
}
}
UserMybatisDO.java
package com.ybchen.domain; import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName; import java.util.Date; /**
* @Description:mybatis plus方式实体类
* @Author:chenyanbin
* @Date:2021/4/16 上午9:47
* @Versiion:1.0
*/
@TableName("t_user")
public class UserMybatisDO {
@TableId(value = "id")
private String id;
@TableField("user_name")
private String userName;
@TableField("age")
private Integer age;
@TableField("create_time")
private Date createTime;
@TableField("tags")
private Integer tags; public String getId() {
return id;
} public void setId(String id) {
this.id = id;
} public String getUserName() {
return userName;
} public void setUserName(String userName) {
this.userName = userName;
} public Integer getAge() {
return age;
} public void setAge(Integer age) {
this.age = age;
} public Date getCreateTime() {
return createTime;
} public void setCreateTime(Date createTime) {
this.createTime = createTime;
} public Integer getTags() {
return tags;
} public void setTags(Integer tags) {
this.tags = tags;
} @Override
public String toString() {
return "UserMybatisDO{" +
"id='" + id + '\'' +
", userName='" + userName + '\'' +
", age=" + age +
", createTime=" + createTime +
", tags=" + tags +
'}';
}
}
UserMapper.java
package com.ybchen.mapper; import com.ybchen.domain.UserDO; import java.util.List; public interface UserMapper {
//查询
List<UserDO> all(); //添加
Integer add(UserDO userDO); //更新
Integer update(UserDO userDO); //删除
Integer delete(String id);
}
UserMybatisPlusMapper.java
package com.ybchen.mapper; import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.ybchen.domain.UserMybatisDO; public interface UserMybatisPlusMapper extends BaseMapper<UserMybatisDO> {
}
UserService.java
package com.ybchen.service; import com.ybchen.domain.UserDO;
import com.ybchen.domain.UserMybatisDO; import java.util.List; /**
* @Description:
* @Author:chenyanbin
* @Date:2021/4/15 下午4:52
* @Versiion:1.0
*/
public interface UserService {
//mybatis 查询
List<UserDO> all(); //mybatisplus 查询
List<UserMybatisDO> allMybatisPlus(); //添加
Integer add(UserDO userDO); //更新
Integer update(UserDO userDO); //删除
Integer delete(String id);
}
UserServiceImpl.java
package com.ybchen.service.impl; import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.github.pagehelper.PageHelper;
import com.ybchen.domain.UserDO;
import com.ybchen.domain.UserMybatisDO;
import com.ybchen.mapper.UserMapper;
import com.ybchen.mapper.UserMybatisPlusMapper;
import com.ybchen.service.UserService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID; /**
* @Description:
* @Author:chenyanbin
* @Date:2021/4/15 下午4:53
* @Versiion:1.0
*/
@Service
@Slf4j
public class UserServiceImpl implements UserService {
@Autowired
UserMapper userMapper;
@Autowired
UserMybatisPlusMapper userMybatisPlusMapper; @Override
public List<UserDO> all() {
log.info("---info----");
log.debug("----debug----");
log.error("----error----");
//mybatis 分页
PageHelper.startPage(4, 3);
return userMapper.all();
} @Override
public List<UserMybatisDO> allMybatisPlus() {
//mybatis plus 分页
int start = 4;
int end = 3;
IPage<UserMybatisDO> page = new Page<>(start, end);
return userMybatisPlusMapper.selectPage(page, null).getRecords();
} @Override
//开启事务
@Transactional
public Integer add(UserDO userDO) {
userDO.setId(UUID.randomUUID().toString().replace("-", ""));
userDO.setTags(LocalDateTime.now().getSecond());
userMapper.add(userDO);
//模拟事务失败
int num = 1 / 0;
userDO.setAge(99);
userDO.setId(UUID.randomUUID().toString().replace("-", ""));
return userMapper.add(userDO);
} @Override
public Integer update(UserDO userDO) {
return userMapper.update(userDO);
} @Override
public Integer delete(String id) {
return userMapper.delete(id);
}
}
UserController.java
package com.ybchen.controller; import com.ybchen.domain.UserDO;
import com.ybchen.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*; /**
* @Description:
* @Author:chenyanbin
* @Date:2021/4/15 下午4:52
* @Versiion:1.0
*/
@RestController
public class UserController {
@Autowired
private UserService userService; @PostMapping("all")
public Object all() {
return userService.all();
} @PostMapping("allMybatisPlus")
public Object allMybatisPlus() {
return userService.allMybatisPlus();
} @PostMapping("add")
public Object add(@RequestBody UserDO userDO) {
return userService.add(userDO);
} @PostMapping("update")
public Object update(@RequestBody UserDO userDO) {
return userService.update(userDO);
} @GetMapping("delete")
public Object delete(@RequestParam("id") String id) {
return userService.delete(id);
}
}
数据库
案例源码
链接: https://pan.baidu.com/s/1j8h4YJSShWKYjwCeX56O_w 密码: 8fpe
SpringBoot 整合 Sharding-JDBC 分库分表的更多相关文章
- 【ShardingSphere技术专题】「ShardingJDBC」SpringBoot之整合ShardingJDBC实现分库分表(JavaConfig方式)
前提介绍 ShardingSphere介绍 ShardingSphere是一套开源的分布式数据库中间件解决方案组成的生态圈,它由Sharding-JDBC.Sharding-Proxy和Shardin ...
- SpringBoot+MybatisPlus+Mysql+Sharding-JDBC分库分表实践
一.序言 在实际业务中,单表数据增长较快,很容易达到数据瓶颈,比如单表百万级别数据量.当数据量继续增长时,数据的查询性能即使有索引的帮助下也不尽如意,这时可以引入数据分库分表技术. 本文将基于Spri ...
- SpringBoot 使用Sharding-JDBC进行分库分表及其分布式ID的生成
为解决关系型数据库面对海量数据由于数据量过大而导致的性能问题时,将数据进行分片是行之有效的解决方案,而将集中于单一节点的数据拆分并分别存储到多个数据库或表,称为分库分表. 分库可以有效分散高并发量,分 ...
- 分布式事务-Sharding 数据库分库分表
Sharding (转)大型互联网站解决海量数据的常见策略 - - ITeye技术网站 阿里巴巴Cobar架构设计与实践 - 机械机电 - 道客巴巴 阿里分布式数据库服务原理与实践:沈询_文档下载 ...
- spring整合sharding-jdbc实现分库分表
1.创建两个库,每个库创建两个分表t_order_1,t_order_2 DROP TABLE IF EXISTS `t_order_1`; CREATE TABLE `t_order_1` ( `i ...
- Sharding-JDBC基本使用,整合Springboot实现分库分表,读写分离
结合上一篇docker部署的mysql主从, 本篇主要讲解SpringBoot项目结合Sharding-JDBC如何实现分库分表.读写分离. 一.Sharding-JDBC介绍 1.这里引用官网上的介 ...
- mysql、oracle分库分表方案之sharding-jdbc使用(非demo示例)
选择开源核心组件的一个非常重要的考虑通常是社区活跃性,一旦项目团队无法进行自己后续维护和扩展的情况下更是如此. 至于为什么选择sharding-jdbc而不是Mycat,可以参考知乎讨论帖子https ...
- 分库分表后跨分片查询与Elastic Search
携程酒店订单Elastic Search实战:http://www.lvesu.com/blog/main/cms-610.html 为什么分库分表后不建议跨分片查询:https://www.jian ...
- 【大数据和云计算技术社区】分库分表技术演进&最佳实践笔记
1.需求背景 移动互联网时代,海量的用户每天产生海量的数量,这些海量数据远不是一张表能Hold住的.比如 用户表:支付宝8亿,微信10亿.CITIC对公140万,对私8700万. 订单表:美团每天几千 ...
- 分库分表技术演进&最佳实践
每个优秀的程序员和架构师都应该掌握分库分表,这是我的观点. 移动互联网时代,海量的用户每天产生海量的数量,比如: 用户表 订单表 交易流水表 以支付宝用户为例,8亿:微信用户更是10亿.订单表更夸张, ...
随机推荐
- 【题解】[NOIP2001 普及组] 装箱问题
[NOIP2001 普及组] 装箱问题 这是一道动态规划题. 那就先定义状态吧(这里用的是一维滚动数组). \(f[j]\) 代表当我有 \(j\) 这么多容量可以用时,能装的最大重量是多少. 好,状 ...
- 用 C 语言开发一门编程语言 — 基于 Lambda 表达式的函数设计
目录 文章目录 目录 前文列表 函数 Lambda 表达式 函数设计 函数的存储 实现 Lambda 函数 函数的运行环境 函数调用 可变长的函数参数 源代码 前文列表 <用 C 语言开发一门编 ...
- 移动通信网络中的 FDD/TDD 无线帧
目录 文章目录 目录 前文列表 无线帧 FDD 与 TDD 的区别 FDD 无线帧 TDD 无线帧 前文列表 <移动通信网络中的资源类型> 无线帧 LTE 支持两种类型的无线帧:FDD(F ...
- OAI SDR LTE 基站部署
目录 文章目录 目录 硬件设备要求 物料购买 部署架构图 安装 LTE/EPC 前期准备 运维相关 操作系统要求 内核要求 CPU Frequency scaling,将 CPU 频率打满 eNode ...
- 【Azure Developer】如何通过Azure Portal快速获取到对应操作的API并转换为Python代码
问题描述 对于Azure资源进行配置操作,门户上可以正常操作.但是想通过Python代码实现,这样可以批量处理.那么在没有SDK的情况下,是否有快速办法呢? 问题解答 当然可以,Azure Porta ...
- mogodb replication set复制集
replication set复制集 简要命令 replication set复制集 replicattion set 多台服务器维护相同的数据副本,提高服务器的可用性. Replication se ...
- vue学习笔记之父组件子组件的传值
一 在前端开发过程中,很多情况下一个页面无法装载大部分的代码,所以需要子组件来完成父组件的任务,下面我来展示一下,组件之间如何进行传值以及常见的坑,首先上代码 1.1 父组件代码 <tem ...
- FFmpeg开发笔记(二十三)使用OBS Studio开启RTMP直播推流
OBS是一个开源的直播录制软件,英文全称叫做Open Broadcaster Software,广泛用于视频录制.实时直播等领域.OBS不但开源,而且跨平台,兼容Windows.Mac OS.Lin ...
- C# Afroge摄像头翻转90
1.dll和命名空间就不在此列举了,如下只是将转换方法介绍: 第一个函数: public void Rotate90() { // 计算角度,类变量 //dAngle = dAngle + 90; / ...
- 授权调用: 介绍 Transformers 智能体 2.0
简要概述 我们推出了 Transformers 智能体 2.0! ⇒ 在现有智能体类型的基础上,我们新增了两种能够 根据历史观察解决复杂任务的智能体. ⇒ 我们致力于让代码 清晰.模块化,并确保最终提 ...