告别手撸原生 SQL,Sql 构建器 + Enjoy 模板打造动态查询新范式

1.png2.png3.png4.png5.png6.png


使用示例:

222.png

333.png

444.png

666.png

// 统计数量
Console.log("统计所有企业数量: ", CompanyInfo.dao.count(Sql.of()), "\n");
Console.log("统计北京朝阳区企业数量: ", CompanyInfo.dao.count(Sql.of().jsonContains(CompanyInfo.AREA, "朝阳区", "$.County")), "\n");
Console.log("统计带有‘科技’字样企业数量: ", CompanyInfo.dao.count(Sql.of().like(CompanyInfo.NAME, "科技")), "\n");
Console.log("统计‘科技’字样结尾企业数量: ", CompanyInfo.dao.count(Sql.of().leftLike(CompanyInfo.NAME, "科技")), "\n");
Console.log("统计‘科技’字样开头企业数量: ", CompanyInfo.dao.count(Sql.of().rightLike(CompanyInfo.NAME, "科技")), "\n");
Console.log("统计不带‘科技’字样企业数量: ", CompanyInfo.dao.count(Sql.of().notLike(CompanyInfo.NAME, "科技")), "\n");

Console.log("最大id: ", CompanyInfo.dao.queryFirst(Sql.of().max(CompanyInfo.ID)).getLong("max"), "\n");
Console.log("最小id: ", CompanyInfo.dao.queryFirst(Sql.of().min(CompanyInfo.ID)).getLong("min"), "\n");
Console.log("id求平均值: ", CompanyInfo.dao.queryFirst(Sql.of().avg(CompanyInfo.ID)).getLong("avg"), "\n");
Console.log("id求和: ", CompanyInfo.dao.queryFirst(Sql.of().sum(CompanyInfo.ID)).getLong("sum"), "\n");
Console.log("统计EMAIL存在的企业数量: ", CompanyInfo.dao.queryFirst(Sql.of().count(CompanyInfo.EMAIL)).getLong("total"), "\n");

String selects[] = new String[]{CompanyInfo.ID, CompanyInfo.NAME, CompanyInfo.INSURED_COUNT};
Sql sql = Sql.of().select(selects);

Console.log("查询指定列: ", JsonKit.toJson(CompanyInfo.dao.query(sql.copy().idIn("10,11").orderByDesc(CompanyInfo.ID).limit(2))), "\n");
Console.log("查询id > 100的第一列: ", JsonKit.toJson(CompanyInfo.dao.query(sql.copy().gt(CompanyInfo.ID, 100).limit(1))), "\n");
Console.log("查询id = 100的列: ", JsonKit.toJson(CompanyInfo.dao.query(sql.copy().eq(CompanyInfo.ID, 100).limit(1))), "\n");
Console.log("查询id < 100的第一列: ", JsonKit.toJson(CompanyInfo.dao.query(sql.copy().lt(CompanyInfo.ID, 100).orderByDesc(CompanyInfo.ID).limit(1))), "\n");
Console.log("排它锁: 在事务中锁定选中的行,防止其他事务修改或读取(取决于隔离级别),直到当前事务提交或回滚");
Db.tx(()-> {
	Console.log("查询id = 5的列: ", JsonKit.toJson(CompanyInfo.dao.query(sql.copy().eq(CompanyInfo.ID, 5).forUpdate())));
	int update = CompanyInfo.dao.update(Sql.of().setField(CompanyInfo.INSURED_COUNT, SqlExpr.custom(CompanyInfo.INSURED_COUNT + " + 1"))
			.eq(CompanyInfo.ID, 5));
	Console.log("更新id = 5的列 insuredCount = insuredCount + 1 : ", update == 1 ? "已更新" : "更新失败");
	Console.log("更新后查询id = 5的列: ", JsonKit.toJson(CompanyInfo.dao.query(sql.copy().eq(CompanyInfo.ID, 5))));
	return update == 1;
});
Console.log("事务结束:释放排它锁", "\n");
Console.log("合并查询: ", JsonKit.toJson(CompanyInfo.dao.query(Sql.of().select(CompanyInfo.ID, CompanyInfo.NAME)
		.unionAll(Sql.of().from(CompanyJob.TABLE_NAME).select(CompanyJob.ID, CompanyJob.ENTNAME)).orderByAsc(CompanyInfo.NAME).limit(5))), "\n");

==================================执行结果==================================				
Sql: SELECT COUNT(*) FROM  s_company_info
Sql: 3 ms | SELECT COUNT(*) FROM  s_company_info
统计所有企业数量:  11685 

Sql: SELECT COUNT(*) FROM  s_company_info WHERE  JSON_CONTAINS(area,  ?, ?) 
Sql: 124 ms | SELECT COUNT(*) FROM  s_company_info WHERE  JSON_CONTAINS(area,  '"朝阳区"', '$.County') 
统计北京朝阳区企业数量:  4073 

