Weed3 for java 新的微型ORM框架
[WMV=400,300,True]upload/[/WMV]Weed3,微型ORM框架(支持:java sql,xml sql,annotation sql;存储过程;事务;缓存;监听;等...)
05年时开发了第一代;
08年时开发了第二代,那时候进入互联网公司,对性能有了全新的认识;
14年时开发了第三代。因为不喜欢滥用反射,不喜欢有很多配置,所以一直在执着的没放弃。
前两代,都是在.net开发的;第三代,重点放在了java上。应该算是个功能全面且最小的ORM框架,无其它依赖,仅0.1mb。对外的接口也不多,主要由DbContext上的四个接口发起所有的操作。
因为一些执念写的东西都算是比较微型的:
Snack3(Json框架 70kb,有序列化,有Jsonpath,有格式转换机制;强调构建能力)
Solon(Web框架 80kb)
一个手机浏览器(0.1mb,可是有完整功能哦;算是一个创意作品)
Weed3 特点和理念:
高性能:两年前有个同事测过四个ORM框架,它是性能最好的(不知道现在是不是)。
跨平台:可以嵌入到JVM脚本引擎(js, groovy, lua, python, ruby);有.net,php版本(久没维护了)。
很小巧:只有0.1Mb嘛(且是功能完整,方案丰富;可极大简化数据库开发)。
有个性:不喜欢反射、不喜欢配置...(除了连接,不需要任何配置)。
其它的:支持缓存控制和跨数据库事务(算是分布式事务的一种吧)。
Weed3 组件:
组件 说明
org.noear:weed3-parent 框架版本管理
org.noear:weed3 主框架(没有任何依赖)
org.noear:weed3-maven-plugin Maven插件,用于生成Xml sql mapper
org.noear:weed3.cache.memcached 基于 Memcached 封装的扩展缓存服务
org.noear:weed3.cache.redis 基于 Redis 封装的扩展缓存服务
org.noear:weed3.cache.ehcache 基于 ehcache 封装的扩展缓存服务
org.noear:weed3.cache.j2cache 基于 j2cache 封装的扩展缓存服务
Weed3 meven配置:
org.noear
weed3
3.2.4.1
org.noear
weed3-maven-plugin
3.2.4.1
Weed3 入手流程:
配置DataSource信息
实始化DbContext
调用DbContext上的接口(需要大至了解一下语法...)
一、 上下文对象 DbContext
所有weed3的操作,都是基于DbContext上的接口的操作。即,一切从实例化DbContext开始:
1.使用application.yml配置数据源(或别的格式配置,或配置服务),格式示例:
demodb:
schema: demo
url: jdbc:mysql://localdb:3306/demo?...
driverClassName: com.mysql.cj.jdbc.Driver
username: demo
password: UL0hHlg0Ybq60xyb
2.有配置之后开始实列化DbContext:
如果是 Spring 框架,可以通过注解获取配置
如果是 solon 框架,可以通过注解 或 接口获取配置
//使用Properties配置的示例
Properties properties = XApp.cfg().getProp("demodb"); //这是solon框架的接口
DbContext db = new DbContext(properties);
//使用Map配置的示例
DbContext db = new DbContext(map);
//使用proxool线程池配置的示例(好像现在不流行了)//proxool通过xml配置
DbContext db = new DbContext("user","proxool.xxx_db");
//使用DataSource配置的示例(一般使用连接池框架时用;推荐 Hikari 连接池)
//下行demo里用的正是 Hikari 连接池
DataSource dataSource = new HikariDataSource(...);
DbContext db = new DbContext("user", dataSource);
//还有就是用url,username,password(这个就不需要配置了)
DbContext db = new DbContext("user","jdbc:mysql://x.x.x:3306/user","root","1234");
/* 我平时都用配置服务,所以直接由配置提供数据库上下文对象。 */
//使用配置服务直接拿到DbContext
DbContext db = WaterClient.Config.get("demodb").getDb();
二、四大接口 db.mapper(), db.table(), db.call(), db.sql()
四大接口,也是DbContext在不同场景上的四种应用方案
核心接口:db.mapper(), db.table()。代表两种完全不同的风格和口味。
补充接口:db.call(), db.sql()。应对特殊的应用场景。
其中db.table(), db.call(), db.sql() 可以友好的嵌入到JVM脚本引擎(js, groovy, lua, python, ruby)和部分GraalVM语言使用。
因为作者还有个嵌入式FaaS引擎。统一的执行发起对象、无注入无配置、且弱类型的接口作用重大;可以便利的嵌入各种语言中,并提供统一的ORM体验。
(一)db.mapper(),提供mapper操作支持
mapper风格,是现在极为流行的一种。大多人都在用。
此接口提供了BaseMapper模式,@Sql注入模式,Xml sql配置模式。其中,Xml sql 的内部处理会在启动时预编译为Java class;性能应该是靠谱的(好像有点儿jsp的预编译味道)。
1.db.mapperBase(clz) 获取BaseMapper实例
自Xxx-plus之后,要是个没有BaseMapper,好像都不好意思说自己是个ORM框架了。
这个接口确实带来了极大的方法,简单的CRUD完全省掉了。
//直接使用BaseMapper
BaseMapper userDao= db.mapperBase(User.class);
//增
userDao.insert(user,false); //false:表示排除null值
//删
userDao.deleteById(12);
//改:通过ID改
userDao.updateById(user,false); //false:表示排除null值
//改:通过条件改
userDao.update(user,false,m->m.whereEq(User::getType,12).andEq(User::getSex,1));
//查.通过ID查
User user = userDao.selectById(12);
//查.通过条件查(条件,可以是字符串风格;可以是lambda风格)
User user = userDao.selectItem(m -> m.whereEq(User::getId,12));
2.db.mapper(clz),获取Mapper实例
@Namespace("demo.dso.db")
public interface UserDao { //此接口,可以扩展自 BaseMapper
@sql("select * from `user` where id=@{id}") //变量风格
User getUserById(int id);
@sql("select * from `user` where id=?") //占位符风格
User getUserById2(int id);
long addUser(User m); //没有注解,需编写xml sql配置
}
UserDao userDao = db.mapper(UserDao.class);
User user = userDao.getUserById(12);
userDao.addUser(user);
3.db.mapper(xsqlid, args),获取Xml sql mapper结果
此接口的好处是,可以把DAO做成一个中台:把xml sql 放在数据库里,统一管理;并通过开发一个DAO网关,以RPC或REST API方式提供服务。
```java
Map args = new HashMap<>();
args.put("id",22);
//xsqlid = @{sqlid} = @{namespace}.{id}
User user = db.mapper("@demo.dso.db.getUserById",args);
```
(二)db.table(),提供纯java链式操作
这是Weed3最初的样子,这也是我最喜欢的方法。也是具体跨平台嵌入的关键能力。
BaseMapper内部也是由db.table()实现的,简单几行代就OK了。
灵活,有弹性,直接,可以实现任何SQL代码效果。开发管理后台,很爽(因为查询条件又杂又乱)。
db.table() 支持 两种风格:
1.字符串风格:弹性大、自由方便、可嵌入,语法便于跨平台;但改字段名字时麻烦;
2.lambda风格:约束性强、便于管理(改字段名时极方便);但写起来麻烦;语法不好跨平台性。
增,INSEERT
User user = new User();
..
//单条插入
db.table("user").set("name","noear").insert();
db.table("user").setEntity(user).insert();
db.table("user").setEntityIf(user, (k,v)->v!=null).insert(); //过滤null
//批量插入
db.table("user").insertList(list);
删,DELETE
//删掉id<12的记录
db.table("user").whereLt("id",12).delete();
//删掉id=12的记录 (lamdba风格)
db.table(User.class).whereEq(User::getId,12).delete();
改,UPDATE
//改掉id=23的sex字段
db.table("user").set("sex",1).whereEq("id",23).update();
//根据手机号,新增或更新
public void saveUser(UserModel m){
db.talbe("user").setEntityIf(m, (k,v)->v!=null).upsert("mobile");
}
查,SELECT
//统计id<100, 名字长度>10的记录数(可以自由的使用SQL函数)
db.table("user").where("id", 100).and("LENGTH(name)>?",10).count();
//查询20条,id>10的记录
db.table("user").whereGte("id", 10).limit(20).select("*").getMapList();
//关联查询并输出一个实体(lamdba风格) //还是字符串风格更有弹性和简洁
db.table(User.class)
.innerJoin(UserEx.class).onEq(User::getId,UserEx::getUserId)
.where(User::getId, 10).andEq(UserEx::getSex,1)
.limit(1)
.select(User.class,$(UserEx::getSex).alias("user_sex"))
.getItem(User.class);
具有过滤能力的接口:whereIf, andIf, orIf, setIf, setMapIf, setEntityIf
//如果有名字,加名字条件;(管理后台的查询,很实用的; 省了很多if)
db.talbe("user").whereIf(name!=null, "name=?", name).limit(10).select("");
//插入,过滤null
db.table("user").setMapIf(map,(k,v)->v!=null).insert(); //过滤null
//更新
db.table("user")
.setIf(name!=null, "name",name)
.setIf(sex>0, "sex", sex)
.setIf(mobile!=null && mobile.length() =11,"mobile",mobile)
.where("id=?",id)
.update();
(三)db.call(),提供call操作
call数据库存储过程
//数据库存储过程使用
//
User user = db.call("user_get").set("id",1).getItem(User.class);
call查询过程
//查询储过程使用 (@sql内部由此实现)
//
User user = db.call("select * from user where id=@{id}").set("id",1).getItem(User.class);
call Xmlsql
//Xml sql的弱类型使用方式 //需@开始
//
User user = db.call("@demo.dso.db.getUser").set("id",1).getItem(User.class);
(四)db.sql(),提供手写sql操作
//所以接口最终都会转为db.sql(),算是最底层的一个接口
//
User user = db.sql("select * from user where id=?",12).getItem(User.class);
Long total = db.sql("select count(*) from user").getValue(0l);
//db.sql() 的快捷版: db.exe(),用于快速批处理
//
db.exe("delete from user where id=12");
db.exe("update user sex=1 where id=12");
三、Mapper 语法
(一)BaseMapper 接口
Long insert(T entity, boolean excludeNull);
void insertList(List list);
Integer deleteById(Object id);
Integer deleteByIds(Iterable