【MybatisPlus】条件构造器Wrapper、分页查询、自定义SQL、Service层接口、代码生成器

时间:2023-02-16 19:09:16

MybatisPlus基础篇

(目录)


1.条件构造器Wrapper

1.1 概述

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

​ 其继承体系如下:

【MybatisPlus】条件构造器Wrapper、分页查询、自定义SQL、Service层接口、代码生成器

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

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

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

​ 完整的AbstractWrapper方法可以参照:https://baomidou.com/guide/wrapper.html#abstractwrapper

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

QueryWrapper,UpdateWrapper,【LambdaQueryWrapper】


1.2 常用AbstractWrapper方法

  • eq:equals,等于

  • gt:greater than ,大于 >

  • ge:greater than or equals,大于等于≥

  • lt:less than,小于<

  • le:less than or equals,小于等于≤

  • between:相当于SQL中的BETWEEN

  • like:模糊匹配。like("name","黄"),相当于SQL的name like '%黄%'

  • likeRight:模糊匹配右半边。likeRight("name","黄"),相当于SQL的name like '黄%'

  • likeLeft:模糊匹配左半边。likeLeft("name","黄"),相当于SQL的name like '%黄'

  • notLike:notLike("name","黄"),相当于SQL的name not like '%黄%'

  • isNull

  • isNotNull

  • and:SQL连接符AND

  • or:SQL连接符OR

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

  • groupBy: groupBy("id","name")相当于 group by id,name

  • orderByAsc :orderByAsc("id","name")相当于 order by id ASC,name ASC

  • orderByDesc :orderByDesc ("id","name")相当于 order by id DESC,name DESC

示例一

SQL语句如下:

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

如果用Wrapper写法如下:

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

示例二

SQL语句如下:

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

如果用Wrapper写法如下:

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

示例三

SQL语句如下:

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

如果用Wrapper写法如下:

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

1.3 常用QueryWrapper方法

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

示例一

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

SQL语句如下:

SELECT 
	id,user_name
FROM 
	USER 

MP写法如下:

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

示例二

select(Class<T> entityClass, Predicate<TableFieldInfo> predicate)

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

SQL语句如下:

SELECT 
	id,user_name
FROM 
	USER 

MP写法如下:

    @Test
    public void testSelect02(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>();
        queryWrapper.select(User.class, new Predicate<TableFieldInfo>() {
            @Override
            public boolean test(TableFieldInfo tableFieldInfo) {
                return "user_name".equals(tableFieldInfo.getColumn());
            }
        });
        List<User> users = userMapper.selectList(queryWrapper);
        System.out.println(users);
    }

示例三

select(Predicate<TableFieldInfo> predicate)

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

SQL语句如下:

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

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

MP写法如下:

    @Test
    public void testSelect03(){
        QueryWrapper<User> queryWrapper = new QueryWrapper<>(new User());
        queryWrapper.select(new Predicate<TableFieldInfo>() {
            @Override
            public boolean test(TableFieldInfo tableFieldInfo) {
                return !"address".equals(tableFieldInfo.getColumn());
            }
        });
        List<User> users = userMapper.selectList(queryWrapper);
        System.out.println(users);
    }

1.4 常用UpdateWrapper方法

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

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

示例

SQL语句如下:

UPDATE 
	USER
SET 
	age = 99
where 
	id > 1

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

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

1.5 Lambda条件构造器

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

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

示例

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

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

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

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

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

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

2.自定义SQL

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

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

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

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

2.0 准备工作

①准备数据

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

/*Data for the table `orders` */

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

②创建实体类

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Orders  {

    private Long id;

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

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

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

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

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

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

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


}

2.1 Mybatis方式

①定义方法

​ 在Mapper接口中定义方法

public interface UserMapper extends BaseMapper<User> {

    User findMyUser(Long id);
}

②创建xml

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

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

​ 创建对应的xml映射文件

③在xml映射文件中编写SQL

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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sangeng.mapper.UserMapper">

    <select  resultType="com.sangeng.domian.User">
       select * from user where id = #{id} 
    </select>
</mapper>

2.2 Mybatis方式结合条件构造器

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

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

添加Warpper类型的参数,并且要注意给其指定参数名。

public interface UserMapper extends BaseMapper<User> {

    User findMyUserByWrapper(@Param(Constants.WRAPPER) Wrapper<User> wrapper);
}

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

    <select  resultType="com.sangeng.domian.User">
        select * from user ${ew.customSqlSegment}
    </select>

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


3.分页查询

3.1 基本分页查询

①配置分页查询拦截器

@Configuration
public class PageConfig {

    /**
     * 3.4.0之前的版本
     * @return
     */
   /* @Bean
    public PaginationInterceptor paginationInterceptor(){
        return  new PaginationInterceptor();
    }*/

    /**
     * 3.4.0之后版本
     * @return
     */
   @Bean
   public MybatisPlusInterceptor mybatisPlusInterceptor(){
       MybatisPlusInterceptor mybatisPlusInterceptor = new MybatisPlusInterceptor();
       mybatisPlusInterceptor.addInnerInterceptor(new PaginationInnerInterceptor());
       return mybatisPlusInterceptor;
   }
}

