MyBatis-Plus框架技术总结

MybatisPlus

1、概述

MybatisPlus是一款Mybatis增强工具,用于简化开发,提高效率。 它在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。

​ 官网: https://mp.baomidou.com/

2、快速入门

2.0、准备工作

①准备数据

CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT 'id',
  `user_name` varchar(20) NOT NULL COMMENT '用户名',
  `password` varchar(20) NOT NULL COMMENT '密码',
  `name` varchar(30) DEFAULT NULL COMMENT '姓名',
  `age` int(11) DEFAULT NULL COMMENT '年龄',
  `address` varchar(100) DEFAULT NULL COMMENT '地址',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=5 DEFAULT CHARSET=utf8;

insert  into `user`(`id`,`user_name`,`password`,`name`,`age`,`address`) values (1,'ruiwen','123','瑞文',12,'山东'),(2,'gailun','1332','盖伦',13,'平顶山'),(3,'timu','123','提姆',22,'蘑菇石'),(4,'daji','1222','妲己',221,'狐山');

②创建SpringBoot工程

添加依赖
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.0</version>
    </parent>
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>
创建启动类
@SpringBootApplication
public class SGApplication {

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

③准备实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    private Long id;
    private String userName;
    private String password;
    private String name;
    private Integer age;
    private String address;
}

2.1、使用MybatisPlus

①添加依赖

        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.5.3.1</version>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>

②配置数据库信息

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/kuangstudy_mybatisplus?characterEncoding=utf-8&serverTimezone=UTC
    username: root
    password: xxxx
    driver-class-name: com.mysql.cj.jdbc.Driver

③创建Mapper接口

创建Mapper接口继承BaseMapper接口

public interface UserMapper extends BaseMapper<User> {
}

BaseMapper接口中已经提供了很多常用方法。所以我们只需要直接从容器中获取Mapper就可以进行操作了,不需要自己去编写Sql语句。

④配置Mapper扫描

​ 在启动类上配置我们的Mapper在哪个包。

@SpringBootApplication
@MapperScan("com.sangeng.mapper")
public class SGApplication {
    public static void main(String[] args) {
        SpringApplication.run(SGApplication.class,args);
    }
}

⑤获取Mapper进行测试

@SpringBootTest
public class MPTest {

    @Autowired
    private UserMapper userMapper;

    @Test
    public void testQueryList(){
        List<User> users = userMapper.selectList(null);
        System.out.println(users);
    }

}

3、常用设置

3.1、设置表映射规则

​ 默认情况下MyBatisPlus操作的数据库表名就是实体类的类名,但是如果表名类名不一致就需要我们自己设置映射规则。

3.1.1、单独设置

​ 可以在实体类的类名上加上@TableName注解进行标识。

例如:

​ 如果数据库表名是tb_user,而实体类名是User则可以使用以下写法。

@TableName("tb_user")
public class User {
	//....
}

3.1.2、全局设置表名前缀

​ 一般一个项目表名的前缀都是统一风格的,这个时候如果一个个设置就太麻烦了。我们可以通过配置来设置全局的表名前缀。

例如:

​ 如果一个项目中所有的表名相比于类名都是多了个前缀: tb_ 。这可以使用如下方式配置

mybatis-plus:
  global-config:
    db-config:
      #表名前缀
      table-prefix: tb_

3.2、设置主键生成策略

3.2.0、测试代码

@Test
public void testInsert(){
    User user = new User();
    user.setUserName("林小秦");
    user.setPassword("Augenestern_QXL");
    int r = userMapper.insert(user);
    System.out.println(r);
}

我们尝试使用上述代码插入一条数据到表中,按道理插入一条数据 id 是自增的5,但是由于MyBatisPlus插入数据时默认的策略是基于雪花算法的自增的唯一id,所以我们需要设置主键生成策略。

3.2.1、单独设置

​ 默认情况下使用MyBatisPlus插入数据时,如果在我们没有设置主键生成策略的情况下默认的策略是基于雪花算法的自增id。

​ 如果我们需要使用别的策略可以在定义实体类时,在代表主键的字段上加上@TableId注解,使用其type属性指定主键生成策略。

例如:

​ 我们要设置主键自动增长则可以设置如下:在实体类的 id 上面加上注解

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
    @TableId(type = IdType.AUTO)
    private Long id;
    private String userName;
    private String password;
    private String name;
    private Integer age;
    private String address;
}

​ 全部主键策略定义在了枚举类IdType中,IdType有如下的取值

  • AUTO

    数据库ID自增,依赖于数据库。该类型请确保数据库设置了 ID自增 否则无效

  • NONE

    未设置主键类型。若在代码中没有手动设置主键,则会根据主键的全局策略自动生成(默认的主键全局策略是基于雪花算法的自增ID)

  • INPUT

    需要手动设置主键,若不设置。插入操作生成SQL语句时,主键这一列的值会是null

  • ASSIGN_ID

    当没有手动设置主键,即实体类中的主键属性为空时,才会自动填充,使用雪花算法

  • ASSIGN_UUID

    当实体类的主键属性为空时,才会自动填充,使用UUID

3.2.2、全局设置

