增删改查
# 天宫框架方法
以下方法框架core需要升级到 2.2.1_release
# 查看sql
天宫框架的sql都是参数化的, 为了方便开发, 经常需要拿到完整的sql
这里推荐一款插件 MyBatis Log Free
底部工具栏展示sql
里面就有项目运行过程中的sql了
# 新增
- 插入一条 insert / insertOne
- 插入一组集合 insertList
这里批量插入的方式比较简单, 直接采用的是mybatis的 BATCH方式, 性能不是很高, 好在比较通用
如果批量插入的数据量非常非常大, 并且对性能要求很高的, 建议采用原生JDBC来操作
# 删除
框架已有的方法
- 删除by主键
- 批量删除by主键字符串, 例如: id1,id2,id3
- 删除by实体, 前提也是实体的主键得有值
- 删除by数据库列名, 例如: (I_E_FLAG, "I")
- 批量删除by主键, 传入一个集合
开发人员可扩展的方法
appGenDao.delete()
.eq(AppGenEntity::getId,9527)
.eq(AppGenEntity::getName,"1hello10")
.execute();
//sql如下:
//DELETE FROM app_gen WHERE (id = 9527 AND name = '1hello10')
2
3
4
5
6
7
使用lambda的方式, 可以构造各种各样的删除语句, 属于自由发挥了, 基本都够用了
建议删除还是要根据主键id来删
批量删除建议分两步走, 先查出来要删除的id集合, 然后根据id集合批量删
# 更新
更新主要还是根据主键id来更新, 更新涉及到nul值需不需要更新的问题
// 更新方法
appGenDao.updateByEntityId(entity);
2
框架提供了6种更新策略, 枚举类UpdateStrategy
public enum UpdateStrategy {
/**
* 整个对象全量更新
*/
FULL,
/**
* 指定字段全量更新, 其他字段不更新
*/
FULL_OF_SPECIFIED_FIELDS,
/**
* 整个对象非null更新 (默认)
*/
NONULL,
/**
* 指定字段非null的才更新, 其他字段不更新
*/
NONULL_OF_SPECIFIED_FIELDS,
/**
* 指定全量, 其余非null
*/
SPECIFIED_FULL_OTHER_NONULL,
/**
* 指定非null, 其余全量
*/
SPECIFIED_NONULL_OTHER_FULL
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
策略名称 | 更新字段 | 更新值策略 | 说明 |
---|---|---|---|
FULL | 实体所有字段 | 不管值是什么包括null均更新 | 对整个对象更新 |
FULL_OF_SPECIFIED_FIELDS | 实体类指定的字段 | 不管值是什么包括null均更新 | 指定字段全量更新, 其他字段不更新 |
NONULL | 实体所有字段 | 不为null的才更新 | 整个对象非null字段更新 (框架默认) |
NONULL_OF_SPECIFIED_FIELDS | 实体类指定的字段 | 不为null的才更新, 其他字段不更新 | 指定字段非null的才更新, 其他字段不更新 |
SPECIFIED_FULL_OTHER_NONULL | 实体类指定的字段 | 指定字段的包括null均更新, 其他非null更新 | 指定全量, 其余非null |
SPECIFIED_NONULL_OTHER_FULL | 实体类指定的字段 | 指定字段的非null更新, 其他字段全部更新 | 指定非null, 其余全量 |
如果需要指定策略
// 1. 指定策略SPECIFIED_NONULL_OTHER_FULL
entity.setUpdateStrategy(UpdateStrategy.SPECIFIED_NONULL_OTHER_FULL);
// 2. 指定哪些字段是非null更新的, 这里使用驼峰,不是下划线
entity.setEffectFields("name,custCode,gNo");
// 3. 将对象丢给方法
appGenDao.updateByEntityId(entity);
2
3
4
5
6
7
8
# 查询
# 查询一个列表
List<AppGenEntity> list = appGenDao
.select()
//.select("id", "g_no")
//.select(AppGenEntity::getId, AppGenEntity::getiEFlag)
.eq(AppGenEntity::getiEFlag, "E")
.eq("id", 212)
.gt(AppGenEntity::getPrice, 2)
.between("create_time", new Date(), new Date())
.likeLeft(AppGenEntity::getName, "hello")
.orderByAsc(AppGenEntity::getId)
.and(item -> {
item.eq(AppGenEntity::getCustCode, "1").or().
eq(AppGenEntity::getCustCode, "2");
})
.list();
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- 实际sql语句
SELECT id,name,cust_code,used_flag,price,i_e_flag,g_no,create_time,detail
FROM app_gen A
WHERE (
i_e_flag = 'E'
AND id = 212 AND price > 2
AND create_time BETWEEN '2024-04-03 10:31:34.177' AND '2024-04-03 10:31:34.177'
AND name LIKE '%hello'
AND (cust_code = '1' OR cust_code = '2'))
ORDER BY id ASC
2
3
4
5
6
7
8
9
10
select() 可以指定查哪些列, 不写就是查全部的列
.and 实际上是开了一个括号, 将 or的条件放在了里面
符号 | 文字描述 |
---|---|
eq | 等于 |
ne | 不等于 |
gt | 大于 |
lt | 小于 |
ge | 大于等于 |
le | 小于等于 |
like | %模糊% |
notLike | 不包含 |
leftLike | %左模糊 |
rightLike | 右模糊% |
isNull | 字段为空 |
isNotNull | 字段不为空 |
between | 范围 |
in | 数据库in |
notIn | 数据库not in |
inSql | in (一段sql) |
orderByAsc | 正序 |
orderByDesc | 倒叙 |
groupBy | 分组 |
exists | 存在 |
notExists | 不存在 |
appendSql | 拼接字符串sql |
参数说明:
可以传lambda表达式 如: AppGenEntity::getiEFlag
可以传数据库字段字符串, 如: create_time, 这里是数据库的字段
# 分页查询
// 分页参数为: 前端上下文中的分页参数
TgPageInfo<AppGenEntity> page = appGenDao
.select("id", "g_no")
.eq(AppGenEntity::getiEFlag, "E")
.eq("id", 212)
.page();
// 自己指定分页参数, 第1页, 取50条
TgPageInfo<AppGenEntity> page = appGenDao
.select("id", "g_no")
.eq(AppGenEntity::getiEFlag, "E")
.eq("id", 212)
.page(1,50);
2
3
4
5
6
7
8
9
10
11
12
13
说明:
如果是自定义的语句, 并没有.page()方法, 仍然需要分页, 则需要手动调一次 startPage() 方法
# outSql
有个需求, 就是我们希望用分页后的结果再去关联别的表, 形成一个结果集
public TgPageInfo queryPageList(AppGenEntity entity) {
// 指定 outSql 的sql语句
DBHelper.setOutSql("(SELECT PARA_NAME FROM sys_parameters WHERE PARA_CODE = A.used_flag and para_type ='g2_sck_modf_markcd') para_name ");
// 之前通用的方法
EciQuery<AppGenEntity> eciQuery = EciQuery.buildQuery(entity);
List<AppGenEntity> entities = appGenDao.queryPageList(eciQuery);
return EciQuery.getPageInfo(entities);
}
2
3
4
5
6
7
8
9
最终的sql语句
SELECT
A.*,
( SELECT PARA_NAME FROM sys_parameters WHERE PARA_CODE = A.used_flag AND para_type = 'g2_sck_modf_markcd' ) para_name
FROM
(
SELECT
id,
NAME,
cust_code,
used_flag,
price,
i_e_flag,
g_no,
create_time,
detail
FROM
app_gen A
WHERE
( A.NAME LIKE '%hello%' AND A.CREATE_TIME BETWEEN '2022-02-15 00:00:00.0' AND '2024-05-15 23:59:59.0' )
ORDER BY
A.price ASC,
A.cust_code DESC
LIMIT 10
) A
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
说明:
有个约定: A特指原表的结果集别名
这个种方案我本人并不推荐使用
- sql较原先相比, 更加复杂, 不易于阅读
- 关联查询影响sql的执行效率, 存在性能问题
优化建议:
- 如果关联表数据基本不变, 使用框架提供的代码名称转换去解决
- 将分页的结果集取出, 在程序层面使用 select xxx from 子表 where key in (xx), 后台对结果做一次组装, 然后返回给前端
# 查一条记录
AppGenEntity one = appGenDao
.select("id", "g_no")
.one();
2
3
# 查找个数
Long count = appGenDao
.select("id")
.eq(AppGenEntity::getiEFlag, "E")
.count();
2
3
4
# 是否存在
boolean exists = appGenDao
.select( "g_no")
.eq(AppGenEntity::getiEFlag, "E")
.eq("id", 214)
.exists();
2
3
4
5
# 分组查询
List<AppGenEntity> list = appGenDao
.select("i_e_flag", "sum(price) as price")
.eq(AppGenEntity::getUsedFlag,entity.getUsedFlag())
.groupBy(AppGenEntity::getiEFlag)
.list();
2
3
4
5
// 执行的sql
SELECT i_e_flag,sum(price) as price FROM app_gen A WHERE (used_flag = '1' ) GROUP BY i_e_flag
2
注意事项:
- select里面的字段得写成sql里面的片段, 结果需要使用实体类中存在的字段去接受
- 其他条件正常拼接
- 语义满足sql规范
# 多表关联查询
场景说明:
- 表头关联表体
- 还想使用__queryField动态拼接查询参数
- 列表展示, 查询条件 同时存在表头和表体的字段
# 表结构展示
表头: app_gen
表体: app_gen_list
app_gen_id 为表头的id
# 实体类 entity
这里我建了一个实体类AppGenFull, 里面包含了表头和表体以及后面可能查询或展示的时候会用到的字段
public class AppGenFull extends EciBaseEntity {
/**
* 主键
*/
@ApiModelProperty("主键(19)")
@TableId("id")
private Long id;
/**
* 名字
*/
@ApiModelProperty("名字(1,020)")
@TableField("name")
private String name;
/**
* 代码
*/
@ApiModelProperty("代码(1,020)")
@TableField("cust_code")
private String custCode;
/**
* 标记
*/
@ApiModelProperty("标记(1,020)")
@TableField("used_flag")
private String usedFlag;
/**
* 价格
*/
@ApiModelProperty("价格(12)")
@TableField("price")
private BigDecimal price;
/**
* 进出口|I=进口,E=出口,IE=进出口
*/
@ApiModelProperty("进出口|I=进口,E=出口,IE=进出口(20)")
@TableField("i_e_flag")
private String iEFlag;
/**
* 商品编号
*/
@ApiModelProperty("商品编号(40)")
@TableField("g_no")
private String gNo;
/**
* 创建时间
*/
@ApiModelProperty("创建时间(0)")
@TableField("create_time")
private Date createTime;
@ApiModelProperty("创建时间开始")
@TableField(exist=false)
private Date createTimeStart;
@ApiModelProperty("创建时间结束")
@TableField(exist=false)
private Date createTimeEnd;
/**
* 监管方式
*/
@ApiModelProperty("监管方式(8)")
@TableField("trade_mode")
private String tradeMode;
/**
* 商品编码
*/
@ApiModelProperty("商品编码(10)")
@TableField("code_ts")
private String codeTs;
/**
* 贸易国别
*/
@ApiModelProperty("贸易国别(10)")
@TableField("country")
private String country;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
# 数据访问层 dao
这里自定义一个方法selectHeadBodyList
public interface AppGenDao extends EciBaseDao<AppGenEntity> {
// 自定义sql: 用户列表展示的查询方法
List<AppGenFull> selectHeadBodyList(@Param(Constants.WRAPPER) Wrapper queryWrapper);
}
2
3
4
5
说明:
参数: queryWrapper是封装查询条件的对象, 直接照着写就行了
返回: 就是一个全量的查询对象集合
# mybatis xml
<select id="selectHeadBodyList" parameterType="com.baomidou.mybatisplus.core.conditions.Wrapper"
resultType="com.eci.project.appGen.entity.AppGenFull">
select
h.id,h.name,h.cust_code, h.used_flag,h.price, h.i_e_flag, h.g_no, h.create_time,
b.trade_mode,b.country,b.code_ts
from
app_gen h
left join app_gen_list b on h.id = b.app_gen_id
${ew.customSqlSegment}
</select>
2
3
4
5
6
7
8
9
10
说明:
h: 表头 b: 表体
select标签为查询标签
id: 就是dao层的查询方法
parameterType: 参数类型, 就是dao传进来的参数
resultType: 返回类型, 就是上面定义的一个比较全的实体类
${ew.sqlSegment}: 参数占位, 这里面有前端发送的__queryField参数, 照着写就行
# 业务层 service
@Override
public TgPageInfo selectPageListFull(AppGenFull entity) {
// 获取eciQuery对象
EciQuery<AppGenEntity> eciQuery = EciQuery.buildQuery(entity);
// 开始分页, 自定义的方法, 自己决定是否要分页
startPage();
// 调用自定义的dao查询方法
List<AppGenFull> appGenFulls = appGenDao.selectHeadBodyList(eciQuery);
// 将返回参数用分页器封装一下
return EciQuery.getPageInfo(appGenFulls);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
# 访问层 controller
@ApiOperation("测试代码生成:分页查询列表")
@EciLog(title = "测试代码生成:分页查询列表", businessType = BusinessType.SELECT)
@PostMapping("/selectPageListFull")
public ResponseMsg selectPageListFull(@RequestBody AppGenFull entity){
return ResponseMsgUtil.successPlus(10001,appGenService.selectPageListFull(entity));
}
2
3
4
5
6
说明:
我这里是重新命名了一个方法, 当然, 也可以直接覆盖selectPageList方法
# 请求参数JSON
{
"type": "1",
"paging": {
"pageSize": 10,
"pageNum": 1,
"sortFields": [
{
"sortField": "h.price",
"asc": true
},
{
"sortField": "h.custCode",
"asc": false
}
]
},
"entity": {
"name": "hello",
"createTimeStart": "2023-02-15",
"createTimeEnd": "2024-03-15",
"__queryField": "name|h.NAME|s|l|and,createTime|b.CREATE_TIME|d|between|and"
},
"query": {
"totalColumn": "",
"selectColumns": ""
},
"tgTime": 13213123,
"businessType": "SELECT"
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
说明:
"__queryField": "name|h.NAME|s|l|and,createTime|b.CREATE_TIME|d|between|and"
由于是多表, 别名位置不能写错, 一定要写sql中自己取的那个别名
# DBHelper
这里提供了直接操作数据库的方法
# 查询
List<AppGenEntity> appGenEntities = DBHelper.selectList(
"select id,name,cust_code,used_flag,price,i_e_flag,g_no,create_time from app_gen where name like ? and g_no = ?",
AppGenEntity.class,
"%hello%", "1");
2
3
4
参数说明:
1 sql语句, 变量写 ?
2 将数据封装到一个实体类中, 实体类的class
3 可变参数列表
# 增删改
DBHelper.execute("update app_gen set used_flag = '1' where id = 2");
# 切换数据源
// 切换ds
public void changeDs(String dsName);
// 会话级切换数据源
public void changeSessionDs(String dsName);
// 全局切换数据源
public void changeGlobalDs(String dsName);
// 是否有该数据源
public boolean hasDs(String dsName);
2
3
4
5
6
7
8
9
10
11
多数据源切换, 需要加入天宫多数据源模块, tiangong-dynamicds