mybatis-plus的使用

1.为什么要用mybatis-plus?

mybatis在持久层框架中还是比较火的,一般项目都是基于ssm。虽然mybatis可以直接在xml中通过SQL语句操作数据库,很是灵活。但正其操作都要通过SQL语句进行,就必须写大量的xml文件,很是麻烦。mybatis-plus就很好的解决了这个问题。

2.什么是mybatis-plus?

Mybatis-Plus(简称MP)是一个 Mybatis 的增强工具,在 Mybatis 的基础上只做增强不做改变,为简化开发、提高效率而生。这是官方给的定义,关于mybatis-plus的更多介绍及特性,可以参考mybatis-plus官网。那么它是怎么增强的呢?其实就是它已经封装好了一些crud方法,我们不需要再写xml了,直接调用这些方法就行,就类似于JPA。

简单来讲:

假如有一个实体类User,要使用mybatis实现增删改查需要写很多的xml配置文件来实现,但是用mybatisplus之后直接继承BaseMapper接口即可实现,无需编写任何的xml文件

3.MyBatis优缺点,为什么使用MyBatis

对比:
Hibernate自动生成sq|,,有些语句较为繁琐,会多消耗一些性能;
Mybatis手动编写sql,可以避免不需要的查询,提高系统性能;
对象管理比对
Hibernate是完整的对象关系映射的框架,开发工程中,无需过多关注底层实现,只要去管理对象即可;
Mybatis需要自行管理映射关系;

缓存方面
相同点:
Hibernate和Mybatis的二级缓存除了采用系统默认的缓存机制外,都可以通过实现你自己的缓存或为其他第三方缓方案,创建适
配器来完全覆盖缓存行为。
不同点:
Hibernate的二级缓存配置在SessionFactory生成的配置文件中进行详细配置,然后再在具体的表对象映射中配置是那种缓存。
MyBatis的二级缓存配置都是在每个具体的表对象映射中进行详细配置,这样针对不同的表可以自定义不同的缓存机制。粗Mybatis
可以在命名空间中共享相同的缓存配置和实例,通过Cache ref来实现。

4. 来个小例子感受一下,增删改查

4.1 表结构
现有一张 User 表,其对应的数据库 Schema 脚本如下:
DROP TABLE IF EXISTS user;

CREATE TABLE user
(
id BIGINT(20) NOT NULL COMMENT ‘主键ID’,
name VARCHAR(30) NULL DEFAULT NULL COMMENT ‘姓名’,
age INT(11) NULL DEFAULT NULL COMMENT ‘年龄’,
email VARCHAR(50) NULL DEFAULT NULL COMMENT ‘邮箱’,
PRIMARY KEY (id)
);

INSERT INTO user (id, name, age, email) VALUES
(1, ‘Jone’, 18, ‘test1@baomidou.com’),
(2, ‘Jack’, 20, ‘test2@baomidou.com’),
(3, ‘Tom’, 28, ‘test3@baomidou.com’),
(4, ‘Sandy’, 21, ‘test4@baomidou.com’),
(5, ‘Billie’, 24, ‘test5@baomidou.com’);

4.2 Maven Dependency
引入 Spring Boot Starter 父工程:

org.springframework.boot
spring-boot-starter-parent
2.2.4.RELEASE


引入 spring-boot-starter、spring-boot-starter-test、mybatis-plus-boot-starter、lombok、h2 依赖:


org.springframework.boot
spring-boot-starter


org.springframework.boot
spring-boot-starter-test
test


org.projectlombok
lombok
true


com.baomidou
mybatis-plus-boot-starter
3.3.1.tmp


com.h2database
h2
runtime

4.2 yml的配置

DataSource Config

spring:
datasource:
driver-class-name: org.h2.Driver
schema: classpath:db/schema-h2.sql
data: classpath:db/data-h2.sql
url: jdbc:h2:mem:test
username: root
password: test

4.3 启动类
在 Spring Boot 启动类中添加 @MapperScan 注解,扫描 Mapper 文件夹:
@SpringBootApplication
@MapperScan(“com.baomidou.mybatisplus.samples.quickstart.mapper”)
public class Application {

public static void main(String[] args) {
    SpringApplication.run(QuickStartApplication.class, args);
}

}
4.4 实体类
编写实体类 User.java(此处使用了 Lombok 简化代码)
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
}
4.4 Mapper类
public interface UserMapper extends BaseMapper {
}
这套项目中没有属于测试,没有写service层

