目录
一、前言
mybatis-puls在mybatis的前提下只做增强不做修改,对于单表查询更加灵活,在有mybatis的基础下最多需要三天就能游刃有余。
二、配置
依赖文件
<!-- mybatis-plus -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.2</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
yml文件
#Mysql
datasource:
spring:
url: jdbc:mysql://localhost:3306/ipiw?characterEncoding=utf-8&userSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #设置打印日志
扫描mapper
@SpringBootApplication
//扫描mapper接口所在的包
@MapperScan("com.example.demo2.mapper")
public class Demo2Application {
public static void main(String[] args) {
SpringApplication.run(Demo2Application.class, args);
}
}
三、入门案例
查询全部数据(selectList)
//继承 BaseMappe 接口,需要指定表查询映射对应的Bean
@Repository //标识为持久层主键 ,使@Autowired不会报错
public interface UserMapper extends BaseMapper<User2> {
}
@Autowired
private UserMapper userMapper;
@Testpublic void test01(){
//通过条件构造器插叙一个List集合,如果没有条件,则可以设置null为参数
List<User2> list=userMapper.selectList(null);
list.forEach(System.out::println);
}
四、具体用法
插入
插入数据,并且获取自增主键(insert)
user类
public class User2 {
@TableId(value = "id",type = IdType.AUTO) //绑定自增主键的类属性
private int id; //自动自增主键
private String name;
private Integer mima;
}
@Test
public void testInset(){
User2 user2=new User2();
user2.setMima(1234);
user2.setName("王五");
int rest= userMapper.insert(user2); //插入
log.info("插入成功:{},自增加主键:{}",rest,user2.getId());
}
删除
通过id删除数据(deleteById)
@Test
public void test02(){
int result = userMapper.deleteById(8);
log.info("删除条数:{}",result);
}
map条件删除(deleteByMap)
@Test
public void test03(){
//设置map条件
Map<String,Object>map =new HashMap<>();
map.put("name","王五");
map.put("mima",12);
//根据map执行条件删除(map为且条件)
int result=userMapper.deleteByMap(map);
// int result=userMapper.deleteByMap(null); 删除全部数据
log.info("删除条数:{}",result);
}
根据id批量删除(deleteBatchIds)
@Test
public void test04(){
//根据id执行批量删除
List<Integer>list= Arrays.asList(1,2,3);
int result =userMapper.deleteBatchIds(list); //根据id执行批量删除
log.info("删除条数:{}",result);
}
修改
根据实体类的id修改数据(updateById)
//修改数据
@Test
public void test05(){
//根据实体类的对象的id修改数据
User2 user2=new User2();
user2.setId(9); //设置id,会根据这个id修改数据
user2.setName("小红");
user2.setMima(0);
int result=userMapper.updateById(user2); // 修改数据,会根据实体类id匹配id字段
log.info("删除条数:{}",result);
}
查询
根据id查询(selectById)
@Test
public void test06(){
User2 user2=userMapper.selectById(14);
log.info("查询数据:{}",user2);
}
根据id批量查询(selectBatchIds)
@Test
public void test07(){
List<Integer>user2Ids= Arrays.asList(11,13,15); //创建id集合
List<User2> user2s=userMapper.selectBatchIds(user2Ids);//根据id集合批量查询
log.info("查询数据:{}",user2s);
}
根据map条件查询(selectByMap)
//根据map条件查询
@Test
public void test08(){
Map<String,Object> map=new HashMap<>();
map.put("name","老大");
map.put("mima",676); //map条件为且条件( name=‘老大’ AND mima=676)
List<User2> user2s=userMapper.selectByMap(map); //根据map条件查询
//List<User2> user2s=userMapper.selectByMap(null); //为null时候代表查询全部
log.info("查询数据:{}",user2s);
}
查询全部(selectList)
@Test
public void test09(){
//查询全部数据
List<User2> user2s=userMapper.selectList(null);
log.info("查询数据:{}",user2s);
}
五、自定义SQL(XML)
mybatis-puls默认扫描 resources/**/*.xml文件
自定义查询,直接写自定义方法
@Repository //标识为持久层主键
public interface UserMapper extends BaseMapper<User2> {
//查询结果封装成map
Map<String,Object> selectMapById(@Param("id")Integer id);
//查询结果封装成User2,puls插件有这个方法
User2 selectUser2ById(@Param("id")Integer id);
}
xml
<select id="selectMapById" resultType="map">
select id,name,mima from user2 where id=#{id}
</select>
<select id="selectUser2ById" resultType="com.example.demo2.pojo.User2">
select * from user2 where id=#{id}
</select>
六、通用Service接口
Mybatis-Plus提供了一个IService接口以及ServiceImpl实现类,封装了常见的业务逻辑
定义接口
//自定义接口(UserService) 继承IService<User2> User2为当前查询结果表映射的bean
public interface UserService extends IService<User2> {
}
//自定义实现(UserServiceImpl) 继承 ServiceImpl类 和 UserService接口
@Service //UserMapper为对应的dao接口 // User2为当前查询结果表映射的bean
public class UserServiceImpl extends ServiceImpl<UserMapper, User2> implements UserService {
}
方法
统计
@Test
public void test01(){
//查询总记录数目
long count= userService.count();
log.info("数据条数:{}",count);
}
批量添加
本质是多次循环调用添加,
效率不如直接写xml
//批量添加
@Test
public void test02(){
List<User2> list= new ArrayList<>();
for (int i = 0; i < 10; i++) {
User2 user=new User2();
user.setName("张三"+i);
user.setMima(23+i);
list.add(user);
}
boolean b= userService.saveBatch(list);
//批量添加传入个集合
log.info("批量插入是否成功:{},插入条数:{}",b,list.size());
}
七、注解
映射表@TableName("数据库表名")
设置bean对应的数据库表
@Data
@NoArgsConstructor
@AllArgsConstructor
@TableName("user2") //对应表名
public class User2 {
@TableId(value = "id",type = IdType.AUTO)
private int id; //自动自增主键
private String name;
private Integer mima;
}
全局映射表前缀yml
mybatis-plus:
global-config:
db-config:
table-prefix: t- # user名的bean映射对应数据库表t-user (对全部表生效,不仅仅是user)
映射主键@TableId
将bean属性映射数据库表中主键(当前属性名字需要和字段名字相同)
@TableId //什么也不加只是设置主键
private int name; // 数据库表中name为主键,当前属性名字需要和字段名字相同
bean主键和数据库主键名字不一致时候
@TableId(value = "uid") //@TableId("uid")
private int id; //映射主键,id--uid
设置映射自增主键,不使用默认的雪花算法
@TableId(value = "id",type = IdType.AUTO)
private int id; //自动自增主键 ,且数据库需要设置主键自动递增
统一设置IdType
global-config:
db-config:
table-prefix: t-
id-type: auto #主统一键生成策略
雪花算法:能够保证不同的主键的不重复性
字段映射@TableField
在mybaits-plus中 userName---user_name (驼峰名字自动转换成数字库名中的_),而mybatis需要配置才行
自定义映射字段
@TableField("user_name") //自定义字段 name--user_name
private String name;
逻辑删除@TableLogic
1、物理删除:
真实删除,将对应数据从数据库中删除,之后查询不到此条被删除的数据
2、逻辑删除:
假删除,将对应数据中代表是否被删除字段的状态改为“被删除状态”,之后在数据库中仍然可见
使用场景:可以进行数据恢复
使用:
@TableLogic //逻辑删除
private Integer isDeleted; //在数据库中is_deleted字段的默认值是0,执行删除操作时候之后把当前is_deleted修改成0
//同时is_deleted为1的数据也不会被查询到
八、条件构造器wapper
修改:UpdateWrapper、LambdaUpdatewrapper
查询/删除:QueryWrapper、LambdaQueryWrapper
查询(QueryWrapper)
条件查询
@Testpublic void test01(){
//查询条件
//查询id在[9,16]之间,且名字不为老王,且mima包含4
QueryWrapper<User2> queryWrapper=new QueryWrapper<>(); //查询条件
//数据库字段--value
queryWrapper.between("id",9,16) //id在[9,16]之间
.notIn("name","老王") //名字不能为老王
.like("mima",4); //密码有 4, 模糊查询 %4%
List<User2> user2s = userMapper.selectList(queryWrapper);
log.info("查询结果:{}",user2s);
}
查询结果排序
@Testpublic void test02(){
//查询条件
//按mima大小排序,如果mima相同就按id大小排序
QueryWrapper<User2> queryWrapper=new QueryWrapper<>();
queryWrapper.orderByAsc("mima") //先按照mima升序排序
.orderByAsc("id"); //再按照id 升序排序
List<User2> user2s = userMapper.selectList(queryWrapper);
user2s.forEach(System.out::println);
}
删除
条件删除
@Testpublic void test03(){
//删除mima有3的数据
QueryWrapper<User2> queryWrapper=new QueryWrapper<>();
queryWrapper.like("mima",3);
int result=userMapper.delete(queryWrapper);
log.info("删除数据条数:{}",result);
}
修改
条件修改
queryWrapper.gt() // gt表示>, ge表示>=, lt表示
or 上下默认AND连接,使用or连接上下(一句)
(A) or (B) A表示上条件,B表示下条件即当前or内部条件
.or(queryWrapper2->queryWrapper2.isNull("name").isNull("id"))
//条件修改
@Testpublic void test04(){
//将mima>24或者name名字有包含“三”的数据修改成 mima=11,name=猫猫
UpdateWrapper<User2> updateWrapper=new UpdateWrapper<>();
// queryWrapper.gt() // gt表示>, ge表示>=, lt表示<,lr表示<=, between 表示区间[]
updateWrapper.gt("mima",23)
.like("name","三")
.or() //上下默认AND连接,使用or连接上下(一句)
.isNull("id");
User2 user2=new User2();
user2.setMima(11); //修改密码
user2.setName("猫猫"); //修改名字
int update = userMapper.update(user2,updateWrapper);//修改结果--修改条件
log.info("修改成功条数:{}",update);
}
条件优先级括号
在sql中括号具有最高优先级
.and(i->i.XX.XX)
表示 (A)AND (B) ;A表示上层所有,B表示下层所有
.or(i->i.XX.XX)
表示 (A)OR (B) ;A表示上层所有,B表示下层所有
//条件优先级
@Test
public void test05(){
//将 name包含猫 且 (mima>=12 且 id!=null)
//修改成 name=狗
UpdateWrapper<User2> updateWrapper=new UpdateWrapper<>();
updateWrapper.like("name","猫")
.and(user2UpdateWrapper2 ->user2UpdateWrapper2.ge("mima",12).isNotNull("id")); //表示 A and B (A表示上层所有,B表示下层所有)
//.or(user2UpdateWrapper2 ->user2UpdateWrapper2.ge("mima",12).isNotNull("id")); //表示 A or B (A表示上层所有,B表示下层所有)
User2 user2=new User2();
user2.setName("狗"); //只修改名字
int update = userMapper.update(user2,updateWrapper);
//修改结果--修改条件
log.info("修改成功条数:{}",update);
}
上面UpdateWrapper都可以替换成QueryWrapper
UpdateWrapper 直接设置修改值
推荐使用这个
//修改
@Test
public void test08(){
// 将name中包含狗且(id<30 or mima<100) 的修改成 猫
UpdateWrapper<User2> updateWrapper=new UpdateWrapper<>();
updateWrapper.like("name","狗")
.and(que2->que2.lt("id",30).or().lt("mima",100));
updateWrapper.set("name","猫"); //可以直接设置修改的字段
int result = userMapper.update(null, updateWrapper);
log.info("修改数据条数:{}",result);
}
组装select
自定义返回表的字段
//自定义返回的数据字段
@Testpublic void test06(){
//只返回表的id、name字段信息
QueryWrapper<User2> queryWrapper=new QueryWrapper<>();
queryWrapper.select("id","name"); //只返会 id,name字段
List<Map<String, Object>> maps = userMapper.selectMaps(queryWrapper); //返回多个map
maps.forEach(System.out::println);
}
子查询
//子查询
@Test
public void test07(){
// 查询id小于等于100的用户信息
QueryWrapper<User2> queryWrapper=new QueryWrapper<>();
// **** id in (sql)
queryWrapper.inSql("id","select id from user2 where id<=100");
List<User2> user2s = userMapper.selectList(queryWrapper);
user2s.forEach(System.out::println);
}
组装状态条件
动态组装
如果第一个参数为false,那么当前条件失效,不会组装成sql条件
@Test
public void test10(){
String name="";
Integer mimaBegin=20;
Integer mimaEnd=30;
QueryWrapper<User2> queryWrapper=new QueryWrapper<>();
queryWrapper.like(StringUtils.isNotBlank(name),"name","a") // 为空时候不组装当前条件
.ge(mimaBegin!=null,"mima",20) // 为空时候不组装当前条件
.le(mimaEnd!=null,"mima",30); // 为空时候不组装当前条件
List<User2> user2s = userMapper.selectList(queryWrapper);
user2s.forEach(System.out::println);
}
防止字段写错
防止数据库字段,可以使用LambdaUpdateWrapper、LambdaQueryWrapper
直接使用表映射的类数据成员充当字段名字
@Test
public void test11(){
String name="";
Integer mimaBegin=20;
Integer mimaEnd=30;
LambdaQueryWrapper<User2> lambdaQueryWrapper=new LambdaQueryWrapper<>();
lambdaQueryWrapper.like(StringUtils.isNotBlank(name),User2::getName,name) //User2::getName --name
.ge(mimaBegin!=null,User2::getMima,20) //User2::getMima--mima
.le(mimaEnd!=null,User2::getMima,30); //User2::getMima--mima
List<User2> user2s = userMapper.selectList(lambdaQueryWrapper);
user2s.forEach(System.out::println);
}
//修改
@Test
public void test12(){
// 将name中包含狗且(id<30 or mima<100) 的修改成 猫
LambdaUpdateWrapper<User2> updateWrapper=new LambdaUpdateWrapper<>();
updateWrapper.like(User2::getName,"狗")
.and(que2->que2.lt(User2::getId,30).or().lt(User2::getId,100));
updateWrapper.set(User2::getName,"猫"); //可以直接设置修改
int result = userMapper.update(null, updateWrapper);
log.info("修改数据条数:{}",result);
}
九、插件
分页插件
常用分页属性
page.hasNext() 是否有下一页
page.hasPrevious() 是否有上一页
page.getRecords() 当前页面数据
page.getCurrent() 当前页码
page.getPages() 总页数
page.getTotal() 总数据条数
,page.getSize() 每页最大数据条数
配置类
@Configuration
//扫描mapper接口所在的包
@MapperScan("com.example.demo2.mapper")
public class MybaitsPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor=new MybatisPlusInterceptor(); //创建配置
//添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
}
使用
@Test
public void test01(){
Page<User2> page=new Page<>(5,3); //第五页 ,每页3条数据
//查询数据封装到page中
// 第一个参数 分页数据,第二个参数 查询条件
userMapper.selectPage(page,null); //把结果存储到page,当前是第五页的的结果
log.info("page数据:{}",page);
log.info("有没有下一页数据:{}",page.hasNext());
log.info("有没有上一页:{}",page.hasPrevious());
log.info("当前页面数据:{}",page.getRecords());
log.info("当前页码:{}",page.getCurrent());
log.info("总页数:{}",page.getPages());
log.info("总数据条数:{}",page.getTotal());
log.info("每页最大数据条数:{}",page.getSize()); //每页最大数据条数
}
自定义分页
sql是自己写的xml时候,分页需要进行自定义
接口
//通过id进行查询 并且分页,注意返回值和第一个参数需要是Page
Page<User2> selectPageVo(@Param("page") Page<User2> page,@Param("id") Integer id);
sql
<select id="selectPageVo" resultType="com.example.demo2.pojo.User2">
select id,name,mima from user2 where id>#{id}
</select>
//返回值的类型是映射的类类型
//结果会自动映射成page
使用
@Test
public void test02(){
Page<User2> page=new Page<>(1,3); //当前页,每页条数
userMapper.selectPageVo(page,20); //结果会映射到page ,当前是第一页的数据
log.info("page数据:{}",page);
log.info("有没有下一页数据:{}",page.hasNext());
log.info("有没有上一页:{}",page.hasPrevious());
log.info("当前页面数据:{}",page.getRecords());
log.info("当前页码:{}",page.getCurrent());
log.info("总页数:{}",page.getPages());
log.info("总数据条数:{}",page.getTotal());
log.info("每页最大数据条数:{}",page.getSize()); //每页最大数据条数
}
乐观锁
悲观锁:
每次只能一个人操作,此人操作完成后才释放锁
乐观锁:
每个人操作时候都会有一个版本号,如果一个人在未完成操作时候版本号发生改变(被另一个人操作修改了)那么此人的操作失败,数据库设置一个字段version用来表示版本号
使用@Version注解
设置当前数据字段为版本号,与数据库版本号字段绑定,字段可以设置int类型,默认值为0
映射类
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Product {
@TableId(value = "id",type = IdType.AUTO) //自动递增主键
private Integer id;
private String name;
private Double price;
@Version //标识乐观锁版本号字段
private Integer version;
}
配置类:添加乐观锁插件
@Configuration
//扫描mapper接口所在的包
@MapperScan("com.example.demo2.mapper")
public class MybaitsPlusConfig {
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor(){
MybatisPlusInterceptor interceptor=new MybatisPlusInterceptor(); //创建配置
//添加分页插件
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
// 添加乐观锁插件
interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
return interceptor;
}
}
操作失败的解决办法:
如果当前人因为版本号不同而操作失败,那么进行重试即可解决
十、通用枚举
在枚举中加入@EnumValue注解 ,把枚举的值存储到数据库中,而不是枚举名字,但该注解需要在yml里配置扫描才能生效
扫描@EnumValue注解
mybatis-plus:
type-enums-package:com.at.mybaits.enums #扫描枚举所在的包,新版本不用配置
枚举:
@Getter
public enum MyIp {
WIN("192.168.1","win"),
LIU("192.168.2","lux"); //每一个相当于对象
private String ip; // 成员属性
@EnumValue //当成员是枚举时候需要写入数据库的属性
private String name; // 成员属性
MyIp(String ip,String name){
//私有构造方法
this.ip=ip;
this.name=name;
}
}
映射表
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private MyIp name; //当前表映射是枚举,会把@EnumValue 注解的属性写入数据库
private Integer mima;
}
测试方法
@Autowired
MyUserMapper mapper;
@Test
public void test01(){
User user=new User();
user.setName(MyIp.WIN); //设置枚举
user.setMima(1111);
int result= mapper.insert(user);
log.info("插入数据:{}",result);
}
十一、多数据源
依赖
<!-- 多数据源-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>dynamic-datasource-spring-boot-starter</artifactId>
<version>3.3.2</version>
</dependency>
配置文件
spring:
datasource:
dynamic:
primary: master #设置默认的数据源或者数据源组
strict: false # 严格匹配数据源 ,默认false,true未匹配到指定数据源时候抛出异常
datasource:
master: # 数据源 1
url: jdbc:mysql://localhost:3306/ipiw?serTimezone=UTC&character=utf8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
slaver_1: # 数据源 2
url: jdbc:mysql://localhost:3306/text?serTimezone=UTC&character=utf8
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
对应mapper接口
需要使用@DS(“数据源”) ;来表名操作的数据源,也可以标识在方法上
@DS("master") //指定dao操作的数据源,也可以标识在Service层
@Repository //标识为持久层主键
public interface UserMapper extends BaseMapper<User2> {
}
另一个数据源
@DS("slaver_1") //当前接口操作的都是 slaver_1 数据库中的表
@Repositorypublic interface DateMapper extends BaseMapper<Date> {
}
测试
@Autowired
UserMapper userMapper;
@Autowired
DateMapper dateMapper;
@Test
public void test01(){
List<User2> user2s = userMapper.selectList(null);
user2s.forEach(System.out::println);
List<Date> dates = dateMapper.selectList(null);
dates.forEach(System.out::println);
}
十一:总结
一下子不知道怎么办就直接写SQL,对比MyBatis来说确实更好用了,如果单人开发选择它可以更加舒服,多人一起开发还是写sql吧。