MybatisPlus学习笔记

1.简介

  1. MyBatis-Plus(简称 MP)是一个 MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
  2. mybatis和mybatis-plus的依赖只能二选一
  3. mybatis-plus提供了通用mapper和service,不需要我们像mybatis一样手动编写接口和sql映射文件。
  4. mybatis-plus自动生成主键id使用的是雪花算法,且自动包含主键回填的功能,可以直接获取到主键id,位数比较长,所以我们在设计表的时候需要将id设置成32位。
  5. mybatis-plus可以自动将表中下划线的字段转化成实体类中的驼峰形式。

2.通用BaseMapper接口用法

  • mybatis-plus会根据BaseMaper的泛型实体类,自动帮我们写sql。
  • Ioc容器只能存在类所对应的bean,不能存在接口对应的bean,所以我们需要在UserMapper上添加@Repository注解。
  • mybatis-plus查询时,将根据我们数据库中的表对应BaseMaper的泛型实体类,进行一一对应,然后赋值。
  • BaseMapper中自带增删改查的方法,如果查询条件很复杂,可以添加条件构造器queryWrapper.
  1. mapper接口层需要继承BaseMapper<User>,并标名实体类。
    @Repository  
    // dao层  
    public interface UserMapper extends BaseMapper<User> {  
      
    }
    

3.通用service接口用法

  • 通用service对BaseMapper进一步封装,方法更多,如批量添加。
  • 其中saveOrUpdate类型方法是添加或者更新,当传入实体类有id时,就执行更新操作,没有id,就执行添操作。
  • ServiceImpl实现了IService,提供了IService中基础功能的实现
  • 若ServiceImpl无法满足业务需求,则可以使用自定的UserService定义方法,并在实现类中实现。
  1. service接口层需要实现IService<User>,并标名实体类。
    public interface UserService extends IService<User> {  
    }
    
  2. service实现类需要继承ServiceImpl<UserMapper, User>,并实现我们刚刚的接口。
    @Service  
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {  
    }
    

4.常用注解

  • @TableName(“t_user”)
    1. 将实体类与数据库表名一一对应。默认BaseMapper将实体类名的小写映射成数据库中的表名,所以我们没有告诉它操作哪个表,它也能找到对应的表,以进行增删改查操作。如果数据库表名与实体类名不一致,可以使用这个注解进行映射。
    2. 如果想要给每个实体类添加前缀,可以配置全局的前缀,这样就不用每个实体类都去使用@TableName进行映射了。
  • @TableId(value = “uid”,type = IdType.AUTO)

    1, @TableId注解的value属性用于指定主键的字段,实体类字段与表中字段不同时可以用。
    2. @TableId注解的type属性用于指定主键生成策略,不想用雪花算法生成id时,可以换成自增策略,需要数据库开启id自增,且type = IdType.AUTO。

    @Data  
    @NoArgsConstructor  
    @AllArgsConstructor  
    //@TableName("t_user")// 将实体类映射到数据库表名  
    public class User {  
        // 映射表中id字段,主键自增策略已在配置文件中设置,此处省略type设置  
        @TableId(value = "id")  
        private Long uid;  
        private String name;  
        private Integer age;  
        private String email;  
    }
    
  • @TableField

    解决实体类属性和表中字段不一致的问题,将实体类属性和表中字段进行对应。

  • @TableLogic

    逻辑删除,直接用在实体类属性上即可。

    // 逻辑删除  
    @TableLogic  
    private Integer isDeleted;
    