②进行分页查询

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

3.2 多表分页查询

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

示例

需求

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

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

增加一个userName属性

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Orders  {
	//省略无关代码
    private String userName;
}
实现

①定义接口,定义方法

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

public interface OrdersMapper extends BaseMapper<Orders> {

    IPage<Orders> findAllOrders(Page<Orders> page);
}

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

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.sangeng.mapper.OrdersMapper">
    <select  resultType="com.sangeng.domian.Orders">
        SELECT
            o.*,u.`user_name`
        FROM
            USER u,orders o
        WHERE
            o.`user_id` = u.`id`
    </select>
</mapper>

然后调用方法测试即可

    @Autowired
    private OrdersMapper ordersMapper;
    @Test
    public void testOrdersPage(){
        Page<Orders> page = new Page<>();
        //设置每页大小
        page.setSize(2);
        //设置当前页码
        page.setCurrent(2);
        ordersMapper.findAllOrders(page);
        System.out.println(page.getRecords());
        System.out.println(page.getTotal());
    }

4.Service 层接口

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

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

4.1 基本使用

4.1.1 改造前

定义接口

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

定义实现类

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

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

4.1.2 改造后

接口

public interface UserService extends IService<User> {

}

实现类

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

测试

    @Autowired
    private UserService userService;


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

4.2自定义方法

public interface UserService extends IService<User> {


    User test();
}

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

    @Autowired
    private OrdersMapper ordersMapper;

    @Override
    public User test() {
        UserMapper userMapper = getBaseMapper();
        List<Orders> orders = ordersMapper.selectList(null);
        User user = userMapper.selectById(3);
        //查询用户对于的订单
        QueryWrapper<Orders> wrapper = new QueryWrapper<>();
        wrapper.eq("user_id",3);
        List<Orders> ordersList = ordersMapper.selectList(wrapper);
        return user;
    }
}

5.代码生成器

当有一个新的业务实现时,对接口的功能实现上,我们通常来说需要构建下面的信息:

  • PO类

数据库表和实体类的映射 Java Bean。

  • DAO层

需要编写接口 Mapper ,接口 Mapper 需要去继承 MP 中的 BaseMapper 接口。

  • Service层

编写 Service 层接口和实现类。业务接口需要去继承 MP 中的 IService,业务实现类需要继承 MP 中的 ServiceImpl 和 实现业务接口。

  • Controller层

编写 Controller 并标注 Spring MVC 中的相关注解。

​ 从上面的各类代码中可以放下,代码都是模板性的,如果用手工copy、修改的方式来实现,太烦人也没效率,而这时就是代码生成器小展身手的时候,使用代码生成器生成模板性的代码,减少手工操作的繁琐,集中精力在业务开发上,提升开发效率。


①添加依赖

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

②生成

​ 修改相应配置后执行以下代码即可


public class GeneratorTest {
	@Test
	public void generate() {
		AutoGenerator generator = new AutoGenerator();

		// 全局配置
		GlobalConfig config = new GlobalConfig();
		String projectPath = System.getProperty("user.dir");
		// 设置输出到的目录
		config.setOutputDir(projectPath + "/src/main/java");
		config.setAuthor("sangeng");
		// 生成结束后是否打开文件夹
		config.setOpen(false);

		// 全局配置添加到 generator 上
		generator.setGlobalConfig(config);

		// 数据源配置
		DataSourceConfig dataSourceConfig = new DataSourceConfig();
		dataSourceConfig.setUrl("jdbc:mysql://localhost:3306/mp_db?characterEncoding=utf-8&serverTimezone=UTC");
		dataSourceConfig.setDriverName("com.mysql.cj.jdbc.Driver");
		dataSourceConfig.setUsername("root");
		dataSourceConfig.setPassword("root");

		// 数据源配置添加到 generator
		generator.setDataSource(dataSourceConfig);

		// 包配置, 生成的代码放在哪个包下
		PackageConfig packageConfig = new PackageConfig();
		packageConfig.setParent("com.sangeng.mp.generator");

		// 包配置添加到 generator
		generator.setPackageInfo(packageConfig);

		// 策略配置
		StrategyConfig strategyConfig = new StrategyConfig();
		// 下划线驼峰命名转换
		strategyConfig.setNaming(NamingStrategy.underline_to_camel);
		strategyConfig.setColumnNaming(NamingStrategy.underline_to_camel);
		// 开启lombok
		strategyConfig.setEntityLombokModel(true);
		// 开启RestController
		strategyConfig.setRestControllerStyle(true);
		generator.setStrategy(strategyConfig);
		generator.setTemplateEngine(new FreemarkerTemplateEngine());

        // 开始生成
		generator.execute();
	}
}

6.MybatisX 快速开发插件

MybatisX 是一款基于 IDEA 的快速开发插件,为效率而生。

安装方法:打开 IDEA,进入 File -> Settings -> Plugins -> Browse Repositories,输入 mybatisx 搜索并安装。

功能:

  • Java 与 XML 调回跳转
  • Mapper 方法自动生成 XML

【MybatisPlus】条件构造器Wrapper、分页查询、自定义SQL、Service层接口、代码生成器