如果要设置其他表插入也要自增,这个时候可以进行全局设置。

mybatis-plus:
  global-config:
    db-config:
      # id生成策略 auto为数据库自增
      id-type: auto

3.3、设置字段映射关系

​ 默认情况下MyBatisPlus会根据实体类的属性名去映射表的列名。

​ 如果数据库的列表和实体类的属性名不一致了我们可以使用@TableField注解的value属性去设置映射关系。

例如:

​ 如果表中一个列名叫 address而 实体类中的属性名为addressStr则可以使用如下方式进行配置。

@TableField("address")
private String addressStr;

3.4、设置字段和列名的驼峰映射

​ 默认情况下MyBatisPlus会开启字段名列名的驼峰映射, 即从经典数据库列名 A_COLUMN(下划线命名) 到经典 Java 属性名 aColumn(驼峰命名) 的类似映射 。

​ 如果需要关闭我们可以使用如下配置进行关闭。

mybatis-plus:
  configuration:
	#是否开启自动驼峰命名规则(camel case)映射
    map-underscore-to-camel-case: false

3.5、日志

​ 如果需要打印MyBatisPlus操作对应的SQL语句等,可以配置日志输出。

配置方式如下:

mybatis-plus:
  configuration:
    # 日志
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl

4、基本使用

4.1、插入数据

​ 我们可以使用insert方法来实现数据的插入。

示例:

@Test
public void testInsert(){
    User user = new User();
    user.setUserName("林小秦");
    user.setPassword("Augenestern_QXL");
    int r = userMapper.insert(user);
    System.out.println(r);
}

4.2、删除操作

​ 我们可以使用deleteXXX方法来实现数据的删除。

示例:

@Test
public void testDelete(){
    List<Integer> ids = new ArrayList<>();
    ids.add(4);
    ids.add(5);
    int i = userMapper.deleteBatchIds(ids);
    System.out.println(i);
}
@Test
public void testDeleteById(){
    int i = userMapper.deleteById(3);
    System.out.println(i);
}
@Test
public void testDeleteByMap(){
    Map<String, Object> map = new HashMap<>();
    map.put("name","提姆");
    map.put("age",22);
    int i = userMapper.deleteByMap(map);
    System.out.println(i);
}

4.3、更新操作

​ 我们可以使用updateXXX方法来实现数据的删除。

示例:

@Test
public void testUpdate(){
    //把id为2的用户的年龄改为14
    User user = new User();
    user.setId(2L);
    user.setAge(14);
    int i = userMapper.updateById(user);
    System.out.println(i);
}

5、条件构造器Wrapper

5.1、概述

​ 我们在实际操作数据库的时候会涉及到很多的条件。所以MyBatisPlus为我们提供了一个功能强大的条件构造器 Wrapper 。使用它可以让我们非常方便的构造条件。

​ 其继承体系如下:

在这里插入图片描述

​ 在其子类AbstractWrapper中提供了很多用于构造Where条件的方法。

AbstractWrapper的子类QueryWrapper则额外提供了用于针对Select语法的select方法。可以用来设置查询哪些列。

AbstractWrapper的子类UpdateWrapper则额外提供了用于针对SET语法的set方法。可以用来设置对哪些列进行更新。

​ 完整的AbstractWrapper方法可以参照:条件构造器 | MyBatis-Plus (baomidou.com)

介绍是用来干什么的。它的实现类有哪些

QueryWrapper,UpdateWrapper,【LambdaQueryWrapper】

5.2、常用AbstractWrapper方法

eq:equals,等于
gt:greater than ,大于 >
ge:greater than or equals,大于等于≥
lt:less than,小于<
le:less than or equals,小于等于≤
between:相当于SQL中的BETWEEN
like:模糊匹配。like(“name”,“黄”),相当于SQL的name like ‘%黄%’
likeRight:模糊匹配右半边。likeRight(“name”,“黄”),相当于SQL的name like ‘黄%’
likeLeft:模糊匹配左半边。likeLeft(“name”,“黄”),相当于SQL的name like ‘%黄’
notLike:notLike(“name”,“黄”),相当于SQL的name not like ‘%黄%’
isNull
isNotNull
and:SQL连接符AND
or:SQL连接符OR