Sql: SELECT COUNT(*) FROM  s_company_info WHERE  name LIKE ?
Sql: 6 ms | SELECT COUNT(*) FROM  s_company_info WHERE  name LIKE '%科技%'
统计带有‘科技’字样企业数量:  2923 

Sql: SELECT COUNT(*) FROM  s_company_info WHERE  name LIKE ?
Sql: 4 ms | SELECT COUNT(*) FROM  s_company_info WHERE  name LIKE '%科技'
统计‘科技’字样结尾企业数量:  0 

Sql: SELECT COUNT(*) FROM  s_company_info WHERE  name LIKE ?
Sql: 0 ms | SELECT COUNT(*) FROM  s_company_info WHERE  name LIKE '科技%'
统计‘科技’字样开头企业数量:  1 

Sql: SELECT COUNT(*) FROM  s_company_info WHERE  name NOT LIKE ?
Sql: 4 ms | SELECT COUNT(*) FROM  s_company_info WHERE  name NOT LIKE '%科技%'
统计不带‘科技’字样企业数量:  8762 

Sql: SELECT  MAX(id) AS max  FROM  s_company_info
Sql: 0 ms | SELECT  MAX(id) AS max  FROM  s_company_info
最大id:  11687 

Sql: SELECT  MIN(id) AS min  FROM  s_company_info
Sql: 0 ms | SELECT  MIN(id) AS min  FROM  s_company_info
最小id:  1 

Sql: SELECT  AVG(id) AS avg  FROM  s_company_info
Sql: 2 ms | SELECT  AVG(id) AS avg  FROM  s_company_info
id求平均值:  5844 

Sql: SELECT  SUM(id) AS sum  FROM  s_company_info
Sql: 2 ms | SELECT  SUM(id) AS sum  FROM  s_company_info
id求和:  68295829 

Sql: SELECT  COUNT(email) AS total  FROM  s_company_info
Sql: 5 ms | SELECT  COUNT(email) AS total  FROM  s_company_info
统计EMAIL存在的企业数量:  11685 

Sql: SELECT  id, name, insuredCount  FROM  s_company_info WHERE   id IN (  ?,   ? )  ORDER BY id DESC LIMIT 2 
Sql: 1 ms | SELECT  id, name, insuredCount  FROM  s_company_info WHERE   id IN (  10,   11 )  ORDER BY id DESC LIMIT 2 
查询指定列:  [{"name":"保利民爆科技集团股份有限公司","id":11,"insuredCount":"70"},{"name":"中国泛海控股集团有限公司","id":10,"insuredCount":"128"}] 

Sql: SELECT  id, name, insuredCount  FROM  s_company_info WHERE  id > ? LIMIT 1 
Sql: 0 ms | SELECT  id, name, insuredCount  FROM  s_company_info WHERE  id > 100 LIMIT 1 
查询id > 100的第一列:  [{"name":"保利科技有限公司","id":101,"insuredCount":"328"}] 

Sql: SELECT  id, name, insuredCount  FROM  s_company_info WHERE  id = ? LIMIT 1 
Sql: 0 ms | SELECT  id, name, insuredCount  FROM  s_company_info WHERE  id = 100 LIMIT 1 
查询id = 100的列:  [{"name":"北京丽源有限公司","id":100,"insuredCount":"81"}] 

Sql: SELECT  id, name, insuredCount  FROM  s_company_info WHERE  id < ? ORDER BY id DESC LIMIT 1 
Sql: 0 ms | SELECT  id, name, insuredCount  FROM  s_company_info WHERE  id < 100 ORDER BY id DESC LIMIT 1 
查询id < 100的第一列:  [{"name":"中国轻工业出版社有限公司","id":99,"insuredCount":"188"}] 

排它锁: 在事务中锁定选中的行,防止其他事务修改或读取(取决于隔离级别),直到当前事务提交或回滚
Sql: SELECT  id, name, insuredCount  FROM  s_company_info   WHERE  id = ?  FOR UPDATE  
Sql: 0 ms | SELECT  id, name, insuredCount  FROM  s_company_info   WHERE  id = 5  FOR UPDATE  
查询id = 5的列:  [{"name":"北京大龙建设集团有限公司","id":5,"insuredCount":"220"}]
Sql: UPDATE  s_company_info SET  insuredCount =  insuredCount + 1 WHERE  id = ?
Sql: 0 ms | UPDATE  s_company_info SET  insuredCount =  insuredCount + 1 WHERE  id = 5
更新id = 5的列 insuredCount = insuredCount + 1 :  已更新
Sql: SELECT  id, name, insuredCount  FROM  s_company_info WHERE  id = ?
Sql: 0 ms | SELECT  id, name, insuredCount  FROM  s_company_info WHERE  id = 5
更新后查询id = 5的列:  [{"name":"北京大龙建设集团有限公司","id":5,"insuredCount":"221"}]
Sql: 1 ms | 
事务结束:释放排它锁 