5.条件构造器(通过添加条件,进一步删改查)

  1. QueryWrapper
    QueryWrapper<User> queryWrapper = new QueryWrapper<>();  
    queryWrapper.like("name", "t").between("age", 23, 24)  
            .isNotNull("email")  
            .orderBy(true, false, "age");  
    List<User> list = userService.list(queryWrapper);
    
  • 条件优先级

    queryWrapper.like("name", "t").and(i -> i.gt("age", 20).or().isNull("email"));
    
  • 组装select

    QueryWrapper<User> queryWrapper = new QueryWrapper<>();  
    queryWrapper.select("name","age","email");  
    // 返回的是map集合,很多个map,一个map包含一个人的数据  
    //{name=tyu0, age=20, email=test1@baomidou.com}  
    List<Map<String, Object>> maps = userService.listMaps(queryWrapper);  
    for (int i = 0; i < maps.size(); i++) {  
        System.out.println(maps.get(i));  
    }
    
  • 组装子查询
    使用子查询的话,如果实体类字段名与数据库表中字段不一致,会出现查到数据为null的情况。

    QueryWrapper<User> queryWrapper = new QueryWrapper<>();  
    // 子查询条件  
    queryWrapper.inSql("id","select id from t_user where age >= 22");  
    queryWrapper.like("name","t");  
    // 再调用查全部,指定查询条件即可实现子查询  
    List<User> list = userService.list(queryWrapper);  
    for (int i = 0; i < list.size(); i++) {  
        System.out.println(list.get(i));  
    }
    
  • 组装查询条件

    /**  
     * 组装查询条件(复杂方式)  
     * 根据用户传入的参数进行查询,如果该参数存在,就添加该查询条件,反之不添加  
     */  
    @Test  
    public void querryWapperselectTest() {  
        String name = "23";  
        int age = 12;  
      
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();  
        if (StringUtils.isNotBlank(name)){  
            queryWrapper.like("name","t");  
        }  
        if (age>=23){  
            queryWrapper.ge("age",23);  
        }  
        if (age<=23){  
            queryWrapper.le("age",23);  
        }  
      
        List<User> list = userService.list(queryWrapper);  
        for (int i = 0; i < list.size(); i++) {  
            System.out.println(list.get(i));  
        }  
      
    }  
      
    /**  
     * 组装查询条件(简单方式)  
     * 根据用户传入的参数进行查询,如果该参数存在,就添加该查询条件,反之不添加  
     */  
    @Test  
    public void querryWapperselect2Test() {  
        String name = "23";  
        int age = 12;  
      
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();  
        // 根据判断条件,确定是否需要添加该查询条件  
        queryWrapper.like(StringUtils.isNotBlank(name),"name","t");  
        queryWrapper.ge(age>=23,"age",23);  
        queryWrapper.le(age<=23,"age",23);  
      
        List<User> list = userService.list(queryWrapper);  
        for (int i = 0; i < list.size(); i++) {  
            System.out.println(list.get(i));  
        }  
      
    }
    
  1. UpdateWrapper

更新的时候,可以直接在条件构造器中设置修改的值,不用创建对象。
/** * 使用UpdateWrapper实现更新功能功能 * 更新年龄大于等于23,或者邮箱为空,名字全部改为ikun */@Test public void updateWapperTest() { UpdateWrapper<User> updateWrapper = new UpdateWrapper<>(); updateWrapper.ge("age",23).or().isNull("email").set("name","ikun"); boolean res = userService.update(null, updateWrapper); System.out.println(res); }
3. #### LambdaQueryWrapper

  • lambda表达式可以防止字段名写错,通过函数式接口访问实体类中属性对应的字段名。
    /**  
     * 使用lambdaQueryWrapper进行组装条件的查询。  
     */  
    @Test  
    public void lambdaquerryWapperTest() {  
        String name = "23";  
        int age = 12;  
      
        LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper<>();  
        // 使用lambda表达式的函数式接口封装查询条件  
        lambdaQueryWrapper.like(StringUtils.isNotBlank(name),User::getName,"t");  
        lambdaQueryWrapper.ge(age>=23,User::getAge,23);  
        lambdaQueryWrapper.le(age<=23,User::getAge,23);  
      
        List<User> list = userService.list(lambdaQueryWrapper);  
        for (int i = 0; i < list.size(); i++) {  
            System.out.println(list.get(i));  
        }  
    }
    
  1. LambdaUpdateWrapper

与LambdaQueryWrapper类似,此处略。

6.插件

  1. 分页插件