in: in(“age",{1,2,3})相当于 age in(1,2,3)

groupBy: groupBy(“id”,“name”)相当于 group by id,name

orderByAsc :orderByAsc(“id”,“name”)相当于 order by id ASC,name ASC

orderByDesc :orderByDesc (“id”,“name”)相当于 order by id DESC,name DESC

示例一

SQL语句如下:

SELECT 
	id,user_name,PASSWORD,NAME,age,address 
FROM 
	USER 
WHERE 
	age > 18 AND address = '狐山'

如果用Wrapper写法如下:

/**
 * 测试 QueryWrapper01
 */
@Test
public void testWrapper01(){
    QueryWrapper wrapper = new QueryWrapper();
    wrapper.gt("age",18);
    wrapper.eq("address","狐山");
    List<User> users = userMapper.selectList(wrapper);
    System.out.println(users);
}

示例二

SQL语句如下:

SELECT 
	id,user_name,PASSWORD,NAME,age,address 
FROM 
	USER 
WHERE 
	id IN(1,2,3) AND 
	age BETWEEN 12 AND 29 AND 
	address LIKE '%山%'

如果用Wrapper写法如下:

/**
 * 测试 QueryWrapper02
 */
@Test
public void testWrapper02(){
    QueryWrapper wrapper = new QueryWrapper();
    wrapper.in("id",1,2,3);
    wrapper.between("age",12,29);
    wrapper.like("address","山");
    List<User> users = userMapper.selectList(wrapper);
    System.out.println(users);
}

示例三

SQL语句如下:

SELECT 
	id,user_name,PASSWORD,NAME,age,address 
FROM 
	USER 
WHERE 
	id IN(1,2,3) AND 
	age > 10 
ORDER BY 
	age DESC

如果用Wrapper写法如下:

/**
  * 测试 QueryWrapper03
  */
@Test
public void testWrapper03(){
    QueryWrapper wrapper = new QueryWrapper();
    wrapper.in("id",1,2,3);
    wrapper.gt("age",10);
    wrapper.orderByDesc("age");
    List<User> users = userMapper.selectList(wrapper);
    System.out.println(users);
}

5.3、常用QueryWrapper方法

QueryWrapper的 select 可以设置要查询的列。

示例一

select(String… sqlSelect) 方法的测试为要查询的列名

  • 上方参数其实就是字符串的可变参,相当于是一个字符串数组,我们可以向里面传入想查询的列

SQL语句如下:

SELECT 
	id,user_name
FROM 
	USER 

MP写法如下:

/**
  * 测试 testSelect01
  */
@Test
public void testSelect01(){
    QueryWrapper wrapper = new QueryWrapper();
    wrapper.select("id","user_name");
    List<User> users = userMapper.selectList(wrapper);
    System.out.println(users);
}

示例二

select(Class entityClass, Predicate predicate)

  • 第一个参数是字节码类型的对象(传入对应实体类的字节码对象)
  • 第二个参数为Predicate类型,可以使用lambda的写法,过滤要查询的字段 (主键除外)
    • 主键除外:因为主键肯定会查,不用去做过滤,所以主要对主键之外的列过滤

SQL语句如下:

SELECT 
	id,user_name
FROM 
	USER 

MP写法如下:

/**
 * 测试 testSelect02
 */
@Test
public void testSelect02(){
    QueryWrapper wrapper = new QueryWrapper();
    wrapper.select(User.class, new Predicate<TableFieldInfo>() {
        @Override
        public boolean test(TableFieldInfo tableFieldInfo) {
            // 如果 tableFieldInfo.getColumn() 的列名和 user_name 字段一致,那么就放行
            return "user_name".equals(tableFieldInfo.getColumn());
        }
    });
    List<User> users = userMapper.selectList(wrapper);
    System.out.println(users);
}

示例三

select(Predicate predicate)

  • 方法第一个参数为Predicate类型,可以使用lambda的写法,过滤要查询的字段 (主键除外) 。

SQL语句如下:

SELECT 
	id,user_name,PASSWORD,NAME,age 
FROM 
	USER

就是不想查询address这列,其他列都查询了

MP写法如下:

/**
 * 测试 testSelect03
 */
@Test
public void testSelect03(){
    QueryWrapper wrapper = new QueryWrapper(new User());
    wrapper.select(User.class, new Predicate<TableFieldInfo>() {
        @Override
        public boolean test(TableFieldInfo tableFieldInfo) {
            // 不想查 address 列
            return !"address".equals(tableFieldInfo.getColumn());
        }
    });
    List<User> users = userMapper.selectList(wrapper);
    System.out.println(users);
}

5.4、常用UpdateWrapper方法

我们前面在使用update方法时需要创建一个实体类对象传入,用来指定要更新的列及对应的值。但是如果需要更新的列比较少时,创建这么一个对象显的有点麻烦和复杂,如下代码

@Test
public void testUpdate(){
    //把id为2的用户的年龄改为14
    User user = new User();
    user.setId(2L);
    user.setAge(14);
    int i = userMapper.updateById(user);
    System.out.println(i);
}

​ 所以我们可以使用UpdateWrapper的set方法来设置要更新的列及其值。同时这种方式也可以使用Wrapper去指定更复杂的更新条件。

示例

SQL语句如下:

UPDATE 
	USER
SET 
	age = 99
where 
	id > 1

​ 我们想把id大于1的用户的年龄修改为99,则可以使用如下写法:

@Test
public void testUpdateWrapper(){
    UpdateWrapper<User> updateWrapper = new UpdateWrapper<>();
    updateWrapper.gt("id",1);
    updateWrapper.set("age",99);
    userMapper.update(null,updateWrapper);
}

5.5、Lambda条件构造器

​ 我们前面在使用条件构造器时列名都是用字符串的形式去指定。这种方式无法在编译期确定列名的合法性。

​ 所以MP提供了一个Lambda条件构造器可以让我们直接以实体类的方法引用的形式来指定列名。这样就可以弥补上述缺陷。

示例

要执行的查询对应的SQL如下

SELECT 
	id,user_name,PASSWORD,NAME,age,address 
FROM 
	USER 
WHERE 
	age > 18 AND address = '狐山'

如果使用之前的条件构造器写法如下

@Test
public void testLambdaWrapper(){
    QueryWrapper<User> queryWrapper = new QueryWrapper();
    queryWrapper.gt("age",18);
    queryWrapper.eq("address","狐山");
    List<User> users = userMapper.selectList(queryWrapper);
}

如果使用Lambda条件构造器写法如下

@Test
public void testLambdaWrapper2(){
    LambdaQueryWrapper<User> queryWrapper = new LambdaQueryWrapper<>();
    queryWrapper.gt(User::getAge,18);
    queryWrapper.eq(User::getAddress,"狐山");
    List<User> users = userMapper.selectList(queryWrapper);
}

6、自定义SQL

​ 虽然MP为我们提供了很多常用的方法,并且也提供了条件构造器。但是如果真的遇到了复制的SQL时,我们还是需要自己去定义方法,自己去写对应的SQL,这样SQL也更有利于后期维护。

​ 因为MP是对mybatis做了增强,所以还是支持之前Mybatis的方式去自定义方法。

​ 同时也支持在使用Mybatis的自定义方法时使用MP的条件构造器帮助我们进行条件构造。

​ 接下去我们分别来讲讲。

6.0、准备工作

①准备数据

CREATE TABLE `orders` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `price` int(11) DEFAULT NULL COMMENT '价格',
  `remark` varchar(100) DEFAULT NULL COMMENT '备注',
  `user_id` int(11) DEFAULT NULL COMMENT '用户id',
  `update_time` timestamp NULL DEFAULT NULL COMMENT '更新时间',
  `create_time` timestamp NULL DEFAULT NULL COMMENT '创建时间',
  `version` int(11) DEFAULT '1' COMMENT '版本',
  `del_flag` int(1) DEFAULT '0' COMMENT '逻辑删除标识,0-未删除,1-已删除',
  `create_by` varchar(100) DEFAULT NULL COMMENT '创建人',
  `update_by` varchar(100) DEFAULT NULL COMMENT '更新人',
  PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8;

/*Data for the table `orders` */

insert  into `orders`(`id`,`price`,`remark`,`user_id`,`update_time`,`create_time`,`version`,`del_flag`,`create_by`,`update_by`) values (1,2000,'无',2,'2021-08-24 21:02:43','2021-08-24 21:02:46',1,0,NULL,NULL),(2,3000,'无',3,'2021-08-24 21:03:32','2021-08-24 21:03:35',1,0,NULL,NULL),(3,4000,'无',2,'2021-08-24 21:03:39','2021-08-24 21:03:41',1,0,NULL,NULL);

②创建实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Orders  {

    private Long id;

    /**
     * 价格
     */
    private Integer price;

    /**
     * 备注
     */
    private String remark;

    /**
     * 用户id
     */
    private Integer userId;

    /**
     * 更新时间
     */
    private LocalDateTime updateTime;

    /**
     * 创建时间
     */
    private LocalDateTime createTime;

    /**
     * 版本
     */
    private Integer version;

    /**
     * 逻辑删除标识,0-未删除,1-已删除
     */
    private Integer delFlag;


}

6.1、Mybatis方式

①定义方法

​ 在Mapper接口中定义方法

public interface UserMapper extends BaseMapper<User> {

    /**
     * 查找所有用户
     * @param id
     * @return
     */
    User findMyUser(Long id);
}

②创建xml

​ 先配置xml文件的存放目录

mybatis-plus:
  mapper-locations: classpath*:/mapper/*.xml

​ 创建对应的xml映射文件

③在xml映射文件中编写SQL

​ 创建对应的标签,编写对应的SQL语句

<?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.kuang.mapper.UserMapper">

    <select id="findMyUser" resultType="com.kuang.domain.entity.User">
        select * from user where id = #{id}
    </select>
</mapper>

④测试

/**
 * 测试 findMyUser 方法 (Mybatis 方式)
 */
@Test
public void testFindMyUser(){
    User myUser = userMapper.findMyUser(1L);
    System.out.println(myUser);
}

6.2、Mybatis方式结合条件构造器

​ 我们在使用上述方式自定义方法时。如果也希望我们的自定义方法能像MP自带方法一样使用条件构造器来进行条件构造的话只需要使用如下方式即可。

①方法定义中添加Warpper类型的参数

添加Warpper类型的参数,并且要注意给其指定参数名 使用 @Param注解指定参数名

  • 注意:导Wrapper包的正确性
public interface UserMapper extends BaseMapper<User> {

    /**
     * Mybatis Plus 条件构造器 @Param注解指定参数的名字
     * @param wrapper
     * @return
     */
    User findMyUserByWrapper(@Param(Constants.WRAPPER) Wrapper<User> wrapper);
}

在这里插入图片描述

②在SQL语句中获取Warpper拼接的SQL片段进行拼接。

<!-- Mybatis Plus 条件构造器 -->
<select id="findMyUserByWrapper" resultType="com.kuang.domain.entity.User">
    select * from user ${ew.customSqlSegment}
</select>

注意:不能使用#{}应该用${}

③测试

/**
 * 测试 findMyUser 方法 (Mybatis Plus 条件构造器)
 */
@Test
public void testFindMyUser01(){
    QueryWrapper wrapper = new QueryWrapper();
    wrapper.eq("id",1);
    User user = userMapper.findMyUserByWrapper(wrapper);
    System.out.println(user);
}

最终执行的SQL语句为

select * from user WHERE id = 1

7、分页查询

7.1、基本分页查询

①配置分页查询拦截器

@Configuration
public class MybatisPlusPageConfig {

    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
        return interceptor;
    }
}