Sql: SELECT  id, name  FROM  s_company_info  UNION  ALL (SELECT id, entname FROM s_company_job)   ORDER BY name ASC LIMIT 5 
Sql: 21 ms | SELECT  id, name  FROM  s_company_info  UNION  ALL (SELECT id, entname FROM s_company_job)   ORDER BY name ASC LIMIT 5 
合并查询:  [{"name":"《中华医学杂志》社有限责任公司","id":788},{"name":"《中国中医药报》社有限公司","id":4245},{"name":"《中国企业报》股份有限公司","id":7966},{"name":"《中国农村金融》杂志社有限责任公司","id":7178},{"name":"《中国化工报》社有限公司","id":1999}] 
				


SELECT DISTINCT

Sql.of().distinct().select("department").from("user");
// SELECT DISTINCT department FROM user


使用 Sql 构建的支持子查询作为 FROM 源 —— 构建复杂动态 SQL(如报表、分析查询)

Sql sub = Sql.of().select("user_id, COUNT(*) AS order_count").from("orders").groupBy("user_id");
Sql main = Sql.of().from(sub).as("user_stats").gt("order_count", 400);
//select * from (SELECT user_id, COUNT(*) AS order_count FROM orders GROUP BY user_id) user_stats where order_count > 400


JOIN查询

Sql.of().select("u.id, u.name").from("user").as("u").join("left join", "role", "r", on -> on.eq("role_id", SqlExpr.custom("r.id")));
// select u.id, u.name from user u left join role r on u.role_id = r.id

Sql.of().select("u.id, u.name").from("user").as("u").innerJoin("role", "r", on -> on.eq("role_id", SqlExpr.custom("r.id")));
// select u.id, u.name from user u inner join role r on u.role_id = r.id

 

WHERE 条件

Sql.of().from("user").where("age", "=", 20)
// select * from user where age = 20
Sql.of().from("user").as("u").where("age", "=", 20)
// select * from user u where u.age = 20

Sql.of().select("u.*").from("user").as("u").eq("age", 20)
// select u.* from user where u.age = 20

Sql.of().select("u.*").from("user").as("u").ne("age", 20)
// select u.* from user where u.age != 20

Sql.of().from("user").like("name", "admin")
// select * from user where name like '%admin%'

Sql.of().from("user").notLike("name", "admin")
// select * from user where name not like '%admin%'

Sql.of().from("user").isNullVal("deleted_at")
// select * from user where deleted_at IS NULL

Sql.of().from("user").findInSet("tags", "O'Reilly") 
// select u.* from user where FIND_IN_SET('O''Reilly', u.tags)

Sql.of().from("user").between("age", 18, 60)
// select * from user where age BETWEEN 18 AND 60

Sql.of().from("user").in("status", Arrays.asList(1, 2))
// select * from user where status in (1, 2)
Sql.of().from("user").in("type", "A,B,C")
// select * from user where type in ("A", "B", "C")
Sql.of().from("user").in("id", new int[]{1,2,3})
// select * from user where id in (1, 2, 3)
Sql.of().from("user").in("id", "1,2,3")
// select * from user where id in (1, 2, 3)

Sql.of().from("user").or("name", "=", "张三").or("email", "=", "zhang@example.com")
// select * from user where (name = '张三' OR email = 'zhang@example.com')

OR 条件组(组内各个条件为AND关系),多个orAndGroup之间是OR的关系
1:基础多组 OR(每组多个 AND 条件)
Sql.of().from("user")
.orAndGroup(g -> g.eq("status", 1).gt("score", 90))          // 组1: status=1 AND score>90
.orAndGroup(g -> g.eq("vip", true).eq("has_paid", true));    // 组2: vip=true AND has_paid=true
//select * from user where (status=1 AND score>90) OR (vip=true AND has_paid=true)

2:混合简单条件与子查询
Sql.of().from("user")
.orAndGroup(g -> g.eq("role", "admin"))	// 组1: 管理员
.orAndGroup(g -> g.eq("active", true) 	// 组2: 已激活
.exists("SELECT 1 FROM orders o WHERE o.user_id = user.id AND o.total > 100")); // 组3: 活跃用户且有大额订单
// select * from user where (role = 'admin') OR (active = true AND EXISTS (SELECT 1 FROM orders o WHERE o.user_id = user.id AND o.total > 100))

3:空组自动跳过(安全设计)
Sql.of().from("user")
.orAndGroup(g -> {})  // 空组 → 被忽略
.orAndGroup(g -> g.like("name", "张"));
// select * from user where (name LIKE '%张%')

