这么优雅的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的资料请关注毛票票其它相关文章!