这么优雅的Java ORM没见过吧!
Java的ORM框架有很多,但由于Java语言的限制大部分都不够优雅也不够简单,所以作者只能另辟蹊径造轮子了。照旧先看示例代码了解个大概,然后再解释实现原理。
一、ORM示例
1.Insert
publicCompletableFutureinsert(){ varobj=newsys.entities.Demo("MyName");//构造参数为主键 obj.Age=100;//设置实体属性的值 returnobj.saveAsync(); }
2.Update
更新单个实体(必须具备主键)
publicCompletableFutureupdate(sys.entities.Demoobj){ obj.Age=200; returnobj.saveAsync(); }
根据条件更新(必须指定条件以防误操作)
publicCompletableFuture>update(){ varcmd=newSqlUpdateCommand(); cmd.update(e->e.City="Wuxi");//更新字段 cmd.update(e->e.Age=e.Age+1);//更新累加字段 cmd.where(e->e.Name=="Johne");//更新的条件 varouts=cmd.output(e->e.Age);//更新的同时返回指定字段 returncmd.execAsync().thenApply(rows->{ System.out.println("更新记录数:"+rows); System.out.println("返回的值:"+outs.get(0)); return"Done."; }); }
3.Delete
删除单个实体(必须具备主键)
publicCompletableFutureupdate(sys.entities.Demoobj){ obj.markDeleted();//先标记为删除状态 returnobj.saveAsync();//再调用保存方法 }
根据条件删除(必须指定条件以防误操作)
publicCompletableFuture>delete(){ varcmd=newSqlDeleteCommand(); cmd.where(e->e.Age<0||e.Age>200); returncmd.execAsync(); }
4.Transaction
由于作者讨厌隐式事务,所以事务命令必须显式指定。
publicCompletableFuture>transaction(){ varobj1=newsys.entities.Demo("Demo1"); obj1.Age=11; varobj2=newsys.entities.Demo("Demo2"); obj2.Age=22; returnDataStore.DemoDB.beginTransaction().thenCompose(txn->{//开始事务 returnobj1.saveAsync(txn)//事务保存obj1 .thenCompose(r->obj2.saveAsync(txn))//事务保存obj2 .thenCompose(r->txn.commitAsync());//递交事务 }).thenApply(r->"Done"); }
5.Sql查询
Where条件
publicCompletableFuture>query(Stringkey){ varq=newSqlQuery(); q.where(e->e.Age>10&&e.Age<80); if(key!=null) q.andWhere(e->e.Name.contains(key));//拼接条件 returnq.toListAsync();//返回List }
分页查询
publicCompletableFuture>query(intpageSize,intpageIndex){ varq=newSqlQuery(); returnq.skip(pageSize*pageIndex) .take(pageSize) .toListAsync(); }
结果映射至匿名类
publicCompletableFuture>query(){ varq=newSqlQuery(); returnq.toListAsync(e->newObject(){//返回List<匿名类> publicfinalStringName=e.Name;//匿名类属性=实体属性表达式 publicfinalintAge=e.Age+10; publicfinalStringFather=e.Parent.Name; }).thenApply(appbox.data.JsonResult::new); }
结果映射至继承的匿名类
publicCompletableFuture>query(){ varq=newSqlQuery(); q.where(e->e.Parent.Name=="Rick"); returnq.toListAsync(e->newsys.entities.Demo(){//返回List publicfinalStringFather=e.Parent.Name; }); }
结果映射至树状结构列表
publicCompletableFuture>tree(){ varq=newSqlQuery(); q.where(t->t.Name=="Rick"); returnq.toTreeAsync(t->t.Childs);//参数指向EntitySet(一对多成员) }
EntityRef(一对一引用的实体成员)自动Join
publicCompletableFuture>query(){ varq=newSqlQuery(); q.where(cus->cus.City.Name=="Wuxi"); returnq.toListAsync(); } 生成的Sql: Selectt.*From"Customer"tLeftJoin"City"j1Onj1."Code"=t."CityCode"
手工指定Join
publicCompletableFuture>join(){ varq=newSqlQuery(); varj=newSqlQueryJoin (); q.leftJoin(j,(cus,city)->cus.CityCode==city.Code); q.where(j,(cus,city)->city.Name=="Wuxi"); returnq.toListAsync(); }
子查询
publicCompletableFuture>subQuery(){ varsq=newSqlQuery(); sq.where(s->s.ParentName=="Rick"); varq=newSqlQuery (); q.where(t->DbFunc.in(t.Name,sq.toSubQuery(s->s.Name))); returnq.toListAsync(); }
GroupBy
publicCompletableFuture>groupBy(){ varq=newSqlQuery(); q.groupBy(t->t.ParentName)//多个可重复 .having(t->DbFunc.sum(t.Age)>10); returnq.toListAsync(t->newObject(){ publicfinalStringgroup=t.ParentName==null?"可怜的孩子":t.ParentName; publicfinalinttotals=DbFunc.sum(t.Age); }).thenApply(appbox.data.JsonResult::new); }
二、实现原理
其实以上的示例代码并非最终运行的代码,作者利用Eclipsejdt将上述代码在编译发布服务模型时分析转换为最终的运行代码,具体过程如下:
1.jdt分析服务虚拟代码生成AST抽象语法树;
2.遍历AST树,将实体对象的读写属性改写为getXXX(),setXXX();
varname=obj.Name;//读实体属性 obj.Name="Rick";//写实体属性
改写为:
varname=obj.getName(); obj.setName("Rick");
3.遍历AST树,将查询相关方法的参数转换为运行时表达式;
publicCompletableFuture>query(Stringkey){ varq=newSqlQuery(); q.where(e->e.Manager.Name+"a"==key+"b"); returnq.toListAsync(); }
转换为:
publicCompletableFuture>query(Stringkey){ varq=newappbox.store.query.SqlQuery<>(-7018111290459553788L,SYS_Employee.class); q.where(e->e.m("Manager").m("Name").plus("a").eq(key+"b")); returnq.toListAsync(); }
4.根据服务模型使用到的实体模型生成相应实体的运行时代码;
5.最后编译打包服务模型的字节码。
以上请参考源码的ServiceCodeGenerator及EntityCodeGenerator类。
三、性能与小结
作者写了个简单查询的服务,测试配置为MacBook主机(wrk压测+数据库)->4核I7虚拟机(服务端),测试结果如下所示qps可达1万,已包括实体映射转换及序列化传输等所有开销。这里顺便提一下,由于框架是全异步的,所以没有使用传统的JDBC驱动,而是使用了jasync-sql(底层为Netty)来驱动数据库。
wrk-c200-t2-d20s-spost_bin.luahttp://10.211.55.8:8000/api Running20stest@http://10.211.55.8:8000/api 2threadsand200connections ThreadStatsAvgStdevMax+/-Stdev Latency18.97ms5.84ms89.15ms81.55% Req/Sec5.32k581.926.48k65.00% 211812requestsin20.02s,36.76MBread Requests/sec:10578.90 Transfer/sec:1.84MB
以上就是这么优雅的JavaORM没见过吧!的详细内容,更多关于javaorm的资料请关注毛票票其它相关文章!