②进行分页查询

/**
 * 测试分页 selectPage 方法
 */
@Test
public void testPage(){
    IPage<User> page = new Page<>();
    //设置每页条数
    page.setSize(2);
    //设置查询第几页
    page.setCurrent(1);
    userMapper.selectPage(page, null);
    System.out.println(page.getRecords());//获取当前页的数据
    System.out.println(page.getTotal());//获取总记录数
    System.out.println(page.getCurrent());//当前页码
}

7.2、多表分页查询

​ 如果需要在多表查询时进行分页查询的话,就可以在mapper接口中自定义方法,然后让方法接收Page对象。

示例

需求

​ 我们需要去查询Orders表,并且要求查询的时候除了要获取到Orders表中的字段,还要获取到每个订单的下单用户的用户名。

准备工作
SQL准备
SELECT 
	o.*,u.`user_name`
FROM 
	USER u,orders o
WHERE 
	o.`user_id` = u.`id`

查出来的内容除了 orders 表里面的内容,还多了一个 User 表中的 userName,所以封装一个新的 vo 类

增加一个userName属性

/**
 * @ClassName OrdersVO
 * @Description 订单表实体类 + userName 属性
 * @Author Augenestern_QXL
 * @Version 1.0
 */
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Orders  {

    private Long id;

    /**
     * 价格
     */
    private Integer price;

    /**
     * 备注
     */
    private String remark;

    /**
     * 用户id
     */
    private Integer userId;

    /**
     * 更新时间
     */
    private LocalDateTime updateTime;

    /**
     * 创建时间
     */
    private LocalDateTime createTime;

    /**
     * 版本
     */
    private Integer version;

    /**
     * 逻辑删除标识,0-未删除,1-已删除
     */
    private Integer delFlag;

    /**
     * 增加的 userName 属性(订单对应的下单用户名)
     */
    private String userName;

}