开始使用
添加测试类,进行功能测试:
@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleTest {
@Autowired
private UserMapper userMapper;

@Test
public void testSelect() {
    System.out.println(("----- selectAll method test ------"));
    List<User> userList = userMapper.selectList(null);
    Assert.assertEquals(5, userList.size());
    userList.forEach(System.out::println);
}

}
UserMapper 中的 selectList() 方法的参数为 MP 内置的条件封装器 Wrapper,所以不填写就是无任何条件

控制台输出:
User(id=1, name=Jone, age=18, email=test1@baomidou.com)
User(id=2, name=Jack, age=20, email=test2@baomidou.com)
User(id=3, name=Tom, age=28, email=test3@baomidou.com)
User(id=4, name=Sandy, age=21, email=test4@baomidou.com)
User(id=5, name=Billie, age=24, email=test5@baomidou.com)

5… 复杂的条件查询

案例:
@RunWith(SpringRunner.class)
@SpringBootTest
public class SampleTest {
@Autowired
private UserMapper userMapper;

@Test
public void SelectAll() {
   
    List<User> userList = userMapper.selectList(null);
    Assert.assertEquals(5, userList.size());
    userList.forEach(System.out::println);
}

@Test
public void SelectQuery() {
    QueryWrapper<User> queryWrapper= new QueryWrapper<User>();
    queryWrapper.eq("name","Jone");  //使用eq添加条件
    //eq:等于 like:模糊 gt:大于 ge;等于 lt:小于 le:小于等于 orderBy排序  等
    List<User> userList = userMapper.selectList(queryWrapper);
    Assert.assertEquals(5, userList.size());
    userList.forEach(System.out::println);
}


@Test
public void updateAge() {
     UpdateWrapper<User> updateWrapper = new UpdateWrapper<User>();
     updateWrapper.eq("name_","Jone");
     updateWrapper.set("age","18"); //添加要修改的数据
     User user = new User();
     userMapper.update(user,updateWrapper);
}

}

// new一个EntityWrapper,User为实体类
EntityWrapper wrapper = new EntityWrapper<>();
// eq:等于
wrapper.eq(“sex”, “1”); // 相当于 sex = 1

// like:模糊查询
wrapper.like(“login_name”, “admin”); // 相当于 login_name LIKE ‘%admin%’

// gt:大于
wrapper.gt(“age”, “11”); // 相当于 age > 11

// ge:大于等于
wrapper.ge(“age”, “11”); // 相当于 age >= 11

// lt:小于
wrapper.lt(“age”, “21”); // 相当于 age < 21

// le:小于等于
wrapper.le(“DATE(create_date)”, “2019-05-12”); // 相当于 DATE(create_date) <= ‘2019-05-12’

// orderBy:排序(true:ASC;false:DESC)
wrapper.orderBy(“create_date”, true); // 相当于 ORDER BY create_date ASC

官方提供了两种:QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper)
说明: QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父类
用于生成 sql 的 where 条件, entity 属性也用于生成 sql 的 where 条件
注意: entity 生成的 where 条件与 使用各个 api 生成的 where 条件没有任何关联行为
//详细使用查看官方手册(上方有链接)

6.多表连查

MP自带的条件构造器虽然很强大,有时候也避免不了写稍微复杂一点业务的sql。mybatis plus的多表联查与mybatis中的相似,在xml中进行操作

设计两个表
create table student
(
sid int not null,
name varchar(50),
tid int,
primary key (sid)
);

alter table student comment ‘学生表’;

create table teacher
(
tid int not null auto_increment,
name varchar(50),
primary key (tid)
);

alter table teacher comment ‘讲师表’;

alter table student add constraint FK_Reference_1 foreign key (tid)
references teacher (tid) on delete restrict on update restrict;
在这里插入图片描述
@TableName(“teachar”)
@Data
public class Teachar {

private Integer tid;
private String name;

}

@TableName(“teachar”)
@Data
public class Teachar {

private Integer tid;
private String name;

}

这里在Mapper 中写个自定义id
@Mapper
public interface StudentMapper extends BaseMapper {

public List<Student> selectAll();

}
在Studentmappering.xml写sql 和之前的方法一样

<?xml version="1.0" encoding="ISO-8859-1"?>
<resultMap id="StudentMap" type="com.example.demo.model.Student">
    <id property="id" column="id"></id>
    <result column="name" property="name"/>
    <association property="teachar" javaType="com.example.demo.model.Teachar" column="tid"
       select="selectByid"></association>
</resultMap>

