内容简介: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框架的权限控制管理
- 用大型开发框架开发小程序那点事儿
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Programming Amazon Web Services
James Murty / O'Reilly Media / 2008-3-25 / USD 49.99
Building on the success of its storefront and fulfillment services, Amazon now allows businesses to "rent" computing power, data storage and bandwidth on its vast network platform. This book demonstra......一起来看看 《Programming Amazon Web Services》 这本书的介绍吧!