内容简介:1. 用户使用手册 - 目录 快速使用 高级运用 TopFox配置参数 上下文对象 核心使用 条件匹配器 实体查询构造器 流水号生成器 数据校验组件 更新日志组件 自动填充组件 Response 返回结果对象 1.1. 必备 文中涉及的例...
1. 用户使用手册 - 目录
1.1. 必备
-
文中涉及的例子源码下载: https://gitee.com/topfox/topfox-sample
-
TopFox技术交流群 QQ: 874732179
1.2. topfox 介绍
在 srpingboot2.x.x 和MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。
编程规范参考《阿里巴巴 Java 开发手册》
借鉴mybaties plus部分思想
特性:
-
无侵入:只做增强不做改变,引入它不会对现有工程产生影响
-
损耗小:启动即会自动注入基本 CURD,性能基本无损耗,直接面向对象操作
-
集成 Redis 缓存: 自带Redis缓存功能, 支持多主键模式, 自定义redis-key. 实现对数据库的所有操作, 自动更新到Redis, 而不需要你自己写任何代码; 当然也可以针对某个表关闭.
-
强大的 CRUD 操作:内置通用 Mapper、通用 Service,仅仅通过少量配置即可实现单表大部分 CRUD 操作,更有强大的条件构造器,满足各类使用需求
-
支持 Lambda 形式调用:通过 Lambda 表达式,方便的编写各类查询条件,无需再担心字段写错
-
支持主键自动生成:可自由配置,充分利用Redis提高性能, 完美解决主键问题. 支持多主键查询、修改等
-
内置分页实现:基于 MyBatis 物理分页,开发者无需关心具体操作,写分页等同于普通查询
-
支持devtools/jrebel热部署
-
热加载 支持在不使用devtools/jrebel的情况下, 热加载 mybatis的mapper文件
-
内置全局、局部拦截插件:提供delete、update 自定义拦截功能
-
拥有预防 Sql 注入攻击功能
-
无缝支持spring cloud: 后续提供分布式调用的例子
2. 更新日志
2.1. 版本1.2.4 更新日志 2019-07-24
- 全局缓存参数开关
新增 一级缓存开关 top.service.thread-cache
新增 二级缓存开关 top.service.redis-cache
删除 top.service.open-redis
-
多主键的支持, 包括: 更新, 删除, 查询, 数据校验组件, 修改日志组件;
-
java远程调用返回空对象的处理;
-
技术文档修改
3. 快速入门
3.1. 入门例子: 以用户表为例, 开发者只需要完成以下4步的代码, 就能实现很多复杂的功能
3.1.1. 新建实体对象 UserDTO
@Setter
@Getter
@Accessors(chain = true)
@Table(name = "users", cnName = "用户表")
public class UserDTO extends DataDTO {
@Id private Integer id;
private String code;
private String name;
private String password;
private String sex;
private Integer age;
...等
}
3.1.2. 新建查询条件对象Query( 即UserQTO )
@Setter
@Getter
@Accessors(chain = true)
@Table(name = "users")
public class UserQTO extends DataQTO {
private String id;
private String code;
private String name;
private String nameOrEq;
private String sex;
private Date lastDateFrom;
private Date lastDateTo;
}
3.1.3. 新建UserDao
@Component
public interface UserDao extends BaseMapper<UserDTO> {
/**
* 自定方法 mapper.xml 代码略
* @param qto
* @return
*/
UserDTO test(UserQTO qto);
}
3.1.4. 新建 UserService
@Service
public class userService extends SimpleService<UserDao, UserDTO> {
@Override
public int insert(UserDTO dto) {
return super.insert(dto);
}
@Override
public int update(UserDTO dto) {
return super.update(dto);
}
@Override
public int deleteByIds(Number... ids) {
return super.deleteByIds(ids);
}
@Override
public int deleteByIds(String... ids) {
return super.deleteByIds(ids);
}
//以上4个方法的代码可以删除, 没什么逻辑, 这里只是告诉读者有这些方法, 但父类的方法远远不止这4个
/**
* 自定的方法
* @param qto
* @return
*/
public List<userDTO> test(UserQTO qto) {
return baseMapper.test(qto);
}
}
实现哪些具体的功能呢, 详见后面的章节
3.2. 功能强大的查询
3.2.1. 条件匹配器Condition 查询一
以下仅仅是条件匹配器的部分功能, 更多功能等待用户挖掘.
@RestController
@RequestMapping("/condition")
public class ConditionController {
@Autowired
UserService userService;
/**
* 条件匹配器的一个例子
*/
@GetMapping("/query1")
public List<UserDTO> query1(){
//**查询 返回对象 */
List<UserDTO> listUsers = userService.listObjects(
Condition.create() //创建条件匹配器对象
.between("age",10,20) //生成 age BETWEEN 10 AND 20
.eq("sex","男") //生成 AND(sex = '男')
.eq("name","C","D","E")//生成 AND(name = 'C' OR name = 'D' OR name = 'E')
.like("name","A", "B") //生成 AND(name LIKE '%A%' OR name LIKE '%B%')
//不等
.ne("name","张三","李四")
//等同于 .eq("substring(name,2)","平")
.add("substring(name,2)='平' ")//自定义条件
.le("loginCount",1)//小于等于
.lt("loginCount",2)//小于
.ge("loginCount",4)//大于等于
.gt("loginCount",3)//大于
.isNull("name")
.isNotNull("name")
);
return listUsers;
}
}
生成的WHERE条件如下:
SELECT id,code,name,password,sex,age,amount,mobile,isAdmin,loginCount,lastDate,deptId,createUser,updateUser
FROM users a
WHERE age BETWEEN 10 AND 20
AND (sex = '男')
AND (name = 'C' OR name = 'D' OR name = 'E')
AND (name LIKE '%A%' OR name LIKE '%B%')
AND (name <> '张三' AND name <> '李四')
AND substring(name,2)='平'
AND (loginCount <= 1)
AND (loginCount < 2)
AND (loginCount >= 4)
AND (loginCount > 3)
AND name is null
AND name is not null
LIMIT 0,6666
3.2.2. 条件匹配器Condition 查询二
@RestController
@RequestMapping("/condition")
public class ConditionController {
@Autowired
UserService userService;
@GetMapping("/query2")
public List<UserDTO> query2(){
//**查询 返回对象 */
List<UserDTO> listUsers = userService.listObjects(
userService.where() // 等同于 Condition.create() 创建一个条件匹配器对象
.eq("concat(name,id)","A1") //生成 (concat(name,id) = 'A1')
.eq("concat(name,id)","C1","D2","E3")//生成 AND (concat(name,id) = 'C1' OR concat(name,id) = 'D2' OR concat(name,id) = 'E3' )
);
return listUsers;
}
}
生成的WHERE条件如下:
SELECT id,code,name,password,sex,age,amount,mobile,isAdmin,loginCount,lastDate,deptId,createUser,updateUser
FROM users a
WHERE (concat(name,id) = 'A1')
AND (concat(name,id) = 'C1'
OR concat(name,id) = 'D2'
OR concat(name,id) = 'E3' )
3.3. 高级查询 带分组, 排序, 自定select 后字段, 指定分页的查询
利用查询构造器 EntitySelect 和 Condition的查询
/**
* 核心使用 继承了 topfox 的SimpleService
*/
@Service
public class CoreService extends SimpleService<UserDao, UserDTO> {
public List<UserDTO> demo2(){
List<UserDTO> listUsers=listObjects(
select("name, count('*')") //通过调用SimpleService.select() 获得或创建一个新的 EntitySelect 对象,并返回它
.where() //等同于 Condition.create()
.eq("sex","男") //条件匹配器自定义条件 返回对象 Condition
.endWhere() //条件结束 返回对象 EntitySelect
.orderBy("name") //设置 排序 的字段 返回对象 EntitySelect
.groupBy("name") //设置分组的字段 返回对象 EntitySelect
.setPage(10,5) //设置分页(查询第10页, 每页返回5条记录)
);
return listUsers;
}
}
输出sql如下:
SELECT name, count('*')
FROM users a
WHERE (sex = '男')
GROUP BY name
ORDER BY name
LIMIT 45,5
3.4. 查询时如何才能不读取缓存
TopFox 实现了缓存处理, 当前线程的缓存 为一级缓存, redis为二级缓存.
通过设置 readCache 为false, 能实现在开启一级/二级缓存的情况下又不读取缓存, 从而保证读取出来的数据和数据库中的一模一样, 下面通过5个例子来说明.
@RestController
@RequestMapping("/demo")
public class DemoController {
@Autowired
UserService userService;
@TokenOff
@GetMapping("/test1")
public Object test1(UserQTO userQTO) {
//例1: 根据id查询, 通过第2个参数传false 就不读取一二级缓存了
UserDTO user = userService.getObject(1, false);
//例2: 根据多个id查询, 要查询的id放入Set容器中
Set setIds = new HashSet();
setIds.add(1);
setIds.add(2);
//通过第2个参数传false 就不读取一二级缓存了
List<UserDTO> list = userService.listObjects(setIds, false);
//例3: 通过QTO 设置不读取缓存
list = userService.listObjects(
userQTO.readCache(false) //禁用从缓存读取(注意不是读写) readCache 设置为 false, 返回自己(QTO)
);
//或者写成:
userQTO.readCache(false);
list = userService.listObjects(userQTO);
//例4: 通过条件匹配器Condition 设置不读取缓存
list = userService.listObjects(
Condition.create() //创建条件匹配器
.readCache(false) //禁用从缓存读取
);
return list;
}
}
3.5. 查询 缓存开关 thread-cache redis-cache与readCache区别
请读者先阅读 章节 《TopFox配置参数》
一级缓存 top.service.thread-cache 大于 readCache
二级缓存 top.service.redis-cache 大于 readCache
也就说, 把一级二级缓存关闭了, readCache设置为true, 也不会读取缓存. 所有方式的查询也不会读取缓存.
3.6. 查询 一级缓存的效果
- 一级缓存默认是关闭的
只打开某个 service的操作的一级缓存
@Service
public class UserService extends AdvancedService<UserDao, UserDTO> {
@Override
public void init() {
sysConfig.setThreadCache(true); //打开一级缓存
}
全局开启一级缓存, 项目配置文件 application.properties 增加
top.service.thread-cache=true
- 开启一级缓存后
-
一级缓存是只当前线程级别的, 线程结束则缓存消失
-
下面的例子, 在开启一级缓后 user1,user2和user3是一个实例的
-
一级缓存的效果我们借鉴了Hibernate框架的数据实体对象持久化的思想
@RestController
@RequestMapping("/demo")
public class DemoController {
@Autowired
UserService userService;
@TokenOff
@GetMapping("/test2")
public UserDTO test2() {
UserDTO user1 = userService.getObject(1);//查询后 会放入一级 二级缓存
UserDTO user2 = userService.getObject(1);//会从一级缓存中获取到
userService.update(user2.setName("张三"));
UserDTO user3 = userService.getObject(1);//会从一级缓存中获取到
return user3;
}
}
3.7. QTO后缀增强查询
我们修改 UserQTO 的源码如下:
@Setter
@Getter
@Table(name = "users")
public class UserQTO extends DataQTO {
private String id; //用户id, 与数据字段名一样的
private String name; //用户姓名name, 与数据字段名一样的
private String nameOrEq; //用户姓名 后缀OrEq
private String nameAndNe; //用户姓名 后缀AndNe
private String nameOrLike; //用户姓名 后缀OrLike
private String nameAndNotLike;//用户姓名 后缀AndNotLike
...
}
- 字段名 后缀OrEq
当 nameOrEq 写值为 "张三,李四" 时, 源码如下:
package com.test.service;
/**
* 核心使用 demo1 源码 集成了 TopFox 的 SimpleService类
*/
@Service
public class CoreService extends SimpleService<UserDao, UserDTO> {
public List<UserDTO> demo1(){
UserQTO userQTO = new UserQTO();
userQTO.setNameOrEq("张三,李四");//这里赋值
//依据QTO查询 listObjects会自动生成SQL, 不用配置 xxxMapper.xml
List<UserDTO> listUsers = listObjects(userQTO);
return listUsers;
}
}
则生成SQL:
SELECT ...
FROM SecUser
WHERE (name = '张三' OR name = '李四')
- 字段名 后缀AndNe
当 nameAndNe 写值为 "张三,李四" 时, 则生成SQL:
SELECT ...
FROM SecUser
WHERE (name <> '张三' AND name <> '李四')
- 字段名 后缀OrLike
当 nameOrLike 写值为 "张三,李四" 时, 则将生成SQL:
SELECT ...
FROM SecUser
WHERE (name LIKE CONCAT('%','张三','%') OR name LIKE CONCAT('%','李四','%'))
- 字段名 后缀AndNotLike
当 nameAndNotLike 写值为 "张三,李四" 时, 则生成SQL:
SELECT ...
FROM SecUser
WHERE (name NOT LIKE CONCAT('%','张三','%') AND name NOT LIKE CONCAT('%','李四','%'))
以上例子是TopFox全自动生成的SQL
3.8. 更多的查询方法
-
Response< List < DTO > > listPage(EntitySelect entitySelect)
-
List< Map < String, Object > > selectMaps(DataQTO qto)
-
List< Map < String, Object > > selectMaps(Condition where)
-
List< Map < String, Object > > selectMaps(EntitySelect entitySelect)
-
selectCount(Condition where)
-
selectMax(String fieldName, Condition where)
-
等等
3.9. 自定条件更新 updateBatch
-
@param xxxDTO 要更新的数据, 不为空的字段才会更新. Id字段不能传值
-
@param where 条件匹配器
-
@return List< DTO >更新的dto集合
@Service
public class UnitTestService {
@Autowired UserService userService;
public void test(){
UserDTO dto = new UserDTO();
dto.setAge(99);
dto.setDeptId(11);
dto.addNullFields("mobile, isAdmin");//将指定的字段更新为null
List<UserDTO> list userService.updateBatch(dto, where().eq("sex","男"));
// list为更新过得记录
}
}
生成的Sql语句如下:
UPDATE users
SET deptId=11,age=99,mobile=null,isAdmin=null
WHERE (sex = '男')
3.10. 更多的 插入 和更新的代码例子
@Service
public class UnitTestService {
@Autowired UserService userService;
...
public void insert(){
//Id为数据库自增, 新增可以获得Id
UserDTO dto = new UserDTO();
dto.setName("张三");
dto.setSex("男");
userService.insertGetKey(dto);
logger.debug("新增用户的Id 是 {}", dto.getId());
}
public void update(){
UserDTO user1 = new UserDTO();
user1.setAge(99);
user1.setId(1);
user1.setName("Luoping");
//将指定的字段更新为null, 允许有空格
user1.addNullFields(" sex , lastDate , loginCount");
// //这样写也支持
// user1.addNullFields("sex","lastDate");
// //这样写也支持
// user1.addNullFields("sex, lastDate","deptId");
userService.update(user1);//只更新有值的字段
}
public void update1(){
UserDTO user1 = new UserDTO();
user1.setAge(99);
user1.setId(1);
user1.setName("Luoping");
userService.update(user1);//只更新有值的字段
}
public void updateList(){
UserDTO user1 = new UserDTO();
user1.setAge(99);
user1.setId(1);
user1.setName("张三");
user1.addNullFields("sex, lastDate");
UserDTO user2 = new UserDTO();
user2.setAge(88);
user2.setId(2);
user2.setName("李四");
user2.addNullFields("mobile, isAdmin");
List list = new ArrayList();
list.add(user1);
list.add(user2);
userService.updateList(list);//只更新有值的字段
}
数据校验组件之实战- 重复检查
假如用户表中已经有一条用户记录的 手机号是 13588330001, 然后我们再新增一条手机号相同的用户, 或者将其他某条记录的手机号更新为这个手机号, 此时我们希望 程序能检查出这个错误, CheckData对象就是干这个事的.
检查用户手机号不能重复有如下多种写法:
3.10.1. 示例一
@Service
public class CheckData1Service extends AdvancedService<UserDao, UserDTO> {
@Override
public void beforeInsertOrUpdate(List<UserDTO> list) {
//多行记录时只执行一句SQL完成检查手机号是否重复, 并抛出异常
checkData(list) // 1. list是要检查重复的数据
// 2.checkData 为TopFox在 SimpleService里面定义的 new 一个 CheckData对象的方法
.addField("mobile", "手机号") //自定义 有异常抛出的错误信息的字段的中文标题
.setWhere(where().ne("mobile","*")) //自定检查的附加条件, 可以不写(手机号为*的值不参与检查)
.excute();// 生成检查SQL, 并执行, 有结果记录(重复)则抛出异常, 回滚事务
}
}
控制台 抛出异常 的日志记录如下:
##这是 inert 重复检查 TopFox自动生成的SQL:
SELECT concat(mobile) result
FROM SecUser a
WHERE (mobile <> '*')
AND (concat(mobile) = '13588330001')
LIMIT 0,1
14:24|49.920 [4] DEBUG 182-com.topfox.util.CheckData | mobile {13588330001}
提交数据{手机号}的值{13588330001}不可重复
at com.topfox.common.CommonException$CommonString.text(CommonException.java:164)
at com.topfox.util.CheckData.excute(CheckData.java:189)
at com.topfox.util.CheckData.excute(CheckData.java:75)
at com.sec.service.UserService.beforeInsertOrUpdate(UserService.java:74)
at com.topfox.service.AdvancedService.beforeSave2(AdvancedService.java:104)
at com.topfox.service.SimpleService.updateList(SimpleService.java:280)
at com.topfox.service.SimpleService.save(SimpleService.java:451)
at com.sec.service.UserService.save(UserService.java:41)
-
异常信息的 "手机号" 是 .addField("mobile", "手机号") 指定的中文名称
-
假如用户表用两条记录, 第一条用户id为001的记录手机号为13588330001, 第一条用户id为002的记录手机号为13588330002.
<br>如果我们把第2条记录用户的手机号13588330002改为13588330001, 则会造成了 数据重复, TopFox执行的检查重复的SQL语句为:
##这是 update时重复检查 TopFox自动生成的SQL:
SELECT concat(mobile) result
FROM SecUser a
WHERE (mobile <> '*')
AND (concat(mobile) = '13588330001')
AND (id <> '002') ## 修改用户手机号那条记录的用户Id
LIMIT 0,1
通过这个例子, 希望读者能理解 新增和更新 TopFox 生成SQL不同的原因.
3.10.2. 更多例子请参考 << 数据校验组件>> 章节
3.11. 更新日志组件 ChangeManager 分布式事务 回滚有用哦
获得修改日志可写入到 mongodb 中, 控制分布式事务 回滚有用哦
读取修改日志的代码很简单, 共写了2个例子, 如下:
@Service
public class UserService extends AdvancedService<UserDao, UserDTO> {
@Override
public void afterInsertOrUpdate(UserDTO userDTO, String state) {
if (DbState.UPDATE.equals(state)) {
// 例一:
ChangeManager changeManager = changeManager(userDTO)
.addFieldLabel("name", "用户姓名") //设置该字段的日志输出的中文名
.addFieldLabel("mobile", "手机号"); //设置该字段的日志输出的中文名
//输出 方式一 参数格式
logger.debug("修改日志:{}", changeManager.output().toString() );
// 输出样例:
/**
修改日志:
id:000000, //用户的id
用户姓名:开发者->开发者2,
手机号:13588330001->1805816881122
*/
// 输出 方式二 JSON格式
logger.debug("修改日志:{}", changeManager.outJSONString() );
// 输出样例: c是 current的简写, 是当前值, 新值; o是 old的简写, 修改之前的值
/**
修改日志:
{
"appName":"sec",
"executeId":"1561367017351_14",
"id":"000000",
"data":{
"version":{"c":"207","o":206},
"用户姓名":{"c":"开发者2","o":"开发者"},
"手机号":{"c":"1805816881122","o":"13588330001"}
}
}
*/
//************************************************************************************
// 例二 没有用 addFieldLabel 设置字段输出的中文名, 则data中的keys输出全部为英文
logger.debug("修改日志:{}", changeManager(userDTO).outJSONString() );
// 输出 JSON格式
/**
修改日志:
{
"appName":"sec",
"executeId":"1561367017351_14",
"id":"000000",
"data":{
"version":{"c":"207","o":206},
"name":{"c":"开发者2","o":"开发者"},
"mobile":{"c":"1805816881122","o":"13588330001"}
}
}
*/
//************************************************************************************
}
}
}
3.12. 流水号生成器 等等更多功能这里不在描述
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:- ABP开发框架前后端开发系列---(2)框架的初步介绍
- ABP开发框架前后端开发系列---(14)基于Winform的ABP快速开发框架
- ABP开发框架前后端开发系列---(8)ABP框架之Winform界面的开发过程
- 如何基于Winform开发框架或混合框架基础上进行项目的快速开发
- ABP开发框架前后端开发系列---(9)ABP框架的权限控制管理
- 用大型开发框架开发小程序那点事儿
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。