Topfox 1.2.4 发布,快速开发框架

1. 用户使用手册 - 目录

1.1. 必备

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

  • 开启一级缓存后
  1. 一级缓存是只当前线程级别的, 线程结束则缓存消失

  2. 下面的例子, 在开启一级缓后 user1,user2和user3是一个实例的

  3. 一级缓存的效果我们借鉴了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. 流水号生成器 等等更多功能这里不在描述

猜你喜欢

转载自www.oschina.net/news/108522/topfox-1-2-4-released