MyBatis-Plus 之Lambda条件构造器
首先创建一个数据库表,如下图所示:
然后创建一个Spring Boot项目,pom.xml和配置如下:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.kaven</groupId>
<artifactId>mybatis-plus</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.4.0</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.49</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
spring:
application:
name: mybatis-plus
datasource:
driver-class-name: com.mysql.jdbc.Driver
username: root
password: ITkaven@123
url: jdbc:mysql://127.0.0.1:3306/test?characterEncoding=utf-8&useSSL=false
server:
port: 8085
logging:
level:
root: warn
com.kaven.mybatisplus.dao: trace
pattern:
console: '%p%m%n'
mybatis-plus:
mapper-locations: classpath:mappers/*.xml
实体类User:
package com.kaven.mybatisplus.entity;
import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
@TableName("user")
@Data
public class User {
@TableId
private String id;
@TableField("username")
private String username;
@TableField("password")
private String password;
@TableField("age")
private Integer age;
/**
* 使用 @TableField(exist = false) ,表示该字段在数据库中不存在 ,所以不会插入数据库中
* 使用 transient 、 static 修饰属性也不会插入数据库中
*/
@TableField(exist = false)
private String phone;
}
Mapper接口UserMapper:
package com.kaven.mybatisplus.dao;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.kaven.mybatisplus.entity.User;
import org.springframework.stereotype.Component;
@Component
public interface UserMapper extends BaseMapper<User> {}
启动类:
package com.kaven.mybatisplus;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@MapperScan(basePackages = "com.kaven.mybatisplus.dao")
public class AppRun {
public static void main(String[] args) {
SpringApplication.run(AppRun.class , args);
}
}
@MapperScan(basePackages = "com.kaven.mybatisplus.dao")
这个一定要加上。
我们先在数据库中添加几行数据,方便演示。
这里介绍两种Lambda
条件构造器。
LambdaQueryWrapper<T>
创建LambdaQueryWrapper<T>
实例的三种方法:
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper();
LambdaQueryWrapper<User> lambdaQueryWrapper1 = new QueryWrapper<User>().lambda();
// 官方建议下面这种方法
LambdaQueryWrapper<User> lambdaQueryWrapper2 = Wrappers.lambdaQuery();
LambdaQueryWrapper<T>
类源码:
package com.baomidou.mybatisplus.core.conditions.query;
import com.baomidou.mybatisplus.core.conditions.AbstractLambdaWrapper;
import com.baomidou.mybatisplus.core.conditions.SharedString;
import com.baomidou.mybatisplus.core.conditions.segments.MergeSegments;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.metadata.TableInfoHelper;
import com.baomidou.mybatisplus.core.toolkit.ArrayUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.function.Predicate;
/**
* Lambda 语法使用 Wrapper
*
* @author hubin miemie HCL
* @since 2017-05-26
*/
@SuppressWarnings("serial")
public class LambdaQueryWrapper<T> extends AbstractLambdaWrapper<T, LambdaQueryWrapper<T>>
implements Query<LambdaQueryWrapper<T>, T, SFunction<T, ?>> {
/**
* 查询字段
*/
private SharedString sqlSelect = new SharedString();
/**
* 不建议直接 new 该实例,使用 Wrappers.lambdaQuery(entity)
*/
public LambdaQueryWrapper() {
this((T) null);
}
/**
* 不建议直接 new 该实例,使用 Wrappers.lambdaQuery(entity)
*/
public LambdaQueryWrapper(T entity) {
super.setEntity(entity);
super.initNeed();
}
/**
* 不建议直接 new 该实例,使用 Wrappers.lambdaQuery(entity)
*/
public LambdaQueryWrapper(Class<T> entityClass) {
super.setEntityClass(entityClass);
super.initNeed();
}
/**
* 不建议直接 new 该实例,使用 Wrappers.lambdaQuery(...)
*/
LambdaQueryWrapper(T entity, Class<T> entityClass, SharedString sqlSelect, AtomicInteger paramNameSeq,
Map<String, Object> paramNameValuePairs, MergeSegments mergeSegments,
SharedString lastSql, SharedString sqlComment, SharedString sqlFirst) {
super.setEntity(entity);
super.setEntityClass(entityClass);
this.paramNameSeq = paramNameSeq;
this.paramNameValuePairs = paramNameValuePairs;
this.expression = mergeSegments;
this.sqlSelect = sqlSelect;
this.lastSql = lastSql;
this.sqlComment = sqlComment;
this.sqlFirst = sqlFirst;
}
/**
* SELECT 部分 SQL 设置
*
* @param columns 查询字段
*/
@SafeVarargs
@Override
public final LambdaQueryWrapper<T> select(SFunction<T, ?>... columns) {
if (ArrayUtils.isNotEmpty(columns)) {
this.sqlSelect.setStringValue(columnsToString(false, columns));
}
return typedThis;
}
/**
* 过滤查询的字段信息(主键除外!)
* <p>例1: 只要 java 字段名以 "test" 开头的 -> select(i -> i.getProperty().startsWith("test"))</p>
* <p>例2: 只要 java 字段属性是 CharSequence 类型的 -> select(TableFieldInfo::isCharSequence)</p>
* <p>例3: 只要 java 字段没有填充策略的 -> select(i -> i.getFieldFill() == FieldFill.DEFAULT)</p>
* <p>例4: 要全部字段 -> select(i -> true)</p>
* <p>例5: 只要主键字段 -> select(i -> false)</p>
*
* @param predicate 过滤方式
* @return this
*/
@Override
public LambdaQueryWrapper<T> select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) {
super.setEntityClass(entityClass);
this.sqlSelect.setStringValue(TableInfoHelper.getTableInfo(getEntityClass()).chooseSelect(predicate));
return typedThis;
}
@Override
public String getSqlSelect() {
return sqlSelect.getStringValue();
}
/**
* 用于生成嵌套 sql
* <p>故 sqlSelect 不向下传递</p>
*/
@Override
protected LambdaQueryWrapper<T> instance() {
return new LambdaQueryWrapper<>(getEntity(), getEntityClass(), null, paramNameSeq, paramNameValuePairs,
new MergeSegments(), SharedString.emptyString(), SharedString.emptyString(), SharedString.emptyString());
}
@Override
public void clear() {
super.clear();
sqlSelect.toNull();
}
}
我们来演示一下LambdaQueryWrapper<T>
的用法。
package com.kaven.mybatisplus.dao;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.kaven.mybatisplus.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperLambdaTest {
@Autowired
private UserMapper userMapper;
@Test
public void selectList(){
LambdaQueryWrapper<User> lambdaQueryWrapper = new LambdaQueryWrapper();
lambdaQueryWrapper.like(User::getUsername , "k").lt(User::getAge , 30);
List<User> userList = userMapper.selectList(lambdaQueryWrapper);
userList.forEach(System.out::println);
}
}
结果如下:
结果显然是正确的。
LambdaQueryChainWrapper<T>
源码如下:
package com.baomidou.mybatisplus.extension.conditions.query;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.query.Query;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.baomidou.mybatisplus.core.metadata.TableFieldInfo;
import com.baomidou.mybatisplus.core.toolkit.ExceptionUtils;
import com.baomidou.mybatisplus.core.toolkit.support.SFunction;
import com.baomidou.mybatisplus.extension.conditions.AbstractChainWrapper;
import java.util.function.Predicate;
public class LambdaQueryChainWrapper<T> extends AbstractChainWrapper<T, SFunction<T, ?>, LambdaQueryChainWrapper<T>, LambdaQueryWrapper<T>> implements ChainQuery<T>, Query<LambdaQueryChainWrapper<T>, T, SFunction<T, ?>> {
private BaseMapper<T> baseMapper;
public LambdaQueryChainWrapper(BaseMapper<T> baseMapper) {
this.baseMapper = baseMapper;
super.wrapperChildren = new LambdaQueryWrapper();
}
@SafeVarargs
public final LambdaQueryChainWrapper<T> select(SFunction<T, ?>... columns) {
((LambdaQueryWrapper)this.wrapperChildren).select(columns);
return (LambdaQueryChainWrapper)this.typedThis;
}
public LambdaQueryChainWrapper<T> select(Class<T> entityClass, Predicate<TableFieldInfo> predicate) {
((LambdaQueryWrapper)this.wrapperChildren).select(entityClass, predicate);
return (LambdaQueryChainWrapper)this.typedThis;
}
public String getSqlSelect() {
throw ExceptionUtils.mpe("can not use this method for \"%s\"", new Object[]{"getSqlSelect"});
}
public BaseMapper<T> getBaseMapper() {
return this.baseMapper;
}
}
LambdaQueryChainWrapper<T>
和LambdaQueryWrapper<T>
的用法差不多,只不过在创建LambdaQueryChainWrapper<T>
实例时需要传Mapper接口。
package com.kaven.mybatisplus.dao;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.conditions.query.LambdaQueryChainWrapper;
import com.kaven.mybatisplus.entity.User;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.List;
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserMapperLambdaTest {
@Autowired
private UserMapper userMapper;
@Test
public void selectList2(){
List<User> userList = new LambdaQueryChainWrapper<User>(userMapper)
.like(User::getUsername , "k").lt(User::getAge , 30).list();
userList.forEach(System.out::println);
}
}
结果如下:
结果也是正确的。
为什么需要Lambda
条件构造器,它和之前说的条件构造器有什么区别?其实从上面演示的代码中,就可以看出来,Lambda
条件构造器指定列名是通过Lambda
表达式,如User::getUsername
,它本质上是一个Lambda
表达式,这样设计可以避免防误写,比如我们把列名写成usermame
。
写博客是博主记录自己的学习过程,如果有错误,请指正,谢谢!