Mybatis-plus:mp框架

1、什么是mp,优点是什么?

Mybatis-plus框架,简称mp。mp是mybatis的一种增强工具【只做增强,不做改变】。

mp是java进阶开发的必学框架,企业开发中很多人说的CRUD,码农几乎不是用mp就是在用mp的路上。
优点:简化开发,提高开发效率,简单易上手;

总之,方便DAO层单表操作,不需要写sql;

2、依赖、配置

废话不多说,直接撸代码,mp就是要经常用才会记得,才会熟练。

1)数据库环境准备【mp_db】
2)创建SpringBoot工程,引入MybatisPlus的启动依赖【mybatis-plus-boot-starter】

现在的企业开发中几乎都是用mp-3,所以选择版本的时候注意看。

        <!--mybatisplus起步依赖-->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
            <version>3.4.1</version>
        </dependency>
   

3)yml配置

#mybatis-plus配置控制台打印完整带参数SQL语句
mybatis-plus:
  configuration:
    log-impl: org.apache.ibatis.logging.stdout.StdOutImpl


4)测试【增、删、改、查】

        具体如何操作,涉及的开发注解和代码结构比较繁多,拆分成下文。

3、必备代码插件

在开始学习之前,idea编辑器需要装上Mybatis-X这个插件。想必大家多多少少也是听过的或者用过的,这里只针对小白或者还在用eclipse的同学。

idea这个软件非常强大,具备很多的插件,这些插件都是一些大牛呕心沥血依据自己的编码经验开发出来的,并且免费开源,为了方便我们日常的便捷开发。而且现在公司企业的java开发也都是用这个软件,不多说装个idea,点击File->setting->plugins->搜索Mybatis-x,安装后重启idea

 这个插件的作用就是依据yml配置和你的数据库源,自动生成基础代码。

4、Mybatis X代码生成器使用

安装好后,怎么使用呢,4张图说明一切

【1】idea编辑器找到DataBase【一般在编辑器的左边】,准备连接数据库,这里我就用mysql举例

【2】 输入数据库端口、账密

host:数据库所在地址,如果在你本地就输入:127.0.0.1或者localhost;如果是在你的服务器上就输入服务器域名;

port:数据库占用的端口

user/password:账户名、密码

输入完成后,点击【Test Connection】,如果出现绿色对勾并显示【Succeeded】表示连接成功

 【3】连接成功,选择想要生成代码的表,右击选择Mybatis-x选项,设置说明如下。设置之后,next

 【4】接下来的设置建议和我一样

        设置生成引擎用mybatis-plus3;

        并且生成的代码自动加上lombok注解(@Data);

        并且需要生成实体的Model对象;

        mapper文件设置不用变,使用插件给你的默认设置

 【5】点击finish后,你的dal工程和Resource就会多出这些文件

 

 5、须知注解

@TableName("表名")

        位于dal层实体类上,绑定当前实体类和数据库表

@TableId

        位于类的成员属性上,绑定当前表的主键,也就是说这个注解在哪个属性上,哪个属性就是主键。通过这个注解还可以通过type属性配置主键的生成策略

@TableField

        位于类的成员属性上,指定 java类的属性 和 数据库字段 的映射【属性名和表列名不一致】,所以如果我们要在实体类中加上一些数据库表不存在的字段,那么记得加上这个注解,并且设置exist=false

        注意:我所说的实体类,值得是java中对应数据库表的类,不是说所有的类都是实体类。一般开发时,类与类之间区分很严谨(业务类、配置类、工具类、请求参数类、响应类、异常类等等),总之实体类值得就是与数据库表绑定的类

        这个注解的作用很强大,里面属性有很多的作用,这里我只列举开发中常用的几个用法,基本上可以掌握这几个用法就足够了。

作用:

【1】当数据库字段和java属性名不一致,起到映射的作用 

【2】设置忽略属性

