MyBatis-plus学习总结(2) CRUD方法(insert select delete update) 高级查询

Mybatis-plus

mybatis-plus 是在mybatis的基础上做增强不做改变 , 简化了CRUD操作 . 如何体现: 我们新建一个项目试试

  1. 新建springboot项目, 导入依赖

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.4.3</version>
        <relativePath/>
    </parent>
​
    <dependencies>
​
        <dependency>
​
            <groupId>com.baomidou</groupId>
​
            <artifactId>mybatis-plus-boot-starter</artifactId>
​
            <version>3.4.0</version>
​
        </dependency>
​
        <dependency>
​
            <groupId>com.alibaba</groupId>
​
            <artifactId>druid-spring-boot-starter</artifactId>
​
            <version>1.1.17</version>
​
        </dependency>
​
        <dependency>
​
            <groupId>mysql</groupId>
​
            <artifactId>mysql-connector-java</artifactId>
​
            <version>8.0.22</version>
​
        </dependency>
​
        <dependency>
​
            <groupId>org.springframework.boot</groupId>
​
            <artifactId>spring-boot-starter-test</artifactId>
​
        </dependency>
​
        <dependency>
​
            <groupId>org.projectlombok</groupId>
​
            <artifactId>lombok</artifactId>
​
            <version>1.18.16</version>
​
            <scope>provided</scope>
​
        </dependency>
​
    </dependencies>
  1. 配置数据库四要素, 注: 需要加上时区配置, 因为加入的mysql依赖版本是8的

#mysql
​
spring.datasource.url=jdbc:mysql://localhost/mybatis-plus?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8
​
spring.datasource.username=root
​
spring.datasource.password=admin
​
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
​
# 配置slq打印日志
​
logging.level.cn.wolfcode.mp.mapper=debug
  1. 编写domain和mapper

@Setter
@Getter
@ToString
public class Employee {
    private Long id;
    private String name;
    private String password;
    private String email;
    private int age;
    private int admin;
    private Long deptId;
}
/**
 *  mybatis-plus 映射接口自定义
 *  1. 自定义一个接口, 继承 BaseMapper
 *  2. 指定接口泛型: 当前映射接口操作实体对象 : Employee
 *
 *  自定义EmployeeMapper接口没有自定义crud方法 为什么测试类可以调用
 *    EmployeeMapper接口继承BaseMapper接口, name就继承父接口定义所有能继承的方法
 *
 *  没有写crud的sql语句, 为什么测试类中可以使用crud操作呢
 *     crud操作, 核心是拼接sql, 而拼接的核心是 : 表, 列, 参数, 关键字
 *     以查询数据为例子, select id,name,password,email,age,admin,deptId from employee
 *     要拼接表: employee
 *     关键词: select
 *       参数: 无  (*)
 *
 *
 *    执行查询mapper映射方法: employeeMapper.selectList(null);
 *    根据上面分析发现, sql拼接核心其实只需要解析出 要拼接的列, 跟拼接的表即可
 *    mybatis-plus 怎么解析 列 , 表 呢?
 *
 *    mybatis-plus 在启动并初始化时 , 会解析所有的mapper映射接口, 获取该接口上面指定的泛型操作实体泛型对象 :
 *    比如 :  Employee , 获取该泛型字节码对象
 *      然后通过字节码对象解析该类的类名, 该类的字段名
 *      以 employee 为例子 :
 *          Employee.class
 *         类名: Employee
 *         字段名: id,name,password,email,age,admin,deptId
 *    之后mybatis-plus默认,类名就是表名, 字段名就是表的列
 *    最终, sql语句就可以拼接成功
 *
 *
 */
public interface EmployeeMapper extends BaseMapper<Employee> {
}
  1. 编写启动类

@SpringBootApplication
@MapperScan("cn.wolfcode.mp.mapper")
public class App {
    public static void main(String[] args) {
        SpringApplication.run(App.class,args);
    }
}
  1. 编写测试类 贴SpringBootTest注解

@SpringBootTest
public class CRUDTest {
    @Autowired
    private EmployeeMapper employeeMapper;
    @Test
    public void testSave(){
        Employee employee = new Employee();
        employee.setAdmin(1);
        employee.setAge(18);
        employee.setDeptId(1L);
        employee.setEmail("dafei@wolfcode");
        employee.setName("dafei");
        employee.setPassword("111");
        employeeMapper.insert(employee);
    }
    @Test
    public void testUpdate(){
        Employee employee = new Employee();
        employee.setId(1327139013313564673L);
        employee.setAdmin(1);
        employee.setAge(18);
        employee.setDeptId(1L);
        employee.setEmail("dafei@wolfcode.cn");
        employee.setName("xiaofei");
        employee.setPassword("111");
        employeeMapper.updateById(employee);
    }
    @Test
    public void testDelete(){
        employeeMapper.deleteById(1327139013313564673L);
    }
    @Test
    public void testGet(){
        System.out.println(employeeMapper.selectById(1327139013313564673L));
    }
    @Test
    public void testList(){
        System.out.println(employeeMapper.selectList(null));
    }
}