mybatisplus使用分页插件需要创建配置类,然后配置分页插件。

  • 配置类
    @Configuration  
    @MapperScan("pers/jl/mapper")// 推荐将扫描mapper包的注解卸载mybatisplus的配置类上  
    public class MybatisPlusConfig {  
      
        // 配置一个分页插件  
        @Bean  
        public MybatisPlusInterceptor mybatisPlusInterceptor(){  
            // 创建一个mybatisplus拦截器对象  
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();  
            // 添加分页插件,并设置分页数据库类型为Mysql  
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));  
            return interceptor;  
        }  
    }
    
  • 测试案例
    /**  
     * 分页测试  
     */  
    @Test  
    public void pageTest(){  
        Page<User> page = userService.page(new Page<>(2, 3));  
        // 分页各参数说明  
        System.out.println("查询到的数据:"+page.getRecords());// 查询到的数据  
        System.out.println("总记录数:"+page.getTotal());// 总记录数  
        System.out.println("总页数:"+page.getPages());// 总页数  
        System.out.println("当前页码:"+page.getCurrent());// 当前页码  
        System.out.println("当前页数据条数:"+page.getSize());// 当前页数据条数  
        System.out.println("是否有下一页:"+page.hasNext());// 是否有下一页  
        System.out.println("是否有上一页:"+page.hasPrevious());// 是否有上一页  
    }
    
  • 自定义分页功能
    将我们查询到的结果进行分页(自己写的sql语句)。
    1. 定义接口
    /**  
     * 通过年龄查询用户信息并分页  
     * @param page Mybatis-plus所提供的分页对象,必须位于第一个参数的位置  
     * @param age  
     * @return  
     */  
    // 自定义分页功能  
    // @Param是用户通过接口,来向sql传递参数的注解方式,以便使用sql来查询数据库。  
    // @Param("page") Page<User> page用于将查询结果,实现分页功能  
    Page<User> pageByMySelf(@Param("page") Page<User> page, @Param("age") Integer age);
    
    1. 编写SQL映射文件
    <!--这里需要在application.yml中配置别名User-->  
    <!--这里#{age}就是用户通过接口传递过来的参数-->  
    <!--这里sql查询数据并没有分页,用户传过来的page对象就是用来实现分页功能的-->  
    <select id="pageByMySelf" resultType="User">  
        select id,name,age,email from t_user where age > #{age}  
    </select>
    
    1. 设置实体类别名
    # 配置包内实体类别名  
    type-aliases-package: pers.jl.pojo
    
    1. 测试案例
    /**  
     * 自定义分页功能  
     */  
    @Test  
    public void pageTest2(){  
        // 测试自定义分页功能  
        Page<User> page = userMapper.pageByMySelf(new Page<>(2, 3), 20);  
        // 查询到的数据  
        System.out.println("查询到的数据:"+page.getRecords());// 5,6,8  
    }
    
  1. 乐观锁插件

乐观锁可以通过在数据库中添加一个version字段实现的。
乐观锁是每次修改都要检查版本号,版本号一致才会修改成功。
悲观锁是只有上一个用户执行完,下一个用户才能使用。

  • 没有加乐观锁
    /**  
     * 测试商品加锁和不加锁修改的不同  
     */  
    @Test  
    public void lockTest(){  
      
        // 小李取数据  
        Product productLi = productMapper.selectById(1);  
        System.out.println("小李查询到的金额:"+productLi.getPrice());  
        // 小王取数据  
        Product productWang = productMapper.selectById(1);  
        System.out.println("小王查询到的金额:"+productWang.getPrice());  
        //小李修改数据+50  
        productLi.setPrice(productLi.getPrice()+50);  
        productMapper.updateById(productLi);// 150  
        //小王修改数据-30  
        productWang.setPrice(productWang.getPrice()-30);  
        productMapper.updateById(productWang);// 70  
        // 老板查询数据  
        Product productLaoBan = productMapper.selectById(1);  
        // 不加锁的话,小李的修改被小王的修改覆盖掉了,无法实现预期的目的。  
        System.out.println("老板查询到的金额:"+productLaoBan.getPrice());// 70  
      
    }
    
  • 使用MybatisPlus实现乐观锁优化之后
    1. 需要先在实体类version字段上添加@Version,标识乐观锁版本号注解
      @Data  
      @AllArgsConstructor  
      @NoArgsConstructor  
      public class Product {  
          private Long id;  
          private String name;  
          private Integer price;  
          @Version // 标识乐观锁版本号注解  
          private Integer version;  
      }
      
    2. 配置类中添加乐观锁插件
      // 添加乐观锁插件  
      interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
      
    3. 优化后的代码
      /**  
       * 测试商品加锁和不加锁修改的不同  
       * 通过乐观锁解决修改冲突的问题  
       */  
      @Test  
      public void lockTest(){  
          // 小李取数据  
          Product productLi = productMapper.selectById(1);  
          System.out.println("小李查询到的金额:"+productLi.getPrice());  
          // 小王取数据  
          Product productWang = productMapper.selectById(1);  
          System.out.println("小王查询到的金额:"+productWang.getPrice());  
          //小李修改数据+50  
          productLi.setPrice(productLi.getPrice()+50);  
          productMapper.updateById(productLi);// 150  
          //小王修改数据-30  
          productWang.setPrice(productWang.getPrice()-30);  
          int res = productMapper.updateById(productWang);// 70  
          // 操作失败,重试  
          if (res == 0){  
              // 获取最新的版本号  
              Product productNew = productMapper.selectById(1);  
              // 修改数据  
              productNew.setPrice(productNew.getPrice()-30);  
              // 重新更新  
              productMapper.updateById(productNew);  
          }  
          // 老板查询数据  
          Product productLaoBan = productMapper.selectById(1);  
          // 不加锁的话,小李的修改被小王的修改覆盖掉了,无法实现预期的目的。  
          System.out.println("老板查询到的金额:"+productLaoBan.getPrice());// 70  
      }
      