实现

①定义接口,定义方法

方法第一个测试定义成Page类型

public interface OrdersMapper extends BaseMapper<OrdersVO> {

    /**
     * 查找所有订单及其下单用户
     * @return
     */
    IPage<OrdersVO> findAllOrders(Page<OrdersVO> page);


}

在xml中不需要关心分页操作,MP会帮我们完成。

<?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.kuang.mapper.OrdersMapper">

    <!-- 查询所有订单 -->
    <select id="findAllOrders" resultType="com.kuang.domain.vo.OrdersVO">
        SELECT
            o.*,u.`user_name`
        FROM
            USER u,orders o
        WHERE
            o.`user_id` = u.`id`
    </select>
</mapper>

然后调用方法测试即可

@Autowired
private OrdersMapper ordersMapper;

/**
 * 查询所有订单及其下单用户
 */
@Test
public void testOrdersPage(){
    Page<OrdersVO> page = new Page<>();
    //设置每页大小
    page.setSize(2);
    //设置当前页码
    page.setCurrent(2);
    ordersMapper.findAllOrders(page);
    System.out.println(page.getRecords());
    System.out.println(page.getTotal());
}

8、Service 层接口

​ MP也为我们提供了Service层的实现。我们只需要编写一个接口,继承IService,并创建一个接口实现类继承ServiceImpl,即可使用。

​ 相比于Mapper接口,Service层主要是支持了更多批量操作的方法。

8.1、基本使用

8.1.1、改造前

定义接口

public interface UserService {
    List<User> list();
}

定义实现类

@Service
public class UserServiceImpl implements UserService {
    @Autowired
    private UserMapper userMapper;

    @Override
    public List<User> list() {
        return userMapper.selectList(null);
    }
}

8.1.2、造后

接口

public interface UserService extends IService<User> {

}

实现类

// 泛型里面两个参数:第一个传Mapper、第二个传实体类
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User>  implements UserService {

  /* 
   这就不需要自己注入 Mapper、也不需要自己写方法
   @Autowired
    private UserMapper userMapper;

    @Override
    public List<User> list() {
        return userMapper.selectList(null);
    }
    */
}

测试

    @Autowired
    private UserService userService;


    @Test
    public void testSeervice(){
        List<User> list = userService.list();
        System.out.println(list);
    }

8.2、自定义方法

public interface UserService extends IService<User> {


    User test();
}

@Service
public class UserServiceImpl  extends ServiceImpl<UserMapper, User>  implements UserService {


    /**
     * 如果我们需要其他的Mapper,仍然需要注入
     */
    @Resource
    private OrdersMapper ordersMapper;