MP中有几个常用的注解需要理解

  1. TableName 表名注解 , 用于指定当前实体类映射到数据库的那张表, 默认是根实体类的类名一致

  2. TableField 字段注解 , 用于指定当前属性映射到数据库表的哪一列, 默认是根属性名一致

@TableField(value="employename",exist = false)
    private String name;

  1. Tableld 主键注解 , 用于指定当前属性为数据库表的主键

    type = IdType.AUTO 指定id生成算法,默认雪花算法

 @TableId(value="id", type=IdType.AUTO)
    private Long id;

Mybatis-plus的mapper的通用方法

insert:

  • int insert(T entity); 新增, 传入的参数为实体对象

update:

  • updateById()

在使用updateById时, 可能会出现设置了ID, 但是其他值未设置, 此时此属性就不会参与sql拼接, 就可能会出现修改完之后, 某个属性值调用修改方法前有值, 但调用方法后这个值变空了.为解决本情况, 有几种解决方式 :

     mybatis-plus sql拼接规则:
     1. 参数实体属性值如果为null , 不参与sql拼接
     2. 如果参数实体属性类型是8基本类型,有默认值, mybatis-plus认为有属性值, 参与sql拼接
     基本类型默认值的存在, 导致基本属性参与sql拼接 容易出现参数丢失问题(列值丢失)
     方案1: 将基本类型改为包装类型
     方案2: 先查询单个,再设置值(修改) 然后更新
     方案3: 使用update(null,wrapper) 方法操作 -> 部分字段更新

可以选择使用 update方法

  • update

@Test
    public void testUpdate2(){
        // 条件构造器: 暂时理解 where条件拼接逻辑
        UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
        wrapper.eq("id",20);  // 等价 where id= 20
        wrapper.set("name","fengjianbin");  // 等于set name = fengjianbin
​
        employeeMapper.update(null,wrapper);
    }

如何选择

    /**
     * updateById:
     * 1> sql的where条件id时, 使用
     * 2> 全量字段更新时, 使用
     *
     * update(null,wrapper)
     * 1> sql 的where条件不一定是id时, eg:where age = 19
     * 2> 部分字段更新时使用
     */

delete

  • deleteById() 根据ID删除信息

    // 需求: 根据ID=21删除员工信息
    @Test
    public void testDeleteById(){
        employeeMapper.deleteById(21L);
    }

  • deleteBatchIds(idList) 删除List集合中所有ID的信息

    // 需求 : 批量删除id=20 19 的员工信息
    // 底层: where id in (?,?)
    @Test
    public void testDeleteBatchId(){
        employeeMapper.deleteBatchIds(Arrays.asList(20L,19L));
    }

  • deleteByMap(map) 删除集合里满足该集合的所有条件的信息

    // 需求: 删除name=fengjianbin 并且age=18的员工信息
    // 底层 where name = ? and age = ?
    @Test
    public void testDeleteBYMap(){
        Map<String,Object> map = new HashMap<>();
        map.put("name","fengjianbin");
        map.put("age",18);
        employeeMapper.deleteByMap(map);
    }

  • delete(wrapper) 删除满足条件构造器条件的信息

    // 需求: 删除name=fengjianbin 并且age=18的员工信息
    @Test
    public void testDelete(){
        // UpdateWrapper--- 更新操作使用
        // QueryWrapper --- 查询/条件操作使用
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        wrapper.eq("name","fengjianbin");
        wrapper.eq("age",18);
​
        employeeMapper.delete(wrapper);
    }

select

  • selectById

// 需求: 查询 id =1 的员工信息
    @Test
    public void testSelectById(){
        Employee employee = employeeMapper.selectById(1L);
        System.err.println("employee = " + employee);
    }

  • selectBatchIds ( 查找数组中所有ID的信息, 如果是LONG类型, 可以不加L)

