Java的ORM框架有很多,但由于Java语言的限制大部分都不够优雅也不够简单,所以作者只能另辟蹊径造轮子了。照旧先看示例代码了解个大概,然后再解释实现原理。

一、ORM示例

1. Insert

public CompletableFuture insert() {

var obj = new sys.entities.Demo("MyName"); //构造参数为主键

obj.Age = 100; //设置实体属性的值

return obj.saveAsync();

}

2. Update

更新单个实体(必须具备主键)public CompletableFuture update(sys.entities.Demo obj) {

obj.Age = 200;

return obj.saveAsync();

}

根据条件更新(必须指定条件以防误操作)public CompletableFuture> update() {

var cmd = new SqlUpdateCommand();

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 update(sys.entities.Demo obj) {

obj.markDeleted(); //先标记为删除状态

return obj.saveAsync(); //再调用保存方法

}

根据条件删除(必须指定条件以防误操作)public CompletableFuture> delete() {

var cmd = new SqlDeleteCommand();

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();

q.where(e -> e.Age > 10 && e.Age < 80);

if (key != null)

q.andWhere(e -> e.Name.contains(key)); //拼接条件

return q.toListAsync(); //返回List

}

分页查询public CompletableFuture> query(int pageSize, int pageIndex) {

var q = new SqlQuery();

return q.skip(pageSize * pageIndex)

.take(pageSize)

.toListAsync();

}

结果映射至匿名类public CompletableFuture> query() {

var q = new SqlQuery();

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();

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();

q.where(t -> t.Name == "Rick");

return q.toTreeAsync(t -> t.Childs); //参数指向EntitySet(一对多成员)

}

EntityRef(一对一引用的实体成员)自动Joinpublic CompletableFuture> query() {

var q = new SqlQuery();

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"

手工指定Joinpublic CompletableFuture> join() {

var q = new SqlQuery();

var j = new SqlQueryJoin();

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();

sq.where(s -> s.ParentName == "Rick");

var q = new SqlQuery();

q.where(t -> DbFunc.in(t.Name, sq.toSubQuery(s -> s.Name)));

return q.toListAsync();

}

GroupBypublic CompletableFuture> groupBy() {

var q = new SqlQuery();

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();

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("Manager").m("Name").plus("a").eq(key + "b"));

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_这么优雅的Java ORM没见过吧!相关推荐

  1. java小游戏英文文献,连连看Java小游戏毕业设计论文

    连连看Java小游戏毕业设计论文 连连看连连看 JavaJava 小游戏毕业论文小游戏毕业论文 设计设计 学生姓名学生姓名 学学 号号 系系 别别 专专 业业 指导教师指导教师 软件 071 班 目目 ...

  2. 五道java小题,补更四道java小题

    一:分析以下需求,并用代码实现     1.定义List集合,存入多个字符串     2.删除集合中字符串"def"     3.然后利用迭代器遍历集合元素并输出 1 import ...

  3. java小程序扑克牌_用Java来写一个模拟斗地主发牌的小程序

    一副扑克有54张牌:大小王+4*13,接下来我们来模拟一下斗地主的发牌过程 首先,我们需要买牌,新买来的牌都是按顺序摆放的,因此下一步是洗牌,最后就是发牌了,不过发牌的时候要注意还要在最后留三张底牌. ...

  4. java 一二三四五_五道java小题,补更四道java小题

    一:分析以下需求,并用代码实现 1.定义List集合,存入多个字符串 2.删除集合中字符串"def" 3.然后利用迭代器遍历集合元素并输出 import java.util.Arr ...

  5. java 小抄_专门为Java初学者准备的Java小抄集合(2)

    Map HashMap 以Entry[]数组实现的哈希桶数组,用Key的哈希值取模桶数组的大小可得到数组下标. 插入元素时,如果两条Key落在同一个桶(比如哈希值1和17取模16后都属于第一个哈希桶) ...

  6. 我翻遍全网资源,找到了18份阿里内部Java技术教程,我打赌你都没见过(含大数据实时数仓技术)

    2017年阿里就面向全球发布了<阿里巴巴Java开发规约>,这一规约集合了众多中国工程师的智慧与经验,帮助全球开发者实现了更为高效.更加容错.更有协作性的Java开发.目前这一规约已经成为 ...

  7. java小细节_为什么我喜欢Java的细节

    java小细节 他们说,Java太冗长了. 您可以找到Hello World程序的比较,这些程序在ruby中使用2行,在Java中使用10行,而要读取文件,您需要使用Java 20行和php中1行. ...

  8. java applet怎么运行_配置Java Applet的运行环境

    Java小程序,也就是Java Applet,可以在Web浏览器中运行.Java Applet必须以脚本的形式嵌入到HTML页面中,才能在web浏览器中运行. 之前总以为本地安装了JDK,指定好JAV ...

  9. spring java配置_Spring:使基于Java的配置更加优雅

    spring java配置 大家好,我很久没有写新文章了. 积累了很多资料,需要在不久的将来在我的博客中发布. 但是现在我想谈谈Spring MVC应用程序配置. 确切地说,我想谈谈基于Java的Sp ...

最新文章

  1. Zookeeper的java实例
  2. python创建新文件-python创建和删除文件
  3. (原创)用讯飞语音实现人机交互的功能
  4. 计算机组成原理we指什么,计算机组成原理课后习题答案一到九章
  5. boost::callable_traits的has_member_qualifiers的测试程序
  6. 自定义报错返回_Spring Cloud Feign的使用和自定义配置
  7. socket通信简单介绍
  8. 48 FI配置-财务会计-固定资产-与总账集成-定义折旧范围到总账的过账方式
  9. 多线程的那点儿事(之顺序锁)
  10. 25GbE可以解决数据中心过载问题吗?
  11. 转: android编译过程(流程图)
  12. java dispose事件_Android-在 ViewModel 中使用 AutoDispose2 解决 RxJava 的内存泄露问题
  13. Java实现Unicode编码和中文互转
  14. ACM_贪心(HDU2037HDU1789)
  15. 程序员自编 “购房宝典” 火爆 GitHub!
  16. windows windows计划任务访问网络存储NAS的问题
  17. 北京车牌到底有什么优势?
  18. java 小数乘法,乐乐课堂四年级数学网课-四年级下册01-第08讲-四边形分类(1).mp4...
  19. win7cmd闪退_Win7运行bat批处理闪退怎么解决
  20. 谈谈eve-ng仿真模器

热门文章

  1. [Eclipse插件] Eclipse设置Tab键为空格(ctrl+shirt+f格式化生效)!
  2. C++ 虚函数表解析 继承
  3. jquery --- Poshy Tip jQuery Plugin
  4. 转载一个程序员的游戏之路
  5. Base 6 FAQ v8
  6. 程序员的算法课(16)-B+树在数据库索引中的作用
  7. php 导出excel类,php 导出excel类
  8. ASP.NET Core Docker Nginx分权,多网站部署
  9. 转载-聊一聊深度学习的activation function
  10. bd3.1 Python 高级