【3】设置自动填充 (详情可看最后

【4】保存空值(详情可看最后

@TableName("tb_user") // 指定表名
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class User {
    /**
        TODO:1  @TableId: 指定当前表的主键
     *      value: 建立数据库主键字段与实体字段的对应关系
     *      type: 设置主键生成策略,
     *          IdType.NONE/ASSIGN_ID:默认情况下使用雪花算法
     *          IdType.AUTO:使用数据库的主键自增
     *          IdType.INPUT:由用户自己指定主键大小
     *          ASSIGN_UUID:使用UUID生成一个全局唯一字符串
     */
    @TableId(type = IdType.AUTO)
    private Long id;
    private String userName;//数据库:user_name   驼峰映射
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private String password;//数据库:password    名称一样
    /*
        TODO:2 @TableField("表列名") 指定映射关系
			以下情况可以省略:
             [1]名称一样
             [2]数据库字段使用_分割,实体类属性名使用驼峰名称(自动开启驼峰映射)
    */
    @TableField("t_name")
    private String name;//数据库里是t_name,必须用注解映射
    private Integer age;//数据库:password    名称一样
     //TODO:3 @TableField("表列名") 指定增删改查时的忽略属性
    @TableField(exist = false)
    private String email;
}

      

6、基本增删改查-Mapper层

6.1 增:insert(T)

//【1】插入操作:insert()
    @Test
    public void testInsert(){
        User user = User.builder().userName("liuyan")
                .age(23)
                .email("liuyan@163.com")
                .name("柳岩")
                .password("123456")
                .build();
        int i=userMapper.insert(user);
        if (i>0) {
            System.out.println("添加成功!");
        }
    }

6.2 删:deleteById(int id);         deleteBatchIds(List list)         deleteByMap(Map map);       

//【2】删除操作
    //1.根据id删除:deleteById(int i)
    @Test
    public void testDelete01(){
        int id=22;
        int i=userMapper.deleteById(id);
        if(i>0)
            System.out.println("删除成功!");
    }
    //2.根据id集合删除:deleteBatchIds(List list)
    @Test
    public void testDelete02(){
        ArrayList<Integer> listId = new ArrayList<>();
        Collections.addAll(listId,23,24,25);//一次性添加多个元素
        int i=userMapper.deleteBatchIds(listId);
        if(i>0)
            System.out.println("删除成功!");
    }
    //3.根据map构成条件删除:deleteByMap(Map map)
    @Test
    public void testDelete03(){
        HashMap<String,Object> map = new HashMap<>();
        //map集合作为删除条件【键=数组库字段名  值=数组库字段值】
        map.put("user_name","liuyan");
        int i=userMapper.deleteByMap(map);
        if(i>0)
            System.out.println("删除成功!");
    }

6.3 改:updateById(T)

只更新实体类中存在的数据,如果对应的属性为null,不更新;
/【3】更改操作:updateById
    //只更新实体类中存在的数据,如果对应的属性为null,不更新;
    @Test
    public void testUpdate(){
        //根据主键更新
        User user=User.builder().id(16L).userName("zhaoqishang").build();
        int i=userMapper.updateById(user);
        if(i>0)
            System.out.println("修改成功!");
        else
            System.out.println("修改失败!");
    }

6.4 查:【分页、逻辑、模糊、排序、限定】

       6.4.1 分页查询(无条件):selectPage(Page,null)
        【1】配置类:分页拦截器    {在主配置类中设置Bean}
@Configuration
@MapperScan("com.itheima.mapper")
public class MpConfig {
    @Bean
    public MybatisPlusInterceptor mybatisPlusInterceptor() {
        //1 注册拦截器
        MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
        //2 指定分页拦截器的类型(相当于之前的插件PageHelp的方言设置)
        PaginationInnerInterceptor paginationInterceptor = new PaginationInnerInterceptor(DbType.MYSQL);
        //3 配置
        // 设置请求的页面大于最大页后操作, true调回到首页,false 继续请求  默认false
        // paginationInterceptor.setOverflow(false);
        // 设置最大单页限制数量,-1不受限制
        paginationInterceptor.setMaxLimit(-1L);
        //4 添加分页拦截器
        interceptor.addInnerInterceptor(paginationInterceptor);
        return interceptor;
    }
}
        【2】先分页、再查询、最后获取分页信息
@Test
    public void testPageSelect(){
        //1、设置分页:每页5条,初始展示第1页【先分页再查询】
        Page<User> page = new Page<>(1,5);
        //2、开始查询; 参数1:页面设置;参数2:条件设置,相当于sql中where语句【此处无条件,null】
        Page<User> userPage = userMapper.selectPage(page,null);
        //3、获取分页查询后的数据信息
        // 获取分页数据信息
        List<User> userList = page.getRecords();
        for (User user : userList) {
            System.out.println(user);
        }
        // 当前页
        long current = page.getCurrent();
        System.out.println("当前页数是:"+current);
        // 每页显示条数
        long size = page.getSize();
        System.out.println("每页数据数量:"+size);
        // 总条数
        long total = page.getTotal();
        System.out.println("当前查询数据总数:"+total);
        // 总页数
        long pages = page.getPages();
        System.out.println("总共页数:"+pages);
    }
        6.4.2 模糊查询:selectList(wrapper) + 逻辑转换符

        逻辑转换符:

@Test
    public void testQueryWrapper(){
        //1、建立QueryWrapper对象  泛型为User
        QueryWrapper<User> wrapper = new QueryWrapper<>();
        //2、条件筛选
        wrapper.like("user_name","伤")
                .eq("password","123456")
                .in("age",19,25,29)
                .orderByDesc("age");
        //3、查询所有:selectList()
        List<User> users = userMapper.selectList(wrapper);
        //4、遍历
        for (User user : users) {
            System.out.println("user = " + user);
        }
    }
解析:QueryWrapper对象相当于sql语句中where条件,但是它的限制——
QueryWrapper查询数据时,需要手写对应表的 数据库字段,
一旦之后更改了数据库的列名,也就是表结构更改后,那么对应的代码修改量也会变大,维护性较差
[改进方案]____:
LamQueryWrapper 查询数据时,用实体类的get()方法 代替 数据库字段
例如:
    eq("id",18)         >>  eq(User::getId,18)
    in("age",(1,2,3))   >>  in(User::getAge,(1,2,3))
//上述查询用LambdaQueryWrapper()完善后
    @Test
    public void testLambdaQueryWrapper(){
        //1、建立QueryWrapper对象  泛型为User
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        //2、条件筛选
        wrapper.like(User::getName,"伤")
                .eq(User::getPassword,"123456")
                .in(User::getAge,19,25,29)
                .orderByDesc(User::getAge);
        //3、查询所有:selectList()
        List<User> users = userMapper.selectList(wrapper);
        //4、遍历
        for (User user : users) {
            System.out.println("user = " + user);
        }
    }
6.4.3 逻辑查询: or  ->  selectList(Wrapper)
 @Test
    public void testLambdaQueryWrapper02(){
        //1、建立QueryWrapper对象  泛型为User
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        //2、条件筛选
        wrapper.like(User::getName,"伤")
                .or()
                .in(User::getAge,19,25,29)
                .orderByDesc(User::getAge);
        //3、查询所有:selectList()
        List<User> users = userMapper.selectList(wrapper);
        //4、遍历
        for (User user : users) {
            System.out.println("user = " + user);
        }
    }
     逻辑转换符or:主动调用or(),说明下一个方法为【或条件】,默认就是and连接!
 
6.4.4 排序查询: orderByAsc() \ orderByDesc() ->  selectList(Wrapper)
@Test
    public void testLambdaQueryWrapper03(){
        //1、建立QueryWrapper对象  泛型为User
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        //2、条件筛选
        wrapper.orderByAsc(User::getAge)
               .orderByDesc(User::getId);
        //3、查询所有:selectList()
        List<User> users = userMapper.selectList(wrapper);
        //4、遍历
        for (User user : users) {
            System.out.println("user = " + user);
        }
    }
6.4.5 限定查询:select() -> selectList(Wrapper)

        默认selectList()查询所有字段,Wrapper可以用select限定查询的字段。

 @Test
    public void testLambdaQueryWrapper04(){
        //1、建立QueryWrapper对象  泛型为User
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        //2、条件筛选(select:只查询User的Id和Name字段)
        wrapper.like(User::getName,"伤")
                .or()
                .in(User::getAge,19,25,29)
                .orderByDesc(User::getAge)
                .select(User::getId,User::getName);
        //3、查询所有:selectList()
        List<User> users = userMapper.selectList(wrapper);
        //4、遍历
        for (User user : users) {
            System.out.println("user = " + user);
        }
    }
6.4.6 分页条件查询:selectPage(Page,LambdaQueryWrapper)
/*
其实就是第【1】个无条件查询中,
将第二个参数null用wrapper代替了,只不过之前没有讲LambdaQueryWrapper
 */
@Test
    public void testLambdaQueryWrapper05(){
        //1、建立QueryWrapper对象和Page对象  泛型为User
        Page<User> page = new Page<>(2,3);//【先分页再查询】
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        //2、条件筛选
        wrapper.like(User::getName,"伤")
                .or()
                .in(User::getAge,19,25,29)
                .orderByDesc(User::getAge)
                .select(User::getId,User::getName);
        //3、分页条件查询:selectPage()
        Page<User> userPage = userMapper.selectPage(page,wrapper);
        //4、获取分页数据信息:getRecords()
        List<User> userList = page.getRecords();
        for (User user : userList) {
            System.out.println(user);
        }
        long current = page.getCurrent();
        System.out.println("当前页数是:"+current);
        // 每页显示条数
        long size = page.getSize();
        System.out.println("每页数据数量:"+size);
        // 总条数
        long total = page.getTotal();
        System.out.println("当前查询数据总数:"+total);
        // 总页数
        long pages = page.getPages();
        System.out.println("总共页数:"+pages);
    }

7、业务增删改查-service层

MP实现Service封装【所有的crud在参数中就已经实现!】
1.定义一个服务扩展接口,该接口继承公共接口IService;
2.定义一个服务实现类,该类继承ServiceImpl<Mapper,Entity>,并实现自定义的扩展接口;

注意事项:
  1.ServiceImpl父类已经注入了UserMapper对象,名称叫做baseMapper,
    所以当前实现类直接可以使用baseMapper完成操作
  2.因为ServiceImpl已经实现了IService下的方法,所以当前服务类没有必要再次实现

主要Api:
增——
save(T):boolean 保存单条操作【增加操作】
saveBatch(Collection<T>):boolean 批量保存操作【批量增加】
删——
removeById(Serializable):boolean 根据id删除
removeByMap(Map<String,Object>):boolean 根据指定条件删除
改——
updateById(T):boolean 根据id更新【如果id不存在,更新失败】
update(Wrapper<T>):boolean 根据wrapper条件更新【wrapper只是筛选条件,要想更新必须set:wrapper.set】
查——
getById(Serializable):T  根据id查询
listByIds(Collection<? extends Serializable> lsit):List<T> 根据id集合查询
getOne(Wrapper<T>):T 根据指定条件,查询符合条件的某一条信息
list(Wrapper<T>):List<T> 根据指定条件,查询所有符合改条件的信息
page(E,Wrapper):E 将E按照条件Wrapper分页查询

目的: 

  • 开发更加快捷,对业务层也进行了封装Service层注入mapper ; 
  • 业务层开发时,提供的接口和实现类,编码更加高效

操作步骤:

1.定义一个服务扩展接口,该接口继承公共接口IService;

public interface UserService extends IService<User> {
}

2.定义一个服务实现类,该类继承ServiceImpl<Mapper,Entity>,并实现自定义的扩展接口;

@Service
//继承在前,实现在后
public class UserServiceImpl 
extends ServiceImpl<UserMapper, User> 
implements UserService{}

使用: 测试类中使用时不需要注入mapper,但是Service。实际应用中,因为业务开发都在Service层中,所以不需要@Autowired注入,只要定义一个Service的对象即可

下面主要Api的测试:

//【1】 save(T):boolean 保存单条操作【增加操作】
    @Test
    public void testUserService01(){
        User user1 = User.builder().name("wangwu1").userName("laowang2").
                email("444@163.com").age(20).password("333").build();
        boolean isSuccess = userService.save(user1);
        System.out.println(isSuccess?"保存成功":"保存失败");
    }
    //【2】saveBatch(Collection<T>):boolean 批量保存操作【批量增加】
    @Test
    public void testUserService02(){
        User user1 = User.builder().name("wangwu2").userName("laowang2").
                email("444@163.com").age(20).password("333").build();
        User user2 = User.builder().name("wangwu3").userName("laowang3").
                email("444@163.com").age(20).password("333").build();
        /*
        Arrays.asList(xx1,xx2,xx3)
            将同类型的对象型数组变成List集合;
            不适用于基本数据类型:int long char String
            不支持add()、remove()、clear()等方法
        总结:Array.asList()得到的集合,只能用来遍历!!!
         */
        boolean isSuccess = userService.saveBatch(Arrays.asList(user1, user2));
        System.out.println(isSuccess?"保存成功":"保存失败");
    }
    //【3】removeById(Serializable):boolean 根据id删除
    @Test
    public void testUserService03(){
        int id=31;
        boolean result = userService.removeById(id);
        System.out.println("result = " + result);
    }
    //【4】removeByMap(Map<String,Object>):boolean 根据指定条件删除
    @Test
    public void testUserService04(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("id","32");
        boolean result = userService.removeByMap(map);
        System.out.println("result = " + result);
    }
    //【5】updateById(T):boolean 根据id更新一条数据【如果id不存在,更新失败】
    @Test
    public void testUserService05(){
        //将id为30的user_name修改为:老王
        User user = User.builder().userName("老王").id(30L).build();
        boolean result = userService.updateById(user);
        System.out.println("result = " + result);
    }
    //【6】update(Wrapper<T>):boolean 根据wrapper条件批量更新【wrapper.set】
    @Test
    public void testUserService06(){
        //将id为{1,3,5}的age修改为:40       LambdaUpdateWrapper实现
        LambdaUpdateWrapper<User> wrapper = new LambdaUpdateWrapper<>();
        wrapper.in(User::getId,Arrays.asList(1l,3l,5l)).set(User::getAge,40);
        boolean result = userService.update(wrapper);
        System.out.println("result = " + result);
    }
    //【7】根据id查询
    @Test
    public void testUserService07(){
        int id=15;
        User user = userService.getById(id);
        System.out.println("user = " + user);
    }
    //【8】listByIds(Collection<? extends Serializable> lsit):List<T> 根据id集合批量查询
    @Test
    public void testUserService08(){
        ArrayList<Integer> list = new ArrayList<>();
        Collections.addAll(list,1,3,5);
        List<User> userList = userService.listByIds(list);
        for (User user : userList) {
            System.out.println("user = " + user);
        }
    }
    //【9】getOne(Wrapper<T>):T 根据指定条件,查询符合条件的某一条信息
    //    ***getOne:sql查询的结果必须为1条或者没有,否则报错 !!!!***
    @Test
    public void testUserService09(){
        //查询年龄<19的用户【必须只有一个,如果有多个用户年龄<19,查询报错!!】
        // LambdaQueryWrapper实现
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.lt(User::getAge, 19);
        User one = userService.getOne(wrapper);
        System.out.println(one);
    }
    //【10】list(Wrapper<T>):List<T> 根据指定条件,查询所有符合改条件的信息
    @Test
    public void testUserService10(){
        //查询id:1~5中,年龄=20的用户
        // LambdaQueryWrapper实现
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        ArrayList<Integer> idList = new ArrayList<>();
        Collections.addAll(idList,1,2,3,4,5);
        wrapper.in(User::getId,idList).eq(User::getAge,20);
        List<User> userList = userService.list(wrapper);
        for (User user : userList) {
            System.out.println("user = " + user);
        }
    }
    //【11】page(E,Wrapper):E 将E按照条件Wrapper分页查询    先分页,在查询
    @Test
    public void testUserService11(){
        //1.Page分页
        Page<User> userPage = new Page<>(1, 4);
        //2.Wrapper条件筛选
        LambdaQueryWrapper<User> wrapper = new LambdaQueryWrapper<>();
        wrapper.lt(User::getAge,35);
        //3.查询
        Page<User> page = userService.page(userPage, wrapper);
        //4.获取分页信息
        System.out.println(page.getRecords());//记录内容
        System.out.println(page.getPages());//总页数
        System.out.println(page.getTotal());//当前查询到的记录数量
    }

8、MybatisPlus配置自动填充

应用场景:插入或者更新数据时,希望有些字段可以自动填充数据,比如密码、version等。例如:初始注册的用户,密码默认都是123456
怎么用?【@TableField的fill属性+配置】
    【1】编写配置类,实现MetaObjectHandler接口

如图:我想给每个实体类的通用字段create_time、create_by、update_time、update_by自动填充

填充规则:新增【重写insertFill方法】or修改【重写updateFill方法】某条记录的时候

注意:我这里对于create_time、update_time的填充是当前时间;对于create_by、update_by的填充是用自己封装的工具类SecurityUtils来获取当前操作用户的信息进行填充

@Component
public class CommonMetaObjectHandler implements MetaObjectHandler {

    // 插入操作时,自动填充
    @Override
    public void insertFill(MetaObject metaObject) {
        Date now = new Date();
        this.strictInsertFill(metaObject, "deleteFlag", Boolean.class, Boolean.FALSE);
        this.strictInsertFill(metaObject, "createTime", Date.class, now);
        this.strictInsertFill(metaObject, "createBy", String.class, SecurityUtils.getUserName());
        this.strictInsertFill(metaObject, "updateTime", Date.class, now);
        this.strictInsertFill(metaObject, "updateBy", String.class, SecurityUtils.getUserName());
    }

    //  更新操作时,自动填充
    @Override
    public void updateFill(MetaObject metaObject) {
        Date now = new Date();
//        this.strictUpdateFill(metaObject, "updateTime", Date.class, now);
//        this.strictUpdateFill(metaObject, "updateBy", String.class, SecurityUtils.getUserName());
        this.setFieldValByName("updateTime", now, metaObject);
        this.setFieldValByName("updateBy", SecurityUtils.getUserName(), metaObject);
    }
}
   【2】在实体类的需要自动填充的字段上添加注解:
        @TableField并设置属性fill,fill本质是一个枚举四种权限————
        DEFAULT,默认不处理
        INSERT,插入时填充字段
        UPDATE,更新时填充字段
        INSERT_UPDATE,插入和更新时填充字段【常用!】
    

9、如何保留空值修改

有时候前端传参的时候,某些字段会给一个空值或者null,但是mybatis修改数据的时候对于null或者为空的字段会进行忽略,这样在修改的时候调用updateById等方法时会忽略到这个条件,导致修改结果不符合预期。

举例说明:

        现在有一条记录表示余额的money字段为100,year字段为1;

        前端请求传值的时候money为null表示余额清0,year为2,由于前端没有对money传值,那么mybatis在更新只会将year更新为2,而money仍然为100。这显然与结果不符合,那么这个时候就需要在实体类的money字段上添加一个注解来避免这种情况。

【当然,也可以在业务层做阻断,如果前端传null,就初始化为0,这种解决方式也可以】

        @TableField(updateStrategy = FieldStrategy.IGNORED)

        

        
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值