这么优雅的Java ORM没见过吧!
Java的ORM框架有很多,但由于Java语言的限制大部分都不够优雅也不够简单,所以作者只能另辟蹊径造轮子了。照旧先看示例代码了解个大概,然后再解释实现原理。
一、ORM示例
1. Insert
public CompletableFuture<Void> insert() {
var obj = new sys.entities.Demo("MyName"); //构造参数为主键
obj.Age = 100; //设置实体属性的值
return obj.saveAsync();
}
2. Update
- 更新单个实体(必须具备主键)
public CompletableFuture<Void> update(sys.entities.Demo obj) {
obj.Age = 200;
return obj.saveAsync();
}
- 根据条件更新(必须指定条件以防误操作)
public CompletableFuture<?> update() {
var cmd = new SqlUpdateCommand<sys.entities.Demo>();
cmd.update(e -> e.City = "Wuxi"); //更新字段
cmd.update(e -> e.Age = e.Age + 1); //更新累加字段
cmd.where(e -> e.Name == "Johne"); //更新的条件
var outs = cmd.output(e -> e.Age); //更新的同时返回指定字段
return cmd.execAsync().thenApply(rows -> {
System.out.println("更新记录数: " + rows);
System.out.println("返回的值: " + outs.get(0));
return "Done.";
});
}
3. Delete
- 删除单个实体(必须具备主键)
public CompletableFuture<Void> update(sys.entities.Demo obj) {
obj.markDeleted(); //先标记为删除状态
return obj.saveAsync(); //再调用保存方法
}
- 根据条件删除(必须指定条件以防误操作)
public CompletableFuture<?> delete() {
var cmd = new SqlDeleteCommand<sys.entities.Demo>();
cmd.where(e -> e.Age < 0 || e.Age > 200);
return cmd.execAsync();
}
4. Transaction
由于作者讨厌隐式事务,所以事务命令必须显式指定。
public CompletableFuture<?> transaction() {
var obj1 = new sys.entities.Demo("Demo1");
obj1.Age = 11;
var obj2 = new sys.entities.Demo("Demo2");
obj2.Age = 22;
return DataStore.DemoDB.beginTransaction().thenCompose(txn -> { //开始事务
return obj1.saveAsync(txn) //事务保存obj1
.thenCompose(r -> obj2.saveAsync(txn)) //事务保存obj2
.thenCompose(r -> txn.commitAsync()); //递交事务
}).thenApply(r -> "Done");
}
5. Sql查询
- Where条件
public CompletableFuture<?> query(String key) {
var q = new SqlQuery<sys.entities.Demo>();
q.where(e -> e.Age > 10 && e.Age < 80);
if (key != null)
q.andWhere(e -> e.Name.contains(key)); //拼接条件
return q.toListAsync(); //返回List<sys.entities.Demo>
}
- 分页查询
public CompletableFuture<?> query(int pageSize, int pageIndex) {
var q = new SqlQuery<sys.entities.Demo>();
return q.skip(pageSize * pageIndex)
.take(pageSize)
.toListAsync();
}
- 结果映射至匿名类
public CompletableFuture<?> query() {
var q = new SqlQuery<sys.entities.Demo>();
return q.toListAsync(e -> new Object() { //返回List<匿名类>
public final String Name = e.Name; //匿名类属性 = 实体属性表达式
public final int Age = e.Age + 10;
public final String Father = e.Parent.Name;
}).thenApply(appbox.data.JsonResult::new);
}
- 结果映射至继承的匿名类
public CompletableFuture<?> query() {
var q = new SqlQuery<sys.entities.Demo>();
q.where(e -> e.Parent.Name == "Rick");
return q.toListAsync(e -> new sys.entities.Demo() { //返回List<? extens Demo>
public final String Father = e.Parent.Name;
});
}
- 结果映射至树状结构列表
public CompletableFuture<?> tree() {
var q = new SqlQuery<sys.entities.Demo>();
q.where(t -> t.Name == "Rick");
return q.toTreeAsync(t -> t.Childs); //参数指向EntitySet(一对多成员)
}
- EntityRef(一对一引用的实体成员)自动Join
public CompletableFuture<?> query() {
var q = new SqlQuery<sys.entities.Customer>();
q.where(cus -> cus.City.Name == "Wuxi");
return q.toListAsync();
}
生成的Sql:
Select t.* From "Customer" t Left Join "City" j1 On j1."Code"=t."CityCode"
- 手工指定Join
public CompletableFuture<?> join() {
var q = new SqlQuery<sys.entities.Customer>();
var j = new SqlQueryJoin<sys.entities.City>();
q.leftJoin(j, (cus, city) -> cus.CityCode == city.Code);
q.where(j, (cus, city) -> city.Name == "Wuxi");
return q.toListAsync();
}
- 子查询
public CompletableFuture<?> subQuery() {
var sq = new SqlQuery<sys.entities.Demo>();
sq.where(s -> s.ParentName == "Rick");
var q = new SqlQuery<sys.entities.Demo>();
q.where(t -> DbFunc.in(t.Name, sq.toSubQuery(s -> s.Name)));
return q.toListAsync();
}
- GroupBy
public CompletableFuture<?> groupBy() {
var q = new SqlQuery<sys.entities.Demo>();
q.groupBy(t -> t.ParentName) //多个可重复
.having(t -> DbFunc.sum(t.Age) > 10);
return q.toListAsync(t -> new Object() {
public final String group = t.ParentName == null ? "可怜的孩子" : t.ParentName;
public final int totals = DbFunc.sum(t.Age);
}).thenApply(appbox.data.JsonResult::new);
}
二、实现原理
其实以上的示例代码并非最终运行的代码,作者利用Eclipse jdt将上述代码在编译发布服务模型时分析转换为最终的运行代码,具体过程如下:
1. jdt分析服务虚拟代码生成AST抽象语法树;
2. 遍历AST树,将实体对象的读写属性改写为getXXX(), setXXX();
var name = obj.Name; //读实体属性
obj.Name = "Rick"; //写实体属性
改写为:
var name = obj.getName();
obj.setName("Rick");
3. 遍历AST树,将查询相关方法的参数转换为运行时表达式;
public CompletableFuture<?> query(String key) {
var q = new SqlQuery<sys.entities.Employee>();
q.where(e -> e.Manager.Name + "a" == key + "b");
return q.toListAsync();
}
转换为:
public CompletableFuture<?> query(String key) {
var q = new appbox.store.query.SqlQuery<>(-7018111290459553788L, SYS_Employee.class);
q.where(e -> e.m("Name").plus(obj.getName()).plus("c").eq("Rick"));
return q.toListAsync();
}
4. 根据服务模型使用到的实体模型生成相应实体的运行时代码;
5. 最后编译打包服务模型的字节码。
以上请参考源码的ServiceCodeGenerator及EntityCodeGenerator类。
三、性能与小结
作者写了个简单查询的服务,测试配置为MacBook主机(wrk压测 + 数据库)->4核I7虚拟机(服务端),测试结果如下所示qps可达1万,已包括实体映射转换及序列化传输等所有开销。这里顺便提一下,由于框架是全异步的,所以没有使用传统的JDBC驱动,而是使用了jasync-sql(底层为Netty)来驱动数据库。
wrk -c200 -t2 -d20s -s post_bin.lua http://10.211.55.8:8000/api
Running 20s test @ http://10.211.55.8:8000/api
2 threads and 200 connections
Thread Stats Avg Stdev Max +/- Stdev
Latency 18.97ms 5.84ms 89.15ms 81.55%
Req/Sec 5.32k 581.92 6.48k 65.00%
211812 requests in 20.02s, 36.76MB read
Requests/sec: 10578.90
Transfer/sec: 1.84MB
边码代码边码文实属不易,作者需要您的支持请您多多点赞推荐!另欢迎感兴趣的小伙伴加入我们!
这么优雅的Java ORM没见过吧!的更多相关文章
- 这是你没见过的不一样的redis
转: 这是你没见过的不一样的redis 提到Redis,大家一定会想到的几个点是什么呢? 高并发,KV存储,内存数据库,丰富的数据结构,单线程(6版本之前) 那么,接下来,上面提到的这些,都会一一给大 ...
- 超赞!12套你没见过的社交媒体 & 社交网站图标
如今,社交网络成为我们信息获取和传播的重要途径,很多网站都有把内容分享到社交媒体的功能.社交媒体图标作为向用户传递信息的重要媒介,不管是在网页还是 Web 应用程序中都非常需要.今天这篇文章和大家分享 ...
- 【原创】怎样才能写出优雅的 Java 代码?这篇文章告诉你答案!
本文已经收录自 JavaGuide (59k+ Star):[Java学习+面试指南] 一份涵盖大部分Java程序员所需要掌握的核心知识. 本文比较简短,基本就是推荐一些对于写好代码非常有用的文章或者 ...
- 这份java多线程笔记,你真得好好看看,我还没见过总结的这么全面的
1.线程,进程和多线程 1.程序:指指令和数据的有序集合,其本身没有任何意义,是一个静态的概念 2.进程:指执行程序的一次执行过程,是一个动态的概念.是系统资源分配的单位(注意:很多多线程是模拟出来的 ...
- StackOverflow: 你没见过的七个最好的Java答案
StackOverflow发展到目前,已经成为了全球开发者的金矿.它能够帮助我们找到在各个领域遇到的问题的最有用的解决方案,同时我们也会从中学习到很多新的东西.这篇文章是在我们审阅了StackOver ...
- java中三种常见内存溢出错误的处理方法
更多 10 相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深入的 ...
- Qt国际化相关类(以前没见过codec->toUnicode,QTextCodec,QLocale.toString和QLocale::setDefault,QInputMethod::locale())
QTextCodec QTextCodec为文本编码之间提供转换. Qt用Unicode 来存储,绘制和操作字符串.在很多情况下你可能希望操作不同编码的数据.例如,大部分日本文档是以Shift-JIS ...
- java中三种常见内存溢出错误的处理方法(good)
相信有一定java开发经验的人或多或少都会遇到OutOfMemoryError的问题,这个问题曾困扰了我很长时间,随着解决各类问题经验的积累以及对问题根源的探索,终于有了一个比较深入的认识. 在解决j ...
- Java ORM Hibernate 入门笔记
一.下载 官网地址:http://hibernate.org/ Hibernate下有ORM(关系型数据库).OGM(NoSQL数据库).Search(对象全文检索).Validator的工具. OR ...
随机推荐
- Day4 【Scrum 冲刺博客】
每日会议总结 昨天已完成的工作 方晓莹(PIPIYing) 完善人员管理页的未完成部分 方子茵(Laa-L):无 黄芯悦(Sheaxx) 开始投诉反馈页面的开发 舒雯钰(LittleTaro) 博客的 ...
- Codeforces Edu Round 61 A-C + F
A. Regular Bracket Sequence 显然,"\(()\)"不影响结果它是自我匹配的,可以把所有的\(((\)和\())\)都放在左边/右边,这样只要检查它们的数 ...
- SpringBoot + Layui + JustAuth +Mybatis-plus实现可第三方登录的简单后台管理系统
1. 简介 在之前博客:SpringBoot基于JustAuth实现第三方授权登录 和 SpringBoot + Layui +Mybatis-plus实现简单后台管理系统(内置安全过滤器)上改造 ...
- Unity GameObject
GameObject 游戏对象 GameObject是unity所有实体的基类 gameObject 获取当前脚本所挂载的游戏对象 一般来说,在属性视图中能看到或修改的属性,我们同样可以在脚本中获取并 ...
- abp.zero 9.0框架的前端Angular使用说明
abp.zero 9.0框架的前端Angular使用说明 目录 abp.zero 9.0框架的前端Angular使用说明 摘要 1 部署及启动 1.1 依赖包安装 1.2 使用yarn安装依赖包 1. ...
- 工具-Redis-介绍(99.6.1)
@ 目录 1.nosql介绍 2.Redis特性 3.redis应用场景 关于作者 1.nosql介绍 not only sql,不支持SQL语法.存储结构跟传统关系型数据库中的那种关系表完全不同,n ...
- 使用 vue 仿写的一个购物商城
在学习了 vue 之后,决定做一个小练习,仿写了一个有关购物商城的小项目.下面就对项目做一个简单的介绍. 项目源码: github 项目的目录结构 -assets 与项目有关的静态资源,包括 css, ...
- RabbiMQ重新安装会遇到的错误-SpiritMark
这里只做安装过程中遇到错误的介绍,不喜勿喷,如果对您有帮助右上角关注一下,是对我最大的肯定 重新安装的注意事项: 先卸载RabbitMQ,后卸载Erlang RabbitMQ卸载,选择uninstal ...
- Winform Dock顺序调整
在布局的时候,当一个窗体内有多个控件使用了Dock属性来布局,Dock顺序的调整: 最近被.net winform中的控件布局搞困惑了,由于控件都是使用Dock方式的,操作起来也是比较方便,如果最大化 ...
- matplotlib学习日记(十一)---坐标轴高阶应用
(一)设置坐标轴的位置和展示形式 (1)向画布中任意位置添加任意数量的坐标轴 ''' 通过在画布的任意位置和区域,讲解设置坐标轴的位置和坐标轴的展示形式的实现方法, 与subplot,subplots ...