数据库脚本请参考文件 db.sql, 共3张表: 部门表depts, 用户表users, 多主键字段表 salary
使用数据库为 mysql8
本示例项目依据 topfox 开发, 网址 https://gitee.com/topfoxs/topfox
以下内容 摘录自 《topfox 用户使用手册》, 详见 https://gitee.com/topfoxs/topfox
以用户表为例, 开发者只需要完成以下4步的代码, 就能实现很多复杂的功能
@Data
@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;
...等
}
@Data
@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;
}
@Component
public interface UserDao extends BaseMapper<UserDTO> {
/**
* 自定方法 mapper.xml 代码略
* @param qto
* @return
*/
UserDTO test(UserQTO qto);
}
@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);
}
}
实现哪些具体的功能呢, 详见后面的章节
以下仅仅是条件匹配器的部分功能, 更多功能等待用户挖掘.
@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
@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' )
利用查询构造器 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
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;
}
}
请读者先阅读 章节 《TopFox配置参数》
一级缓存 top.service.thread-cache 大于 readCache
二级缓存 top.service.redis-cache 大于 readCache
也就说, 把一级二级缓存关闭了, readCache设置为true, 也不会读取缓存. 所有方式的查询也不会读取缓存.
只打开某个 service的操作的一级缓存
@Service
public class UserService extends SimpleService<UserDao, UserDTO> {
@Override
public void init() {
sysConfig.setThreadCache(true); //打开一级缓存
}
全局开启一级缓存, 项目配置文件 application.properties 增加
top.service.thread-cache=true
@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;
}
}
只打开某个 service的操作的二级缓存
@Service
public class UserService extends SimpleService<UserDao, UserDTO> {
@Override
public void init() {
sysConfig.setRedisCache(true); //打开一级缓存
}
全局开启一级缓存, 项目配置文件 application.properties 增加
top.service.redis-cache=true
:::备注
我们修改 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
...
}
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 = '李四')
SELECT ...
FROM SecUser
WHERE (name <> '张三' AND name <> '李四')
SELECT ...
FROM SecUser
WHERE (name LIKE CONCAT('%','张三','%') OR name LIKE CONCAT('%','李四','%'))
SELECT ...
FROM SecUser
WHERE (name NOT LIKE CONCAT('%','张三','%') AND name NOT LIKE CONCAT('%','李四','%'))
以上例子是TopFox全自动生成的SQL
@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 = '男')
@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对象就是干这个事的. 检查用户手机号不能重复有如下多种写法:
@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)
##这是 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不同的原因.
获得修改日志可写入到 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"}
}
}
*/
//************************************************************************************
}
}
}
:::示例一
package com.test.service;
@Service
public class KeyBuildService extends AdvancedService<UserDao, UserDTO> {
public void test1() {
//logger为TopFox声明的日志对象
//例: 根据UserDTO中字段名id 来获取一个纯 3位数 递增的流水号
logger.debug(
keyBuild() //创建一个 KeyBuild对象, 会自动获取当前Service的 UserDTO 对象
.getKey("id",3) //参数id 必须是 UserDTO中存在的字段
); //打印出来的值是 003
}
}
:::示例二
package com.test.service;
@Service
public class KeyBuildService extends AdvancedService<UserDao, UserDTO> {
public void test2() {
logger.debug(keyBuild().getKey("id",2)); //打印出来的值是 08
logger.debug(keyBuild().getKey("id",3)); //打印出来的值是 114
logger.debug(keyBuild().getKey("id",4)); //打印出来的值是 2224
//这个例子说明是按照 id字段 值的长度隔离的.
}
}
总结:
package com.test.service;
@Service
public class KeyBuildService extends AdvancedService<UserDao, UserDTO> {
/**
* 每行数据执行本方法一次,新增和修改的 之前的逻辑写到这里, 如通用的检查, 计算值得处理
*/
public void test3() {
//获取一个 带前缀TL 带日期字符(yyMMdd) + 6位数递增的序列号 的流水号
logger.debug(
keyBuild()
.setPrefix("TL") //设置前缀
.setSuffix("END") //设置后缀
.setDateFormat("yyyyMMdd") //设置日期格式
.getKey("id",3) //参数依次是 1.字段名 2.序列号长度
);
}
}
一次要获得多个流水号, 如企业内部系统 的 订单导入等, 建议用如下办法获得一批流水号
package com.test.service;
@Service
public class KeyBuildService extends AdvancedService<UserDao, UserDTO> {
public void test4() {
logger.debug("获得多个流水号");
//获得多个序列号
ConcurrentLinkedQueue<String> queue =
keyBuild("TL", "yyMMdd") //前缀, 设置日期格式
.getKeys("id", 6, 4); //参数依次是 1.字段名 2.序列号长度 3.要获得流水号个数
// poll 执行一次, 容器 queue里面少一个
logger.debug(queue.poll());//获得第1个序列号
logger.debug(queue.poll());//获得第2个序列号
logger.debug(queue.poll());//获得第3个序列号
logger.debug(queue.poll());//获得第4个序列号
}
}
也可以写成
package com.test.service;
@Service
public class KeyBuildService extends AdvancedService<UserDao, UserDTO> {
public void test5() {
logger.debug("获得多个流水号");
//获得多个序列号
ConcurrentLinkedQueue<String> queue =
keyBuild()
.setPrefix("TL") //设置前缀
.setDateFormat("yyyyMMdd") //设置日期格式
.getKeys("id", 6, 4); //参数依次是 1.字段名 2.序列号长度 3.要获得流水号个数
... 略
}
}
下面这个表有两个字段作为主键, userId 和 deptId :
/**
* 薪水津贴模板表
* 假定一个主管 管理了多个部门, 每管理一个部门, 就有管理津贴作为薪水
*/
@Setter
@Getter
@Accessors(chain = true)
@Table(name = "salary")
public class SalaryDTO extends DataDTO {
/**
* 两个主键字段, 用户Id 和部门Id
*/
@Id
private Integer userId;
@Id
private Integer deptId;
/**
* 管理津贴
*/
@JsonFormat(shape = JsonFormat.Shape.NUMBER, pattern = "###0.00")
private BigDecimal amount;
...
}
表 salary 的数据如下:
userId | deptId | amount | createUser | updateUser |
---|---|---|---|---|
1 | 1 | 11 | * | * |
1 | 2 | 22 | * | * |
1 | 3 | 33 | * | * |
::: 重要备注:
1-1, 1-2, 1-2 我们称之为3组主键Id值, 任何一组主键值 可以定位到 唯一的行.
多主键时, sql语句主键字段的拼接顺序是 按照 SalaryDTO 中定义的字段顺序来的.
具体来说, 如 concat(userId,'-', deptId) 这个先是 userId, 然后是deptId, 与 SalaryDTO 中定义的字段顺序一致. 因此在拼接Id值时注意顺序要一致.
单组主键值查询, 获得单个DTO对象:
@RestController
@RequestMapping("/salary")
public class SalaryController {
@Autowired
SalaryService salaryService;
protected Logger logger = LoggerFactory.getLogger(getClass());
@GetMapping("/test1")
public SalaryDTO test1() {
return salaryService.getObject("1-2");
}
}
输出SQL:
SELECT userId,deptId,amount,createUser,updateUser
FROM salary a
WHERE (concat(userId,'-', deptId) = '1-2')
多组主键值查询, 获得多个DTO对象:
@RestController
@RequestMapping("/salary")
public class SalaryController {
@Autowired SalaryService salaryService;
@GetMapping("/test2")
public List<SalaryDTO> test2() {
return salaryService.listObjects("1-1,1-2,1-3");
}
}
输出SQL:
SELECT userId,deptId,amount,createUser,updateUser
FROM salary a
WHERE (concat(userId,'-', deptId) = '1-1'
OR concat(userId,'-', deptId) = '1-2'
OR concat(userId,'-', deptId) = '1-3')
下面的程序代码 打印出来的是字符串: (concat(userId,'-', deptId)
@RestController
@RequestMapping("/salary")
public class SalaryController {
@Autowired SalaryService salaryService;
@GetMapping("/test3")
public String test3() {
String idFieldsBySql = salaryService.tableInfo().getIdFieldsBySql();
logger.debug(idFieldsBySql);
return idFieldsBySql;
}
}
@RestController
@RequestMapping("/salary")
public class SalaryController {
@Autowired SalaryService salaryService;
@GetMapping("/test4")
public void test4() {
salaryService.deleteByIds("1-1,1-2");
}
}
输出SQL:
DELETE FROM salary
WHERE (concat(userId,'-', deptId) = '1-1'
OR concat(userId,'-', deptId) = '1-2')
下面源码中的 RestSession和RestSessionConfig对象可以参考 <<快速使用>>章节中的相关内容
AppContext 提供了几个静态方法, 直接获取相关对象.
package com.user.controller;
import com.topfox.annotation.TokenOff;
import cnn.sys.RestSession;
import AbstractRestSessionConfig;
import com.topfox.common.AppContext;
import com.topfox.common.SysConfigRead;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/context")
public class AppContextController {
/**
* AppContext.getRestSessionHandler()是同一个实例
*/
@Autowired
RestSessionConfig restSessionConfig;
@TokenOff
@GetMapping("/test1")
public void test1() {
Environment environment = AppContext.environment();
RestSessionConfig restSessionHandlerConfig = (RestSessionConfig) AppContext.getRestSessionHandler();
restrestSessionConfig RestSession restSession = AppContext.getRestSession();
SysConfigRead configRead = AppContext.getSysConfig();
System.out.println(configRead);
}
@TokenOff
@GetMapping("/test2")
public void test2() {
RestSession restSession = restSessionConfig.restSessionConfigsConfigRead
configRead = restSessionConfig.restSessionConfig
}
}
以下参数在项目 application.properties 文件中配置, 不配置会用默认值. 下面的等号后面的值就是默认值.
debug 当前线程开始 日志输出分割
debug 中间日志输出前缀
debug 当前线程结束 日志输出分割符
分页时,默认的每页条数
不分页时(pageSize<=0),查询时最多返回的条数
是否开启一级缓存(线程缓存), 默认false 关闭, 查询不会读取一级缓存
是否开启二级缓存(redis缓存), 默认false 关闭, 替代老的 open-redis
service层是否开启redis缓存,
日志级别是DEBUG时, 是否打印 操作redis的操作日志
默认 false 不打印操作redis的日志
true 打印操作redis的日志
参数配置为true时, 控制台打印的日志大概如下:
# DEBUG 112-com.topfox.util.DataCache 更新后写入Redis成功 com.user.entity.UserDTO hashCode=2125196143 id=0
##DEBUG 112-com.topfox.util.DataCache更新后写入Redis成功 com.user.entity.UserDTO hashCode=1528294732 id=1
##DEBUG 112-com.topfox.util.DataCache查询后写入Redis成功 com.user.entity.UserDTO hashCode=620192016 id=2
# redis序列化支持两种, true:jackson2JsonRedisSerializer false:JdkSerializationRedisSerializer
# 注意, 推荐生产环境下更改为 false, 类库将采用JdkSerializationRedisSerializer 序列化对象,
# 这时必须禁用devtools(pom.xml 注释掉devtools), 否则报错.
更新时DTO序列化策略 和 更新SQL生成策略
重要参数:
参数值为 1 时, service的DTO=提交的数据.
更新SQL 提交数据不等null 的字段 生成 set field=value
参数值为 2 时, service的DTO=修改前的原始数据+提交的数据.
更新SQL (当前值 != 原始数据) 的字段 生成 set field=value
参数值为 3 时, service的DTO=修改前的原始数据+提交的数据.
更新SQL (当前值 != 原始数据 + 提交数据的所有字段)生成 set field=value
始终保证了前台(调用方)提交的字段, 不管有没有修改, 都能生成更新SQL, 这是与2最本质的区别
top.service.update-mode=1 时本参数才生效
默认值为false
更新之前是否先查询(获得原始数据). 如果需要获得修改日志, 又开启了redis, 建议在 update-mode=1时, 将本参数配置为true
根据Id更新记录时, sql执行结果(影响的行数)为0时是否抛出异常
默认 true 抛出异常
false 不抛异常
生成SQL 是否驼峰转下划线 默认 OFF
一共有3个值:
package com.topfox.util;
public interface SysConfig extends SysConfigRead {
/**
* 对应配置文件中的 top.service.update-mode
*/
void setUpdateMode(Integer value);
/**
* 对应配置文件中的 top.service.open-redis
*/
void setRedisCache(Boolean value);
/**
* 对应配置文件中的 top.service.update-not-result-error
*/
void setUpdateNotResultError(Boolean value);
...等等, 没有全部列出
}
以参数 open-redis为例:
我们假定项目配置文件 application.properties中开启了 读写Redis 的功能, 即 top.service.open-redis=true , 此时的含义表示, 当前项目的所有service操作数据库的增删改查的数据都会同步到Redis中. 那问题来了, 假如刚好 UserService 需要关闭open-redis, 怎么处理呢, 代码如下:
@Service
public class UserService extends AdvancedService<UserDao, UserDTO> {
@Override
public void init() {
/**
1. sysConfig 为 AdvancedService的父类 SuperService 中定义的 变量, 直接使用即可
2. sysConfig的默认值 来自于 application.properties 中的设置的值,
如果 application.properties 中没有定义, 则TopFox会自动默认一个
3.sysConfig中定义的参数在这里都可以更改
*/
//关闭了 UserService 读写redis的功能, 其他service不受影响
sysConfig.setOpenRedis(false);
}
}
这样调用了 UserService 的 getObject listObjects update insert delete 等方法操作的数据是不会同步到redis的 .
其他参数同理可以在运行时修改
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。
1. 开源生态
2. 协作、人、软件
3. 评估模型