pom.xml
<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>com.baomidou</groupId>
<artifactId>mybatisplus-spring-boot</artifactId>
<version>1.0</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.3.RELEASE</version>
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<java.version>1.8</java.version>
<mybatis-plus-boot-starter.version>3.0-gamma</mybatis-plus-boot-starter.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!-- mybatis-plus begin -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>${mybatis-plus-boot-starter.version}</version>
</dependency>
<!-- mybatis-plus end -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-dbcp2</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
这个工程里依赖 mysql, dbcp2, gson, lombok
工程的结构
-
config.mybatis
mybatis的配置 -
config.mybatis.scanner
扩展mybatis的mapper scanner -
controller
接口控制器 -
db
访问数据库相关的对象 -
db.entity
实体 -
db.mapper
mybatis mapper -
exception
例外处理器,用来处理controller里的例外 -
repository
仓库 -
resources.mapper
mybatis mapper的xml文件 -
resources.META-INF
向spring注册组件
application.yml 配置文件
#app
server:
port: 8080
#spring
spring:
devtools:
restart:
enabled: false
# H2 DATABASE CONFIG
datasource:
type: org.apache.commons.dbcp2.BasicDataSource
dbcp2:
driver-class-name: com.mysql.jdbc.Driver
username: xxxx
password: xxxxx
jmx-name: xxxxxx
url: jdbc:mysql://xxxxxx:3306/xxxxxxx?zeroDateTimeBehavior=convertToNull&allowMultiQueries=true&characterEncoding=UTF-8
mybatis:
mapperScanner:
basePackage: com.baomidou.springboot.db.mapper*
#mybatis
mybatis-plus:
mapper-locations: classpath:/mapper/*Mapper.xml
#实体扫描,多个package用逗号或者分号分隔
typeAliasesPackage: com.baomidou.springboot.db.entity
typeEnumsPackage: com.baomidou.springboot.db.entity.enums
global-config:
#刷新mapper 调试神器
db-config:
#主键类型 0:"数据库ID自增", 1:"用户输入ID",2:"全局唯一ID (数字类型唯一ID)", 3:"全局唯一ID UUID";
id-type: id_worker
#字段策略 0:"忽略判断",1:"非 NULL 判断"),2:"非空判断"
field-strategy: not_empty
#驼峰下划线转换
column-underline: true
#数据库大写下划线转换
#capital-mode: true
#逻辑删除配置
logic-delete-value: Y
logic-not-delete-value: N
db-type: mysql
refresh: true
sql-injector: com.baomidou.mybatisplus.extension.injector.LogicSqlInjector
#自定义填充策略接口实现
#meta-object-handler: com.baomidou.springboot.xxx
#自定义SQL注入器
#sql-injector: com.baomidou.springboot.xxx
configuration:
map-underscore-to-camel-case: true
cache-enabled: false
mybatis-plus部分的配置中:
-
mapper-locations
存放mapper xml文件的位置 -
typeAliasesPackage
数据对象的目录 -
typeEnumsPackage
数据对象中使用的enum定义 -
id-type
id的生成策略 -
field-strategy
字段处理策略 -
column-underline
对代码中的驼峰字段映射到数据库字段的策略 -
logic-delete-value
逻辑删除的标记值 -
logic-not-delete-value
逻辑未删除的标记值 -
sql-injector
SQL注入器。 这里为了处理逻辑删除操作,使用了LogicSqlInjector注入器 -
db-type
数据库类型。支持主流的数据库 -
map-underscore-to-camel-case
数据库字段与数据对象字段的映射策略
类的说明
MybatisPlusConfig
@Configuration
public class MybatisPlusConfig {
... 一些配置, 例如,配置 datasource, sqlSessionFactory, transaction manager 等等
}
MybatisPlusAutoConfiguration
@Configuration
public class MybatisPlusAutoConfiguration {
/**
* 将自定义的converter注册到ConversionService中
*
* @return
*/
@Bean
public ConversionService conversionService() {
ConversionServiceFactoryBean bean = new ConversionServiceFactoryBean();
Set<Converter> converters = new HashSet<>();
converters.add(new String2SqlInjectorConverter());
bean.setConverters(converters);
bean.afterPropertiesSet();
return bean.getObject();
}
/**
* 将String 转成 ISqlInjector 实例
*/
public class String2SqlInjectorConverter implements Converter<String, ISqlInjector> {
@Override
public ISqlInjector convert(String s) {
if(StringUtils.isEmpty(s))
return null;
try {
Class clazz = ClassUtils.getClass(s);
ISqlInjector sqlInjector = (ISqlInjector)clazz.newInstance();
return sqlInjector;
} catch (ClassNotFoundException|IllegalAccessException|InstantiationException e) {
ReflectionUtils.rethrowRuntimeException(e);
}
return null;
}
}
}
向spring注册自定义的 converter,否则,application.yml中mybatis-plus.sql-injector配置会出错,提示没有相应的转换器
这个文件需要在resources/META-INF/spring.factories中注册:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.baomidou.springboot.config.mybatis.MybatisPlusAutoConfiguration
这个文件里的程序会先于spring组建bean之前执行
SuperEntity
@Data
public abstract class SuperEntity<T extends Model> extends Model<T> {
/**
* 主键ID , 这里故意演示注解可以无
*/
@TableId
private String uuid;
@Override
protected Serializable pkVal() {
return this.uuid;
}
}
数据库对象的父类, 在这个类中可以定义一些公共的字段。 mybatis-plus只把id作为主键字段,非id的字段,需要用@TableId来标注。pkVal() 是用来支持组合主键的,现在版本的mybatis-plus不支持组合主键
WxTokenObj
@Data
@TableName("tb_wx_token")
public class WxTokenObj extends SuperEntity<WxTokenObj>{
private String token;
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date createTime;
@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
private Date modifyTime;
private String accountUuid;
private String name;
private String avatarPath;
@TableLogic
private String isDelete;
private String unionId;
}
所有的数据对象都需要继承SuperEntity, 如果表名与数据对象名称不对应,需要用@TableName标注数据表名。mybatis-plus可以配置自动解析驼峰命名的字段为下划线分割的数据库字段名。如果数据库字段名与数据字段名不一致,需要用@TableField来标注
@TableLogic标注的字段,是用来做逻辑删除的字段,在调用删除方法的时候,会将这个字段改成删除状态,而不是真正删除数据
SuperMapper
/**
* 演示 mapper 父类,注意这个类不要让 mp 扫描到!!
*/
public interface SuperMapper<T> extends BaseMapper<T> {
// 这里可以放一些公共的方法
}
所有mapper的父接口,在这个接口中可以放一些公共方法。这个接口需要放到mapper目录外,不能让mybatis scanner 扫描到。
WxTokenMapper
public interface WxTokenMapper extends SuperMapper<WxTokenObj> {
@Select("select * from tb_wx_token where uuid=#{uuid}")
WxTokenObj selectOneBySQL(String uuid);
WxTokenObj selectOneByWrapper(@Param("ew") Wrapper wrapper);
List<WxTokenObj> selectAllTokens();
}
一个具体的mapper接口。 接口可以使用@Select等标注原生的sql,也可以使用mapper.xml 放置灵活的数据库操作。
自定义的mapper需要继承SuperMapper,也就是需要从BaseMapper中继承其定义的方法。BaseMapper中定义了很多类似JPA的自定义函数,方便开发使用:
public interface BaseMapper<T> {
/**
* <p>
* 插入一条记录
* </p>
*
* @param entity 实体对象
*/
Integer insert(T entity);
/**
* <p>
* 根据 ID 删除
* </p>
*
* @param id 主键ID
*/
Integer deleteById(Serializable id);
/**
* <p>
* 根据 columnMap 条件,删除记录
* </p>
*
* @param columnMap 表字段 map 对象
*/
Integer deleteByMap(@Param("cm") Map<String, Object> columnMap);
/**
* <p>
* 根据 entity 条件,删除记录
* </p>
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
Integer delete(@Param("ew") Wrapper<T> queryWrapper);
/**
* <p>
* 删除(根据ID 批量删除)
* </p>
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
Integer deleteBatchIds(@Param("coll") Collection<? extends Serializable> idList);
/**
* <p>
* 根据 ID 修改
* </p>
*
* @param entity 实体对象
*/
Integer updateById(@Param(Constants.META_OBJ_PREFIX) T entity);
/**
* <p>
* 根据 whereEntity 条件,更新记录
* </p>
*
* @param entity 实体对象 (set 条件值,不能为 null)
* @param updateWrapper 实体对象封装操作类(可以为 null,里面的 entity 用于生成 where 语句)
*/
Integer update(@Param(Constants.META_OBJ_PREFIX) T entity, @Param("ew") Wrapper<T> updateWrapper);
/**
* <p>
* 根据 ID 查询
* </p>
*
* @param id 主键ID
*/
T selectById(Serializable id);
/**
* <p>
* 查询(根据ID 批量查询)
* </p>
*
* @param idList 主键ID列表(不能为 null 以及 empty)
*/
List<T> selectBatchIds(@Param("coll") Collection<? extends Serializable> idList);
/**
* <p>
* 查询(根据 columnMap 条件)
* </p>
*
* @param columnMap 表字段 map 对象
*/
List<T> selectByMap(@Param("cm") Map<String, Object> columnMap);
/**
* <p>
* 根据 entity 条件,查询一条记录
* </p>
*
* @param queryWrapper 实体对象
*/
T selectOne(@Param("ew") Wrapper<T> queryWrapper);
/**
* <p>
* 根据 Wrapper 条件,查询总记录数
* </p>
*
* @param queryWrapper 实体对象
*/
Integer selectCount(@Param("ew") Wrapper<T> queryWrapper);
/**
* <p>
* 根据 entity 条件,查询全部记录
* </p>
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<T> selectList(@Param("ew") Wrapper<T> queryWrapper);
/**
* <p>
* 根据 Wrapper 条件,查询全部记录
* </p>
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Map<String, Object>> selectMaps(@Param("ew") Wrapper<T> queryWrapper);
/**
* <p>
* 根据 Wrapper 条件,查询全部记录
* 注意: 只返回第一个字段的值
* </p>
*
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
List<Object> selectObjs(@Param("ew") Wrapper<T> queryWrapper);
/**
* <p>
* 根据 entity 条件,查询全部记录(并翻页)
* </p>
*
* @param page 分页查询条件(可以为 RowBounds.DEFAULT)
* @param queryWrapper 实体对象封装操作类(可以为 null)
*/
IPage<T> selectPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
/**
* <p>
* 根据 Wrapper 条件,查询全部记录(并翻页)
* </p>
*
* @param page 分页查询条件
* @param queryWrapper 实体对象封装操作类
*/
IPage<Map<String, Object>> selectMapsPage(IPage<T> page, @Param("ew") Wrapper<T> queryWrapper);
}
在定义mapper是,推荐使用idea ide + mybatisx 插件,支持从mapper 源代码 到 mapper.xml 的双向跳转、代码自动生成等。
IWxTokenRepository
仓库接口。因为仓库是用JDK proxy做增强,所有的仓库都需要单独定义接口(如果换成用cglib做增强,可以不用单独定义接口)
public interface IWxTokenRepository extends IService<WxTokenObj>{
WxTokenObj getToken(String uuid);
WxTokenObj getToken(Wrapper wrapper);
}
仓库接口需要继承IService接口。IService接口中定义了很多方便使用的函数(主要是与mapper对应的函数):
public interface IService<T> {
/**
* <p>
* 插入一条记录(选择字段,策略插入)
* </p>
*
* @param entity 实体对象
* @return boolean
*/
boolean save(T entity);
/**
* <p>
* 插入(批量),该方法不适合 Oracle
* </p>
*
* @param entityList 实体对象集合
* @return boolean
*/
boolean saveBatch(Collection<T> entityList);
/**
* <p>
* 插入(批量)
* </p>
*
* @param entityList 实体对象集合
* @param batchSize 插入批次数量
* @return boolean
*/
boolean saveBatch(Collection<T> entityList, int batchSize);
/**
* <p>
* 批量修改插入
* </p>
*
* @param entityList 实体对象集合
* @return boolean
*/
boolean saveOrUpdateBatch(Collection<T> entityList);
/**
* <p>
* 批量修改插入
* </p>
*
* @param entityList 实体对象集合
* @param batchSize
* @return boolean
*/
boolean saveOrUpdateBatch(Collection<T> entityList, int batchSize);
/**
* <p>
* 根据 ID 删除
* </p>
*
* @param id 主键ID
* @return boolean
*/
boolean removeById(Serializable id);
/**
* <p>
* 根据 columnMap 条件,删除记录
* </p>
*
* @param columnMap 表字段 map 对象
* @return boolean
*/
boolean removeByMap(Map<String, Object> columnMap);
/**
* <p>
* 根据 entity 条件,删除记录
* </p>
*
* @param wrapper 实体包装类 {@link Wrapper}
* @return boolean
*/
boolean remove(Wrapper<T> wrapper);
/**
* <p>
* 删除(根据ID 批量删除)
* </p>
*
* @param idList 主键ID列表
* @return boolean
*/
boolean removeByIds(Collection<? extends Serializable> idList);
/**
* <p>
* 根据 ID 选择修改
* </p>
*
* @param entity 实体对象
* @return boolean
*/
boolean updateById(T entity);
/**
* <p>
* 根据 whereEntity 条件,更新记录
* </p>
*
* @param entity 实体对象
* @param wrapper 实体包装类 {@link Wrapper}
* @return boolean
*/
boolean update(T entity, Wrapper<T> wrapper);
/**
* <p>
* 根据ID 批量更新
* </p>
*
* @param entityList 实体对象集合
* @return boolean
*/
boolean updateBatchById(Collection<T> entityList);
/**
* <p>
* 根据ID 批量更新
* </p>
*
* @param entityList 实体对象集合
* @param batchSize 更新批次数量
* @return boolean
*/
boolean updateBatchById(Collection<T> entityList, int batchSize);
/**
* <p>
* TableId 注解存在更新记录,否插入一条记录
* </p>
*
* @param entity 实体对象
* @return boolean
*/
boolean saveOrUpdate(T entity);
/**
* <p>
* 根据 ID 查询
* </p>
*
* @param id 主键ID
* @return T
*/
T getById(Serializable id);
/**
* <p>
* 查询(根据ID 批量查询)
* </p>
*
* @param idList 主键ID列表
* @return Collection<T>
*/
Collection<T> listByIds(Collection<? extends Serializable> idList);
/**
* <p>
* 查询(根据 columnMap 条件)
* </p>
*
* @param columnMap 表字段 map 对象
* @return Collection<T>
*/
Collection<T> listByMap(Map<String, Object> columnMap);
/**
* <p>
* 根据 Wrapper,查询一条记录
* </p>
*
* @param wrapper 实体对象
* @return T
*/
T getOne(Wrapper<T> wrapper);
/**
* <p>
* 根据 Wrapper,查询一条记录
* </p>
*
* @param wrapper {@link Wrapper}
* @return
*/
Map<String, Object> getMap(Wrapper<T> wrapper);
/**
* <p>
* 根据 Wrapper,查询一条记录
* </p>
*
* @param wrapper {@link Wrapper}
* @return Object
*/
Object getObj(Wrapper<T> wrapper);
/**
* <p>
* 根据 Wrapper 条件,查询总记录数
* </p>
*
* @param wrapper 实体对象
* @return int
*/
int count(Wrapper<T> wrapper);
/**
* <p>
* 查询列表
* </p>
*
* @param wrapper 实体包装类 {@link Wrapper}
* @return
*/
List<T> list(Wrapper<T> wrapper);
/**
* <p>
* 翻页查询
* </p>
*
* @param page 翻页对象
* @param wrapper 实体包装类 {@link Wrapper}
* @return
*/
IPage<T> page(IPage<T> page, Wrapper<T> wrapper);
/**
* <p>
* 查询列表
* </p>
*
* @param wrapper {@link Wrapper}
* @return
*/
List<Map<String, Object>> listMaps(Wrapper<T> wrapper);
/**
* <p>
* 根据 Wrapper 条件,查询全部记录
* </p>
*
* @param wrapper 实体对象封装操作类(可以为 null)
* @return List<Object>
*/
List<Object> listObjs(Wrapper<T> wrapper);
/**
* <p>
* 翻页查询
* </p>
*
* @param page 翻页对象
* @param wrapper {@link Wrapper}
* @return
*/
IPage<Map<String, Object>> pageMaps(IPage page, Wrapper<T> wrapper);
}
对应的,仓库实现类也就要继承ServiceImpl,来对IService中的函数提供实现。
WxTokenRepository
仓库实现类,对自定义的仓库接口中的函数提供实现:
@Component
public class WxTokenRepository extends ServiceImpl<WxTokenMapper, WxTokenObj> implements IWxTokenRepository{
public WxTokenObj getToken(String uuid) {
return baseMapper.selectOneBySQL(uuid);
}
public WxTokenObj getToken(Wrapper wrapper) {
return baseMapper.selectOneByWrapper(wrapper);
}
}
在继承了ServiceImpl后,可通过baseMapper方法 接口对应的mapper。如果还需要访问其他的mapper,可以用个@Autowired 注入其他mapper。
Controller
用户接口。这里演示如何用仓库访问数据读取数据。
查询1
@GetMapping(value = "/id/{id}")
public String getById(@PathVariable String id){
WxTokenObj wxTokenObj = wxTokenRepository.getToken(id);
return gson.toJson(wxTokenObj);
}
这个接口中,访问的是mapper中使用@Select标注的数据库访问方法。可以在mapper中使用这种方式简单的定义数据库访问操作,这样的mapper函数不需要生成mapper.xml中的配置就可以实现数据操作。
查询2
@GetMapping("/token/{token}")
public String getByToken(@PathVariable String token){
Wrapper<WxTokenObj> wrapper = new QueryWrapper<WxTokenObj>().lambda().eq(WxTokenObj::getToken, token);
WxTokenObj wxTokenObj = wxTokenRepository.getToken(wrapper);
return gson.toJson(wxTokenObj);
}
这个接口中,访问的是自定义的数据库访问方法。这样的mapper函数需要生成mapper.xml配置才能正确的操作数据。
为了提供查询条件,mybatis-plus提供了QueryWrapper来生成where片段。
查询3
@GetMapping("/token2/{token}")
public String getByToken2(@PathVariable String token){
Wrapper<WxTokenObj> wrapper = new QueryWrapper<WxTokenObj>().lambda().eq(WxTokenObj::getToken, token);
WxTokenObj wxTokenObj = wxTokenRepository.selectOne(wrapper);
return gson.toJson(wxTokenObj);
}
这个接口中,使用的是BaseMapper中提供的数据库访问函数。这样的函数可以直接访问,不需要自己定义,也不需要在mapper.xml中配置。这种体验与JPA类似。
删除
@DeleteMapping("/id/{id}")
public String deleteById(@PathVariable String id) {
boolean success = wxTokenRepository.deleteById(id);
return gson.toJson(success);
}
这个接口使用BaseMapper中提供的删除函数来根据ID删除数据。配置了逻辑删除后,这个删除操作只是修改了删除标记,并没有真正的删除数据。
mapper xml说明
<?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.baomidou.springboot.db.mapper.WxTokenMapper">
<!-- 通用查询结果列 -->
<!--<sql id="Base_Column_List">-->
<!--token ,-->
<!--create_time ,-->
<!--modify_time ,-->
<!--account_id ,-->
<!--name ,-->
<!--avatar_path ,-->
<!--is_delete ,-->
<!--uuid ,-->
<!--account_uuid,-->
<!--union_id-->
<!--</sql>-->
<select id="selectOneByWrapper" resultType="com.baomidou.springboot.db.entity.WxTokenObj">
SELECT * FROM tb_wx_token
<!-- 判断 wrapper 是否为空 emptyOfWhere -->
<where>
${ew.sqlSegment}
</where>
</select>
<select id="selectAllTokens" resultType="com.baomidou.springboot.db.entity.WxTokenObj"></select>
<select id="selectTokensByDeleteStatus" resultType="com.baomidou.springboot.db.entity.WxTokenObj"></select>
</mapper>
这里面的select操作的where条件,使用的是传入的Wrapper参数中的sqlSegment来实现的。Wrapper可以通过流式函数风格,以类jpa的方式定义where 条件。
mybatis-plus提供了几种Wrapper实现:
这其中有专用于查询的QueryWrapper, 专用于更新操作的UpdateWrapper。
如果需要按条件删除数据,可以用QueryWrapper传入条件。
当然,也可用根据 columnMap 条件进行查询、删除操作。
有关Wrapper的更多使用案例,可以参考mybatis-plus core里的WrapperTest.java:
private void logSqlSegment(String explain, ISqlSegment sqlSegment) {
System.out.println(String.format(" ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ->(%s)<- ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓", explain));
System.out.println(sqlSegment.getSqlSegment());
}
private <T> void logParams(QueryWrapper<T> wrapper) {
wrapper.getParamNameValuePairs().forEach((k, v) ->
System.out.println("key: '" + k + "'\t\tvalue: '" + v + "'"));
}
@Test
public void test() {
Wrapper<User> wrapper = new QueryWrapper<User>().lambda().eq(User::getName, 123)
.or(c -> c.eq(User::getRoleId, 1).eq(User::getId, 2))
.eq(User::getId, 1);
log(wrapper.getSqlSegment());
}
@Test
public void test1() {
QueryWrapper<User> ew = new QueryWrapper<User>()
.eq("xxx", 123)
.and(i -> i.eq("andx", 65444).le("ande", 66666))
.ne("xxx", 222);
log(ew.getSqlSegment());
ew.getParamNameValuePairs().forEach((k, v) -> System.out.println("key = " + k + " ; value = " + v));
}
============================================
xxx = #{ew.paramNameValuePairs.MPGENVAL1} AND ( andx = #{ew.paramNameValuePairs.MPGENVAL2} AND ande <= #{ew.paramNameValuePairs.MPGENVAL3} ) AND xxx <> #{ew.paramNameValuePairs.MPGENVAL4}
key = MPGENVAL3 ; value = 66666
key = MPGENVAL2 ; value = 65444
key = MPGENVAL1 ; value = 123
key = MPGENVAL4 ; value = 222
============================================
@Test
public void test2() {
UpdateWrapper<User> ew = new UpdateWrapper<User>()
.set("name", "三毛").set("id", 1)
.eq("xxx", 123)
.and(i -> i.eq("andx", 65444).le("ande", 66666))
.ne("xxx", 222);
log(ew.getSqlSet());
log(ew.getSqlSegment());
}
============================================
name=#{ew.paramNameValuePairs.MPGENVAL1},id=#{ew.paramNameValuePairs.MPGENVAL2}
xxx = #{ew.paramNameValuePairs.MPGENVAL3} AND ( andx = #{ew.paramNameValuePairs.MPGENVAL4} AND ande <= #{ew.paramNameValuePairs.MPGENVAL5} ) AND xxx <> #{ew.paramNameValuePairs.MPGENVAL6}
============================================
@Test
public void test3() {
UpdateWrapper<User> ew = new UpdateWrapper<User>()
.setSql("abc=1,def=2").eq("id", 1).ge("age", 3);
log(ew.getSqlSet());
log(ew.getSqlSegment());
}
============================================
abc=1,def=2
id = #{ew.paramNameValuePairs.MPGENVAL1} AND age >= #{ew.paramNameValuePairs.MPGENVAL2}
============================================
@Test
public void testQueryWrapper() {
logSqlSegment("去除第一个 or,以及自动拼接 and,以及手动拼接 or,以及去除最后的多个or", new QueryWrapper<User>().or()
.ge("age", 3).or().ge("age", 3).ge("age", 3).or().or().or().or());
logSqlSegment("多个 or 相连接,去除多余的 or", new QueryWrapper<User>()
.ge("age", 3).or().or().or().ge("age", 3).or().or().ge("age", 3));
logSqlSegment("嵌套,正常嵌套", new QueryWrapper<User>()
.nested(i -> i.eq("id", 1)).eq("id", 1));
logSqlSegment("嵌套,第一个套外的 and 自动消除", new QueryWrapper<User>()
.and(i -> i.eq("id", 1)).eq("id", 1));
logSqlSegment("嵌套,多层嵌套", new QueryWrapper<User>()
.and(i -> i.eq("id", 1).and(j -> j.eq("id", 1))));
logSqlSegment("嵌套,第一个套外的 or 自动消除", new QueryWrapper<User>()
.or(i -> i.eq("id", 1)).eq("id", 1));
logSqlSegment("嵌套,套内外自动拼接 and", new QueryWrapper<User>()
.eq("id", 11).and(i -> i.eq("id", 1)).eq("id", 1));
logSqlSegment("嵌套,套内外手动拼接 or,去除套内第一个 or", new QueryWrapper<User>()
.eq("id", 11).or(i -> i.or().eq("id", 1)).or().eq("id", 1));
logSqlSegment("多个 order by 和 group by 拼接,自动优化顺序,last方法拼接在最后", new QueryWrapper<User>()
.eq("id", 11)
.last("limit 1")
.orderByAsc("id", "name", "sex").orderByDesc("age", "txl")
.groupBy("id", "name", "sex").groupBy("id", "name"));
logSqlSegment("只存在 order by", new QueryWrapper<User>()
.orderByAsc("id", "name", "sex").orderByDesc("age", "txl"));
logSqlSegment("只存在 group by", new QueryWrapper<User>()
.groupBy("id", "name", "sex").groupBy("id", "name"));
}
============================================
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ->(去除第一个 or,以及自动拼接 and,以及手动拼接 or,以及去除最后的多个or)<- ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
age >= #{ew.paramNameValuePairs.MPGENVAL1} OR age >= #{ew.paramNameValuePairs.MPGENVAL2} AND age >= #{ew.paramNameValuePairs.MPGENVAL3}
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ->(多个 or 相连接,去除多余的 or)<- ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
age >= #{ew.paramNameValuePairs.MPGENVAL1} OR age >= #{ew.paramNameValuePairs.MPGENVAL2} OR age >= #{ew.paramNameValuePairs.MPGENVAL3}
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ->(嵌套,正常嵌套)<- ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
( id = #{ew.paramNameValuePairs.MPGENVAL1} ) AND id = #{ew.paramNameValuePairs.MPGENVAL2}
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ->(嵌套,第一个套外的 and 自动消除)<- ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
( id = #{ew.paramNameValuePairs.MPGENVAL1} ) AND id = #{ew.paramNameValuePairs.MPGENVAL2}
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ->(嵌套,多层嵌套)<- ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
( id = #{ew.paramNameValuePairs.MPGENVAL1} AND ( id = #{ew.paramNameValuePairs.MPGENVAL2} ) )
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ->(嵌套,第一个套外的 or 自动消除)<- ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
( id = #{ew.paramNameValuePairs.MPGENVAL1} ) AND id = #{ew.paramNameValuePairs.MPGENVAL2}
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ->(嵌套,套内外自动拼接 and)<- ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
id = #{ew.paramNameValuePairs.MPGENVAL1} AND ( id = #{ew.paramNameValuePairs.MPGENVAL2} ) AND id = #{ew.paramNameValuePairs.MPGENVAL3}
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ->(嵌套,套内外手动拼接 or,去除套内第一个 or)<- ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
id = #{ew.paramNameValuePairs.MPGENVAL1} OR ( id = #{ew.paramNameValuePairs.MPGENVAL2} ) OR id = #{ew.paramNameValuePairs.MPGENVAL3}
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ->(多个 order by 和 group by 拼接,自动优化顺序,last方法拼接在最后)<- ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
id = #{ew.paramNameValuePairs.MPGENVAL1} GROUP BY id,name,sex,id,name ORDER BY id,name,sex ASC , age,txl DESC limit 1
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ->(只存在 order by)<- ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
1=1 ORDER BY id,name,sex ASC , age,txl DESC
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ->(只存在 group by)<- ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
1=1 GROUP BY id,name,sex,id,name
============================================
@Test
public void testCompare() {
QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
.allEq(getMap()).allEq((k, v) -> true, getMap())
.eq("id", 1).ne("id", 1)
.or().gt("id", 1).ge("id", 1)
.lt("id", 1).le("id", 1)
.or().between("id", 1, 2).notBetween("id", 1, 3)
.like("id", 1).notLike("id", 1)
.or().likeLeft("id", 1).likeRight("id", 1);
logSqlSegment("测试 Compare 下的方法", queryWrapper);
logParams(queryWrapper);
}
============================================
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ->(测试 Compare 下的方法)<- ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
column1 = #{ew.paramNameValuePairs.MPGENVAL1} AND column0 = #{ew.paramNameValuePairs.MPGENVAL2} AND nullColumn IS NULL AND column1 = #{ew.paramNameValuePairs.MPGENVAL3} AND column0 = #{ew.paramNameValuePairs.MPGENVAL4} AND nullColumn IS NULL AND id = #{ew.paramNameValuePairs.MPGENVAL5} AND id <> #{ew.paramNameValuePairs.MPGENVAL6} OR id > #{ew.paramNameValuePairs.MPGENVAL7} AND id >= #{ew.paramNameValuePairs.MPGENVAL8} AND id < #{ew.paramNameValuePairs.MPGENVAL9} AND id <= #{ew.paramNameValuePairs.MPGENVAL10} OR id BETWEEN #{ew.paramNameValuePairs.MPGENVAL11} AND #{ew.paramNameValuePairs.MPGENVAL12} AND id NOT BETWEEN #{ew.paramNameValuePairs.MPGENVAL13} AND #{ew.paramNameValuePairs.MPGENVAL14} AND id LIKE #{ew.paramNameValuePairs.MPGENVAL15} AND id NOT LIKE #{ew.paramNameValuePairs.MPGENVAL16} OR id LIKE #{ew.paramNameValuePairs.MPGENVAL17} AND id LIKE #{ew.paramNameValuePairs.MPGENVAL18}
key: 'MPGENVAL3' value: '1'
key: 'MPGENVAL2' value: '0'
key: 'MPGENVAL1' value: '1'
key: 'MPGENVAL18' value: '1%'
key: 'MPGENVAL17' value: '%1'
key: 'MPGENVAL16' value: '%1%'
key: 'MPGENVAL15' value: '%1%'
key: 'MPGENVAL14' value: '3'
key: 'MPGENVAL13' value: '1'
key: 'MPGENVAL9' value: '1'
key: 'MPGENVAL12' value: '2'
key: 'MPGENVAL8' value: '1'
key: 'MPGENVAL11' value: '1'
key: 'MPGENVAL7' value: '1'
key: 'MPGENVAL10' value: '1'
key: 'MPGENVAL6' value: '1'
key: 'MPGENVAL5' value: '1'
key: 'MPGENVAL4' value: '0'
============================================
@Test
public void testFunc() {
QueryWrapper<User> queryWrapper = new QueryWrapper<User>()//todo in 方法是不是各个加个后缀好点
.isNull("nullColumn").or().isNotNull("notNullColumn")
.orderByAsc("id").orderByDesc("name")
.groupBy("id", "name").groupBy("id2", "name2")
.in("inColl", getList()).or().notIn("notInColl", getList())
.in("inArray").notIn("notInArray", 1, 2, 3)
.inSql("inSql", "1,2,3,4,5").notInSql("inSql", "1,2,3,4,5")
.having("sum(age) > {0}", 1).having("id is not null");
logSqlSegment("测试 Func 下的方法", queryWrapper);
logParams(queryWrapper);
}
============================================
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ->(测试 Func 下的方法)<- ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
nullColumn IS NULL OR notNullColumn IS NOT NULL AND inColl IN (#{ew.paramNameValuePairs.MPGENVAL1},#{ew.paramNameValuePairs.MPGENVAL2}) OR notInColl NOT IN (#{ew.paramNameValuePairs.MPGENVAL3},#{ew.paramNameValuePairs.MPGENVAL4}) AND notInArray NOT IN (#{ew.paramNameValuePairs.MPGENVAL5},#{ew.paramNameValuePairs.MPGENVAL6},#{ew.paramNameValuePairs.MPGENVAL7}) AND inSql IN (1,2,3,4,5) AND inSql NOT IN (1,2,3,4,5) GROUP BY id,name,id2,name2 HAVING sum(age) > #{ew.paramNameValuePairs.MPGENVAL8} AND id is not null ORDER BY id ASC , name DESC
key: 'MPGENVAL3' value: '0'
key: 'MPGENVAL2' value: '1'
key: 'MPGENVAL1' value: '0'
key: 'MPGENVAL8' value: '1'
key: 'MPGENVAL7' value: '3'
key: 'MPGENVAL6' value: '2'
key: 'MPGENVAL5' value: '1'
key: 'MPGENVAL4' value: '1'
============================================
@Test
public void testJoin() {
QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
.last("limit 1").or()
.apply("date_format(column,'%Y-%m-%d') = '2008-08-08'")
.apply("date_format(column,'%Y-%m-%d') = {0}", LocalDate.now())
.or().exists("select id from table where age = 1")
.or().notExists("select id from table where age = 1");
logSqlSegment("测试 Join 下的方法", queryWrapper);
logParams(queryWrapper);
}
============================================
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ->(测试 Join 下的方法)<- ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
date_format(column,'%Y-%m-%d') = '2008-08-08' AND date_format(column,'%Y-%m-%d') = #{ew.paramNameValuePairs.MPGENVAL1} OR EXISTS (select id from table where age = 1) OR NOT EXISTS (select id from table where age = 1) limit 1
key: 'MPGENVAL1' value: '2018-08-01'
============================================
@Test
public void testNested() {
QueryWrapper<User> queryWrapper = new QueryWrapper<User>()
.and(i -> i.eq("id", 1).nested(j -> j.ne("id", 2)))
.or(i -> i.eq("id", 1).and(j -> j.ne("id", 2)))
.nested(i -> i.eq("id", 1).or(j -> j.ne("id", 2)));
logSqlSegment("测试 Nested 下的方法", queryWrapper);
logParams(queryWrapper);
}
============================================
↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ->(测试 Nested 下的方法)<- ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓ ↓
( id = #{ew.paramNameValuePairs.MPGENVAL1} AND ( id <> #{ew.paramNameValuePairs.MPGENVAL2} ) ) OR ( id = #{ew.paramNameValuePairs.MPGENVAL3} AND ( id <> #{ew.paramNameValuePairs.MPGENVAL4} ) ) AND ( id = #{ew.paramNameValuePairs.MPGENVAL5} OR ( id <> #{ew.paramNameValuePairs.MPGENVAL6} ) )
key: 'MPGENVAL3' value: '1'
key: 'MPGENVAL2' value: '2'
key: 'MPGENVAL1' value: '1'
key: 'MPGENVAL6' value: '2'
key: 'MPGENVAL5' value: '1'
key: 'MPGENVAL4' value: '2'
============================================
@Test
public void testPluralLambda() {
TableInfoHelper.initTableInfo(null, User.class);
QueryWrapper<User> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(User::getName,"sss");
queryWrapper.lambda().eq(User::getName,"sss2");
logSqlSegment("测试 PluralLambda", queryWrapper);
logParams(queryWrapper);
}
private List<Object> getList() {
List<Object> list = new ArrayList<>();
for (int i = 0; i < 2; i++) {
list.add(i);
}
return list;
}
private Map<String, Object> getMap() {
Map<String, Object> map = new HashMap<>();
for (int i = 0; i < 2; i++) {
map.put("column" + i, i);
}
map.put("nullColumn", null);
return map;
}