Springboot3+Mybatis3.5
- 基本框架
- 环境
- 依赖导入
- 配置文件
- 实体类
- 业务类结构
- 基本功能——提供的mapper接口及实现
- 插入
- 删除——返回受影响行数
- 修改数据
- 查询方法
- 自定义mapper(和mybatis一样)
- 相关功能——提供的Service方法
- Service层搭建
- 相关方法
- 添加记录
- 插入或更新(看是否有id)
- 常用注解
- 条件构造器
- 类的基本结构
- 逻辑条件
- 条件查询——QueryWrapper
- 删除条件——QueryWrapper
- 更新操作
- 模拟条件组装
- 其他功能
- Lamda表达式——防止mysql名称写错,直接使用实体类字段
- 分页插件
- 乐观锁和悲观锁
- 通用枚举
- 代码生成器
- MybatisX插件
- 多数据源[官网](https://github.com/baomidou/dynamic-datasource)
- 适用场景
- 相关概念
- 雪花算法
- 核心思想
- 优点
基本框架
环境
springboot3
mysql8+
JDK17
依赖导入
<parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>3.2.0</version></parent>
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency><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><version>3.1.5</version></dependency><!-- springboot3需要使用Mybatisplus3--><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-spring-boot3-starter</artifactId><version>3.5.5</version></dependency>
<!-- 简化操作--><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.34</version></dependency><!-- mysql驱动--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><version>8.0.33</version></dependency>
<!-- 连接池--><dependency><groupId>cn.benma666</groupId><artifactId>druid-spring-boot-starter</artifactId><version>1.2.22</version></dependency>
配置文件
spring:datasource:type: com.alibaba.druid.pool.DruidDataSourcedriver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/mybatisplus?serverTimezone=GMT%2B8&characterEncoding=utf8&useSSL=false#mysql8需要添加serverTimeZone=GMT%2B8username: rootpassword: xxxxxxx#mybatisplus配置
mybatis-plus:configuration:#日志log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
实体类
@Data
@TableName("t_user")
public class User {@TableId(type = IdType.NONE)private Long id;private String name;private Integer age;private String mail;
}
-
表明不同有两种方式
- 使用
@TableName
(“数据库对应表名”) - 如果只是数据库多了前缀——设置全局前缀(慎用)
global-config:db-config:table-prefix:
- 使用
-
主键相关
- BP默认使用id变量作为主键
- 如果变量名和数据库字段名不同意,可以使用
@TableId
、@TableField
指定 - BP的键默认使用雪花算法,(不指定Id默认自动生成),如果需要使用自增id,在
@TableId(type = IdType.AUTO)
或者在配置文件中指定mybatis-plus.global-config.db-config.id-type
- 插入对象默认会有主键回显
业务类结构
定义mapper.XXMapper
接口
@Repository
@Mapper
public interface UserMapper extends BaseMapper<User> {
}
- 说明
- BaseMapper提供了默认的单表实现,不需要自己完成
- 如果需要自定义实现可以添加并实现
- 可以在mapper类上使用
@Mapper
或者在启动类上添加@MapperScan("mapper包路径")
,进行mapper接口扫描
基本功能——提供的mapper接口及实现
插入
删除——返回受影响行数
- 更具id删除——雪花算法使用Long类型
- 根据实体类指定id删除
- 根据map删除——指明条件map
HashMap<String, Object> condition = new HashMap<>();condition.put("name","lisi");condition.put("age",14);int i = userMapper.deleteByMap(condition);System.out.println("return result:"+i);// ==> Preparing: DELETE FROM t_user WHERE (name = ? AND age = ?)// ==> Parameters: lisi(String), 14(Integer)// <== Updates: 1// Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@36525ab]// return result:1
- id列表批量删除
List<Long> ids = Arrays.asList(1L, 3L, 4L);int i = userMapper.deleteBatchIds(ids);System.out.println("return result:"+i);// ==> Preparing: DELETE FROM t_user WHERE id IN ( ? , ? , ? )// ==> Parameters: 1(Long), 3(Long), 4(Long)// <== Updates: 3// Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3b362f1]// return result:3
- 根据
Wrapper
条件删除
修改数据
- 更具实体类更新
updateById
(id必须有:id确定记录,Entity指定需要修改的字段)User user = new User();user.setId(1846522555456847874L);user.setMail("hahahha.@163.com");int i = userMapper.updateById(user);System.out.println("return result:"+i);// ==> Preparing: UPDATE t_user SET mail=? WHERE id=?// ==> Parameters: hahahha.@163.com(String), 1846522555456847874(Long)// <== Updates: 1// Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6451a288]// return result:1
- 根据条件构造器选择数据使用实体类对象更新
查询方法
- 根据id查询
- 根据id列表批量查
List<Long> ids = Arrays.asList(1846522555456847874L, 1846525211126554626L);List<User> users = userMapper.selectBatchIds(ids);users.forEach(System.out::println);// ==> Preparing: SELECT id,name,age,mail FROM t_user WHERE id IN ( ? , ? )// ==> Parameters: 1846522555456847874(Long), 1846525211126554626(Long)// <== Columns: id, name, age, mail// <== Row: 1846522555456847874, dearfriend, 12, hahahha.@163.com// <== Row: 1846525211126554626, dearfriend1, 13, null// <== Total: 2// Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1850f2da]// User(id=1846522555456847874, name=dearfriend, age=12, mail=hahahha.@163.com)// User(id=1846525211126554626, name=dearfriend1, age=13, mail=null)
- 更具map指定条件查询(只能用=)
HashMap<String, Object> condition= new HashMap<>();condition.put("age",12);List<User> users1 = userMapper.selectByMap(condition);users1.forEach(System.out::println);// ==> Preparing: SELECT id,name,age,mail FROM t_user WHERE (age = ?)// ==> Parameters: 12(Integer)// <== Columns: id, name, age, mail// <== Row: 8, haha, 12, null// <== Row: 1846522555456847874, dearfriend, 12, hahahha.@163.com// <== Total: 2// Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3dbb3fb7]// User(id=8, name=haha, age=12, mail=null)// User(id=1846522555456847874, name=dearfriend, age=12, mail=hahahha.@163.com)
- 条件构造器(可为null)
mybatis_216">自定义mapper(和mybatis一样)
- 添加xml映射文件路径
mybatis-plus.mapper-locations:
默认为classpath*:/mapper/**/*.xml
- 添加自定义mapper接口:
mapper.UserMapper.java
@Repository @Mapper public interface UserMapper extends BaseMapper<User> {Map<String,Object> selectMapById(Long id); }
- 实现mapper接口,编写映射文件:
resources.mapper.UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapperPUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd"><!--namespace为接口的全限定符--> <mapper namespace="org.dearfriend.mapper.UserMapper"><!-- 声明sql语句--> <!-- 每个标签为一个接口的实现--><select id="selectMapById" resultType="map">select id, name, t_user.age, t_user.mailfrom t_userwhere id = #{id}</select> </mapper>
- 调用
Map<String, Object> user = userMapper.selectMapById(1846525252671111169L);System.out.println(user);// ==> Preparing: select id, name, t_user.age, t_user.mail from t_user where id = ?// ==> Parameters: 1846525252671111169(Long)// <== Columns: id, name, age, mail// <== Row: 1846525252671111169, dearfriend3, 14, null// <== Total: 1// Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@a69f9d]// {name=dearfriend3, id=1846525252671111169, age=14}
相关功能——提供的Service方法
ibatis封装了IService接口和实现,使用如下前缀区分mapper接口
- get——查询
- save——插入
- update——修改
- remove——删除
- list——查询
- page——分页
注:如果存在自定义方法,推荐创建自己的Service,继承BP提供的基类
IService
提供了单表操作的服务层接口Service Impl
提供了IService的实现
Service层搭建
- 创建自定义Service接口,继承IService
- 实现自定义Serivce接口Service Impl实现了基本的接口,直接继承过来,包含两个范型
Service<M,T>
Mapper接口和实体类对象
如此以来,既能使用BP提供的基本服务,也能够自定义服务
相关方法
添加记录
- 批量添加
saveBatch(T t)
——多次调用insert方法进行ArrayList<User> users = new ArrayList<>();for (int i = 0; i < 10; i++) {User user = new User();user.setName("user"+(i+1));user.setAge(20+i);user.setName("user"+(i+1)+"@163.com");users.add(user);}boolean b = userService.saveBatch(users);System.out.println("添加状态:"+(b? "success":"fail")); // ==> Preparing: INSERT INTO t_user ( id, name, age ) VALUES ( ?, ?, ? ) // ==> Parameters: 1846548533952438273(Long), user1@163.com(String), 20(Integer) // ==> Parameters: 1846548534006964225(Long), user2@163.com(String), 21(Integer) // ==> Parameters: 1846548534006964226(Long), user3@163.com(String), 22(Integer) // ==> Parameters: 1846548534006964227(Long), user4@163.com(String), 23(Integer) // ==> Parameters: 1846548534006964228(Long), user5@163.com(String), 24(Integer) // ==> Parameters: 1846548534011158530(Long), user6@163.com(String), 25(Integer) // ==> Parameters: 1846548534011158531(Long), user7@163.com(String), 26(Integer) // ==> Parameters: 1846548534011158532(Long), user8@163.com(String), 27(Integer) // ==> Parameters: 1846548534011158533(Long), user9@163.com(String), 28(Integer) // ==> Parameters: 1846548534015352834(Long), user10@163.com(String), 29(Integer) // 添加状态:success
插入或更新(看是否有id)
常用注解
注解 | 作用 |
---|---|
@TableName | 指定绑定实体类对应的mysql表名 |
@TableId | 指定绑定实体类中主键字段,type可以指定主键策略 |
@TableField | 绑定实体类中字段和mysql中字段 |
@TableLogic | 逻辑删除-可以进行数据恢复,加载逻辑删除字段上 |
@Version | 乐观锁版本字段-使用版本好的方式实现乐观锁 |
- 绑定
@TableLogic
后,删除默认为逻辑删除,(修改逻辑删除字段),查询的查询的时候也会自动排除已逻辑删除的记录boolean b = userService.removeBatchByIds(Arrays.asList(8L));System.out.println("删除状态:"+(b?"success":"fail!"));// ==> Preparing: UPDATE t_user SET is_deleted=1 WHERE id=? AND is_deleted=0 // ==> Parameters: 8(Long) // 删除状态:successList<User> list = userService.list(); // ==> Preparing: SELECT id,name,age,mail,is_deleted FROM t_user WHERE is_deleted=0 // ==> Parameters: // <== Columns: id, name, age, mail, is_deleted // <== Row: 1846522555456847874, dearfriend, 12, hahahha.@163.com, 0 // <== Row: 1846525211126554626, dearfriend1, 13, null, 0
条件构造器
类的基本结构
-
Wrapper:条件构造抽象类,最顶端必类
- AbstractWrapper:用于查询条件封装,生成sql的where条件
- QueryWrapper:查询条件封装
- UpdateWrapper:Update条件封装
- AbstractLambdaWrapper:使用Lambda语法
- LambdaQueryWrapper:用于Lambda语法使用的查询Wrapper
- LambdaUpdateWrapper: Lambda #iT=J%Wrapper
- AbstractWrapper:用于查询条件封装,生成sql的where条件
-
通过条件锁定记录的操作(select、delete、update)——字段都是条件
QueryWrapper
-
条件中有包含更改目标值(update)——
UpdateWrapper
中可以通过set()
设置值
逻辑条件
- 默认为and
- 调用
.or()
,上下条件为||
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();userQueryWrapper.eq("age",28).or().eq("name","dearfriend");User user = new User();user.setMail("123456@123.com");boolean update = userService.update(user, userQueryWrapper);System.out.println("更新结果:"+(update?"success!":"fail!"));// ==> Preparing: UPDATE t_user SET mail=? WHERE is_deleted=0 AND (age = ? OR name = ?)// ==> Parameters: 123456@123.com(String), 28(Integer), dearfriend(String)// == Updates: 1// Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@73b0ed03]// 更新结果:success!
- 有优先级的逻辑判断——显式调用
.and()/.or()
,传入的参数为Consumer<Param> consumer
,其实还是一个WrapperQueryWrapper<User> userQueryWrapper = new QueryWrapper<>();userQueryWrapper.like("name", "a").and(i -> i.gt("age", 20).or().isNull("mail"));List<User> list = userService.list(userQueryWrapper);list.forEach(System.out::println);//// ==> Preparing: SELECT id,name,age,mail,is_deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND (age > ? OR mail IS NULL))// ==> Parameters: %a%(String), 20(Integer)// <== Total: 0
条件查询——QueryWrapper
注:cloumn字段需要使用数据库表的字段名称,而不是实体类的名称
- 查询指定字段
.select(String... cloumns)
,serivce.listMaps(QueryWrapper wrapper)
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();userQueryWrapper.select("name","age");List<Map<String, Object>> maps = userService.listMaps(userQueryWrapper);maps.forEach(System.out::println);// ==> Preparing: SELECT name,age FROM t_user WHERE is_deleted=0// ==> Parameters: // <== Columns: name, age// <== Row: dearfriend, 12// <== Total: 1// Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@344f9467]// {name=dearfriend, age=12}
- Like、Between、notNull
//用户名包含a,邮箱不为null,年龄(10,30)QueryWrapper<User> condition = new QueryWrapper<>();//链式编程,构造条件condition.like("name","a").isNotNull("mail").between("age",10,30);List<User> list = userService.list(condition);list.forEach(System.out::println);// ==> Preparing: SELECT id,name,age,mail,is_deleted FROM t_user WHERE is_deleted=0 AND (name LIKE ? AND mail IS NOT NULL AND age BETWEEN ? AND ?)
// ==> Parameters: %a%(String), 10(Integer), 30(Integer)
// <== Columns: id, name, age, mail, is_deleted
// <== Row: 1846522555456847874, dearfriend, 12, hahahha.@163.com, 0
// <== Total: 1
// Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@6ec3d8e4]
// User(id=1846522555456847874, name=dearfriend, age=12, mail=hahahha.@163.com, isDeleted=0)
- 排序查询——调用升降序方法,指定排序字段
//根据age升序,id降序QueryWrapper<User> condition = new QueryWrapper<>();condition.orderByAsc("age").orderByDesc("id");List<User> list = userService.list(condition);list.forEach(System.out::println);// ==> Preparing: SELECT id,name,age,mail,is_deleted FROM t_user WHERE is_deleted=0 ORDER BY age ASC,id DESC
// ==> Parameters:
// <== Columns: id, name, age, mail, is_deleted
// <== Row: 1846522555456847874, dearfriend, 12, hahahha.@163.com, 0
// <== Row: 1846525211126554626, dearfriend1, 13, null, 0
// <== Row: 1846525252671111169, dearfriend3, 14, null, 0
// <== Row: 1846548381057470465, user1@163.com, 20, null, 0
// <== Row: 1846548381120385025, user2@163.com, 21, null, 0
// <== Row: 1846548381120385026, user3@163.com, 22, null, 0
// <== Row: 1846548381120385027, user4@163.com, 23, null, 0
// <== Row: 1846548381120385028, user5@163.com, 24, null, 0
// <== Row: 1846548381124579330, user6@163.com, 25, null, 0
// <== Row: 1846548381124579331, user7@163.com, 26, null, 0
// <== Row: 1846548381124579332, user8@163.com, 27, null, 0
// <== Row: 1846548381124579333, user9@163.com, 28, null, 0
// <== Row: 1846548381124579334, user10@163.com, 29, null, 0
// <== Total: 13
- 组装子查询
.insql(cloumn,inval)
——cloumn字段名,inval可以使用sql语句写(里面的内容会直接放入sql语句中,包括明确的值集合,是一个拼接)
删除条件——QueryWrapper
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();userQueryWrapper.isNull("mail");boolean remove = userService.remove(userQueryWrapper);System.out.println("删除状态:"+(remove? "success!":"fail!"));// ==> Preparing: UPDATE t_user SET is_deleted=1 WHERE is_deleted=0 AND (mail IS NULL)
// ==> Parameters:
// <== Updates: 12
// Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1ed9d173]
// 删除状态:success!
更新操作
- queryWrapper+Entity
QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();userQueryWrapper.eq("age",28).or().eq("name","dearfriend");User user = new User();user.setMail("123456@123.com");boolean update = userService.update(user, userQueryWrapper);System.out.println("更新结果:"+(update?"success!":"fail!"));// ==> Preparing: UPDATE t_user SET mail=? WHERE is_deleted=0 AND (age = ? OR name = ?)// ==> Parameters: 123456@123.com(String), 28(Integer), dearfriend(String)// == Updates: 1// Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@73b0ed03]// 更新结果:success!
updateWrapper
(条件+修改值)/updateWrapper
+Entity
UpdateWrapper<User> userUpdateWrapper = new UpdateWrapper<>();// 锁定记录userUpdateWrapper.isNull("mail");userUpdateWrapper.set("mail","default@123.com");boolean update = userService.update(userUpdateWrapper);System.out.println("更新结果:"+(update?"success!":"fail"));// ==> Preparing: UPDATE t_user SET mail=? WHERE is_deleted=0 AND (mail IS NULL) // ==> Parameters: default@123.com(String)// <== Updates: 0// Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1aeff8ca]// 更新结果:fail
模拟条件组装
- 方式一:Wrapper使用链式编程,可以通过判断+链式条件添加
- 方式二:Wrapper中可以通过方法中的condition指定添加的条件(如果满足指定条件bool类型,才会添加字段限制)
boolean condition, R column, Object val
String name="";Integer age = 14;QueryWrapper<User> userQueryWrapper = new QueryWrapper<>();userQueryWrapper.eq(!StringUtils.isBlank(name),"name",name).eq(!ObjectUtils.isEmpty(age),"age",age);List<User> list = userService.list(userQueryWrapper);list.forEach(System.out::println);// ==> Preparing: SELECT id,name,age,mail,is_deleted FROM t_user WHERE is_deleted=0 AND (age = ?)// ==> Parameters: 14(Integer)// <== Columns: id, name, age, mail, is_deleted// <== Row: 1846525252671111169, dearfriend3, 14, null, 0// <== Total: 1// Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@143413cd]// User(id=1846525252671111169, name=dearfriend3, age=14, mail=null, isDeleted=0)
其他功能
Lamda表达式——防止mysql名称写错,直接使用实体类字段
- LamdaQuerryWrapper
LambdaQueryWrapper<User> userLambdaQueryWrapper = new LambdaQueryWrapper<>();userLambdaQueryWrapper.eq(User::getName,"dearfriend");List<User> list = userService.list(userLambdaQueryWrapper);list.forEach(System.out::println);// ==> Preparing: SELECT id,name,age,mail,is_deleted FROM t_user WHERE is_deleted=0 AND (name = ?)// ==> Parameters: dearfriend(String)// <== Columns: id, name, age, mail, is_deleted// <== Row: 1846522555456847874, dearfriend, 12, 123456@123.com, 0// <== Total: 1// Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@1ed9d173]// User(id=1846522555456847874, name=dearfriend, age=12, mail=123456@123.com, isDeleted=0)
- LamdaUpdateWrapper
LambdaUpdateWrapper<User> userLambdaUpdateWrapper = new LambdaUpdateWrapper<>();userLambdaUpdateWrapper.set(User::getName,"Lambda").eq(User::getName,"dearfriend");boolean update = userService.update(userLambdaUpdateWrapper);System.out.println("更新状态:"+(update?"success!":"fail"));// ==> Preparing: UPDATE t_user SET name=? WHERE is_deleted=0 AND (name = ?)// ==> Parameters: Lambda(String), dearfriend(String)// <== Updates: 1// Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@14590fe2]// 更新状态:success!
- 注:主要是在原来的cloumn中使用Lamda表达式使用实体类的方法获取字段,而不是mysql中的列名
分页插件
- 配置分页插件
@Configuration
public class BPconfig {@Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){MybatisPlusInterceptor mybatisPlusInterceptor1 = new MybatisPlusInterceptor();//设置数据库类型——穿件分页拦截器mybatisPlusInterceptor1.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));return mybatisPlusInterceptor1;}
}
- 分页业务代码
- 方式一:传入Page参数,仅作为查询条件page无法存储记录
Page<User> userPage = new Page<>(2,3);List<User> list = userService.list(userPage, null);System.out.println(list);// ==> Preparing: SELECT COUNT(*) AS total FROM t_user WHERE is_deleted = 0
// ==> Parameters:
// <== Columns: total
// <== Row: 6
// <== Total: 1
// ==> Preparing: SELECT id,name,age,mail,is_deleted FROM t_user WHERE is_deleted=0 LIMIT ?,?
// ==> Parameters: 3(Long), 3(Long)
// <== Columns: id, name, age, mail, is_deleted
// <== Row: 1846525252671111169, dearfriend3, 14, null, 0
// <== Row: 1846548381057470465, user1@163.com, 20, null, 0
// <== Row: 1846548381120385025, user2@163.com, 21, null, 0
// <== Total: 3
- 方式二:调用
page()
方法,所有记录存在于page对象中、mapper层也提供了selectPage()
方法
Page<User> userPage = new Page<>(2,2);userService.page(userPage, null);
// System.out.println(list);System.out.println("总页数:"+userPage.getPages());System.out.println("当前页:"+userPage.getCurrent());System.out.println("-------------------------------");System.out.println("本页记录为:");userPage.getRecords().forEach(System.out::println);System.out.println("总记录数:"+userPage.getTotal());System.out.println("-------------------------------");System.out.println("是否有下一页"+userPage.hasNext());System.out.println("是否有上一页"+userPage.hasPrevious());
// ==> Preparing: SELECT COUNT(*) AS total FROM t_user WHERE is_deleted = 0
// ==> Parameters:
// <== Columns: total
// <== Row: 6
// <== Total: 1
// ==> Preparing: SELECT id,name,age,mail,is_deleted FROM t_user WHERE is_deleted=0 LIMIT ?,?
// ==> Parameters: 2(Long), 2(Long)
// <== Columns: id, name, age, mail, is_deleted
// <== Row: 1846525211126554626, dearfriend1, 13, null, 0
// <== Row: 1846525252671111169, dearfriend3, 14, null, 0
// <== Total: 2
// Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@70485aa]
// 总页数:3
// 当前页:2
// -------------------------------
// 本页记录为:
// User(id=1846525211126554626, name=dearfriend1, age=13, mail=null, isDeleted=0)
// User(id=1846525252671111169, name=dearfriend3, age=14, mail=null, isDeleted=0)
// 总记录数:6
// -------------------------------
// 是否有下一页true
// 是否有上一页true
- 自定义mapper结构的分页实现
接口定义——传入Page类型参数
@Repository
@Mapper
public interface UserMapper extends BaseMapper<User> {/*自定sql语句分页查询*/Page<User> selectPageV(@Param("page") Page<User> page, @Param("age") Integer age);
}
接口实现使用别名指定User,需要添加别名配置mybatis-plus.type-aliases-package: 包名
<select id="selectPageV" resultType="User">select t_user.id,t_user.age,t_user.name,t_user.mailfrom t_userwhere age > #{age}</select>
方法调用
@Testpublic void testSelfPage(){Page<User> userPage = new Page<>(1,3);userMapper.selectPageV(userPage,10);userPage.getRecords().forEach(System.out::println);}
乐观锁和悲观锁
-
悲观锁:一方操作资源,其他人全部阻塞
-
乐观锁:此处通过版本号实现,每次修改查询版本号,修改后版本号改变,操作失败;操作成功,更改值,更改版本号+1
-
mybatis-plus实现乐观锁
- 添加version字段使用@Version注解(还是需要自己实现循环的业务代码判断重试)
- 插件配置类配置乐观锁插件
@Bean public MybatisPlusInterceptor mybatisPlusInterceptor(){ MybatisPlusInterceptor mybatisPlusInterceptor1 = new MybatisPlusInterceptor(); //设置数据库类型——穿件分页拦截器 mybatisPlusInterceptor1.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL)); //添加乐观锁插件 mybatisPlusInterceptor1.addInnerInterceptor(new OptimisticLockerInnerInterceptor()); return mybatisPlusInterceptor1; }
通用枚举
- 数据库中添加存储枚举数据的字段(就使用普通的类型,枚举类有多个属性选择一个存储)
- 创建枚举类,实体类添加枚举类属性
注:需要在枚举类中使用@Data@TableName("t_user")public class User {@TableId(type = IdType.NONE)private Long id;private String name;private Integer age;private String mail;// 指定的逻辑删除存储字段@TableLogicprivate Integer isDeleted;@Versionprivate Integer version;private Sex sex;@Getterpublic enum Sex{MALE(1,"男"),FEMALE(2,"女");@EnumValueprivate final Integer code;private final String sexName;Sex(int code, String sexName) {this.code = code;this.sexName = sexName;}} }
@EnumValue
在需要保存的到数据库中的枚举类属性上方进行注解(低版本的BP还要在配置文件中指定枚举类位置mybatis-plus.type-aliases-package——当前版本以弃用)
代码生成器
3.5.1以上代码生成器——此处使用的版本3.5.5
3.5.1以下版本代码生成器
- 依赖的导入
<dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.5</version> </dependency>
- 根据自己的需要,选择对应的生成模版(),还可以选择生成方式(快速生成、交互式生成)
文件时时模版import com.baomidou.mybatisplus.generator.FastAutoGenerator; import com.baomidou.mybatisplus.generator.config.OutputFile; import com.baomidou.mybatisplus.generator.config.rules.DbColumnType; import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;import java.sql.Types; import java.util.Collections;//相关依赖 // <!--BP代码生成依赖--> // <dependency> // <groupId>com.baomidou</groupId> // <artifactId>mybatis-plus-generator</artifactId> // <version>3.5.5</version> // </dependency> // <!--生成模版依赖--> // <dependency> // <groupId>org.freemarker</groupId> // <artifactId>freemarker</artifactId> // <version>2.3.33</version> // </dependency>//--------------------------------------- public class ${ClassName} {public static void main(String[] args) {// 1.配置数据库连接FastAutoGenerator.create("${mysqlurl}", "${username}", "${passworld}").globalConfig(builder -> {builder.author("${author}") // 设置作者.enableSwagger() // 开启 swagger 模式//2.指定输出目录.outputDir("$file_out_dir"); // 指定输出目录}).dataSourceConfig(builder ->builder.typeConvertHandler((globalConfig, typeRegistry, metaInfo) -> {int typeCode = metaInfo.getJdbcType().TYPE_CODE;if (typeCode == Types.SMALLINT) {// 自定义类型转换return DbColumnType.INTEGER;}return typeRegistry.getColumnType(metaInfo);})).packageConfig(builder -> // 3.指定父包目录builder.parent("${groupId}") // 设置父包名 // 4.指定模块目录.moduleName("${artifactId}") // 设置父包模块名 // 5.指定xml映射文件目录.pathInfo(Collections.singletonMap(OutputFile.xml, "${file_out_dir}")) // 设置mapperXml生成路径) // 策略配置.strategyConfig(builder -> // 6.指定需要生成的表builder.addInclude("${table_name}") // 设置需要生成的表名.addTablePrefix("t_", "c_") // 设置过滤表前缀).templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板.execute();} }
MybatisX插件
-
能够自动跳转mapper接口和映射文件
-
能够自动生成代码
-
自动生成CRUD操作(包括xml的sql映射)
多数据源官网
-
依赖导入
<!--spring-boot 1.5.x 2.x.x--> <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot-starter</artifactId><version>${version}</version> </dependency> <!--spring-boot3及以上--> <dependency><groupId>com.baomidou</groupId><artifactId>dynamic-datasource-spring-boot3-starter</artifactId> <version>${version}</version> </dependency>
-
配置数据源
spring:datasource:dynamic:enabled: true #启用动态数据源,默认trueprimary: master #设置默认的数据源或者数据源组,默认值即为masterstrict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源grace-destroy: false #是否优雅关闭数据源,默认为false,设置为true时,关闭数据源时如果数据源中还存在活跃连接,至多等待10s后强制关闭datasource:master:url: jdbc:mysql://xx.xx.xx.xx:3306/dynamicusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置slave_1:url: jdbc:mysql://xx.xx.xx.xx:3307/dynamicusername: rootpassword: 123456driver-class-name: com.mysql.jdbc.Driverslave_2:url: ENC(xxxxx) # 内置加密,使用请查看详细文档username: ENC(xxxxx)password: ENC(xxxxx)driver-class-name: com.mysql.jdbc.Driver#......省略#以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
-
使用
@DS
指定操作的数据库
@DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解。注解 结果 没有@DS 默认数据源 有@DS(“dsName”) dsName可以为组名也可以为具体某个库的名称
适用场景
存粹多库、读写分离、一主多从、混合模式
相关概念
雪花算法
分布式主键生成算法,能够保证不同表的主键不重复,相同表的有序性(有利于索引)
核心思想
- 长度为64bit——Long类型
- 结构
- 第一位:符号位
- 2-42(41bit): 时间戳(当前时间-开始时间)
- 43-52(10bit): 机器id(5bit数据中心,5bit机器id)(可以部署在1024个节点)
- 53-64(12 bit): 毫秒内的流水号(每毫秒可以产生4096个id)
优点
整体上按照时间自增排序,并且整个分布式系统内不会发生id碰撞,且效率高