<select id="selectByid" resultType="com.example.demo.model.Teachar">
    SELECT * FROM `teachar` WHERE tid=#{tid}
</select>

<select id="selectAll" resultMap="StudentMap">
   SELECT * FROM `student`
</select>
这个地方使用 LEFT JOIN的话更加简单 @Mapper public interface StudentMapper extends BaseMapper {
@Select("SELECT \n" +
        "  `student`.*,\n" +
        "  `teachar`.`name`  'teachar.name'\n" +
        "FROM\n" +
        "  `student` \n" +
        " LEFT  JOIN  `teachar` \n" +
        "    ON `student`.`tid` = `teachar`.`tid`  ")
public List<Student> selectAll();

// 学生和讲师的名称都使用了name 所以在sql中指定了别名
}

测试类中直接调用即可
@Resource
private StudentMapper studentMapper;

@Test
public void selectleft() {
    List<Student> students = studentMapper.selectAll();
    System.out.println(students);
}

7. 分页

在config包中,创建MybatisPlus配置类 返回一个分页拦截器
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
return paginationInterceptor;
}
下面调用就可以使用了
@Resource
private UserMapper userMapper;
@Test
public void queryUserForPage(){
IPage userPage = new Page<>(2, 2);//参数一是当前页,参数二是每页个数
userPage = userMapper.selectPage(userPage, null); //这个null 传的应该是QueryWrapper条件
List list = userPage.getRecords();
for(User user : list){
System.out.println(user);
}
}

8. 乐观锁和悲观锁的概念和区别

8.1 乐观锁的作用,了解乐观锁:
乐观锁 (目的就是保证了数据的一致性)
MySql最经常使用的乐观锁时进行版本控制,也就是在数据库表中增加一列,记为version,当我们将数据读出时,将版本号一并读出,当数据进行更新时,会对这个版本号进行加1,当我们提交数据时,会判断数据库表中当前的version列值和当时读出的version是否相同,若相同说明没有进行更新的操作,不然,则取消这次的操作。
8.2 为什么要使用乐观锁? (这里涉及到个知识点)
mybatis-plus 支持租户过滤

先解释一下什么叫多租户,什么场景下使用多租户。
多租户是一种软件架构技术,在多用户的环境下,共有同一套系统,并且要注意数据之间的隔离性。
举个实际例子:曾经开发过一套H5程序,这套程序应用在不同医院的APP上,当医院患者下载医院APP,并且进入相对应的H5页面,APP则会把用户相关数据传输到小编这里。在传输的时候需要带上医院标识(租户ID),以便小编将数据进行隔离。
当不同的租户使用同一套程序,这里就需要考虑一个数据隔离的情况。

数据隔离有三种方案:

1、独立数据库:简单来说就是一个租户使用一个数据库,这种数据隔离级别最高,安全性最好,但是提高成本。
2、共享数据库、隔离数据架构:多租户使用同一个数据裤,但是每个租户对应一个Schema(数据库user)。
3、共享数据库、共享数据架构:使用同一个数据库,同一个Schema,但是在表中增加了租户ID的字段,这种共享数据程度最高,隔离级别最低。

多租户技术,简称SaaS,是一种软件架构技术,是实现如何在多用户环境下(此处的多用户一般是面向企业用户)共用相同的系统或程序组件,并且可确保各用户间数据的隔离性。

    MyBatis-Plus 有多租户模式,可以实现很方便实现一些功能,一开始使用的时候,我觉得这个很好,但是深入了解后又觉得这个设计目前还是有一定的局限性,我的理解是多租户模式是专门针对隔离数据这一种模式而实现的,不要把这种模式扩展开来去使用,滥用的结果就是全局控制和局部控制有可能会相矛盾,单人开发的时候,这个问题还能接受,多人开发的时候,就会引发严重的冲突。

目前多租户模式还不支持全局设定 null 为 is null 来拼接 where 条件,这个也使多租户模式和无租户模式数据很难兼容在一起。
无租户模式可以看做是租户 id 为 null 的情况。
多租户模式可以看成是租户 id 不为 null 的情况。
目前多租户模式的设计只能做到让租户 id 不为 null 的数据做完全的隔离,记下这个坑,希望看到这篇文章的可以更早的意识到这个坑,绕过去。
使用详见:

