• MyBatis-Plus学习笔记总结


    一、查询

    构造器分为QueryWrapper和LambdaQueryWrapper

    创建实体类User

    package com.system.mybatisplus.model;
    
    import com.baomidou.mybatisplus.annotation.IdType;
    import com.baomidou.mybatisplus.annotation.TableField;
    import com.baomidou.mybatisplus.annotation.TableId;
    import com.baomidou.mybatisplus.annotation.TableName;
    import lombok.Data;
    
    import java.io.Serializable;
    
    @TableName("user") // 指定表名,如果表名和类名一致,可以省略
    @Data // 使用Lombok简化开发
    public class User implements Serializable { // 实现序列化接口
        @TableId(type = IdType.AUTO)
        private Long id;
        @TableField("name") // 指定表字段名,如果字段名和属性名一致,可以省略
        private String name;
        // @TableField(select = false) // select = false 表示查询时不查询该字段
        private Integer age;
        private String email;
        @TableField(exist = false) // exist = false 表示该字段不是数据库字段,但是可以使用
        private Boolean isOnline;
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    编写mapper接口UserMapper

    package com.system.mybatisplus.mapper;
    
    import com.baomidou.mybatisplus.core.mapper.BaseMapper;
    import com.system.mybatisplus.model.User;
    import org.apache.ibatis.annotations.Mapper;
    import org.apache.ibatis.annotations.Param;
    
    @Mapper // 此注解用于标记这是一个mybatis的mapper类,否则会报错,因为没有加@Mapper
    public interface UserMapper extends BaseMapper<User> {// 继承BaseMapper
        // 根据名称查询
        User selectByName(@Param("name") String name);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    编写service及其实现类

    package com.system.mybatisplus.service;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.system.mybatisplus.model.User;
    // 需要继承IService
    public interface UserService extends IService<User> {
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    package com.system.mybatisplus.service.impl;
    
    import com.baomidou.mybatisplus.extension.service.IService;
    import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
    import com.system.mybatisplus.mapper.UserMapper;
    import com.system.mybatisplus.model.User;
    import com.system.mybatisplus.service.UserService;
    import org.springframework.stereotype.Service;
    
    @Service // 此注解用于标记这是一个service类,否则会报错,因为没有加@Service
    // 此处继承ServiceImpl(ServiceImpl实现了IService接口),同时实现UserService接口
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1、等值查询

    查询条件必须完全匹配才行,可以拼接多个eq

    	@Test
        void testEq() {
            // 1、创建条件构造器
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            // 2、设置条件,指定字段名和字段值
            wrapper.eq("name", "jone");
            // 3、执行查询,使用selectOne方法,查询结果只能有一条,否则报错,如果查询结果有多条,使用selectList方法
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    	@Test
        void testEq() {
            // 简写
            System.out.println(userMapper.selectList(new QueryWrapper<User>().eq("name", "jone")));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    相当于执行了如下SQL

    SELECT id,name,age,email FROM user WHERE (name = ?)
    
    • 1

    可以使用LambdaQueryWrapper

    LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
    wrapper.eq(User::getName, "jone"); // 等价于wrapper.eq("name", "jone"); 但是这样写更安全,因为不会出现字段名写错的情况
    System.out.println(userMapper.selectList(wrapper));
    
    • 1
    • 2
    • 3

    同时多个查询条件,必须同时满足才行

        @Test
        void testEq() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(User::getName, "jone").eq(User::getAge, 19); // 相当于并列条件
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    SELECT id,name,age,email FROM user WHERE (name = ? AND age = ?)
    
    • 1

    空值null的判断与处理

    当某个查询条件值为空时,不参与拼接

    一般用于多条件查询中

     @Test
        void testNull() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            String name = "jone";
            // 查询条件是否为null的判断
            wrapper.eq(name != null, User::getName, name); // 如果name不为空,就加入条件,否则不加入条件
            System.out.println(userMapper.selectOne(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    多条件查询也可以使用allEq

    使用HashMap存储查询条件,就没法使用LambdaQueryWrapper

     @Test
        void testAllEq() {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            HashMap<String, Object> map = new HashMap<>();
            map.put("name", "jone");
            map.put("age", 19);
            wrapper.allEq(map, false); // 等价于wrapper.eq("name", "jone").eq("age", 19); 如果第二个参数为false,表示如果map中有null值,就不加入条件
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    除了eq,还有ne,即不等值查询

    @Test
        void testNe() {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.ne("name", "jone"); // 不等于
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    SELECT id,name,age,email FROM user WHERE (name <> ?)  <> 不等于
    
    • 1
    2、范围查询
    1. gt 大于
    @Test
        void testGt() {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.gt("age", 20); // 大于,适用于数据类型为数字的字段和日期类型的字段
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. ge 大于等于
        @Test
        void testGe() {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.ge("age", 20); // 大于等于,适用于数据类型为数字的字段和日期类型的字段
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. lt 小于
     @Test
        void testLt() {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.lt("age", 20); // 小于,适用于数据类型为数字的字段和日期类型的字段
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. le 小于等于
     @Test
        void testLe() {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.le("age", 20); // 小于等于,适用于数据类型为数字的字段和日期类型的字段
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. between
        @Test
        void testBetween() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.between(User::getAge, 18, 20); // 适用于数据类型为数字的字段和日期类型的字段, 包含18和20
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. notBetween
        @Test
        void testNotBetween() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.notBetween(User::getAge, 18, 20); // 适用于数据类型为数字的字段和日期类型的字段, 不包含18和20
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    3、模糊查询
    1. like
    @Test
        void testLike() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.like(User::getName, "j"); // 模糊查询
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    SELECT id,name,age,email FROM user WHERE (name LIKE %j%)
    
    • 1
    1. notLike

    查询名称中不含 j的,不区分大小写

    @Test
        void testLike() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.notLike(User::getName, "j"); // 模糊查询
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    SELECT id,name,age,email FROM user WHERE (name NOT LIKE %j%)
    
    • 1
    1. likeLeft

    区分大小写

        @Test
        void testLikeLeft() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.likeLeft(User::getName, "j"); // 左模糊查询, 等价于wrapper.like(User::getName, "%j")
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. likeRight
    @Test
        void testLikeRight() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            // 查找名字以j开头的用户,区分大小写
            wrapper.likeRight(User::getName, "j"); // 右模糊查询, 等价于wrapper.like(User::getName, "j%")
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    4、判空查询
    1. isNull
        @Test
        void testIsNull() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.isNull(User::getEmail); // 查找email为null的用户
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    SELECT id,name,age,email FROM user WHERE (email IS NULL)
    
    • 1
    1. isNotNull
        @Test
        void testIsNull() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.isNotNull(User::getEmail); // 查找email不为null的用户
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    5、包含查询
    1. in
        @Test
        void testIn() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.in(User::getAge, Arrays.asList(18,19,20)); // 查找年龄为18,19,20的用户
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    SELECT id,name,age,email FROM user WHERE (age IN (?,?,?))
    
    • 1
    1. notIn
        @Test
        void testNotIn() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.notIn(User::getAge, Arrays.asList(18,19,20)); // 查找年龄不为18,19,20的用户
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    1. inSql

    可以编写嵌套查询

        @Test
        void testInSql() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.inSql(User::getId, "select id from user where id < 3"); // 查找id小于3的用户
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    SELECT id,name,age,email FROM user WHERE (id IN (select id from user where id < 3))
    
    • 1
    1. notInSql
        @Test
        void testNotInSql() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.notInSql(User::getId, "select id from user where id < 3"); // 查找id不小于3的用户
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    SELECT id,name,age,email FROM user WHERE (id NOT IN (select id from user where id < 3))
    
    • 1
    6、分组查询

    使用groupBy分组

        @Test
        void testGroupBy() {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.groupBy("age"); // 按照年龄分组
            // 这里使用字符串的方式,所以只能使用QueryWrapper,不能使用LambdaQueryWrapper
            wrapper.select("age, count(*) as count"); // 查询年龄和年龄的数量
            System.out.println(userMapper.selectMaps(wrapper)); // 返回类型为List>
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    SELECT age, count(*) as count FROM user GROUP BY age
    
    • 1
    7、聚合查询

    在6的基础上,过滤出count大于等于2的数据,就需要使用having过滤

    先分组,再查询,最后过滤

        @Test
        void testHaving() {
            QueryWrapper<User> wrapper = new QueryWrapper<>();
            wrapper.groupBy("age"); // 按照年龄分组
            wrapper.select("age, count(*) as count"); // 查询年龄和年龄的数量
            wrapper.having("count >= 2"); // 查询数量大于等于2的年龄
            System.out.println(userMapper.selectMaps(wrapper)); // 返回类型为List>
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    SELECT age, count(*) as count FROM user GROUP BY age HAVING count >= 2
    
    • 1

    [{count=2, age=18}, {count=2, age=20}]

    8、排序查询
    1. orderByASC 升序
        @Test
        void testOrderByAsc() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.select(User::getId, User::getName, User::getAge); // 查询id, name, age字段
            wrapper.orderByAsc(User::getAge); // 按照年龄升序排序
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    SELECT id,name,age FROM user ORDER BY age ASC
    
    • 1
    1. orderByDesc 降序
        @Test
        void testOrderByDesc() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.select(User::getId, User::getName, User::getAge); // 查询id, name, age字段
            wrapper.orderByDesc(User::getAge); // 按照年龄降序排序
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    1. orderBy

    相比较前面两个,灵活性更高

        @Test
        void testOrderBy() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.select(User::getId, User::getName, User::getAge); // 查询id, name, age字段
            // 第一个参数表示当该字段值为null时,是否还要作为排序条件
            // 第二个参数表示是否升序排序
            // 第三个参数表示排序的字段
            wrapper.orderBy(true, true, User::getAge); // 按照年龄升序排序
            // 当年龄相同时,按照id降序排序
            wrapper.orderBy(true, false, User::getId); // 按照id降序排序
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    SELECT id,name,age FROM user ORDER BY age ASC,id DESC
    
    • 1
    9、逻辑查询
    (1)内嵌逻辑查询func

    当某个需求的条件有多个时,可以使用func

        @Test
        void testFunc() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            // func函数需要传入一个Consumer接口,该接口的accept方法接收一个LambdaQueryWrapper对象
            wrapper.func(new Consumer<LambdaQueryWrapper<User>>() {
                @Override
                public void accept(LambdaQueryWrapper<User> userLambdaQueryWrapper) {
                    // 这里需要使用实际开发中的业务逻辑来替换
                    if(true) {
                        userLambdaQueryWrapper.eq(User::getId, 1);
                    }else {
                        userLambdaQueryWrapper.ne(User::getId, 1);
                    }
                }
            });
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
        @Test
        void testFunc() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            // func函数需要传入一个Consumer接口,该接口的accept方法接收一个LambdaQueryWrapper对象
            // 使用Lambda表达式来简化代码
            wrapper.func(userLambdaQueryWrapper -> {
                // 这里需要使用实际开发中的业务逻辑来替换
                if(true) {
                    userLambdaQueryWrapper.eq(User::getId, 1);
                }else {
                    userLambdaQueryWrapper.ne(User::getId, 1);
                }
            });
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    (2)and

    正常的拼接默认就是and,表示条件需要同时成立

        @Test
        void testAnd() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.gt(User::getAge, 18).lt(User::getAge, 25); // 查询年龄大于18并且小于20的用户
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    使用and嵌套

    通常需要嵌套or

        @Test
        void testAnd() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(User::getId, 1)
                    .and(userLambdaQueryWrapper -> userLambdaQueryWrapper.eq(User::getAge, 18)
                            .or().eq(User::getAge, 20)); // 查询id为1并且年龄为18或20的用户
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    SELECT id,name,age,email FROM user WHERE (id = ? AND (age = ? OR age = ?))
    
    • 1
    (3)or

    表示多个条件只需要成立其中之一即可

        @Test
        void testOr() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.gt(User::getAge, 25)
                    .or()
                    .lt(User::getAge, 20); // 查询年龄大于25或小于20的用户
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    or也可以嵌套

    (4)nested

    表示嵌套查询

        @Test
        void testNested() {
            // nested表示嵌套查询, 用于构建复杂的查询条件
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.eq(User::getId, 1)
                    .nested(userLambdaQueryWrapper -> userLambdaQueryWrapper.eq(User::getAge, 18)
                            .or().eq(User::getAge, 20)); // 查询id为1并且年龄为18或20的用户
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    10、自定义查询

    使用apply函数

    可以定制更复杂的查询条件

        @Test
        void testApply() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            // apply方法用于拼接SQL语句,这里需要使用实际开发中的业务逻辑来替换
            wrapper.apply("id = 1"); // 查询id为1的用户
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    11、last

    last主要用于分页查询中,需要传入字符串参数

        @Test
        void testLast() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.last("limit 1"); // 查询第一个用户
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    SELECT id,name,age,email FROM user limit 1
    
    • 1
        @Test
        void testLast() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            wrapper.last("limit 0, 2"); // 查询前两条数据
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    SELECT id,name,age,email FROM user limit 0, 2
    
    • 1
    12、exists
        @Test
        void testExists() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            // 查询age为18的用户是否存在,如果存在则返回true,否则返回false
            // 返回true后,会继续执行后面的查询操作
            wrapper.exists("select id from user where age = 18");
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    SELECT id,name,age,email FROM user WHERE (EXISTS (select id from user where age = 18))
    
    • 1
        @Test
        void testNotExists() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            // 查询age为18的用户是否不存在,如果不存在则返回true,否则返回false
            // 返回true后,会继续执行后面的查询操作
            wrapper.notExists("select id from user where age = 18");
            System.out.println(userMapper.selectList(wrapper));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    二、主键策略

    1、AUTO

    需要在表的主键上设置自增,然后在实体类上的对应的属性上加上注解

    @TableId(type = IdType.AUTO)
    private Long id;
    
    • 1
    • 2

    这样设置后,每次增加一条数据时,会自动生成对应的id

    2、INPUT

    不需要在表的主键上设置自增,每次新增数据时需要自己设置id

    @TableId(type = IdType.AUTO)
    private Long id;
    
    • 1
    • 2
    3、ASSIGN_ID

    使用雪花算法可以实现有序、唯一、且不直接暴露排序的数字

    @TableId(type = IdType.ASSIGN_ID)
    private Long id;
    
    • 1
    • 2
    4、NONE

    使用该策略表示不指定主键生成策略,而是跟随全局策略,可以在配置文件中使用id-type指定全局主键策略

    @TableId(type = IdType.NONE)
    private Long id;
    
    • 1
    • 2
    5、ASSIGN_UUID

    UUID是全局唯一标识符,定义为一个字符串主键,采用32位字符组成,保证始终唯一,需要设置id的类型为字符串

    @TableId(type = IdType.ASSIGN_UUID)
    private Long id;
    
    • 1
    • 2

    三、分页查询

    首先需要编写配置类

    以下适用于mybatis-plus 3.5以上版本

    @Configuration
    // @MapperScan("com.system.mybatisplus.mapper") // 如果在启动类上已经配置了,这里就不需要再配置了
    public class MybatisPlusConfig {
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            // DbType.MYSQL 表示数据库类型是mysql
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            return interceptor;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    测试

        @Test
        void testPage() {
            LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
            // 创建一个分页对象,传入两个参数:当前页和每页显示的记录数
            IPage<User> pageParam = new Page<>(1, 2);
            // 调用分页查询的方法,将分页对象和查询条件对象传入
            userMapper.selectPage(pageParam, wrapper);
            // 从分页对象中获取分页数据
            System.out.println("总页数:" + pageParam.getPages());
            System.out.println("总记录数:" + pageParam.getTotal());
            System.out.println("当前页码:" + pageParam.getCurrent());
            System.out.println("每页显示的记录数:" + pageParam.getSize());
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    一般会自定义查询语句,所以通用的写法如下

    controller中

    @RestController
    public class UserController {
        @Autowired
        private UserService userService;
    
        @GetMapping("/users/{page}/{limit}")
        public IPage<User> listPage(@PathVariable("page") Long page, @PathVariable("limit") Long limit) {
            // 创建一个分页对象,传入两个参数:当前页和每页显示的记录数
            IPage<User> pageParam = new Page<>(page, limit);
            // 调用Service中的方法,一般还会传入一个条件查询对象
            return userService.listPage(pageParam);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    Service中

    public interface UserService extends IService<User> {
    
        IPage<User> listPage(IPage<User> pageParam);
    }
    
    • 1
    • 2
    • 3
    • 4
    @Service // 此注解用于标记这是一个service类,否则会报错,因为没有加@Service
    // 此处继承ServiceImpl(ServiceImpl实现了IService接口),同时实现UserService接口
    public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {
    
        @Autowired
        private UserMapper userMapper;
        @Override
        public IPage<User> listPage(IPage<User> pageParam) {
            return userMapper.selectUserByPage(pageParam);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    mapper中

    @Mapper // 此注解用于标记这是一个mybatis的mapper类,否则会报错,因为没有加@Mapper
    public interface UserMapper extends BaseMapper<User> {
    	// 返回值是一个IPage对象
        IPage<User> selectUserByPage(IPage<User> pageParam); // 传入分页参数
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    
    DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
    "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
    
    <mapper namespace="com.system.mybatisplus.mapper.UserMapper">
        <select id="selectUserByPage" resultType="com.system.mybatisplus.model.User">
            select * from user
        select>
    mapper>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    四、SimpleQuery

    SimpleQuery可以对selectList查询后的结果使用Stream流进行了一些封装,使其可以返回一些指定结果,简化了Api的调用。

    1、list

    例如,查询出所有User的姓名,返回一个List

    使用Stream流

        @Test
        void testList() {
            List<User> users = userMapper.selectList(null);
            // 自己调用Stream的Api
            List<String> names = users.stream().map(User::getName).toList();
            names.forEach(System.out::println);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    使用SimpleQuery的list

    // 必须传入一个wrapper,而不能写null       
    List<String> names = SimpleQuery.list(new LambdaQueryWrapper<User>(), User::getName);
            names.forEach(System.out::println);
    
    • 1
    • 2
    • 3

    获取年龄为20的用户姓名列表

            List<String> names = SimpleQuery.list(new LambdaQueryWrapper<User>()
                    .eq(User::getAge, 20), User::getName);
            System.out.println(names);
    
    • 1
    • 2
    • 3
    [Jack, Billie]
    
    • 1
    2、map

    可以指定键值对

    以id为键,user对象为值

            Map<Long, User> map = SimpleQuery.keyMap(new LambdaQueryWrapper<>(), User::getId);
            System.out.println(map);
    
    • 1
    • 2

    只查询age为20的User

            Map<Long, User> map = SimpleQuery.keyMap(new LambdaQueryWrapper<User>().eq(User::getAge, 20), User::getId);
            System.out.println(map);
    
    • 1
    • 2

    以id为键,name为值

    这里需要使用map方法,第二、三个参数分别表示要获得的键、值

            Map<Long, String> longUserMap = SimpleQuery.map(new LambdaQueryWrapper<User>(), User::getId, User::getName);
            System.out.println(longUserMap);
    
    • 1
    • 2

    {1=Jone, 2=Jack, 3=Tom, 4=Sandy, 5=Billie, 6=test}

    3、group

    可以按照字段进行分组

    比如,按照年龄进行分组

    使用group方法,第二个参数是待分组字段

            Map<Integer, List<User>> group = SimpleQuery.group(new LambdaQueryWrapper<User>(), User::getAge);
            System.out.println(group);
    
    • 1
    • 2

    五、逻辑删除

    逻辑删除的目的是方便做统计,状态可恢复(用户的可用与禁用)

    首先数据库中的表字段添加status

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    并使用默认值1,表示未删除

    然后Java实体类添加对应的属性

        @TableLogic(value = "1", delval = "0") // value是默认值,即未删除,delVal是删除时的值
        private Integer status;
    
    • 1
    • 2

    测试

        @Test
        void testLogicDelete() {
            userMapper.deleteById(4L);
            System.out.println(userMapper.selectList(null));
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    首先执行

    UPDATE user SET status=0 WHERE id=? AND status=1
    
    • 1

    将id为4的user的status更新为0,表示删除

    然后再次查询

    SELECT id,name,age,email,status FROM user WHERE status=1
    
    • 1

    查询时都会看status是否为1(未删除)

    可以全局配置逻辑删除字段及其值

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    六、通用枚举

    例如,对性别字段使用枚举

    0–女性, 1–男性

    数据库表中添加字段

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    Java中编写枚举

    public enum GenderEnum {
        MAN(1, "男"), WOMAN(0, "女");
        @EnumValue // 表示插入到数据库中的是gender,即0或1
        private Integer gender;
        private String genderName;
    
        GenderEnum(Integer gender, String genderName){
            this.gender = gender;
            this.genderName = genderName;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    User属性

    private GenderEnum gender; // 必须是gender,对应0或1
    
    • 1

    测试插入

        @Test
        void testEnum() {
            User user = new User();
            user.setName("zzs");
            user.setGender(GenderEnum.MAN); // 使用枚举值
            userMapper.insert(user);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    测试查询

    System.out.println(userMapper.selectList(null));
    
    • 1

    [User(id=1, name=Jone, age=18, email=test1@baomidou.com, isOnline=null, status=1, gender=MAN), User(id=2,

    输出的gender是MAN

    七、字段处理器

    实体类中使用Map集合作为属性接受前端传递的数据,但是这些数据在数据库中存储时,是使用JSON格式的数据进行存储,JSON本质上是一个字符串,即数据库中的varchar类型。要将实体类的Map类型和数据库中的varchar类型的数据相互转换,可以使用字段处理器来完成。

    首先在实体类中添加一个Map集合

    private Map contact; // 联系方式,可以有两种,即手机号和座机号
    
    • 1

    在数据库中添加对应的表字段contact,设置为varchar类型。

    再次修改实体类,需要添加两个地方

    @TableName(value = "user", autoResultMap = true) 
    
    
    @TableField(typeHandler = FastjsonTypeHandler.class)
    private Map<String, String> contact; // 联系方式,可以有两种,即手机号和座机号
    
    • 1
    • 2
    • 3
    • 4
    • 5

    整体代码如下:

    // 使用字段处理器,需要 autoResultMap = true
    @TableName(value = "user", autoResultMap = true) // 指定表名,如果表名和类名一致,可以省略
    @Data
    public class User implements Serializable { // 实现序列化接口
        @TableId(type = IdType.AUTO)
        private Long id;
        @TableField("name") // 指定表字段名,如果字段名和属性名一致,可以省略
        private String name;
        // @TableField(select = false) // select = false 表示查询时不查询该字段
        private Integer age;
        private String email;
        @TableField(exist = false) // exist = false 表示该字段不是数据库字段,但是可以使用
        private Boolean isOnline;
    
        @TableLogic(value = "1", delval = "0") // value是默认值,即未删除,delVal是删除时的值
        private Integer status;
    
    
        private GenderEnum gender;
    
        // 使用FastjsonTypeHandler
        @TableField(typeHandler = FastjsonTypeHandler.class)
        private Map<String, String> contact; // 联系方式,可以有两种,即手机号和座机号
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    添加fastjson依赖

    <!-- fastjson-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>fastjson</artifactId>
                <version>1.2.31_noneautotype</version>
            </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    测试插入,即Map转JSON

        @Test
        void testMapToJson(){
            User user = new User();
            user.setName("xxx");
            user.setAge(23);
            user.setGender(GenderEnum.MAN);
            // 添加Map集合
            Map<String, String> map = new HashMap<>();
            map.put("phone", "19122345566");
            map.put("tel", "001-123456");
            user.setContact(map);
            userMapper.insert(user);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    在数据库中contact字段存储如下

    {"phone":"19122345566","tel":"001-123456"}
    
    • 1

    测试查询,即JSON转Map

        @Test
        void testJsonToMap(){
            System.out.println(userMapper.selectList(null));
        }
    
    • 1
    • 2
    • 3
    • 4
    contact={phone=19122345566, tel=001-123456}
    
    • 1

    八、自动填充

    有一些属性,比如时间,我们可以设置为自动填充

    在实体类User中添加两个属性

        private Date createTime;
    
        private Date updateTime;
    
    • 1
    • 2
    • 3

    数据库对应字段 ,都是datetime类型

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    需要开启下划线和驼峰映射 map-underscore-to-camel-case: true

    # mybatis-plus配置
    mybatis-plus:
      global-config:
        banner: false
      configuration:
        log-impl: org.apache.ibatis.logging.stdout.StdOutImpl # 打印sql语句
        map-underscore-to-camel-case: true # 下划线和小驼峰映射
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    设置自动填充时间

    写一个工具类MyMetaHandler,定义插入和更新数据时怎么自动填充

    @Component
    public class MyMetaHandler implements MetaObjectHandler {
        @Override
        public void insertFill(MetaObject metaObject) {
            // 插入数据时,两个时间都填充
            setFieldValByName("createTime", new Date(), metaObject);
            setFieldValByName("updateTime", new Date(), metaObject);
        }
    
        @Override
        public void updateFill(MetaObject metaObject) {
            setFieldValByName("updateTime", new Date(), metaObject);
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    在属性上写上注解

        @TableField(fill = FieldFill.INSERT) // 插入时填充
        private Date createTime;
    
        @TableField(fill = FieldFill.INSERT_UPDATE) // 插入或更新时填充
        private Date updateTime;
    
    • 1
    • 2
    • 3
    • 4
    • 5

    设置时区对应

    yml文件中连接数据库修改serverTimezone=Asia/Shanghai

        url: jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
    
    • 1

    数据库中新增查询,设置全局时间

    SET GLOBAL time_zone = '+8:00';
    
    SELECT NOW(); // 测试,看是否与本地时间相同
    
    • 1
    • 2
    • 3

    测试,是否能够实现时间自动填充

    插入新数据时,createTime和updateTime都自动填充

    @Test
    void testFillTime(){
        User user = new User();
        user.setName("nihao");
        user.setAge(23);
        user.setGender(GenderEnum.MAN);
        userMapper.insert(user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    修改数据时,updateTime自动修改

    @Test
    void updateTime(){
        User user = new User();
        user.setId(9L);
        userMapper.updateById(user);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    九、防止全表更新与删除插件

    在实际开发中,全表更新和删除是非常危险的操作,Mybatis-plus提供了一个插件可以防止全表更新。

    在mybatis-plus配置类中添加上

    interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
    
    • 1

    整体代码

    @Configuration
    // @MapperScan("com.system.mybatisplus.mapper") // 如果在启动类上已经配置了,这里就不需要再配置了
    public class MybatisPlusConfig {
        @Bean
        public MybatisPlusInterceptor mybatisPlusInterceptor() {
            MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
            // DbType.MYSQL 表示数据库类型是mysql
            interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
            // 防止全表更新插件
            interceptor.addInnerInterceptor(new BlockAttackInnerInterceptor());
            return interceptor;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    测试

    @Test
    void updateBlock(){
        User user = new User();
        user.setGender(GenderEnum.MAN);
        userMapper.update(user, null);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    以上代码更新时未设置条件,就会导致所有数据的性别改为MAN

    但是设置了防止全表更新的插件后,mybatis-plus会自动报错,抛出异常

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    十、逆向工程-MybatisX插件

    在idea插件市场中找到插件并且安装

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    连接数据库,点击红线处

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    自动生成SQL

    在mapper里面写上方法名,然后选中,再Alt+enter

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    十一、并发问题分析

    • 乐观锁

    乐观锁是通过表字段完成设计的,其核心思想是,在读取的时候不加锁,其他请求依然可以读取到这个数据,在修改的时候判断这个数据是否有被修改过,如果有被修改过,那么本次请求的修改操作失败。

    具体的SQL是这样实现的

    update 表 set 字段 = 新值, version = version + 1 where version = 1
    
    • 1

    这样做不会对数据读取产生影响,并发的效率较高,但是目前看到的数据可能不是真实的数据,是被修改之前的,这在多数情况下是不会产生很大的影响。例如,有时候我们看到某种商品是有库存的,或者都加入到购物车了,但是点进去发现库存不足了。

    • 悲观锁

    悲观锁是在查询的时候就锁定数据,在这次请求未完成之前,不会释放锁。等到这次请求完毕后,再释放锁,释放了锁之后,其他请求才可以对这条数据完成读写。

    这样做能够保证读取到的信息就是当前的信息,保证了信息的准确性,但是并发效率很低。

    所以,实际开发中,使用悲观锁的场景很少,因为需要保持效率。

    乐观锁实现

    首先在数据库中添加字段version,默认值为1

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    然后在实体类User中添加对应的字段

        @Version
        private Integer version;
    
    • 1
    • 2

    在mybatis-plus配置类中添加乐观锁插件

            // 乐观锁插件
            interceptor.addInnerInterceptor(new OptimisticLockerInnerInterceptor());
    
    • 1
    • 2

    测试并发修改

        @Test
        void testLock(){
            // 模拟操作1的查询
            User user1 = userMapper.selectById(9L);
            System.out.println("user1查询结果" + user1);
            // 模拟操作2的查询
            User user2 = userMapper.selectById(9L);
            System.out.println("user2查询结果" + user2);
            // 模拟操作1的修改
            user1.setName("zhangsan");
            userMapper.updateById(user1);
            // 模拟操作2的修改
            user2.setName("lisi");
            userMapper.updateById(user2);
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    结果只有操作1修改成功,说明乐观锁生效

    都能查询到,但是只有操作1修改成功,因为操作1修改时,version未发生变化,但是操作2修改时,version被操作1修改为2了。

    user1查询结果User(id=9, name=nihao, age=23, email=null, isOnline=null, status=1, gender=MAN, contact=null, createTime=Thu Sep 07 08:48:53 CST 2023, updateTime=Thu Sep 07 08:52:48 CST 2023, version=1)
    
    user2查询结果User(id=9, name=nihao, age=23, email=null, isOnline=null, status=1, gender=MAN, contact=null, createTime=Thu Sep 07 08:48:53 CST 2023, updateTime=Thu Sep 07 08:52:48 CST 2023, version=1)
    
    • 1
    • 2
    • 3

    十二、代码生成器

    打开mybatis-plus的官网,找到代码生成器章节

    适用版本:mybatis-plus-generator 3.5.1 及其以上版本,对历史版本不兼容!3.5.1 以下的请参考 代码生成器旧

    首先引入依赖

    <dependency>
        <groupId>com.baomidougroupId>
        <artifactId>mybatis-plus-generatorartifactId>
        <version>最新版本version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    本人使用的mybatis-plus版本

            <dependency>
                <groupId>com.baomidougroupId>
                <artifactId>mybatis-plus-boot-starterartifactId>
                <version>3.5.3.1version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    所以mybatis-plus-generator我也使用3.5.3.1

    写个main方法执行

    FastAutoGenerator.create("url", "username", "password")
        .globalConfig(builder -> {
            builder.author("baomidou") // 设置作者
                .enableSwagger() // 开启 swagger 模式
                .fileOverride() // 覆盖已生成文件
                .outputDir("D://"); // 指定输出目录
        })
        .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.baomidou.mybatisplus.samples.generator") // 设置父包名
                .moduleName("system") // 设置父包模块名
                .pathInfo(Collections.singletonMap(OutputFile.xml, "D://")); // 设置mapperXml生成路径
        })
        .strategyConfig(builder -> {
            builder.addInclude("t_simple") // 设置需要生成的表名
                .addTablePrefix("t_", "c_"); // 设置过滤表前缀
        })
        .templateEngine(new FreemarkerTemplateEngine()) // 使用Freemarker引擎模板,默认的是Velocity引擎模板
        .execute();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    十三、SQL分析打印

    需要查看执行的SQL语句时,以及了解它的执行时间,方便分析是否出现了慢SQL问题,可以使用mybatis-plus提供的SQL分析打印的功能,来获取SQL语句执行的时间。

    首先引入依赖

            
            <dependency>
                <groupId>p6spygroupId>
                <artifactId>p6spyartifactId>
                <version>3.9.1version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    修改yml文件中连接数据库的驱动和URL

    jdbc:p6spy:mysql

    driver-class-name: com.p6spy.engine.spy.P6SpyDriver

    # 配置数据库
    spring:
      datasource:
        username: root
        password: 123456
        url: jdbc:p6spy:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=Asia/Shanghai
        driver-class-name: com.p6spy.engine.spy.P6SpyDriver #com.mysql.cj.jdbc.Driver
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    创建

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    module.log=com.p6spy.engine.logging.P6LogFactory,com.p6spy.engine.outage.P6OutageFactory
    # 自定义日志打印
    logMessageFormat=com.p6spy.engine.spy.appender.SingleLineFormat
    #logMessageFormat=com.p6spy.engine.spy.appender.CustomLineFormat
    #customLogMessageFormat=%(currentTime) | SQL耗时: %(executionTime) ms | 连接信息: %(category)-%(connectionId) | 执行语句: %(sql)
    # 使用控制台记录sql
    appender=com.p6spy.engine.spy.appender.StdoutLogger
    ## 配置记录Log例外
    excludecategories=info,debug,result,batc,resultset
    # 设置使用p6spy driver来做代理
    deregisterdrivers=true
    # 日期格式
    dateformat=yyyy-MM-dd HH:mm:ss
    # 实际驱动
    driverlist=com.mysql.cj.jdbc.Driver
    # 是否开启慢SQL记录
    outagedetection=true
    # 慢SQL记录标准 秒
    outagedetectioninterval=2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    十四、多数据源

    首先添加依赖

            
            <dependency>
                <groupId>com.baomidougroupId>
                <artifactId>dynamic-datasource-spring-boot-starterartifactId>
                <version>3.1.0version>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    spring:
      datasource:
        dynamic:
          primary: master #设置默认的数据源或者数据源组,默认值即为master
          strict: false #严格匹配数据源,默认false. true未匹配到指定数据源时抛异常,false使用默认数据源
          datasource:
            master:
              url: jdbc:mysql://xx.xx.xx.xx:3306/dynamic
              username: root
              password: 123456
              driver-class-name: com.mysql.jdbc.Driver # 3.2.0开始支持SPI可省略此配置
            slave_1:
              url: jdbc:mysql://xx.xx.xx.xx:3307/dynamic
              username: root
              password: 123456
              driver-class-name: com.mysql.jdbc.Driver
            slave_2:
              url: ENC(xxxxx) # 内置加密,使用请查看详细文档
              username: ENC(xxxxx)
              password: ENC(xxxxx)
              driver-class-name: com.mysql.jdbc.Driver
           #......省略
           #以上会配置一个默认库master,一个组slave下有两个子库slave_1,slave_2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    # 多主多从                      纯粹多库(记得设置primary)                   混合配置
    spring:                               spring:                               spring:
      datasource:                           datasource:                           datasource:
        dynamic:                              dynamic:                              dynamic:
          datasource:                           datasource:                           datasource:
            master_1:                             mysql:                                master:
            master_2:                             oracle:                               slave_1:
            slave_1:                              sqlserver:                            slave_2:
            slave_2:                              postgresql:                           oracle_1:
            slave_3:                              h2:                                   oracle_2:
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    使用 @DS 切换数据源。

    @DS 可以注解在方法上或类上,同时存在就近原则 方法上注解 优先于 类上注解

    注解结果
    没有@DS默认数据源
    @DS(“dsName”)dsName可以为组名也可以为具体某个库的名称
  • 相关阅读:
    Python如何正确将“爬虫数据”以json格式进行保存
    基于VSR-GUI的视频去字幕水印
    java103-字符串概述
    风投机构加持的NFT明星项目,是否值得追逐?
    设计模式之责任链模式
    简易数控直流稳压电压
    理解case when then else end 的使用,基础概念,建表语句,用例讲解
    java毕业设计电商项目mybatis+源码+调试部署+系统+数据库+lw
    第1关:Hbase数据库的安装
    手把手教你uniapp接入聊天IM即时通讯功能-源码分享
  • 原文地址:https://blog.csdn.net/qq_57987156/article/details/132745782