    @Override
    public User MyUser() {
        /**
         * 就不需要注入 UserMapper 了, 直接调用 getBaseMapper 可以获得UserMapper
         * 因为我们 extends ServiceImpl<UserMapper, User> 继承了 ServiceImpl , 底层通过反射获取 UserMapper
         */

        UserMapper userMapper = getBaseMapper();
        return null;
    }
}

9、代码生成器

​ MP提供了一个代码生成器,可以让我们一键生成实体类,Mapper接口,Service,Controller等全套代码 。使用方式如下

①添加依赖

<!--mybatisplus代码生成器-->
<dependency>
    <groupId>com.baomidou</groupId>
    <artifactId>mybatis-plus-generator</artifactId>
    <version>3.5.3.1</version>
</dependency>
<!--模板引擎-->
<dependency>
    <groupId>org.freemarker</groupId>
    <artifactId>freemarker</artifactId>
</dependency>

②生成

  • 08_代码生成器-上_哔哩哔哩_bilibili

​ 因为使用了 @Test 测试方法,所以在测试环境包下建立类,在修改相应配置后执行以下代码即可

/**
 * @ClassName CodeGenerator
 * @Description 代码生成器测试类
 * @Author Augenestern_QXL
 * @Version 1.0
 */
public class CodeGenerator {

    public static void main(String[] args) {
        FastAutoGenerator.create("url", "username", "password")
                .globalConfig(builder -> {
                    builder.author("baomidou") // 设置作者
                            //.enableSwagger() // 开启 swagger 模式
                            //.fileOverride() // 覆盖已生成文件
                            .outputDir("E:\\Code\\IDEA\\KuangStudy_MyBatisPlus\\src\\main\\java"); // 指定输出目录
                })
                .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 -> {
                    builder.parent("com.kuang") // 设置父包名
                            .moduleName("user") // 设置父包模块名(为 user 模块生成)
                            .pathInfo(Collections.singletonMap(OutputFile.xml, "E:\\Code\\IDEA\\KuangStudy_MyBatisPlus\\src\\main\\resources\\mapper\\user")); // 设置mapperXml生成路径
                })
                .strategyConfig(builder -> {
                    builder.addInclude("xxx_user","xxx_dept") // 设置需要生成的表名(例如数据库有两种表: 1: xxx_user 2: xxx_dept)
                            .addTablePrefix("xxx_"); // 设置过滤表前缀(则最终生成的实体类的名字是 user、dept)
                })
                .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
                .execute();
    }

}

在这里插入图片描述

10、自动填充

​ 在实际项目中的表会和我们的orders表一样,有更新时间创建时间创建人,更新人等字段。

​ 我们可以使用 @TableFieldfill 属性来设置字段的自动填充。让我们能更方便的更新相关字段。

示例

①在对应字段上增加注解

​ 使用TableField注解的fill属性来标注哪些字段需要在自动填充,加了注解MP才会在对应的SQL中为我们预留字段。而属性值代表我们在什么进行什么操作时需要预留字段。例如在 OrdersVO 实体类中代码如下:

/**
* 更新时间
* @TableField(fill = FieldFill.INSERT_UPDATE) 表示在执行插入和更新操作的时候会帮我们自动填充
*/
@TableField(fill = FieldFill.INSERT_UPDATE)
private LocalDateTime updateTime;

/**
* 创建时间
* @TableField(fill = FieldFill.INSERT) 表示只有在插入操作的时候会帮我们自动填充
*/
@TableField(fill = FieldFill.INSERT)
private LocalDateTime createTime;

②自定义填充处理器 MetaObjectHandler ,并将其装配到 Spring 容器中

/**
 * @ClassName MyMetaObjectHandler
 * @Description 自定义填充处理器
 * @Author Augenestern_QXL
 * @Version 1.0
 */
@Component
public class MyMetaObjectHandler implements MetaObjectHandler {
    /**
     * 在插入和更新操作的时候自动填充当前时间
     * 参数一: 字段名
     * 参数二: 当前时间
     * 参数三: metaObject
     * @param metaObject
     */
    @Override
    public void insertFill(MetaObject metaObject) {
        this.setFieldValByName("createTime", LocalDateTime.now(), metaObject);
        this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
    }

    @Override
    public void updateFill(MetaObject metaObject) {
        this.setFieldValByName("updateTime", LocalDateTime.now(), metaObject);
    }
}

③测试

/**
* 测试自动填充
* 我们在设置的时候没有设置更新时间和创建时间,但是执行方法的sql语句里面是填充了时间
*/
@Test
public void testFill(){
    Orders orders = new Orders();
    orders.setPrice(123);
    orders.setId(13L);
    //ordersMapper.update(orders,null);
    ordersMapper.insert(orders);

}

11、逻辑删除

​ MP也支持逻辑删除的处理。我们只需要配置好逻辑删除的实体字段名,代表删除的字段值和代表未删除的字段值后即可。

​ 注意:如果3.3.0版本之前还需要在对应的实体类字段上加上@TableLogic注解

mybatis-plus:
  global-config:
    db-config:
      logic-delete-field: delFlag  # 全局逻辑删除的实体字段名(Orders表下有 delFlag 字段)
      logic-delete-value: 1 # 逻辑已删除值(默认为 1 的时候已删除)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0 的时候未删除)

