这么优雅的Java ORM没见过吧!

 更新时间:2021年01月14日 11:57:12   作者:白菜园  
这篇文章主要介绍了Java ORM的相关资料,帮助大家更好的理解和使用Java,感兴趣的朋友可以了解下

  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("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的资料请关注脚本之家其它相关文章!

相关文章

  • java的正则表达式你知道多少

    java的正则表达式你知道多少

    这篇文章主要为大家详细介绍了java的正则表达式,使用表格进行介绍,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下,希望能够给你带来帮助
    2022-02-02
  • XML Web 服务 Eclipse实现sun-jaxws.xml文件的方法

    XML Web 服务 Eclipse实现sun-jaxws.xml文件的方法

    在sun-jaxws.xml文件,可以配置endpoint、handler-chain等内容,在这个文件中配置的内容会覆盖在Java代码中使用注解属性配置的的内容,本文给大家介绍的非常详细,感兴趣的朋友一起看看吧
    2023-11-11
  • mybatis的大于小于号转义符号一览

    mybatis的大于小于号转义符号一览

    这篇文章主要介绍了mybatis的大于小于号转义符号一览,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-08-08
  • Springboot双mongodb配置方式

    Springboot双mongodb配置方式

    这篇文章主要介绍了Springboot双mongodb配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-05-05
  • springboot启动时运行代码详解

    springboot启动时运行代码详解

    在本篇内容中我们给大家整理了关于在springboot启动时运行代码的详细图文步骤以及需要注意的地方讲解,有兴趣的朋友们学习下。
    2019-06-06
  • 简单介绍Java编程中的线程池

    简单介绍Java编程中的线程池

    这篇文章主要介绍了Java编程中的线程池,进程和线程的并发是Java编程中的重要环节,需要的朋友可以参考下
    2015-09-09
  • 解决mybatis 中collection嵌套collection引发的bug

    解决mybatis 中collection嵌套collection引发的bug

    这篇文章主要介绍了解决mybatis 中collection嵌套collection引发的bug,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • MyBatisPlus3.x中使用代码生成器(全注释)

    MyBatisPlus3.x中使用代码生成器(全注释)

    这篇文章主要介绍了MyBatisPlus3.x中使用代码生成器(全注释),文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-09-09
  • Java中的序列化机制详细解读

    Java中的序列化机制详细解读

    这篇文章主要介绍了Java中的序列化机制详细解读,序列化:将对象的状态信息转换为可以存储或传输的数据形式(比如二进制)的过程,反序列化:与序列化相对,把序列化转换成的可以存储或传输的数据形式转化为对象的状态信息的过程,需要的朋友可以参考下
    2023-11-11
  • java求数组最大值和最小数示例分享

    java求数组最大值和最小数示例分享

    这篇文章主要介绍了java求数组最大值和最小数示例,需要的朋友可以参考下
    2014-03-03

最新评论