简单讲:在一台服务器上运行单个应用实例,它为多个租户(客户)提供服务。从定义中我们可以理解:多租户是一种架构,目的是为了让多用户环境下使用同一套程序,且保证用户间数据隔离。那么重点就很浅显易懂了,多租户的重点就是同一套程序下实现多用户数据的隔离。
注:MyBatis-Plus 在使用多租户功能时、有些接口、我们并不想被多租户过滤、这个时候就用到了 SqlParserFilter sql解析过滤器
SqlParserFilter的doFilter方法中,如果使用MetaObject类的getId方法去判断该方法是过滤还是放行的时候、对比的方法必须是Mapper方法,因为MetaObject的getId方法得到的都是Mapper层的方法
详情点击这里→ 多租户架构的深入讲解

8.3 乐观锁与悲观锁的概念&区别
本质上,数据库的乐观锁做法和悲观锁做法主要就是解决避免丢失更新问题

(1)概念上的区别

乐观锁( Optimistic Locking):顾名思义,对加锁持有一种乐观的态度,即先进行业务操作,不到最后一步不进行加锁,"乐观"的认为加锁一定会成功的,在最后一步更新数据的时候再进行加锁。
  悲观锁(Pessimistic Lock):正如其名字一样,悲观锁对数据加锁持有一种悲观的态度。因此,在整个数据处理过程中,将数据处于锁定状态。悲观锁的实现,往往依靠数据库提供的锁机制(也只有数据库层提供的锁机制才能真正保证数据访问的排他性,否则,即使在本系统中实现了加锁机制,也无法保证外部系统不会修改数据)。
(1)实现方式
乐观锁:
  version方式:一般是在数据表中加上一个数据版本号version字段,表示数据被修改的次数,当数据被修改时,version值会加一。当线程A要更新数据值时,在读取数据的同时也会读取version值,在提交更新时,若刚才读取到的version值为当前数据库中的version值相等时才更新,否则重试更新操作,直到更新成功。
sql实现代码:
update table
set x=x+1, version=version+1
where id=#{id} and version=#{version};
  CAS操作方式:即compare and swap 或者 compare and set,
涉及到三个操作数,数据所在的内存值,预期值,新值。当需要更新时,判断当前内存值与之前
取到的值是否相等,若相等,则用新值更新,若失败则重试,一般情况下是一个自旋操作,即不
断的重试。
悲观锁:
  是由数据库自己实现的,要用的时候,我们直接调用数据库的相关语句就可以了(原理:共享资源每次只给一个线程使用,其它线程阻塞,用完后再把资源转让给其它线程),如行锁、读锁和写锁等,都是在操作之前加锁,在Java中,synchronized的思想也是悲观锁。
(3)使用场景
乐观锁:
  比较适合读取操作比较频繁的场景,如果出现大量的写入操作,数据发生冲突的可能性就会增大,为了保证数据的一致性,应用层需要不断的重新获取数据,这样会增加大量的查询操作,降低了系统的吞吐量。

  悲观锁

总是假设最坏的情况,每次取数据时都认为其他线程会修改,所以都会加锁(读锁、写锁、行锁等),当其他线程想要访问数据时,都需要阻塞挂起。可以依靠数据库实现,如行锁、读锁和写锁等,都是在操作之前加 锁,在Java中,synchronized的思想也是悲观锁。
注:要使用数据库的悲观锁,我们必须关闭mysql数据库的自动提交属性,因为MySQL默认使用autocommit模式,也就是说,当你执行一个更新操作后,MySQL会立刻将结果进行提交。

悲观锁分为两种:共享锁和排它锁

共享锁是其它事务可以读但是不能写
排他锁是只有自己得事务有权限对此数据进行读写

9. 如何使用乐观锁?

实体类 需要在实体类中加入@Version 实体类中加入后 表字段中也需加入version字段
@TableName(value = “User_”)
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String email;
@Version
private Integer version;
}

另外需要在配置中加入
@Bean
public OptimisticLockerInterceptor optimisticLoker() {
return new OptimisticLockerInterceptor();
}
使用案例:
public void upd() {
User user=new User();
user.setId(13);
user.setName(“张洋小朋友”);
user.setAge(13);
user.setVersion(1);
if( userMapper.updateById(user)>0){
System.err.println(“更新成功”);
}
else {
System.err.println(“被其他人更新”);
}
}
控制台输出:
Time:4 ms - ID:com.example.demo.dao.UserMapper.updateById
Execute SQL:UPDATE User_ SET name_=‘张洋小朋友’, age=13, version=2 WHERE id_=13 AND version=1 AND isDelete=0

更新成功

10. 扩展

一、插件机制:
 Mybatis 通过插件(Interceptor) 可以做到拦截四大对象相关方法的执行,根据需求,完 成相关数据的动态改变。
 Executor
 StatementHandler
 ParameterHandler
 ResultSetHandler