12、乐观锁

​ 并发操作时,我们需要保证对数据的操作不发生冲突。乐观锁就是其中一种方式。乐观锁就是先加上不存在并发冲突问题,在进行实际数据操作的时候再检查是否冲突。

​ 我们在使用乐观锁时一般在表中增加一个version列。用来记录我们对每天记录操作的版本。每次对某条记录进行过操作是,对应的版本也需要+1。

​ 然后我们在每次要进行更新操作时,先查询对应数据的version值。在执行更新时, set version = 老版本+1 where version = 老版本

​ 如果在查询老版本号到更新操作的中间时刻有其他人更新了这条数据,这样这次更新语句就会更新失败。

​ 这里在更新时对version的操作如果有我们自己做就会显的有点麻烦。所以MP提供了乐观锁插件。

​ 使用后我们就可以非常方便的实现对version的操作。

12.1、使用

①配置对应插件

@Configuration
public class MybatisPlusConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
        mybatisPlusInterceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
        return mybatisPlusInterceptor;
    }
}

②在实体类的字段上加上@Version注解

@Version
private Integer version;

③更新

注意:在更新前我们一定要先查询到version设置到实体类上再进行更新才能生效

@Test
public void testVersion(){
    //查询id为3的数据
    QueryWrapper<Orders> queryWrapper = new QueryWrapper<>();
    queryWrapper.eq("id",3);
    Orders orders = ordersMapper.selectOne(queryWrapper);


    //对id为3的数据进行更新  把price修改为88
    orders.setPrice(88);
    ordersMapper.updateById(orders);
}

这种情况下我们可以看到执行的sql已经发生了变化。

==>  Preparing: UPDATE orders SET price=?, update_time=?, version=? WHERE id=? AND version=? AND del_flag=0
==> Parameters: 8888(Integer), null, 2(Integer), 2(Long), 1(Integer)

12.2、多插件配置问题

​ 我们在使用3.4.0版本以后的MP时,如果需要用到多个插件的话要注意。在配置的时候只需要注入一个MybatisPlusInterceptor 对象,把插件对象添加到MybatisPlusInterceptor 对象中即可。

​ 例如:

@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
    MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
    interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
    interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    return interceptor;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/94485.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

怎样快速选择正确的可视化图表?

数据可视化的图表类型十分丰富&#xff0c;好的图表可以有效、清晰地呈现数据的信息。对于用户而言&#xff0c;选择正确的图表是十分关键的&#xff0c;不仅可以达到“一图胜千言”的效果&#xff0c;而且会直接影响分析的结果。 用户选择正确的数据可视化图表前&#xff0c;…

嵌入式学习笔记——ARM的编程模式和7种工作模式

ARM提供的指令集 ARM态-ARM指令集&#xff08;32-bit&#xff09; Thumb态-Thumb指令集&#xff08;16-bit&#xff09; Thumb2态-Thumb2指令集&#xff08;16 & 32 bit&#xff09; Thumb指令集是对ARM指令集的一个子集重新编码得到的&#xff0c;指令长度为16位。通常在…

2023年四款热门护眼台灯推荐/测评/排行/选购指南-内含明基/书客/飞利浦

灯具可以说是我们日常生活中使用很频繁的工具了&#xff0c;我们每天都离不开它给我们带来的光亮。当然&#xff0c;现在灯具也有很多种类可以挑选&#xff0c;今天主要带来的就是护眼台灯的选购问题&#xff01; 护眼台灯品牌那么多&#xff0c;怎么选到适合自己的台灯&#x…

Linux下的系统编程——vim/gcc编辑(二)

前言&#xff1a; 在Linux操作系统之中有很多使用的工具&#xff0c;我们可以用vim来进行程序的编写&#xff0c;然后用gcc来生成可执行文件&#xff0c;最终运行程序。下面就让我们一起了解一下vim和gcc吧 目录 一、vim编辑 1.vim的三种工作模式 2.基本操作之跳转字符 &a…

always for

怎么改写 reg test[3:0]; always (posedge clk) begin//int i0;if(rst) begintest[0] <0;test[1] <0;test[2] <0;test[3] <0;endelse beginif(test[0]0) beginif(wea )test[0] < 1;endelse if(test[1]0) begin //优先级if(wea )test[1] < 1;endelse if(te…

【android12-linux-5.1】【ST芯片】HAL移植后没调起来

ST传感器芯片HAL按官方文档移植后&#xff0c;测试一直掉不起来&#xff0c;加的日志没出来。经过分析&#xff0c;是系统自带了一个HAL&#xff0c;影响的。 按照官方文档&#xff0c;移植HAL后&#xff0c;在/device/<vendor\>/<board\>/device.mk*路径增加PROD…

线性代数的学习和整理12: 矩阵与行列式,计算上的差别对比

目录 1 行列式和矩阵的比较 2 简单总结矩阵与行列式的不同 3 加减乘除的不同 3.1 加法不同 3.2 减法不同 3.3 标量乘法/数乘 3.3.1 标准的数乘对比 3.3.2 数乘的扩展 3.4 乘法 4 初等线性变换的不同 4.1 对矩阵进行线性变换 4.2 对行列式进行线性变换 1 行列式和…