// 需求: 查询id=1 , id=2的员工数据
// 底层: SELECT * FROM employee WHERE id IN ( ? , ? )
    @Test
    public void testSelectBatchIds(){
        List<Employee> employees = employeeMapper.selectBatchIds(Arrays.asList(1L, 2L));
        System.out.println("employees = " + employees);
    }

  • selectByMap 查询满足map中的所有信息

// 需求: 查询age=19 , name=fengjianbin的员工数据
    // 底层: where age = ? and name = ?
    @Test
    public void testSelectByMap(){
        Map<String,Object> map = new HashMap<>();
        map.put("age",19);
        map.put("name","fengjianbin");
        List<Employee> employees = employeeMapper.selectByMap(map);
        System.out.println("employees = " + employees);
    }

  • selectCount (null / wrapper) 传null,则表示查询所有数据的数量, 传值则代表查询符合条件的数据数量

    // 需求: 查询所有员工的个数
    @Test
    public void testSelectByCount(){
​
        Integer count = employeeMapper.selectCount(null); // 没有条件的查询( 查所有个数 )
        System.out.println("count = " + count);
    }
    // 需求: 查询 age = 20的员工的个数
    @Test
    public void testSelectByCount1(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        wrapper.eq("age",20);
        employeeMapper.selectCount(wrapper);
    }

  • selectList 如果加wrapper表示条件查询, 如果null表示查询所有

SELECT id,name,password,email,age,admin,dept_id FROM employee WHERE (name = ? AND age = ?)

// 需求: 查询所有的员工信息, 返回List<Employee>
    @Test
    public void testSelectList(){
        List<Employee> employees = employeeMapper.selectList(null); // 如果加wrapper表示条件查询, 如果null表示查询所有
        for (Employee employee : employees) {
            System.out.println("employee.toString() = " + employee.toString());
        }
    }

  • selectMap 查询满足条件的信息, 并把每条信息的行列都封装进map中

SELECT id,name,password,email,age,admin,dept_id FROM employee WHERE (name = ? AND age = ?)

@Test
    public void testSelectMap(){
        List<Map<String, Object>> maps = employeeMapper.selectMaps(null);// 如果加wrapper表示条件查询, 如果null表示查询所有
        for (Map<String, Object> map : maps) {
            System.err.println(map);
        }
    }
/**
 *  思考: 什么时候用 selectList 什么时候使用:selectMaps
 *
 * 查询数据能封装成domain使用selectList
 * 查询数据不能封装成domain使用selectMaps  比如: 项目1里面的报表查询
 */
  • selectPage 用于分页查询

// 需求: 查询第二页员工数据, 每页显示三条(分页返回的数据是实体对象)
    @Test
    public void testSelectPage(){
        // mybatis-plus分页的两个步骤
​
        // 步骤1: 配置分页插件(主配置文件)
​
        // 步骤2: 编写分页代码
        // 参数1: current 当前页
        // 参数2: size 每页显示条数
        IPage<Employee> page = new Page<>(2,3);
​
        // 参数1: 分页参数对象
        // 分数2: wrapper条件对象(条件构造器)
        IPage<Employee> page1 = employeeMapper.selectPage(page, null);
​
        System.out.println(page1 == page); // 内存地址:true
        System.out.println("当前页:" + page.getCurrent());
        System.out.println("每页显示条数" + page.getSize());
        System.out.println("总页数:" + page.getPages());
        System.out.println("总数:" + page.getTotal());
        System.out.println("当前页数据:" + page.getRecords());
    }
​
    /*public IPage<Employee> selectPage(IPage<Employee> page,QueryWrapper wrapper){
        // 分页逻辑
        return page;
    }*/

Mybatis-plus 的条件构造器wrapper

wrapper: 条件构造抽象类 , 最顶级父类 常用的子类有:

  • QueryWrapper: Entity 对象封装操作类,不是用lambda语法 ->用于定义查询条件操作

  • UpdateWrapper: Update 条件封装,用于Entity对象更新操作 -> 用于更新操作

修改型子类(UpdateWrapper,LambdaUpadateWrapper )特有方法 : set, setSql

// set name =? where id = ?
    @Test
    public void testUpdate(){
        UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
        wrapper.eq("id",21L);
        wrapper.set("name","dafei"); //占位符 (预编译)
​
        employeeMapper.update(null,wrapper);
    }
​
    // set name ="dafei" where id =?
    @Test
    public void testUpdate2(){
        UpdateWrapper<Employee> wrapper = new UpdateWrapper<>();
        wrapper.eq("id",21L);
        wrapper.setSql("name=dafei"); // 拼接sql片段, 直接塞
        employeeMapper.update(null,wrapper);
    }