二、插件原理:
 四大对象的每个对象在创建时,都会执行 interceptorChain.pluginAll(),会经过每个插 件对象的 plugin()方法,目的是为当前的四大对象创建代理。代理对象就可以拦截到四 大对象相关方法的执行,因为要执行四大对象的方法需要经过代理。
举例class类型的:
// 乐观锁的配置
@Bean
public OptimisticLockerInterceptor optimisticLockerInterceptor() {
return new OptimisticLockerInterceptor();
}
//分页拦截器
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
return paginationInterceptor;
}
//逻辑删除
//定义自己的通用方法可以实现接口 ISqlInjector 也可以继承抽象类 AbstractSqlInjector 注入通用方法 SQL 语句
// 然后继承 BaseMapper 添加自定义方法,全局配置 sqlInjector 注入 MP 会自动将类所有方法注入到 mybatis 容器中。
@Bean
public ISqlInjector sqlInjector() {
return new LogicSqlInjector();
}
}

举例xml类型的:




















多租户 SQL 解析器
这里配合 分页拦截器 使用, spring boot 例子配置如下:
@Bean
public PaginationInterceptor paginationInterceptor() {
PaginationInterceptor paginationInterceptor = new PaginationInterceptor();
/*
* 【测试多租户】 SQL 解析处理拦截器

* 这里固定写成住户 1 实际情况你可以从cookie读取,因此数据看不到 这条记录( 注意观察 SQL )

*/
List sqlParserList = new ArrayList<>();
TenantSqlParser tenantSqlParser = new TenantSqlParser();
tenantSqlParser.setTenantHandler(new TenantHandler() {
@Override
public Expression getTenantId(boolean where) {
// 该 where 条件 3.2.0 版本开始添加的,用于分区是否为在 where 条件中使用
// 如果是in/between之类的多个tenantId的情况,参考下方示例
return new LongValue(1L);
}

    @Override
    public String getTenantIdColumn() {
        return "tenant_id";
    }

    @Override
    public boolean doTableFilter(String tableName) {
        // 这里可以判断是否过滤表
        /*
        if ("user".equals(tableName)) {
            return true;
        }*/
        return false;
    }
});
sqlParserList.add(tenantSqlParser);
paginationInterceptor.setSqlParserList(sqlParserList);
paginationInterceptor.setSqlParserFilter(new ISqlParserFilter() {
    @Override
    public boolean doFilter(MetaObject metaObject) {
        MappedStatement ms = SqlParserHelper.getMappedStatement(metaObject);
        // 过滤自定义查询此时无租户信息约束出现
        if ("com.baomidou.springboot.mapper.UserMapper.selectListBySQL".equals(ms.getId())) {
            return true;
        }
        return false;
    }
});
return paginationInterceptor;

}

11. 总结

mybatis-plus不仅仅封装了基本的CRUD操作,还内置了防SQL注入操作、常用的分页插件,集合了mybatis和hibernate的优点,可快速的帮助我们提高开发的效率

特性
无侵入:只做增强不做改变,引入它不会对现有工程产生影响,如丝般顺滑
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
支持主键自动生成:支持多达 4 种主键策略(内含分布式唯一 ID 生成器 - Sequence),可自由配置,完美解决主键问题
支持 ActiveRecord 模式:支持 ActiveRecord 形式调用,实体类只需继承 Model 类即可进行强大的 CRUD 操作
支持自定义全局通用操作:支持全局通用方法注入( Write once, use anywhere )
内置代码生成器:采用代码或者 Maven 插件可快速生成 Mapper 、 Model 、 Service 、 Controller 层代码,支持模板引擎,更有超多自定义配置等您来使用
内置分页插件:基于 MyBatis 物理分页,开发者无需关心具体操作,配置好插件之后,写分页等同于普通 List 查询
分页插件支持多种数据库:支持 MySQL、MariaDB、Oracle、DB2、H2、HSQL、SQLite、Postgre、SQLServer 等多种数据库
内置性能分析插件:可输出 Sql 语句以及其执行时间,建议开发测试时启用该功能,能快速揪出慢查询
内置全局拦截插件:提供全表 delete 、 update 操作智能分析阻断,也可自定义拦截规则,预防误操作
#支持数据库
mysql 、 mariadb 、 oracle 、 db2 、 h2 、 hsql 、 sqlite 、 postgresql 、 sqlserver
达梦数据库 、 虚谷数据库 、 人大金仓数据库

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

*小坏。

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值