7.通用枚举

表中某些字段的值是固定的

  1. 创建通用枚举类
    /**  
     * 创建一个枚举类,用于给实体类的字段常量赋值  
     */  
    @Getter  
    @AllArgsConstructor  
    public enum SexEnum {  
      
        // 根据枚举字段,设置枚举变量,创建对象时可以直接调用。  
        MaLE(1,"男"),  
        FEMALE(2,"女");  
      
        // 枚举字段  
        @EnumValue// 该注解标记数据存储的值是sex  
        private final Integer sex;  
        private final String sexName;  
    }
    
  2. 实体类添加该字段
    private SexEnum sex;
    
  3. 案例测试
    /**  
     * 插入一条数据,查看枚举是否配置成功  
     */  
    @Test  
    public void EnumTest(){  
        User user = new User();  
        user.setName("小红2");  
        user.setAge(11);  
        user.setEmail("2174694433@qq.com");  
        user.setSex(SexEnum.MaLE);  
        usermapper.insert(user);  
    }
    

8.代码生成器

可以生成mybatis映射器(mapper接口和xml映射文件)、表实体类、service层接口和实现类、controller层。
直接生成模块,节约开发时间。

  1. 导入依赖
    <!--代码生成器相关依赖-->  
    <dependency>  
        <groupId>com.baomidou</groupId>  
        <artifactId>mybatis-plus-generator</artifactId>  
        <version>3.5.1</version>  
    </dependency>  
    <dependency>  
        <groupId>org.freemarker</groupId>  
        <artifactId>freemarker</artifactId>  
        <version>2.3.31</version>  
    </dependency>
    
  2. 复制模板,修改相应代码
    public class MybatisPlusCodeTest {  
        public static void main(String[] args) {  
            FastAutoGenerator.create("jdbc:mysql://localhost:3306/mybatisplus?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false", "root", "1234")  
                .globalConfig(builder -> {  
                    // 设置作者  
                    builder.author("luge")  
                            // 开启 swagger 模式  
                            //.enableSwagger()  
                            // 覆盖已生成文件  
                            .fileOverride()  
                            // 指定输出目录  
                            .outputDir("C://Users//ASUS//mybatis_plus");  
                })  
                .packageConfig(builder -> {  
                    // 设置父包名  
                    builder.parent("pers.jl")  
                            // 设置模块名  
                            .moduleName("mybatisplus")  
                            // 设置mapperXml生成路径  
                            .pathInfo(Collections.singletonMap(OutputFile.mapperXml, "C://Users//ASUS//mybatis_plus"));  
                })  
                .strategyConfig(builder -> {  
                    // 设置需要生成的表名  
                    builder.addInclude("t_user")  
                            .addInclude("t_product")  
                            // 设置过滤表前缀  
                            .addTablePrefix("t_");  
                })  
                // 使用Freemarker引擎模板,默认的是Velocity引擎模板  
                .templateEngine(new FreemarkerTemplateEngine())  
                .execute();  
        }  
    }
    

9.多数据源

两张表不在一个数据库中,需要分别查询

  1. 引入依赖