​
    // set name=? where id=?
    @Test
    public void testUpdate3(){
        LambdaUpdateWrapper<Employee> wrapper = new LambdaUpdateWrapper<>();
        wrapper.eq(Employee::getId,21L);
        wrapper.set(Employee::getName,"dafei");
        employeeMapper.update(null,wrapper);
    }

查询型子类(QueryWrapper,LambdaQueryWrapper ) 特有方法 select

    // select * from employee where name ="dafei" and id = 21
    @Test
    public void testQuery(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        wrapper.eq("name","dafei").eq("id",21l);
        List<Employee> employees = employeeMapper.selectList(wrapper);
    }

用于创建各种类型的wrapper
​
    @Test
    public void testWrappers(){
        //update
        UpdateWrapper<Employee> updateWrapper1 = new UpdateWrapper<>();
        UpdateWrapper<Employee> updateWrapper2 = Wrappers.<Employee>update()
        LambdaUpdateWrapper<Employee> lambdaUpdateWrapper1 = new LambdaUpdateWrapper<>();
        LambdaUpdateWrapper<Employee> lambdaUpdateWrapper2 = Wrappers.<Employee>lambdaUpdate();
        //UpdateWrapper -->LambdaUpdateWrapper
        LambdaUpdateWrapper<Employee> lambdaUpdateWrapper3 = updateWrapper1.lambda();
        //select
        QueryWrapper<Employee> QueryWrapper1 = new QueryWrapper<>();
        QueryWrapper<Employee> QueryWrapper2 = Wrappers.<Employee>query();
        LambdaQueryWrapper<Employee> lambdaQueryWrapper1 = new LambdaQueryWrapper<>();
        LambdaQueryWrapper<Employee> lambdaQueryWrapper2 = Wrappers.<Employee>lambdaQuery();
        //QueryWrapper -->LambdaQueryWrapper
        LambdaQueryWrapper<Employee> lambdaQueryWrapper3 = QueryWrapper1.lambda();
​
    }

高级查询 :

  • 列查询

        //   需求: 查询所有员工, 返回员工name,age列
        // SELECT id,name,password,email,age,admin,dept_id FROM employee
@Test
    public void testQuery1(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
​
         wrapper.select("name","age"); // 挑选显示列
​
​
        List<Employee> employees = employeeMapper.selectList(wrapper);
​
    }

  • 排序

    // 需求:查询所有员工信息按age正序排, 如果age一样, 按id正序排
    @Test
    public void testQuery2(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
//        wrapper.orderByAsc("age");
//        wrapper.orderByDesc("id");
​
        // 参数1 : 满足什么条件之后, 才执行排序逻辑
        // 参数2 : 是否正序排列
        // 参数3 : 排序的列
        wrapper.orderBy(true,true,"age");
​
        List<Employee> employees = employeeMapper.selectList(wrapper);
    }
​

  • 分组查询 having优先级最低

// 需求: 以部门ID 进行分组查询, 查各个部门员工个数
    // select dept_id count(id) count FROM employee group by dept_id
​
    @Test
    public void testQuery3(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
​
        wrapper.select("dept_id","count(id) count");
        wrapper.groupBy("dept_id");
​
        List<Map<String, Object>> maps = employeeMapper.selectMaps(wrapper);
        maps.forEach(System.out::println);
​
    }
​
// 需求: 以部门ID 进行分组查询, 查每个部门员工个数, 将大于3人的部门过滤出来
    @Test
    public void testQuery4(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
​
        wrapper.select("dept_id","count(id) count");
        wrapper.groupBy("dept_id");
      //  wrapper.having("count > 3");  //sql片段
        wrapper.having("count > {0}",3); // 占位符的方式
​
        List<Map<String, Object>> maps = employeeMapper.selectMaps(wrapper);
        maps.forEach(System.out::println);
    }

  • allEq ( 查询满足传入的所有条件的信息 )

    @Test
    public void testQuery5(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        Map<String,Object> map = new HashMap<>();
        map.put("name","dafei");
        map.put("age",18);
​
        // 所有条件必须都相等
        wrapper.allEq(map); // where (name =? and age =? )
        //wrapper.eq("age",18); // where (age =? )
        //wrapper.ne("age",18); // where ( age <> ? )
​
        employeeMapper.selectList(wrapper);
    }

  • beween , notBetween (字段, val1 ,val2) 指定某列的数值区间