CUDA小白 - NPP(2) -图像处理-算数和逻辑操作

cuda小白 原文链接 NPP GPU架构近些年也有不少的变化&#xff0c;具体的可以参考别的博主的介绍&#xff0c;都比较详细。还有一些cuda中的专有名词的含义&#xff0c;可以参考《详解CUDA的Context、Stream、Warp、SM、SP、Kernel、Block、Grid》 常见的NppStatus&#xff0c…

docker部署前端项目保姆级教程

本地启动docker&#xff08;有不会启动的吗&#xff1f;下载docker&#xff08;小海豚&#xff09;双击起来就行&#xff09; 准备阿里云账号&#xff08;免费&#xff09; 没有就去注册一个&#xff0c;记住密码后面要用到 官网地址&#xff1a;阿里云登录 - 欢迎登录阿里云…

字符设备驱动(内核态用户态内存交互)

前言 内核驱动&#xff1a;运行在内核态的动态模块&#xff0c;遵循内核模块框架接口&#xff0c;更倾向于插件。 应用程序&#xff1a;运行在用户态的进程。 应用程序与内核驱动交互通过既定接口&#xff0c;内核态和用户态访问依然遵循内核既定接口。 环境搭建 系统&#…

华为云Stack的学习(三)

四、华为云Stack公共组件 1.华为云Stack公共负载均衡方案介绍 1.1 LVS原理 LVS是四层负载均衡&#xff0c;建立在OSI模型的传输层之上&#xff0c;所以效率非常高。 LVS有两种转发模式&#xff1a; NAT模式的转发主要通过修改IP地址&#xff08;位于OSI模型的第三层网络层&…

Linux安装Docker

文章目录 先决条件开始安装1.卸载旧版本2.安装依赖3.切换数据源4.安装Docker5.启动服务6.查看版本7.查看端口8.测试拉取镜像 同系列文章 先决条件 Linux内核版本高于3.1 开始安装 1.卸载旧版本 yum remove docker \docker-client \docker-client-latest \docker-common \doc…

【算法与数据结构】404、LeetCode左叶子之和

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;思路比较简单&#xff0c;遍历所有节点然后判断该节点是否为左叶子节点&#xff0c;如果是&#xff0c…

Zblog博客网站搭建与上线发布:在Windows环境下利用cpolar内网穿透实现公网访问的指引

文章目录 1. 前言2. Z-blog网站搭建2.1 XAMPP环境设置2.2 Z-blog安装2.3 Z-blog网页测试2.4 Cpolar安装和注册 3. 本地网页发布3.1. Cpolar云端设置3.2 Cpolar本地设置 4. 公网访问测试5. 结语 1. 前言 想要成为一个合格的技术宅或程序员&#xff0c;自己搭建网站制作网页是绕…

新手将最简单的springboot部署上tomcat出现的意外问题

现阶段springboot部署到tomcat的文章一抓一大把且都相同,便贴一个地址以展示流程: SpringBoot打war包部署Tomcat(最全)_spring boot war 部署tomcat_聊Java的博客-CSDN博客 那么就说一下我出现的问题: 在完整复现流程且确认代码无误的情况下,部署到tomcat,此时问题出现了:启动…

PHPEXCEL 导出excel

$styleArray [alignment > [horizontal > Alignment::HORIZONTAL_CENTER,vertical > Alignment::VERTICAL_CENTER],];$border_style [borders > [allborders > [style > \PHPExcel_Style_Border::BORDER_THIN ,//细边框]]];$begin_date $request->beg…

损失函数介绍

用softmax&#xff0c;就可以将一个输出值转换到概率取值的一个范围。 交叉熵损失CrossEntropyLoss 第一个参数weight&#xff0c; 各类别的loss设置权值&#xff0c; 如果类别不均衡的时候这个参数很有必要了&#xff0c;加了之后损失函数变成这样&#xff1a; 第二个参数ign…

Leetcode 191.位1的个数

编写一个函数&#xff0c;输入是一个无符号整数&#xff08;以二进制串的形式&#xff09;&#xff0c;返回其二进制表达式中数字位数为 1 的个数&#xff08;也被称为汉明重量&#xff09;。 提示&#xff1a; 请注意&#xff0c;在某些语言&#xff08;如 Java&#xff09;中…

微服务之Nacos

1 版本说明 官网地址&#xff1a; https://github.com/alibaba/spring-cloud-alibaba/wiki/%E7%89%88%E6%9C%AC%E8%AF%B4%E6%98%8E 1.1 2021.x 分支 适配 SpringBoot 2.4, Spring Cloud 2021.x 版本及以上的Spring Cloud Alibaba 版本如下表&#xff08;最新版本用*标记&am…

036 - timezone

timestamp随着mysql的time_zone变化而变化&#xff0c;但是datetime不会&#xff1b; -- 查询mysql的变量&#xff1a; show variables;-- 模糊查询变量中带有time_zone的变量&#xff1a; show variables like %time_zone%; -- 创建表 create table test_time_zone (a dateti…