ApiBoot Mybatis Enhance
是一款数据持久化
、动态查询结构化
框架,用户操作单表、多表关联的数据,在Mybatis
的基础上进行封装扩展,
不影响任何原生的使用,使用后完全替代mybatis-core
、mybatis-spring
以及mybatis-spring-boot-starter
,使用SpringBoot配置文件
方式代替传统代码配置,减少繁琐代码编写。
ApiBoot Mybatis Enhance
总结了目前主流框架的使用风格,对开发者来说使用不便的地方进行封装扩展,让数据操作更艺术。
ApiBoot Mybatis Enhance
内部提供了常用的方法实现,可以快速对单条、多条数据进行CRUD
常规操作。
ApiBoot Mybatis Enhance
提供动态查询方式,可以动态查询单表、多表关联指定的列值或者整条数据,可以将结果映射成你想要的类型。
ApiBoot Mybatis Enhance
提供方法规则查询,在对应数据接口Mapper
内,遵循一定规则可以完成对数据的查询、统计、删除等。
ApiBoot Mybatis Enhance
致力于解决简单、快速操作数据问题,省去繁琐SQL
的编写,让开发人员更专注业务编写。
在项目的pom.xml
文件内添加如下依赖:
<!--ApiBoot Mybatis Enhance-->
<dependency>
<groupId>org.minbox.framework</groupId>
<artifactId>api-boot-starter-mybatis-enhance</artifactId>
</dependency>
注意:如果未添加
ApiBoot
版本依赖,请访问版本依赖查看添加方式。
Enhance
所需要的数据实体很简单,只需要告知Enhance
所需要的表信息以及列信息即可(实际在开发过程中编写数据实体是一件很繁琐的事情,因此ApiBoot
为Enhance
编写了专属的代码生成插件,访问Enhance Codegen查看具体使用方式)
利用Enhance Codegen
自动生成的数据实体类如下所示:
import com.gitee.hengboy.mybatis.enhance.common.annotation.Column;
import com.gitee.hengboy.mybatis.enhance.common.annotation.Id;
import com.gitee.hengboy.mybatis.enhance.common.annotation.Table;
import com.gitee.hengboy.mybatis.enhance.common.enums.KeyGeneratorTypeEnum;
import lombok.Data;
import java.sql.Timestamp;
/**
* 用户基本信息
* @author ApiBoot Mybatis Enhance Codegen
*/
@Data
@Table(name = "iot_user_info")
public class UserInfo {
/**
* 用户主键
*/
@Id(generatorType = KeyGeneratorTypeEnum.UUID)
@Column(name = "UI_ID")
private String id;
/**
* 用户编号
*/
@Column(name = "UI_USER_NO")
private String userNo;
/**
* 用户名
*/
@Column(name = "UI_USER_NAME")
private String userName;
/**
* 年龄
*/
@Column(name = "UI_AGE")
private Integer age;
/**
* 性别,boy:男,girl:女,other:其他
*/
@Column(name = "UI_SEX")
private String sex = "other";
/**
* 创建时间
*/
@Column(name = "UI_CREATE_TIME",insertable = false)
private Timestamp createTime;
/**
* 1:正常,0:禁用,-1:删除
*/
@Column(name = "UI_STATUS")
private Integer status = 1;
/**
* 备注信息
*/
@Column(name = "UI_MARK")
private String mark;
}
在上面的数据实体类内我们都是使用的注解形式来完成的配置,简单介绍下注解的作用
@Data
:Lombok
内部的注解,如果没有了解过该插件可以访问恒宇少年的文章第二十九章:基于SpringBoot平台使用Lombok来优雅的编码查看具体使用方法。@Table
:Enhance
内部注解,用于标注该数据实体所使用的表信息
@Id
:Enhance
内部注解,用户标注主键字段@Column
:Enhance
内部注解,用于标注字段对应的列信息
Enhance
所管理的Mybatis Mapper
以及内置的CRUD
操作单表数据的接口方法都是由EnhanceMapper
接口来完成的。
ApiBoot Mybatis Enhance
在项目启动时会自动加载EnhanceMapper
的所有子类进行数据操作方法实现并交付给Spring IOC
进行托管,为每一个方法
都提供对应的Statement
以及SqlSource
的实现,而StatementID
则是Mapper
类的全限定名。
在EnhanceMapper
接口内提供的数据操作方法,如下所示:
// 统计数据
Long countAll() throws EnhanceFrameworkException;
// 清空数据
void deleteAll() throws EnhanceFrameworkException;
// 根据主键数组删除指定数据
void deleteArray(Id... ids) throws EnhanceFrameworkException;
// 根据自定义sql删除数据
void deleteBySql(String sql, Map<String, Object> params) throws EnhanceFrameworkException;
// 根据主键集合删除指定数据
void deleteCollection(Collection<Id> collection) throws EnhanceFrameworkException;
// 删除一条数据
void deleteOne(Id id) throws EnhanceFrameworkException;
// 数据保存
void insert(T t) throws EnhanceFrameworkException;
// 保存数组内的所有数据
void insertArray(T... array) throws EnhanceFrameworkException;
// 保存集合内的所有数据
void insertCollection(Collection<T> collection) throws EnhanceFrameworkException;
// 查询全部数据
List<T> selectAll() throws EnhanceFrameworkException;
// 根据主键数组查询指定数据
List<T> selectArray(Id... ids) throws EnhanceFrameworkException;
// 分页查询数据
List<T> selectByPageable(Pageable pageable) throws EnhanceFrameworkException;
// 自定义sql查询数据
List<Map> selectBySql(String sql, Map<String, Object> params) throws EnhanceFrameworkException;
// 根据主键集合查询指定数据
List<T> selectCollection(Collection<Id> ids) throws EnhanceFrameworkException;
// 根据主键查询单条数据
T selectOne(Id id) throws EnhanceFrameworkException;
// 根据主键更新数据实体
void update(T t) throws EnhanceFrameworkException;
// 自定义sql更新数据
void updateBySql(String sql, Map<String, Object> params) throws EnhanceFrameworkException;
针对上面的UserInfo
数据实体,对应的Mapper
接口定义如下所示:
/**
* 用户数据接口
*
* @author:恒宇少年 - 于起宇
* <p>
* DateTime:2019-05-23 17:02
* Blog:https://blog.yuqiyu.com
* WebSite:http://www.jianshu.com/u/092df3f77bca
* Gitee:https://gitee.com/hengboy
* GitHub:https://github.com/hengboy
*/
public interface UserMapper extends EnhanceMapper<UserInfo, String> {
}
在上面UserMapper
拥有了EnhanceMapper
的全部接口方法并且每一个方法都已经实现了对应的Statement
,而且UserMapper
已经被Spring IOC
托管,我们在实体类内可以直接通过注入
方法来调用使用。
EnhanceMapper
所提供的insert
方法参数为数据对象实例,添加一条数据如下所示:
@Service
@Transactional(rollbackFor = Exception.class)
public class UserService {
/**
* 用户基本信息数据接口
*/
@Autowired
private UserMapper userMapper;
/**
* 保存用户信息
*/
public void saveUser() {
// 创建用户基本信息对象
UserInfo userInfo = new UserInfo();
// 设置用户编号
userInfo.setUserNo("2019xxxx");
// 设置用户年龄
userInfo.setAge(24);
// 执行数据保存
userMapper.insert(userInfo);
}
}
通过上面saveUser
就可以执行保存一条用户基本信息,在上面保存数据方法中并未设置主键的值,下面详细介绍Enhance
内提供的几种主键生成策略。
Enhance
支持自增类型的主键,可以将本次添加数据的自增主键值查询并设置到保存对象配置@Id
注解的字段内,配置使用如下所示:
/**
* 用户主键
*/
@Id(generatorType = KeyGeneratorTypeEnum.AUTO)
@Column(name = "UI_ID")
private Integer userId;
如果主键使用UUID
方式进行存储,Enhance
同样是支持配置,如下所示:
/**
* 用户主键
*/
@Id(generatorType = KeyGeneratorTypeEnum.UUID)
@Column(name = "UI_ID")
private String id;
注意:数据库设置主键字段长度时,
UUID
生成的长度为36
位,包含-
。
除了自增、UUID方式的主键以外,还支持自定义主键
的方式,比如用户编号:2019xxxxx
,如下所示:
/**
* 用户主键
*/
@Id(generatorType = KeyGeneratorTypeEnum.DIY)
@Column(name = "UI_ID")
private String id;
EnhanceMapper
提供了多种查询方法,其中包含主键查询
、根据主键的集合查询
、分页查询
等,下面详细介绍具体的使用方法。
Enhance
支持三种根据主键查询的方法,如下所示:
// 查询单条记录
UserInfo user = mapper.selectOne("test");
// 根据主键数组查询多条记录
List<UserInfo> userArray = mapper.selectArray("test", "admin");
// 根据主键集合查询多条记录
List<UserInfo> userCollection = mapper.selectCollection(Arrays.asList("test", "admin"));
其中selectArray
、selectCollection
两个方法是根据传递的多个主键进行查询出对应多条记录。
Enhance
支持查询表内的全部数据、分页查询数据,如下所示:
// 查询全部数据
List<UserInfo> users = mapper.selectAll();
// 分页查询数据
List<UserInfo> pageUsers = mapper.selectByPageable(Pageable.builder().limit(10).currentPage(2).build());
EnhanceMapper
接口提供的查询记录列表
的方法,有一定的局限性,目前并不支持添加查询条件进行筛选数据,不过ApiBoot Enhance
提供了DSL
方式进行查询数据,具体使用方法请参考本章节下面的文档内容。
EnhanceMapper
提供更新单个对象的方法,具体使用方法如下所示:
UserInfo userInfo = new UserInfo();
userInfo.setId("test");
userInfo.setAge(20);
userInfo.setUserName("测试用户");
mapper.update(userInfo);
更新数据时,不设置值得字段会自动将表内数据更新为null,如果你只想更新某一个字段,可以使用如下两种形式。
查询后更新
// 根据主键查询记录
UserInfo userInfo = mapper.selectOne("test");
// 更新查询出记录的年龄
userInfo.setAge(24);
// 更新查询出记录的名称
userInfo.setUserName("测试用户");
// 执行更新记录
mapper.update(userInfo);
update
方法调用时,参数对象的主键必须设置值,更新时自动根据主键进行设置。
动态更新
详见本章动态更新文档,支持根据主键
、自定义条件
进行筛选定位更新的记录。
EnhanceMapper
提供的删除方法都是物理删除
,会从表内直接将匹配数据删除,EnhanceMapper
提供的删除方法如下所示:
// 根据主键删除单条数据记录
mapper.deleteOne(1);
// 根据主键数组删除多条对应数据记录
mapper.deleteArray(1, 2, 3, 4);
// 根据主键集合删除多条对应数据记录
mapper.deleteCollection(Arrays.asList(1, 2, 3, 4));
// 删除全部记录
mapper.deleteAll();
如果你的业务需求是业务逻辑删除
,只更新某一个状态字段的值时,可以考虑使用动态更新
,详见下面文档。
ApiBoot Enhance
的动态查询可以做很多事情,我们可以对单表数据
、多表数据
进行查询操作,可以使用某一个数据对象
作为查询的返回值映射,也可以自定义DTO
、基本数据类型
来作为查询方法的返回值,每一个查询的查询条件
都可以做到完全自定义,实现一行代码完成对数据的查询。
在通过ApiBoot Enhance
动态操作数据之前需要对应每一张表创建一个动态实体
,ApiBoot
提供了对应每一张表自动生成数据实体
、动态实体
的maven plugin
,访问ApiBoot Enhance Codegen查看具体使用方式。
下面是一个动态实体
示例,通过ApiBoot Enhance Codegen
自动生成,对应数据库内user_info
表结构。
/**
* 用户基本信息
* @author ApiBoot Mybatis Enhance Codegen
*/
public class DUserInfo extends TableExpression<UserInfo> {
public DUserInfo(String root) {
super(root);
}
public static DUserInfo DSL() {
return new DUserInfo("user_info");
}
/**
* 用户主键
*/
public ColumnExpression id = new ColumnExpression("UI_ID", this);
/**
* 用户编号
*/
public ColumnExpression userNo = new ColumnExpression("UI_USER_NO", this);
/**
* 用户名
*/
public ColumnExpression userName = new ColumnExpression("UI_USER_NAME", this);
/**
* 年龄
*/
public ColumnExpression age = new ColumnExpression("UI_AGE", this);
/**
* 性别,boy:男,girl:女,other:其他
*/
public ColumnExpression sex = new ColumnExpression("UI_SEX", this);
/**
* 创建时间
*/
public ColumnExpression createTime = new ColumnExpression("UI_CREATE_TIME", this);
/**
* 1:正常,0:禁用,-1:删除
*/
public ColumnExpression status = new ColumnExpression("UI_STATUS", this);
/**
* 备注信息
*/
public ColumnExpression mark = new ColumnExpression("UI_MARK", this);
@Override
public ColumnExpression[] getColumns() {
return new ColumnExpression[]{id, userNo, userName, age, sex, createTime, status, mark};
}
}
下面从查询单表
内的数据开始一一拆分讲解。
我们了解了EnhanceMapper
接口所提供了根据主键查询的selectOne
方法,这两种方式最终的效果是一样的,动态方式
的主键查询如下所示:
// 实例化动态查询实体
DUserInfo dUserInfo = DUserInfo.DSL();
// 从DUserInfo动态实体对应的数据表内查询主键为1的记录
UserInfo user = dslFactory.createSearchable().selectFrom(dUserInfo)
.where(dUserInfo.id.eq(1))
// 查询的返回值类型是UserInfo数据实体
.resultType(UserInfo.class)
// 查询单条数据
.fetchOne();
通过动态方式查询,可以根据动态实体
内的任意一个
、或者多个
字段作为查询条件,如下所示:
// 实例化动态查询实体
DUserInfo dUserInfo = DUserInfo.DSL();
// 根据userNo字段查询数据
UserInfo user = dslFactory.createSearchable().selectFrom(dUserInfo)
.where(dUserInfo.userNo.eq("2019xxx"))
.resultType(UserInfo.class)
.fetchOne();
一个查询的查询条件
可以有很多个,使用动态查询可以完成and
、or
的条件关联,模拟需求查询用户编号为2019xxx
并且大于20岁
的男孩
指定某一个用户,如下所示:
// 实例化动态查询实体
DUserInfo dUserInfo = DUserInfo.DSL();
// 根据userNo字段查询数据
UserInfo userInfo = dslFactory.createSearchable().selectFrom(dUserInfo)
// where userNo = 2019xxx
.where(dUserInfo.userNo.eq("2019xxx"))
// and sex = boy
.and(dUserInfo.sex.eq("boy"))
// and age > 20
.and(dUserInfo.age.gt(20))
.resultType(UserInfo.class)
.fetchOne();
上面动态查询对应生成的SQL
,如下所示:
-- 生成的SQL
Preparing: SELECT iot_user_info.UI_ID,
iot_user_info.UI_USER_NO,
iot_user_info.UI_USER_NAME,
iot_user_info.UI_AGE,
iot_user_info.UI_SEX,
iot_user_info.UI_CREATE_TIME,
iot_user_info.UI_STATUS,
iot_user_info.UI_MARK
FROM iot_user_info
WHERE (iot_user_info.UI_USER_NO = ? AND iot_user_info.UI_SEX = ? AND iot_user_info.UI_AGE > ?);
-- 参数列表
Parameters: 2019xxx(String), boy(String), 20(Integer)
下面我们模拟需求查询25岁以下用户
或 性别为女孩
的用户列表,动态查询如下所示:
// 实例化动态查询实体
DUserInfo dUserInfo = DUserInfo.DSL();
// 根据userNo字段查询数据
List<UserInfo> users = dslFactory.createSearchable().selectFrom(dUserInfo)
// sex = girl
.where(dUserInfo.sex.eq("girl"))
// or age < 25
.or(dUserInfo.age.lt(25))
.resultType(UserInfo.class)
.fetch();
上面动态查询对应生成的SQL
如下所示:
-- 生成的SQL
Preparing: SELECT iot_user_info.UI_ID,
iot_user_info.UI_USER_NO,
iot_user_info.UI_USER_NAME,
iot_user_info.UI_AGE,
iot_user_info.UI_SEX,
iot_user_info.UI_CREATE_TIME,
iot_user_info.UI_STATUS,
iot_user_info.UI_MARK
FROM iot_user_info
WHERE (iot_user_info.UI_SEX = ?)
OR (iot_user_info.UI_AGE < ?);
-- 参数列表
Parameters: girl(String), 25(Integer)
ApiBoot Enhance
支持leftJoin
、rightJoin
、innerJoin
三种关联方式查询,这三种关联查询的使用方式一致,下面我们来看下多表关联的leftJoin
实现方式:
// 系统用户动态实体
DSystemUser dSystemUser = DSystemUser.DSL();
// 系统用户角色关联动态实体
DSystemUserRole dSystemUserRole = DSystemUserRole.DSL();
// 执行查询并且返回SystemUser类型对象
List<UserInfo> users = dslFactory.createSearchable()
.selectFrom(dSystemUser)
.leftJoin(dSystemUser.id, dSystemUserRole.userId)
.where(dSystemUserRole.roleId.eq("367c8078-a1f1-11e9-9b7e-3417eb9c0f80"))
.and(dSystemUser.status.eq(1))
.resultType(SystemUser.class)
.fetch();
在上面查询定义中,我们查询用户角色为367c8078-a1f1-11e9-9b7e-3417eb9c0f80
的用户列表,并且用户的状态为1
,在leftJoin
方法内有两个动态实体的字段,这两个字段则是在表内存在主外键关系
的(当然不存在主外键关系我们也可以进行关联查询)。
leftJoin
方法参数分别代表了主外键的字段配置,具体配置内容如下所示:
// 可以配置多个leftJoin进行关联多表查询
leftJoin(主表.主键,从表.外键);
上面查询代码对应生成SQL
如下所示:
SELECT iot_system_user.SU_ID, iot_system_user.SU_USER_NAME, iot_system_user.SU_NICK_NAME, iot_system_user.SU_PASSWORD, iot_system_user.SU_STATUS, iot_system_user.SU_CREATE_TIME, iot_system_user.SU_MARK FROM iot_system_user LEFT OUTER JOIN iot_system_user_role on iot_system_user_role.SUR_USER_ID = iot_system_user.SU_ID WHERE (iot_system_user_role.SUR_ROLE_ID = ? AND iot_system_user.SU_STATUS = ?)
有时我们在进行关联或者单表查询时,只需要获取其中的某一个、多个字段,这时我们就可以通过resultType
方法来定义返回的类型,如果是基本数据类型的封装类型(如:Integer
、Long
等)可以直接返回,如果查询结果是非单列数值时,我们可以自定义返回类型
来进行查询结果映射
,简单示例如下所示:
/**
* 自定义系统用户数据转换实体
*
* @author:恒宇少年 - 于起宇
* <p>
* DateTime:2019-07-10 09:08
* Blog:https://blog.yuqiyu.com
* WebSite:http://www.jianshu.com/u/092df3f77bca
* Gitee:https://gitee.com/hengboy
* GitHub:https://github.com/hengboy
*/
@Data
public class SystemUserDTO {
/**
* 主键
*/
@Column(name = "SU_ID")
private String id;
/**
* 用户名
*/
@Column(name = "SU_USER_NAME")
private String userName;
/**
* 用户昵称
*/
@Column(name = "SU_NICK_NAME")
private String nickName;
}
上面是我们定义的数据转换实体,用于接收查询数据表内的多个字段
,查询代码如下所示:
// 系统用户动态查询实体
DSystemUser dSystemUser = DSystemUser.DSL();
List<UserInfo> users = dslFactory.createSearchable()
// 只查询id、userName、nickName对应的列值
.select(dSystemUser.id, dSystemUser.userName, dSystemUser.nickName)
// from table
.from(dSystemUser)
.where(dSystemUser.status.eq(1))
// 自定义实体类类型定义
.resultType(SystemUserDTO.class)
.fetch();
上面查询代码对应生成的SQL
如下所示:
SELECT iot_system_user.SU_ID, iot_system_user.SU_USER_NAME, iot_system_user.SU_NICK_NAME FROM iot_system_user WHERE (iot_system_user.SU_STATUS = ?)
ApiBoot Enhance
在查询时,查询结果集的列值
与实体类内的字段值
对应关系是通过@Column
注解的name
属性来定的,如果查询结果集内的列名
与实体类的字段名
一致,那么@Column
注解可以不配置,如果不一致需要在实体类字段上
配置@Column(name='列名')
.
ApiBoot Enhance
支持动态组装查询对象Searchable
,当我们根据业务逻辑进行判断时可以动态组装查询条件、关联表、返回值、查询字段等,简单示例如下所示:
public List<SystemUser> assembleQuery(boolean isJoin) {
// 系统用户动态查询对象
DSystemUser dSystemUser = DSystemUser.DSL();
// 创建Searchable查询对象
Searchable searchable = dslFactory.createSearchable().selectFrom(dSystemUser)
.where(dSystemUser.status.eq(1));
// 根据参数组装关联查询
if (isJoin) {
// 系统用户角色关联查询对象
DSystemUserRole dSystemUserRole = DSystemUserRole.DSL();
searchable.leftJoin(dSystemUser.id, dSystemUserRole.userId);
}
// 设置返回值类型 & 查询数据
return searchable.resultType(SystemUser.class).fetch();
}
上面查询代码中只是根据参数组装了leftJoin
关联表,ApiBoot Enhance
的组装动态查询还待开发者挖掘。
ApiBoot Enhance
的动态查询不仅仅可以做数据查询
,也可以做数据统计
,使用count函数以及查询条件就可以实现复杂的数据统计功能,示例如下所示:
// 系统用户动态查询实体
DSystemUser dSystemUser = DSystemUser.DSL();
// 系统用户角色动态查询实体
DSystemUserRole dSystemUserRole = DSystemUserRole.DSL();
Long count = dslFactory.createSearchable()
// 建议使用主键统计,主键索引效率会有显著提升
.count(dSystemUser.id)
.from(dSystemUser)
.leftJoin(dSystemUser.id, dSystemUserRole.userId)
.where(dSystemUser.status.eq(1))
// 统计的结果类型
.resultType(Long.class)
// 查询单个结果
.fetchOne();
数据统计
的查询条件跟数据查询
完全一致,可以进行单表、多表关联、组装查询条件等。
上面数据统计
动态查询生成的SQL
如下所示:
SELECT count(iot_system_user.SU_ID) SU_ID FROM iot_system_user LEFT OUTER JOIN iot_system_user_role on iot_system_user_role.SUR_USER_ID = iot_system_user.SU_ID WHERE (iot_system_user.SU_STATUS = ?)
ApiBoot Enhance
还支持针对某一个字段的平均值
查询,使用avg函数以及查询条件可以实现复杂的平均值计算,示例如下所示:
// 系统用户动态查询实体
DSystemUser dSystemUser = DSystemUser.DSL();
Integer avgAge = dslFactory.createSearchable()
// 查询用户年龄平均值
.avg(dSystemUser.age)
.from(dSystemUser)
// 用户状态为1
.where(dSystemUser.status.eq(1))
// 返回值类型为Integer,根据age对应表内字段而定
.resultType(Integer.class)
// 查询单个结果
.fetchOne();
上面动态查询生成的SQL
,如下所示:
SELECT avg(iot_system_user.SU_AGE) SU_AGE FROM iot_system_user WHERE (iot_system_user.SU_STATUS = ?)
ApiBoot Enhance
支持针对某一个字段的总和
查询,使用sum函数以及查询条件可以实现复杂的总和计算,示例如下所示:
// 系统用户动态查询实体
DSystemUser dSystemUser = DSystemUser.DSL();
Long sum = dslFactory.createSearchable()
// 查询用户年龄总和
.sum(dSystemUser.age)
.from(dSystemUser)
// 用户状态为1
.where(dSystemUser.status.eq(1))
// 返回值类型为Integer,根据age对应表内字段而定
.resultType(Long.class)
// 查询单个结果
.fetchOne();
上面动态查询生成的SQL
,如下所示:
SELECT sum(iot_system_user.SU_AGE) SU_AGE FROM iot_system_user WHERE (iot_system_user.SU_STATUS = ?)
ApiBoot Enhance
支持针对某一个字段的最小值
查询,使用min函数以及查询条件可以实现复杂查询指定字段的最小值计算,示例如下所示:
// 系统用户动态查询实体
DSystemUser dSystemUser = DSystemUser.DSL();
Integer min = dslFactory.createSearchable()
// 查询最小年龄的用户
.min(dSystemUser.age)
.from(dSystemUser)
// 用户状态为1
.where(dSystemUser.status.eq(1))
// 返回值类型为Integer,根据age对应表内字段而定
.resultType(Integer.class)
// 查询单个结果
.fetchOne();
上面动态查询生成的SQL
,如下所示:
SELECT min(iot_system_user.SU_AGE) SU_AGE FROM iot_system_user WHERE (iot_system_user.SU_STATUS = ?)
ApiBoot Enhance
支持针对某一个字段的最大值
查询,使用max函数以及查询条件可以实现指定字段复杂查询的最大值计算,示例如下所示:
// 系统用户动态查询实体
DSystemUser dSystemUser = DSystemUser.DSL();
Integer max = dslFactory.createSearchable()
// 查询最大年龄的用户
.max(dSystemUser.age)
.from(dSystemUser)
// 用户状态为1
.where(dSystemUser.status.eq(1))
// 返回值类型为Integer,根据age对应表内字段而定
.resultType(Integer.class)
// 查询单个结果
.fetchOne();
上面动态查询生成的SQL
,如下所示:
SELECT max(iot_system_user.SU_AGE) SU_AGE FROM iot_system_user WHERE (iot_system_user.SU_STATUS = ?)
EnhanceMapper
接口内提供了update
方法可以更新整个对象,如果我们只更新其中某一个、多个字段时update
方法无法满足我们的需求,而ApiBoot Enhance DSL
的动态更新可以完成这一需求,示例如下所示:
// 系统用户动态实体
DSystemUser dSystemUser = DSystemUser.DSL();
dslFactory.createUpdateable()
// 需要更新的表对应的动态实体
.update(dSystemUser)
// 更新age对应列值 = 25
.set(SetFilter.set(dSystemUser.age, 25))
// 更新mark对应列值 = 备注信息
.set(SetFilter.set(dSystemUser.mark, "备注信息"))
// 指定更新用户
.where(dSystemUser.id.eq("58eea57e-a1f1-11e9-9b7e-3417eb9c0f80"))
// 执行更新
.execute();
上面动态更新语句对应生成的SQL
,如下所示:
-- sql
Preparing: UPDATE iot_system_user SET iot_system_user.SU_AGE = ? , iot_system_user.SU_MARK = ? WHERE (iot_system_user.SU_ID = ?)
-- 参数
Parameters: 25(Integer), 备注信息(String), 58eea57e-a1f1-11e9-9b7e-3417eb9c0f80(String)
EnhanceMapper
接口提供的delete
方法只可以根据主键操作数据,我们通过ApiBoot Enhnace DSL
可以完成指定条件筛选删除,示例如下所示:
// 系统用户动态实体
DSystemUser dSystemUser = DSystemUser.DSL();
dslFactory.createDeleteable()
// 删除动态实体对应表内的数据
.delete(dSystemUser)
// status对应列值 = 1
.where(dSystemUser.status.eq(1))
// age对应列值 > 25
.and(dSystemUser.age.gt(25))
// 执行删除
.execute();
上面动态删除语句对应生成的SQL
,如下所示:
-- sql
Preparing: DELETE FROM iot_system_user WHERE (iot_system_user.SU_STATUS = ? AND iot_system_user.SU_AGE > ?)
-- 参数
Parameters: 1(Integer), 25(Integer)
ApiBoot Enhance
动态查询支持根据查询字段
进行正序
、倒序
两种方式进行排序,如下所示:
// 系统用户动态实体
DSystemUser dSystemUser = DSystemUser.DSL();
List<SystemUser> users = dslFactory.createSearchable()
.selectFrom(dSystemUser)
// 查询status字段对应列值 = 1
.where(dSystemUser.status.eq(1))
// 根据age字段对应列值,正序排序,年龄从小到大排序
.orderBy(dSystemUser.age, SortEnum.ASC)
.resultType(SystemUser.class)
.fetch();
SortEnum
是一个排序的枚举,参考值如下所示:
ASC
:正序DESC
:倒序上面动态查询排序语句对应生成的SQL
,如下所示:
SELECT iot_system_user.SU_ID, iot_system_user.SU_USER_NAME, iot_system_user.SU_NICK_NAME, iot_system_user.SU_AGE, iot_system_user.SU_PASSWORD, iot_system_user.SU_STATUS, iot_system_user.SU_CREATE_TIME, iot_system_user.SU_MARK FROM iot_system_user WHERE (iot_system_user.SU_STATUS = ?) ORDER BY iot_system_user.SU_AGE ASC
ApiBoot Enhance
在使用动态查询
时可以对结果集
进行分页
,简单示例如下所示:
// 系统用户动态实体
DSystemUser dSystemUser = DSystemUser.DSL();
List<SystemUser> users = dslFactory.createSearchable()
.selectFrom(dSystemUser)
// 查询status字段对应列值 = 1
.where(dSystemUser.status.eq(1))
// 根据age字段对应列值,正序排序,年龄从小到大排序
.orderBy(dSystemUser.age, SortEnum.ASC)
// 分页的开始位置,0是第一条
.offset(0)
// 每页查询20条
.limit(20)
.resultType(SystemUser.class)
.fetch();
上面动态查询系统用户表内
status=1
的用户列表,并且根据age
字段进行ASC
方式排序,查询出0-20
条数据。
ApiBoot Enhance
的动态分页提供了两个函数,分别是:offset
、limit
。
offset()
:分页开始位置,从0开始limit()
:每页查询记录数量上面分页动态查询对应生成的SQL
,如下所示:
-- sql
Preparing: SELECT iot_system_user.SU_ID, iot_system_user.SU_USER_NAME, iot_system_user.SU_NICK_NAME, iot_system_user.SU_AGE, iot_system_user.SU_PASSWORD, iot_system_user.SU_STATUS, iot_system_user.SU_CREATE_TIME, iot_system_user.SU_MARK FROM iot_system_user WHERE (iot_system_user.SU_STATUS = ?) ORDER BY iot_system_user.SU_AGE ASC limit ?,?
-- 参数
Parameters: 1(Integer), 0(Integer), 20(Integer)
ApiBoot Enhance
支持对指定字段查询分组,示例如下所示:
// 系统用户动态实体
DSystemUser dSystemUser = DSystemUser.DSL();
List<SystemUser> users = dslFactory.createSearchable()
// 查询分组的字段
// 最小年龄
.select(dSystemUser.userName, dSystemUser.age.min())
.from(dSystemUser)
// 查询status字段对应列值 = 1
.where(dSystemUser.status.eq(1))
// 根据userName字段对应的列分组
.groupBy(dSystemUser.userName)
.resultType(SystemUser.class)
.fetch();
在上面动态查询中,根据
userName
对应的列值进行分组。
上面分页动态查询对应生成的SQL
,如下所示:
-- sql
Preparing: SELECT iot_system_user.SU_USER_NAME, min(iot_system_user.SU_AGE) SU_AGE FROM iot_system_user WHERE (iot_system_user.SU_STATUS = ?) GROUP BY iot_system_user.SU_USER_NAME
-- 参数
Parameters: 1(Integer)
ApiBoot Enhance
提供的方法规则查询
不同于动态查询
,并不需要编写过多的代码就可以实现多条件查询单条、多条数据,只需要通过在Mapper
接口内根据方法命名的规则来生成对应的SqlSource
以及Statement
。
方法命名规则
内部是通过正则表达式
进行提取查询的字段。
查询方法命名规则前缀为:findBy,根据某一个字段查询时可以写成findByXxx
,如果根据多个字段则写成findByXxxAndXxx
,当然也可以根据Or
关键字进行查询,注意完全遵循驼峰命名方式
来声明方法的名称
,方法的返回值必须是声明方法内Mapper
对应数据实体类型
,可以是单个对象
、也可以是List<对象>
。
删除方法命名规则前缀为:removeBy,根据某一个字段删除数据时可以写成remoteByXxx
,如果根据多个字段则可以写成remoteByXxxAndXxx
,删除方法没有返回值。
统计方法命名规则前缀为:countBy,根据主键进行统计数据,根据某一个字段作为查询条件可以成countByXxx
,如果根据多个字段则可以写成countByXxxAndXxx
,方法返回值为Long
类型。
注意:方法命名规则的关联关系可以使用
And
也可以使用Or
,尽量排除两种关系字符存在一个方法名称上。
通过下面方法命名规则
定义的方法来查看ApiBoot Enhance
对应给生成的查询语句:
public interface SystemUserMapper extends EnhanceMapper<SystemUser, String> {
/**
* 根据userName查询单条记录
*
* @param userName
* @return
*/
SystemUser findByUserName(@Param("userName") String userName);
/**
* 根据userName and status 查询
*
* @param userName
* @param userStatus
* @return
*/
SystemUser findByUserNameAndStatus(@Param("userName") String userName, @Param("status") Integer userStatus);
/**
* 根据status查询多条记录
*
* @param userStatus
* @return
*/
List<SystemUser> findByStatus(@Param("status") Integer userStatus);
}
方法规则查询
有局限性,并不如动态查询
灵活。
对应上面查询方法生成的SQL
,如下所示:
-- findByUserName
SELECT SU_ID, SU_USER_NAME, SU_NICK_NAME, SU_AGE, SU_PASSWORD, SU_STATUS, SU_CREATE_TIME, SU_MARK FROM IOT_SYSTEM_USER WHERE SU_USER_NAME = ?
-- findByUserNameAndStatus
SELECT SU_ID, SU_USER_NAME, SU_NICK_NAME, SU_AGE, SU_PASSWORD, SU_STATUS, SU_CREATE_TIME, SU_MARK FROM IOT_SYSTEM_USER WHERE SU_USER_NAME = ? AND SU_STATUS = ?
-- findByStatus
SELECT SU_ID, SU_USER_NAME, SU_NICK_NAME, SU_AGE, SU_PASSWORD, SU_STATUS, SU_CREATE_TIME, SU_MARK FROM IOT_SYSTEM_USER WHERE SU_STATUS = ?
ApiBoot Enhance
提供的方法命名规则统计,默认统计主键列
目前无法修改默认方式,方法命名规则统计数据方法名定义方式如下所示:
public interface SystemUserMapper extends EnhanceMapper<SystemUser, String> {
//...
/**
* 根据状态统计
*
* @param status
* @return
*/
Long countByStatus(@Param("status") Integer status);
/**
* 根据用户名 and 状态统计
*
* @param userName
* @param userStatus
* @return
*/
Long countByUserNameAndStatus(@Param("userName") String userName, @Param("status") Integer userStatus);
}
对应上面统计方法生成的SQL
,如下所示:
-- countByStatus
SELECT COUNT(SU_ID) FROM IOT_SYSTEM_USER WHERE SU_STATUS = ?
-- countByUserNameAndStatus
SELECT COUNT(SU_ID) FROM IOT_SYSTEM_USER WHERE SU_USER_NAME = ? AND SU_STATUS = ?
方法名称规则删除示例如下所示:
public interface SystemUserMapper extends EnhanceMapper<SystemUser, String> {
/**
* 根据状态删除
*
* @param status
*/
void removeByStatus(@Param("status") Integer status);
/**
* 根据用户名 and 状态删除
*
* @param userName
* @param userStatus
*/
void removeByUserNameAndStatus(@Param("userName") String userName, @Param("status") Integer userStatus);
}
对应上面删除方法生成的SQL
,如下所示:
-- removeByStatus
DELETE FROM IOT_SYSTEM_USER WHERE SU_STATUS = ?
-- removeByUserNameAndStatus
DELETE FROM IOT_SYSTEM_USER WHERE SU_USER_NAME = ? AND SU_STATUS = ?