// 需求: 查询年龄介于18-30岁的员工信息
    @Test
    public void testQuery6(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
​
        // age between ? and ?
        wrapper.between("age",18,30);
        wrapper.notBetween("age",18,30);
​
        employeeMapper.selectList(wrapper);
    }
  • is null ,not is null 用于查询指定的属性里值为null / 不为null 的信息

// 需求: 查询dept_id 为null 员工信息
    @Test
    public void testQuery7(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
​
        wrapper.isNull("dept_id"); // dept_id is null
        wrapper.isNotNull("dept_id"); // where dept_id is not null
​
        employeeMapper.selectList(wrapper);
    }
  • in

 // 需求: 查询ID 为1,2 的员工信息, 使用 in    where id in (1,2)
    @Test
    public void testQuery8(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
​
        wrapper.in("id",1L,2L);  // where (id in (?,?))
​
        // sql 片段
//        wrapper.inSql("id","1,2"); // where (id IN(1,2))
​
        employeeMapper.selectList(wrapper);
    }
  • 模糊查询 like likeRight likeLeft

// 需求: 查询名字中有fei字样的员工
    @Test
    public void testQuery9(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
​
//        %fei%
        wrapper.like("name","fei");
​
//        fei%
//          wrapper.likeRight("name","fei");
//        %fei
//          wrapper.likeLeft("name","fei");
​
          // wrapper.noLike();
​
        employeeMapper.selectList(wrapper);
    }

  • or and

// 需求: 查询name 含有fei字样, 或者年龄在18到30之间的用户
//select * from employee where name likek '%fei%' or( age >=18 and age <=30)
    @Test
    public void testQuery11(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        
        wrapper.like("name","fei");
        wrapper.or(wp->wp.ge("age",18).le("age",30));
​
        employeeMapper.selectList(wrapper);
    }
​
    // 需求: 查询name含有fei字样的, 并且年龄在小于18或大于30的用户
    //SELECT id,name,password,email,age,admin,dept_id FROM employee WHERE (name LIKE ? AND (age < ? OR age > ?))
    @Test
    public void testQuery12(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
​
        wrapper.like("name","fei")
                .and(wp -> wp.lt("age",18).or().gt("age",30));
        employeeMapper.selectList(wrapper);
    }

注:

/**
     * mybatis-plus 在写sql情况下,如何进行多表关联查询
     * 不能进行多表关联查询,如果硬要处理,只能使用额外sql方法
     *
     */
    //多表查询:查询所有员工数据,同事查询他所在部门信息
    //重点
    @Test
    public void testQuery13(){
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        List<Employee> employees = employeeMapper.selectList(wrapper);
​
        //叫做额外sql方式
        for (Employee employee : employees) {           employee.setDept(departmentMapper.selectById(employee.getDeptId()));
        }
    }
//不然就是还是要使用下面的自定义SQL

    //不建议使用
    //单表
    @Select("select e.* from employee e")
    List<Employee> listByAnnoSingle();
    //多表
@Select("select e.*, d.id d_id, d.name d_name, d.sn d_sn from employee e left join department d on e.dept_id = d.id")
@Results({
        @Result(column="d_id", property = "dept.id"),
        @Result(column="d_name", property = "dept.name"),
        @Result(column="d_sn", property = "dept.sn")
})
List<Employee> listByAnnoJoin();

通用service接口

service接口继承IService <指定泛型, 即操作实体类型> 接口实现类继承ServiceImpl

/**
 *  自定义mybatis-plus 服务层接口
 *  1> 自定义接口 IEmployeeService
 *  2> 继承通用接口IService
 *  3> 指定泛型, 即操作实体类型
 */
public interface IEmployeeService extends IService<Employee> {
​
}
​
/**
 *  自定义mybatis-plus 服务层接口实现类
 *  1> 自定义接口 EmployeeServiceImpl
 *  2> 实现自定义接口 IEmployeeService
 *  3> 继承通用接口IService实现类 ServiceImpl
 *  4> 指定2个泛型: 1. 操作实体类mapper接口  2.操作实体对象类型:employee
 */
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements IEmployeeService {
}

模拟分页

public interface IEmployeeService extends IService<Employee> {
    IPage<Employee> queryPage(EmployeeQuery qo);
}
@Service
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements IEmployeeService {
    @Override
    public IPage<Employee> queryPage(EmployeeQuery qo) {
​
        IPage<Employee> page = new Page<>(qo.getCurrentPage(),qo.getPageSize());
        QueryWrapper<Employee> wrapper = new QueryWrapper<>();
        wrapper.like(StringUtils.hasText(qo.getKeyword()),"name",qo.getKeyword());
​
        return super.page(page,wrapper);
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值