数据库查询
Niucloud常规数据库查询采用MyBatis-Plus,MyBatis-Plus是一个MyBatis的增强工具,在 MyBatis 的基础上只做增强不做改变,为简化开发、提高效率而生。针对联表查询使用MyBatis-Plus-Join,MyBatis-Plus-Join是一个基于MyBatis-Plus的多表查询扩展库。
MyBatis-Plus-Join的优势
MyBatis-Plus-Join是一个专为Mybatis-Plus设计的多表操作插件。它通过扩展Mybatis-Plus的构造器,实现了对多表查询的简化处理,使得开发者能够更加高效地进行数据库操作。该插件的使用非常简单,即使是初学者也能在十分钟内掌握其全部功能。
MyBatis-Plus-Join插件的核心优势在于其对Mybatis-Plus的非侵入性扩展。它仅依赖于lomlock,并且不会对原有项目产生任何影响。此外,该插件支持大部分Mybatis-Plus的常用版本,确保了广泛的兼容性。
在技术实现上,MyBatis-Plus-Join采用了链式调用的方式,使得代码更加简洁和直观。通过使用QueryWraper,它几乎融合了字符串和lambda表达式的优点,提供了强大的查询构建能力。
特别是在大型项目中,当数据库表结构复杂,关联查询频繁时,MyBatis-Plus-Join能够显著提升开发效率,减少代码冗余,提高系统的可维护性。
条件构造器
MyBatis-Plus-Join是一个专为Mybatis-Plus设计的多表操作插件,条件构造器的使用基于Mybatis-plus的语法,具体可以查看Mybatis-plus条件构造器,下面是针对niucloud系统中常用方法做说明。
条件构造函数 | 对应数据库语句 | 实例 | 对应sql语句实例 |
---|---|---|---|
allEq | 设置所有字段相等的条件 | queryWrapper.allEq(Map.of("id", 1, "name", "老王", "age", null)); | SELECT * FROM user WHERE id = 1 AND name = '老王' AND age IS NULL |
eq | = | queryWrapper.eq("name", "老王"); | SELECT * FROM user WHERE name = '老王' |
ne | <> | queryWrapper.ne("name", "老王"); | SELECT * FROM user WHERE name <> '老王' |
gt | > | queryWrapper.gt("age", 18); | SELECT * FROM user WHERE age > 18 |
ge | >= | queryWrapper.ge("age", 18); | SELECT * FROM user WHERE age >= 18 |
lt | < | queryWrapper.lt("age", 18); | SELECT * FROM user WHERE age < 18 |
le | <= | queryWrapper.le("age", 18); | SELECT * FROM user WHERE age <= 18 |
between | BETWEEN | queryWrapper.between("age", 18, 30); | SELECT * FROM user WHERE age BETWEEN 18 AND 30 |
notBetween | NOT BETWEEN | queryWrapper.notBetween("age", 18, 30); | SELECT * FROM user WHERE age NOT BETWEEN 18 AND 30 |
like | like '%search%' | queryWrapper.like("name", "王"); | SELECT * FROM user WHERE name LIKE '%王%' |
notLike | not like '%search%' | queryWrapper.notLike("name", "王"); | SELECT * FROM user WHERE name NOT LIKE '%王%' |
likeLeft | like '%search' | queryWrapper.likeLeft("name", "王"); | SELECT * FROM user WHERE name LIKE '%王' |
likeRight | like 'search%' | queryWrapper.likeRight("name", "王"); | SELECT * FROM user WHERE name LIKE '王%' |
in | in | queryWrapper.in("age", Arrays.asList(1, 2, 3)); | SELECT * FROM user WHERE age IN (1, 2, 3) |
notIn | not in | queryWrapper.notIn("age", Arrays.asList(1, 2, 3)); | SELECT * FROM user WHERE age NOT IN (1, 2, 3) |
groupBy | group by | queryWrapper.groupBy("id", "name"); | SELECT * FROM user GROUP BY id, name |
orderByAsc | order by cloumn asc | queryWrapper.orderByAsc("id", "name"); | SELECT * FROM user ORDER BY id ASC, name ASC |
orderByDesc | order by cloumn desc | queryWrapper.orderByDesc("id", "name"); | SELECT * FROM user ORDER BY id DESC, name DESC |
一般多个查询条件是and,但是也有部分特殊查询使用or,针对这种查询很容易出错,所以niucloud建议使用or嵌套,下面是mybatis-plus的实例
queryWrapper.or(i -> i.and(j -> j.eq("name", "李白").eq("status", "alive"))
.or(j -> j.eq("name", "杜甫").eq("status", "alive")));
对应的sql语句是
SELECT * FROM user WHERE (name = '李白' AND status = 'alive') OR (name = '杜甫' AND status = 'alive')
niucloud系统中使用实例,例如商城插件中查询在特定时间段内是否存在重叠进行的活动,代码如下,里面就用到了多层嵌套
MPJQueryWrapper<ShopActiveGoods> wrapper = new MPJQueryWrapper<>();
wrapper.setAlias("ag"). innerJoin("?_shop_active a ON ag.active_id = a.active_id".replace("?_", GlobalConfig.tablePrefix));
wrapper.eq("a.site_id", RequestUtils.siteId());
wrapper.in("a.active_status", Arrays.asList(ActiveStatusEnum.NOT_ACTIVE.getStatus(), ActiveStatusEnum.ACTIVE.getStatus()));
wrapper.in("ag.goods_id", goodsIds.toArray());
wrapper.eq("ag.active_class", "discount");
Long startTime = DateUtils.StringToTimestamp(shopDiscountParam.getStartTime());
Long endTime = DateUtils.StringToTimestamp(shopDiscountParam.getEndTime());
wrapper.and(i -> i.and(j -> j.between("a.start_time", startTime, endTime))
.or(j -> j.between("a.end_time", startTime, endTime))
.or(j-> j.le("a.start_time", startTime).
ge("a.end_time", endTime))
.or(j-> j.le("start_time", startTime).
ge("a.end_time", endTime))
);
关于嵌套的使用,仔细理解,实际就是整体看待问题,层层深入,比如(i->i.),代表i是一个整体返回逻辑结果,内部可以是and或者or条件也是整体看待,如果不能理解,多次尝试总结。
联表查询
Mybatis-Plus大大缩减了Mybatis关于数据库查询配置xml的复杂度,但是针对联表查询等复杂的查询方式就显得力不从心,所以Niucloud系统引入了Mybatis-Plus-Join用作联表查询,大大减少了编写sql语句的工作量,只要掌握sql语句的书写就可以快速整合到系统中,目前常用业务系统复杂关联表在4到5张表,使用Mybatis-Plus-Join表现出轻松上手并且高效开发的优势,如果你是niucloud的php版的用户可能掌握了php的orm,两者开发效率都很高,如果不同语言开发相同业务逻辑的系统,可能查询方式不同,不必相同查询返回结果,只需查询的数据一致即可。
下面是开发商城分销使用的一条sql语句实例,Mybatis-Plus-Join联表查询中基础的条件构造器语法与Mybatis-Plus是一致的,所以不需要专门学习,只是数据库字段加上联表前缀。大体意思是分销商列表查询,分销商表关联分销商等级表查询等级名称等信息,关联会员表查询分销商的基础会员信息,包括会员基础信息,账户信息等,分销商上级会员id关联会员表查询上级分销商对应会员的基础信息。按照这个意思写出了查询的sql语句,这个使用常用的sql管理工具书写就行
SELECT nm.commission, nm.commission_get, nm.commission_cash_outing,nsf.fenxiao_id, nsf.site_id, nsf.fenxiao_no, nsf.member_id, nsf.level_id,
nsf.parent, nsf.fenxiao_order_num, nsf.fenxiao_total_order, nsf.fenxiao_commission, nsf.agent_commission,
nsf.team_commission, nsf.task_commission, nsf.sale_commission, nsf.child_num, nsf.child_fenxiao_num,
nsf.status, nsf.is_agent, nsf.agent_level, nsf.agent_status, nsf.agent_money, nsf.agent_time, nsf.create_time,
nsf.lock_time, nsf.delete_time, nm.username, nm.nickname, nm.headimg, nm.mobile, nm1.username AS parent_username,
nm1.mobile AS parent_mobile, nm1.nickname AS parent_nickname, nm1.headimg AS parent_headimg,nsfl.level_name
FROM ns_shop_fenxiao nsf
LEFT JOIN ns_shop_fenxiao_level nsfl ON nsf.level_id = nsfl.level_id
LEFT JOIN ns_member nm ON nsf.member_id = nm.member_id
LEFT JOIN ns_member nm1 ON nsf.parent = nm1.member_id
上述sql语句书写完之后按照mybatis-plus-join的语法整理成查询即可,代码如下,几乎与原生sql语句一致,这里注意表前缀
MPJQueryWrapper<ShopFenxiao> queryWrapper = new MPJQueryWrapper<>();
queryWrapper.select("nm.commission, nm.commission_get, nm.commission_cash_outing,nsf.fenxiao_id, nsf.site_id, nsf.fenxiao_no, nsf.member_id, nsf.level_id, nsf.parent, nsf.fenxiao_order_num, nsf.fenxiao_total_order, nsf.fenxiao_commission, nsf.agent_commission, nsf.team_commission, nsf.task_commission, nsf.sale_commission, nsf.child_num, nsf.child_fenxiao_num, nsf.status, nsf.is_agent, nsf.agent_level, nsf.agent_status, nsf.agent_money, nsf.agent_time, nsf.create_time, nsf.lock_time, nsf.delete_time, nm.username, nm.nickname, nm.headimg, nm.mobile, nm1.username AS parent_username, nm1.mobile AS parent_mobile, nm1.nickname AS parent_nickname, nm1.headimg AS parent_headimg,nsfl.level_name")
.setAlias("nsf")
.leftJoin("?_shop_fenxiao_level nsfl ON nsf.level_id = nsfl.level_id".replace("?_", GlobalConfig.tablePrefix))
.leftJoin("?_member nm ON nsf.member_id = nm.member_id".replace("?_", GlobalConfig.tablePrefix))
.leftJoin("?_member nm1 ON nsf.parent = nm1.member_id".replace("?_", GlobalConfig.tablePrefix));
特殊查询操作
进行数据库查询存在一些特殊的字段查询,如果单独书写费时费力,Niucloud进行了单独的封装
针对时间段查询,比如查询会员列表,需要查询2024-11-1到2024-11-15注册的会员,数据表中存储注册时间是时间戳,传入时间是字符串数组,包括开始时间与结束时间,如果后台进行转化书写代码会比较多,所以系统定义了统一的格式,只需要按照格式书写即可。
首先传入时间字段是时间格式的字符串数组,不需要定义开始时间,结束时间的格式,比如会员列表传入参数
/** 创建时间筛选 */
private String[] createTime;
查询条件构造器只需调用固定函数即可
if (ObjectUtil.isNotEmpty(searchParam.getCreateTime())) {
QueryMapperUtils.buildByTime(queryWrapper, "m.create_time", searchParam.getCreateTime());
}