<!--多数据源依赖-->  
<dependency>  
    <groupId>com.baomidou</groupId>  
    <artifactId>dynamic-datasource-spring-boot-starter</artifactId>  
    <version>3.5.0</version>  
</dependency>
  1. 配置文件
# 多数据源测试  
spring:  
  # 配置数据源信息  
  datasource:  
    dynamic:  
      # 设置默认的数据源或者数据源组,默认值即为master  
      primary: master  
      # 严格匹配数据源,默认false.true未匹配到指定数据源时抛异常,false使用默认数据源  
      strict: false  
      datasource:  
        master:  
          url: jdbc:mysql://localhost:3306/mybatisplus?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false  
          driver-class-name: com.mysql.cj.jdbc.Driver  
          username: root  
          password: 1234  
        slave_1:  
          url: jdbc:mysql://localhost:3306/mybatis_plus_1?serverTimezone=GMT%2B8&characterEncoding=utf-8&useSSL=false  
          driver-class-name: com.mysql.cj.jdbc.Driver  
          username: root  
          password: 1234  
mybatis-plus:  
  configuration:  
    # 配置日志功能,可以查看sql  
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  
  global-config:  
    db-config:  
      # 全局配置主键生成策略为id自增,这样就不用每个实体类的主键字段都去设置  
      id-type: auto  
  # 配置包内实体类别名  
  type-aliases-package: pers.jl.pojo
  1. 指定数据源注解
    @DS(“master”),指定操作的数据源,用于类上或方法上,可以在多数据源时发挥作用。
@Service  
@DS("slave_1")  
public class ProductServiceImpl extends ServiceImpl<ProductMapper,Product> implements ProductService {  
}
  1. 测试案例
/**  
 * 多数据源查询测试  
 */  
@Test  
public void dataSourceTest(){  
    User byId = userService.getById(1);  
    Product byId1 = productService.getById(1);  
    System.out.println(byId);  
    System.out.println(byId1);  
}

10.MybatisX插件

  • 可以在mapper接口和xml文件快速跳转。
  • 代码快速生成,跟mybatisPlus的代码生成类似,没有controller层,但是xml文件更详细。可以生成mybatis映射器(mapper接口和xml映射文件)、表实体类、service层接口和service层实现类。
  • 在接口中只需要写接口名字(见名知意的那种),就可以快速生成CRUD接口,并自动帮我们生成sql语句。
    1. mapper接口
    /**  
    * @author ASUS  
    * @description 针对表【t_user】的数据库操作Mapper  
    * @createDate 2023-04-10 16:26:37  
    * @Entity pers.jl.pojo.User  
    */  
    public interface UserMapper extends BaseMapper<User> {  
          
        // 快速生成接口和sql映射文件,多表联查非常有用,可以帮我们生成sql  
      
        int insertAll(User user);  
      
        int delByIdAndEmail(@Param("id") Long id, @Param("email") String email);  
      
        int updateIdAndAge(@Param("id") Long id, @Param("age") Integer age);  
        List<User> selectByIdAndAge(@Param("id") Long id, @Param("age") Integer age);  
      
    }
    
    1. sql映射文件
    <select id="selectByIdAndAge" resultMap="BaseResultMap">  
        select  
        <include refid="Base_Column_List"/>  
        from t_user  
        where  
        id = #{id,jdbcType=NUMERIC}  
        AND age = #{age,jdbcType=NUMERIC}  
    </select>  
    <insert id="insertAll">  
        insert into t_user  
        (id, name, age,  
         email, sex, is_deleted)  
        values (#{id,jdbcType=NUMERIC}, #{name,jdbcType=VARCHAR}, #{age,jdbcType=NUMERIC},  
                #{email,jdbcType=VARCHAR}, #{sex,jdbcType=NUMERIC}, #{isDeleted,jdbcType=NUMERIC})  
      
    </insert>  
    <delete id="delByIdAndEmail">  
        delete  
        from t_user  
        where id = #{id,jdbcType=NUMERIC}  
          AND email = #{email,jdbcType=VARCHAR}  
    </delete>  
    <update id="updateIdAndAge">  
        update t_user  
        set id  = #{id,jdbcType=NUMERIC},  
            age = #{age,jdbcType=NUMERIC}  
    </update>
    
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

一码一上午

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

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

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

打赏作者

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

抵扣说明:

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

余额充值