OR 条件组(组内各个条件为 OR 关系), 多个andOrGroup是and关系
 1:模糊匹配多个字段(“全局搜索”场景)
Sql.of().from("user")
.eq("status", 1)
.andOrGroup(g -> g.like("name", "张").like("email", "a@b.com").like("phone", "137"));
//select * from user where status = 1 AND (name LIKE '%张%' OR email LIKE '%a@b.com%' OR phone LIKE '%137%')

2:多字段精确匹配任一(如登录名兼容)
Sql.of().from("user")
.andOrGroup(g -> g.eq("username", "zhangsan").eq("email", "zhangsan@example.com").eq("phone", "13800138000"));
// select * from user where (username = 'zhangsan' OR email = 'zhangsan@example.com' OR phone = '13800138000')

3:与 orAndGroup 混用(复杂组合)
Sql.of().from("user")
.eq("deleted", false)
.orAndGroup(g -> g.eq("role", "admin").eq("type", 1))	// 管理员
.orAndGroup(g -> g.eq("role", "user").andOrGroup(h -> h.like("name", "VIP").gt("balance", 10000)));	// 普通用户:名字含VIP 或 余额超1万
// select * from user where deleted = false AND ( (role = 'admin' and type = 1) OR (role = 'user' AND (name LIKE '%VIP%' OR balance > 10000)) )

Sql 构建的子查询作为 EXISTS 条件
Sql sub = Sql.of()
  .select("1").from("orders").as("o").eq("user_id, SqlExpr.custom("user.id")).gt("amount", 1000);
Sql main = Sql.of().from("user").exists(sub);
// select * from user where EXISTS (SELECT 1 FROM orders o WHERE o.user_id = user.id AND o.amount > 1000))

添加原始 SQL 片段到 WHERE(无参数绑定)
Sql.of().from("user").whereSql("created_at > '2020-01-01'")
// select * from user where created_at > '2020-01-01' 

添加原始 SQL 片段到 WHERE(参数绑定)
Sql.of().from("user").whereSql(
     "id = #para(id) or name = #para(name)", 
     Kv.of("id", 2).set("name", "kk")
)
// select * from user where (id = 2 or name = 'kk')  

Sql.of().from("order").groupBy("status"); //单个字段分组
Sql.of().from("order").groupBy("status", "type"); //多个字段组合分组

Sql.of().from("order").select("customer_id, SUM(amount) AS total_spent")
.groupby("customer_id").havingField("total_spent", ">", 400); //找出总消费金额大于 400 的客户。
// select customer_id, SUM(amount) AS total_spent from order group by customer_id having total_spent > 400

.having("b.name", "=", "有限公司")
.having("b.id", "=", 1)
.having(
	h ->
	h.gt("b.id", 0)
	.or("b.name", "=", "有限公司")
	.or("b.name", "=", "科技有限公司")
	.having("b.id", "<", 1)
	.orAndGroup(
		and -> 
		and.leftLike("b.contactName", "ddd")
		.notLike("b.contactName", "llii")
	)
	.orAndGroup(
		and ->
		and.between("b.updatedAt", "2025-01-26 22:16:19", "2025-11-26 22:16:19")
		.like("b.city", "北京市")
	)
)
生成sql:
HAVING 
	b.id = ?
	AND b.id > ?
	AND b.name = ?
	AND b.id < ?
	AND ( b.name = ? OR b.name = ? )
	AND ( 
		( b.contactName NOT LIKE ? AND b.contactName LIKE ? )
		OR 
		( b.city LIKE ? AND b.updatedAt BETWEEN ? AND ? ) 
	)
	
Sql sql = Sql.of().setField("status", 0).setField("updated_at", new Date());
User.dao.save(sql);
// INSERT INTO user (status, updated_at) VALUES (0, '2025-12-10 10:00:00')

Sql sql = Sql.of().eq("id", 1).setField("count", SqlExpr.custom("count + 1")).setField("status", 0).setField("updated_at",  SqlExpr.now());
User.dao.update(sql);	
// UPDATE user SET count = count + 1, status = 0, updated_at = now() where id = 1

Sql.of().from("user").limit(10);
// SELECT * from user LIMIT 10
Sql.of().from("user").limit(20, 10);
// SELECT * from user LIMIT 20, 10

Sql sql = Sql.of().page(2, 10) // 第2页,10条/页
User.dao.paginate(sql)
// SELECT * FROM user limit 10, 10

....


7.png

评论区

北流家园网

2026-03-05 17:40

对于复杂的语句,我还是喜欢写SQL,在SQL编辑器上运行没问题了,直接复制粘贴到sql模板中,条件就使用enjoi来判断。

你这个SQL构建在哪里?只截图,不发链接。

热门分享

扫码入社