当前位置:网站首页>一个新的微型ORM开源框架
一个新的微型ORM开源框架
2022-07-05 05:13:00 【林西东】
Weed3 一个微型ORM框架(只有0.1Mb哦)
源码:https://github.com/noear/weed3源码:https://gitee.com/noear/weed3
05年的时候开始写这个框架的1代版本。。。 08年时进入互联网公司重构写了2代版本。。。 14年重构写了现在的3代版本(有java 和 .net 的两个平台版本)。。。 最近被迫加了xml sql mapper的支持。。。 然后顺带加了sql注解。。。 终于包也变大到0.1Mb了。。。
上次一个群里的朋友说,这是个清奇的框架。这个讲法很有意思啊。。
总体上来讲,这个框架的特点就是不喜欢反射、不喜欢配置
(但仍然避免不了)!!!是希望通过良好的接口设计,来完全成简洁的操控体验。或许你觉得随便手写点sql都比它好
(怎么可能呢,哈哈~~)
对于一些老人来说,这样描述可能给较好:它相当于 mybatis + mybatis-puls (有个对标物,容易理解些)。。。不过我没用过它们,可能讲得也不对。
另外,它很小,它很快,它很自由(也有人说,太自由反而难控制)
【1】先Hello world一下
- 建个任何类型的java项目,引入框架包
<dependency> <groupId>org.noear</groupId> <artifactId>weed3</artifactId> <version>3.2.9</version></dependency><!-- 这个是顺带的,数据库连接器总要有一个 --><dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.47</version></dependency>
- 不用任何配置,三行代码就可运行
// hello world 走起...(数据库链接改个对口的...)public static void main(String[] args){ DbContext db = new DbContext("user","jdbc:mysql://127.0.0.1:3306/user","root","1234"); String rst = db.sql("SELECT 'hello world!'").getValue();//获取值 System.out.println(rst);}//// 如果是 mybatis ,估计得先忙会儿配置...//
- 应该算是简单的吧(引入两外包;写三行代码)
不能hello world的东西不是好东西。哈哈:-P
weed3 支持纯java链式写法
或者 xml mapper写法
。安排上会先介绍纯java写法。。。再慢慢讲开来。
【2.1】开始纯java使用
纯java使用时,有三大接口可用:
db.table(..), db.call(..), db.sql()
。一般使用db.table(..)
接口进行链式操作居多。它的接口采用与SQL映射的方式命名。。。使用的人,容易想到能有哪些链式接口。像:.where(..) .and(..) .innerJoin(..)
等...
链式操作的套路: 以
db.table(..)
开始。 以.update(..)
或.insert(..)
或.delete(..)
或.select(..)
。 其中.select(..)
会返回IQuery
接口,提供了各种类型结果的选择。
首先,添加meven依赖
<dependency> <groupId>org.noear</groupId> <artifactId>weed3</artifactId> <version>3.2.9</version></dependency><!-- 数据库连接器,我就不管了 -->
然后,实例化数据库上下文对象
- 所有weed3的操作,都是基于DbContext。所以要先实列化一下。。。
- 需要有配置,可以在
application.properties
获取,可以通过配置服务获取,可以临时手写一下。。
如果是 Spring 框架,可以通过注解获取配置 如果是 solon 框架,可以通过注解 或 Aop.prop().get("xxx")获取配置
2.有配置之后开始实列化DbContext。这里临时手写一下。
//使用proxool线程池配置的示例(好像现在不流行了)DbContext db = new DbContext("user","proxool.xxx_db"); //使用DataSource配置的示例(一般使用连接池框架时用;推荐 Hikari 连接池)//下行demo里用的正是 Hikari 连接池DbContext db = new DbContext("user",new HikariDataSource(...)); //还有就是用url,username,passwordDbContext db = new DbContext("user","jdbc:mysql://x.x.x:3306/user","root","1234");/* 我平时都用配置服务,所以直接由配置提供数据库上下文对象。 */
现在,开始做简单的数据操作
- 常规查询操作
//统计小于10的用户数量long num = db.table("user_info").where("user_id<?", 10).count();//检查用户是不是存在bool rst = db.table("user_info").where("user_id=?", 10).exists();//获取用户性别int sex = db.table("user_info").where("user_id=?", 10) .select("sex").getValue();//获取一个用户信息UserModel mod = db.table("user_info").where("user_id=?", 10).and("sex=1") .select("*").getItem(UserModel.class);
- 再来一把全套的"增删改查"
//简易.增db.table("test").set("log_time", "$DATE(NOW())").insert();//简易.删db.table("test").where("id=?",1).delete();//简易.改db.table("test").set("log_time", "$DATE(NOW())").where("id=?",1).update();//简易.查var map = db.table("test").where("id=?",1).select("*").getMap();
关于条件的另一套接口
//where 组whreEq(filed,val) //filed等于valwhereLt(filed,val) //小于whereLte(filed,val) //小于等于whereGt(filed,val) //大于whereGte(filed,val) //大于等于whereLk(filed,val) // LIKEwhereIn(filed,ary) // INwhereNin(filed,ary) // NOT IN//and 组andEq(filed,val) //filed等于valandLt(filed,val) //小于andLte(filed,val) //小于等于andGt(filed,val) //大于andGte(filed,val) //大于等于andLk(filed,val) // LIKEandIn(filed,ary) // INandNin(filed,ary) // NOT IN//or 组orEq(filed,val) //filed等于valorLt(filed,val) //小于orLte(filed,val) //小于等于orGt(filed,val) //大于orGte(filed,val) //大于等于orLk(filed,val) // LIKEorIn(filed,ary) // INorNin(filed,ary) // NOT IN//demo::db.table("test").whereEq("id",1).delete();db.table("test").whereEq("id",1).orEq("name","xidong").delete();
这是一个简单的开始,希望能有个好的印象。
【2.2】细讲插入和更新
这一节重点讲讲插入和更新的赋值
- 支持常规赋值
String mobile="xxx"; //我的手机号不能写db.table("test") .set("mobile",mobile) //变量赋值 .set("sex",1) //常量赋值 .insert();
- 支持sql附值(这个是可以带来方便的***) 如果值以:$开头,表示后面为SQL代码
(不能出现空隔,且100字符以内。否则视为普通字符串值)
,如下:
//比如:当前时间赋值db.table("test").set("log_time","$NOW()").insert();//再比如:字段加1附值db.table("test").set("num","$num+1") .where("id=?",1).update();//再比如:根据另一个字段的md5,批量更新db.table("test").set("txt_md5","$MD5(txt)") .where("id>? AND id<?",1000,2000).update();/* 如何开启或禁用功能?(其实,它是挺安全的)*///1.只控制本次操作是否使用此功能db.table("test").usingExpr(false) // true 开启,false 关闭//2.全局配置开启或关掉这个功能:WeedConfig.isUsingValueExpression=false; //全局默认关掉
- 支持map附值(字段不能是数据表里没有的..)
Map<String,Object> map = new HashMap<>();...//插入db.table("test").setMap(map).insert();//更新db.table("test").setMap(map).where("id=?",1).update();
- 支持 entity 附值(字段不能是数据表里没有的..)
UserModel user = new UserModel();//插入db.table("test").setEntity(user).insert();//更新db.table("test").setEntity(user).where("id=?",1).update();
- 支持(没有则插入,有则更新)的简化操作
//简化方案db.table("test") .set("mobile","111") .set("sex",1) .set("icon","http://xxxx") .updateExt("mobile");//此代码相当于:(下面这个可麻烦了很多哦)if(db.talbe("test").where("mobile=?","111").exists()){ db.talbe("test") .set("mobile","111") .set("sex",1) .set("icon","http://xxxx") .insert()}else{ db.talbe("test") .set("sex",1) .set("icon","http://xxxx") .where("mobile=?","111").update(); }
- 支持根据情况附值(讲法来怪怪的..)
//1.老套跑var qr = db.table("test").set("sex",1);if(icon!=null){ qr.set("icon",icon);}qr.where("mobile=?","111").update(); //2.链式操作套路db.table("test").set("sex",1).expre((tb)->{ //加个表达式 if(icon!=null){ tb.set("icon",icon); }}).where("mobile=?","111").update();
关于更新和删除的条件,参考查询的章节。条件都是一样的嘛
【2.3.1】查询之输出
查询可是个复杂的话题了,可能我们80%的数据库处理都在查询。
今天先讲讲weed3的查询能输出什么?
- 1.1.快捷查询数量
db.table("user_info").where("user_id<?", 10).count();
- 1.2.快捷查询是否存在
db.table("user_info").where("user_id<?", 10).exists();
- 2.1.查询一行的一个字段,输出单值
bool val = db.table("user_info") .where("user_id=?", 10) .select("sex").getValue(false); //设个默认值为:false
- 2.2.查询多行的一个字段,输出数组
List<String> ary = db.table("user_info") .where("user_id=?", 10) .select("mobile").getArray("mobile");
- 3.1.查询一行,输出map
Map<String,Object> map = db.table("user_info") .where("user_id=?", 10) .select("*").getMap();
- 3.2.查询多行,输出map list
List<Map<String,Object>> list = db.table("user_info") .where("user_id>?", 10).limit(20) //限20条记录 .select("*").getMapList();
- 4.1.查询一行,输出entity
UserModel m = db.table("user_info") .where("user_id=?", 10) .select("*").getItem(UserModel.class); //用户模型(我统叫它模型)//这里写了最简单的格式,可以改为bean风格public class UserModel{ public String name; public String mobile; public int sex;}
- 4.2.查询多行,输出entity list
List<UserModel> list = db.table("user_info") .where("user_id>?", 10).limit(0,20) //分页取20行 .select("*").getList(UserModel.class);
那还能再输出什么?
- 1.select("...") 返回的是一个:IQuery
public interface IQuery extends ICacheController<IQuery> { long getCount() throws SQLException; Object getValue() throws SQLException; <T> T getValue(T def) throws SQLException; Variate getVariate() throws SQLException; Variate getVariate(Act2<CacheUsing,Variate> cacheCondition) throws SQLException; <T extends IBinder> T getItem(T model) throws SQLException; <T extends IBinder> T getItem(T model, Act2<CacheUsing, T> cacheCondition) throws SQLException; <T extends IBinder> List<T> getList(T model) throws SQLException; <T extends IBinder> List<T> getList(T model, Act2<CacheUsing, List<T>> cacheCondition) throws SQLException; <T> T getItem(Class<T> cls) throws SQLException; <T> T getItem(Class<T> cls,Act2<CacheUsing, T> cacheCondition) throws SQLException; <T> List<T> getList(Class<T> cls) throws SQLException; <T> List<T> getList(Class<T> cls,Act2<CacheUsing, List<T>> cacheCondition) throws SQLException; DataList getDataList() throws SQLException; DataList getDataList(Act2<CacheUsing, DataList> cacheCondition) throws SQLException; DataItem getDataItem() throws SQLException; DataItem getDataItem(Act2<CacheUsing, DataItem> cacheCondition) throws SQLException; List<Map<String,Object>> getMapList() throws SQLException; Map<String,Object> getMap() throws SQLException; <T> List<T> getArray(String column) throws SQLException; <T> List<T> getArray(int columnIndex) throws SQLException;}
- 2.其中 getDataList() 返加的是 DataList,它有一些类型转换接口:
/** 将所有列转为类做为数组的数据(类为:IBinder 子类) */List<T> toList(T model);/** 将所有列转为类做为数组的数据 */List<T> toEntityList(Class<T> cls);/** 选1列做为MAP的key,并把行数据做为val */Map<String,Object> toMap(String keyColumn);/** 选两列做为MAP的数据 */Map<String,Object> toMap(String keyColumn,String valColumn);/** 选一列做为SET的数据 */Set<T> toSet(String column)/** 选一列做为数组的数据 */List<T> toArray(String columnName)/** 选一列做为数组的数据 */List<T> toArray(int columnIndex)/** 转为json字符串 */String toJson();
- 其中 getVariate() 返回的是 Variate,也提供了些转换接口
T value(T def);double doubleValue(double def);long longValue(long def);int intValue(int def);String stringValue(String def);
【2.3.2】查询之条件
查询查然是个麻烦的话题。。。
还好这节的条件会比较简单
- 单表条件查询(有了简单的自然能拼成复杂的)
//weed3 的条件构建,是相当自由的String mobile = "111"; db.table("test") .where("mobile=?",mobile).and().begin("sex=?",1).or("sex=2").end() .limit(20) .select("*").getMapList()db.table("test") .where("mobile=?",mobile).and("(sex=? OR sex=2)",1) .limit(20) .select("*").getMapList()db.table("test").where("mible=? AND (sex=1 OR sex=2)",mobile) .limit(20) .select("*")//以上三种,效果是一样的。。。因为很自由,所以很容易使用(也有观点认为:所以很难控制)
- 有时候一些条件需要动态控制
//这个示例,管理后台很常见int type=ctx.paramAsInt("type",0);String key=ctx.param("key");int date_start=ctx.paramAsInt("date_start",0);int date_end=ctx.paramAsInt("date_end",0);DbTableQuery qr = db.table("test").where("1=1");if(type > 0){ qr.and("type=?", type);}if(key != null){ qr.and('"title LIKE ?",key+"%");}if(date_start>0 && date_end >0){ qr.and("( date >=? AND date<=? )", date_start, date_end);}qr.select("id,title,icon,slug").getMapList();
- 多表关联查询:innerJoin(..), leftJoin(..), rightJoin(..)
//innerJoin()db.table("user u") .innerJoin("user_book b").on("u.id = b.user_id") .select("u.name,b.*")//leftJoin()db.table("user u") .leftJoin("user_book b").on("u.id = b.user_id").and("u.type=1") .select("u.name,b.*")//rightJoin()db.table("user u") .rightJoin("user_book b").on("u.id = b.user_id") .where("b.type=1").and("b.price>",12) .select("u.name,b.*")
- 想别的关联查询怎么样?(如:full join)
//因为不是所有的数据库都支持 full join,所以...db.table("user u") .append("FULL JOIN user_book b").on("u.id = b.user_id") .select("u.name,b.*")//.append(..) 可以添加任何内容的接口
【2.3.3】查询之缓存控制
缓存控制,是查询中的重点
框架提供的是控制服务。而非缓存服务本身,了解这个很重要。
缓存控制需要两个重要的接口定义:
- 1.缓存服务适配接口 ICacheService
(平常用它的加强版 ICacheServiceEx )
//用它可以包装各种缓存服务public interface ICacheService { void store(String key, Object obj, int seconds); Object get(String key); void remove(String key); int getDefalutSeconds(); String getCacheKeyHead();}/** weed3内置了三个实现类: *EmptyCache,空缓存 *LocalCache,本地缓存 *SecondCache,二级缓存容器(可以把两个 ICacheService 拼到一起,变成一个二级缓存服务;多嵌套一下就是三级缓存服务了) */
- 2.在缓存服务上进行的操控接口:ICacheController
(所有的weed操控对象都实现了ICacheController)
public interface ICacheController<T> { //使用哪个缓存服务 T caching(ICacheService service); //是否使用缓存 T usingCache(boolean isCache); //使用缓存并设置时间 T usingCache(int seconds); //为缓存添加标签 T cacheTag(String tag);}
有了上面的基础后,现在开始使用缓存控制
- 1.先搞个服务实例出来
//缓存key header为 test , 默认缓存时间为 60sICacheService cache = new LocalCache("test",60);
- 2.用起来
使用缓存,时间为默认(会自动产生稳定的缓存key)
db.table("test").select("*").caching(cache).getMapList();
使用缓存,并缓存30s
db.table("test") .caching(cache).usingCache(30) //也可以放在table() 和 select()之间 .select("*").getMapList();
给缓存加个tag(tag 相当于 缓存key的虚拟文件夹)
db.table("test") .caching(cache) .usingCache(30).cacheTag('test_all') //这是tag,不是key .limit(10,20) .select("*").getMapList();
*3.精细控制
根据查询结果控制缓存时间
db.table("test").where("id=?",10) .caching(cache) .select("*").getItem(UserModel.class,(cu,m)->{ if(m.hot > 2){ uc.usingCache(60 * 5); //热门用户缓存5分钟 }else{ uc.usingCache(30); } });
- 4.缓存清除
以一个分页查询为例
db.table("test").limit(20,10) .caching(cache).cacheTag("test_all") .select("*").getMapList();db.table("test").limit(30,10) .caching(cache).cacheTag("test_all") .select("*").getMapList();//不管你有多少分页,一个tag把它清光cache.clear("test_all");
- 5.缓存更新
这个极少用(需要单项更新的缓存,建议用redis)
db.table("test").where("id=?",10) .caching(cache).cacheTag("test_"+10) .select("*").getItem(UserModel.class);cache.update("test_"+10,(UserModel m)->{ m.sex = 10; return m;});
框架的缓存控制,也是极为自由的哟。应该是的吧?哈合。
缓存服务的可用情况
1.内置缓存服务
- org.noear.weed.cache.EmptyCache // 空缓存
- org.noear.weed.cache.LocalCache // 轻量级本地缓存(基于Map实现)
- org.noear.weed.cache.SecondCache // 二级缓存(组装两个 ICacheServiceEx 实现)
2.扩展缓存服务
- org.noear.weed.cache.ehcache.EhCache // 基于ehcache封装
<dependency> <groupId>org.noear</groupId> <artifactId>weed3.cache.ehcache</artifactId> <version>3.2.3.4</version></dependency>
- org.noear.weed.cache.j2cache.J2Cache // 基于国人开发的J2Cache封装
<dependency> <groupId>org.noear</groupId> <artifactId>weed3.cache.j2cache</artifactId> <version>3.2.3.4</version></dependency>
- org.noear.weed.cache.memcached.MemCache // 基于memcached封装
<dependency> <groupId>org.noear</groupId> <artifactId>weed3.cache.memcached</artifactId> <version>3.2.3.4</version></dependency>
- org.noear.weed.cache.redis.RedisCache // 基于redis封装
<dependency> <groupId>org.noear</groupId> <artifactId>weed3.cache.redis</artifactId> <version>3.2.3.4</version></dependency>
- 也可以自己封装个 ICacheServiceEx ...
要不要自己封装个?
【2.3.4】查询之其它
再补充些查询相关的内容
- 别名
db.table("user u") .limit(20) .select("u.mobile mob");
- 去重
db.table("user") .limit(20) .select("distinct mobile");
- 分组
db.table("user u") .groupBy("u.mobile").having("count(*) > 1") .select("u.mobile,count(*) num");
- 排序
db.table("user u") .orderBy("u.mobile ASC") .select("u.mobile,count(*) num");
- 分组+排序(或者随意组合)
db.table("user u") .where("u.id < ?",1000) .groupBy("u.mobile").having("count(*) > 1") .orderBy("u.mobile ASC") .caching(cache) .select("u.mobile,count(*) num").getMap("mobile,num")
【2.4】存储过程与查询过程
关于存储过程的支持,设计了两个方案
- 1.对接数据库的存储过程调用
db.call("user_get").set("_user_id",1).getMap();
- 2.SQL查询过程(我叫它:查询过程)
看起来跟mybatis的SQL注解代码有点儿像
//由SQL构建的一个查询db.call("SELECT * FROM user WHERE [email protected]{user_id}").set("user_id",1).getMap();
还可以对它们进行实体化(变成一个独立的类)
实体化的作用在于,可将数据处理安排到别的模块(或文件夹)
- 1.对接数据库的存储过程实体化
public class user_get extends DbStoredProcedure { public user_get() { super(DbConfig.test); call("user_get"); set("_userID", () -> userID); } public long userID;}user_get sp =new user_get();sp.userID=10;Map<String,Object> map = sp.caching(cache).getMap();//顺带加个缓存
- 2.查询过程的实体化
public class user_get2 extends DbQueryProcedure { public user_get2() { super(db); sql("select * from user where [email protected]{type} AND [email protected]{sex}"); // 这个绑定写法,想了很久才想出来的(就是不想反射!) set("type", () -> type); set("sex", () -> sex); } public int type; public int sex;}//DbQueryProcedure 提供了与 DbStoredProcedure 相同的接口user_get2 sp =new user_get2();sp.userID=10;Map<String,Object> map = sp.caching(cache).getMap();
【2.5】解决数据库关键字问题
weed3提供了字段和对象格式化支持,通过DbContext进行设定
//以mysql为例DbContext db = new DbContext(...).fieldFormatSet("`%`")//设定字段格式符 .objectFormatSet("`%`");//设定对象格式符//%号为占位符,`%`表过你的字段会转为:`字段名`
字段格式符对会对: .set(..), .select(..), .orderBy(..), .groupBy(..) 里的字段进行处理
对对象格式符对会: .table(..), innerJoin(..), leftJoin(..), rightJoin(..) 里的表名进行处理
如果不设置,则需要自己手动处理关键字;手动处理与自动处理并不冲突。
//手动处理db.table("`user`").where("`count`<10 AND sex=1").count();
格式化是由IDbFormater来处理的,如果觉得里面的实现不好,还可以自己写一个替换它:)
IDbFormater df = new DfNew(); //DfNew 算是自己写的db.formaterSet(df); //搞定//附:public interface IDbFormater { /** 字段格式符设置 */ void fieldFormatSet(String format); /** 对象格式符设置 */ void objectFormatSet(String format); /** 格式化字段(用于:set(..,v)) */ String formatField(String name); /** 格式化多列(用于:select(..) orderBy(..) groupBy(..)) */ String formatColumns(String columns); /** 格式化条件(用于:where(..) and(..) or(..)) */ String formatCondition(String condition); /** 格式化对象(用于:from(..), join(..)) */ String formatObject(String name);}
【2.5】盘点三大java使用接口(table,call,sql)
1.table() 执行:链式ORM操作
此处略(前面主要就讲这个接口)
2.call(..) 执行:存储过程 或 查询过程
//执行存储过程db.call("user_get").set("_user_id",1).getMap();//执行查询过程(我暂时这么叫它)db.call("select * from user where [email protected]{user_id}").set("user_id",1).getMap();
3.sql(..) 执行:SQL语句
db.sql("select * from user where id=?",1).getMap();
db.sql(..) 还有一个快捷版:db.exec(..)。相当于:db.sql(...).execute(); //批处理时,可快速写增、删、改动作 例:
db.exec("DELETE FROM test where a=1")
最终统一返回:IQuery (保证了体验的统一性)
db.table(..).select(..) -> IQuery
db.call(..) -> IQuery
db.sql(..) -> IQuery
public interface IQuery extends ICacheController<IQuery> { long getCount() throws SQLException; Object getValue() throws SQLException; <T> T getValue(T def) throws SQLException; Variate getVariate() throws SQLException; Variate getVariate(Act2<CacheUsing,Variate> cacheCondition) throws SQLException; <T extends IBinder> T getItem(T model) throws SQLException; <T extends IBinder> T getItem(T model, Act2<CacheUsing, T> cacheCondition) throws SQLException; <T extends IBinder> List<T> getList(T model) throws SQLException; <T extends IBinder> List<T> getList(T model, Act2<CacheUsing, List<T>> cacheCondition) throws SQLException; <T> T getItem(Class<T> cls) throws SQLException; <T> T getItem(Class<T> cls,Act2<CacheUsing, T> cacheCondition) throws SQLException; <T> List<T> getList(Class<T> cls) throws SQLException; <T> List<T> getList(Class<T> cls,Act2<CacheUsing, List<T>> cacheCondition) throws SQLException; DataList getDataList() throws SQLException; DataList getDataList(Act2<CacheUsing, DataList> cacheCondition) throws SQLException; DataItem getDataItem() throws SQLException; DataItem getDataItem(Act2<CacheUsing, DataItem> cacheCondition) throws SQLException; List<Map<String,Object>> getMapList() throws SQLException; Map<String,Object> getMap() throws SQLException; <T> List<T> getArray(String column) throws SQLException; <T> List<T> getArray(int columnIndex) throws SQLException;}
【3.1】开始Xml Mapper的使用
使用约定***
- 1.约定
resources/weed3/
为 xml sql 根目录 - 2.项目开启编译参数:
-parameters
准备开始做个简单的例子
这次需要引用一个meven插件(玩过mybatis都懂的)
框架引用
<dependency> <groupId>org.noear</groupId> <artifactId>weed3</artifactId> <version>3.2.3.4</version></dependency>
meven插件引用(用于生成mapper类)
<!-- 放到 build / plugins / 下面 --><plugin> <groupId>org.noear</groupId> <artifactId>weed3-maven-plugin</artifactId> <version>3.2.3.4</version></plugin>
(一)现在,先写个简单的xml文件
- resources/weed3/DbUserApi.xml
<?xml version="1.0" encoding="utf-8" ?><mapper namespace="weed3demo.xmlsql" :db="testdb"> <sql id="user_get" :return="weed3demo.mapper.UserModel" :note="获取用户信息"> SELECT * FROM `user` WHERE [email protected]{user_id} </sql></mapper>
(二)可以有两种方式调用刚才的xml sql
通过 db.call("@...") 调用
DbContext db = new DbContext(...);UserModel um = db.call("@weed3demo.xmlsql.user_get") .set("user_id") .getItem(UserModel.class);
生成Mapper接口,通过动态代理使用
1.用meven插件把它生成(双击:weed3:generator)
2.生成的Java文件(java/weed3demo/xmlsql/DbUserApi.java)
package weed3demo.xmlsql;@Namespace("weed3demo.xmlsql")public interface DbUserApi{ /** 获取用户信息*/ weed3demo.mapper.UserModel user_get(int user_id);}
3.试一下
//全局public static void main(String[] args){ //配置一个上下文,并加上名字(给xml mapper 用) DbContext db = new DbContext(...).nameSet("testdb"); //通过代理获取xml mapper DbUserApi dbUserApi = XmlSqlMapper.get(DbUserApi.class); //使用它 UserModel tmp = dbUserApi.user_get(10);}
【3.2】Xml Mapper的指令和语法
五个指令 + 三种变量形式。先来段xml
这个示例里把各种情况应该呈现出来了
<?xml version="1.0" encoding="utf-8" ?><mapper namespace="weed3demo.xmlsql2" :db="testdb"> <sql id="user_add1" :return="long" :param="m:weed3demo.mapper.UserModel,sex:int" :note="添加用户"> INSERT user(user_id,mobile,sex) VALUES(@{m.user_id},@{m.mobile},@{sex}) </sql> <sql id="user_add2" :return="long" :note="添加用户"> INSERT user(user_id) VALUES(@{user_id:int}) </sql> <sql id="user_add_for" :return="long" :note="批量添加用户3"> INSERT user(id,mobile,sex) VALUES <for var="m:weed3demo.mapper.UserModel" items="list"> (@{m.user_id},@{m.mobile},@{m.sex}) </for> </sql> <sql id="user_del" :note="删除一个用户"> DELETE FROM user WHERE [email protected]{m.user_id:long} <if test="sex gt 0"> AND [email protected]{sex:int} </if> </sql> <sql id="user_set" :note="更新一个用户,并清理相关相存" :caching="localCache" :cacheClear="user_${user_id},user_1"> UPDATE user SET [email protected]{mobile:String},[email protected]{sex:int} <if test="icon != null"> [email protected]{icon:String} </if> </sql> <sql id="user_get_list" :note="获取一批符合条件的用户" :declare="foList:int,user_id:long" :return="List[weed3demo.mapper.UserModel]" :caching="localCache" :cacheTag="user_${user_id},user_1"> SELECT id,${cols:String} FROM user <trim prefix="WHERE" trimStart="AND "> <if test="mobile?!"> AND mobile LIKE '${mobile:String}%' </if> <if test="foList == 0"> AND type='article' </if> <if test="foList == 1"> AND type='post' </if> </trim> </sql> <sql id="user_cols1">name,title,style,label</sql> <sql id="user_cols2">name,title</sql> <sql id="user_get_list2" :note="获取一批符合条件的用户" :declare="foList:int,user_id:long" :return="List[weed3demo.mapper.UserModel]" :caching="localCache" :cacheTag="user_${user_id},user_1"> SELECT id, <if test="foList == 0"> <ref sql="user_cols1"/> </if> <if test="foList == 1"> <ref sql="user_cols2"/> </if> FROM user WHERE sex>1 AND mobile LIKE '${mobile:String}%' </sql></mapper>
四个指令说明
sql 代码块定义指令 :require(属性:导入包或类) :param?(属性:外部输入变量申明;默认会自动生成::新增***) :declare(属性:内部变量类型预申明) :return(属性:返回类型) :db (属性:数据库上下文name) :note(属性:描述、说明、注解) :caching(属性:缓存服务name) //是对 ICacheController 接口的映射 :cacheClear?(属性:清除缓存) :cacheTag?(属性:缓存标签,支持在入参或结果里取值替换) :usingCache?(属性:缓存时间,int)if 判断控制指令(没有else) test (属性:判断检测代码) //xml避免语法增强: //lt(<) lte(<=) gt(>) gte(>=) and(&&) or(||) //例:m.sex gt 12 :: m.sex >=12 //简化语法增强: //??(非null,var!=null) ?!(非空字符串,StringUtils.isEmpty(var)==false) //例:m.icon?? ::m.icon!=null //例:m.icon?! ::StringUtils.isEmpty(m.icon)==falsefor 循环控制指令 (通过 ${var}_index 可获得序号,例:m_index::新增***) var (属性:循环变量申明) items (属性:集合变量名称) sep? (属性:分隔符::新增***)trim 修剪指令 trimStart(属性:开始位去除) trimEnd(属性:结尾位去除) prefix(属性:添加前缀) suffix(属性:添加后缀)ref 引用代码块指令 sql (属性:代码块id)
三种变量形式
name:type = 变量申明(仅用于var ,或:declare)@{name:type} = 变量注入(仅用于代码块)${name:type} = 变量替换(用于代码块,或:cacheTag,或:cacheClear)
关于返回值的几种形式说明
//多行,列表(用[]替代<>):return="List[weed3demo.mapper.UserModel]" //将返回 List<UserModel>:return="List[String]" //将返回 List<String> (Date,Long,...大写开头的单值类型):return="MapList" //将返回 List<Map<String,Object>>:return="DataList" //将返回 DataList//一行:return="weed3demo.mapper.UserModel" //将返回 UserModel:return="Map" //将返回 Map<String,Object>:return="DataItem" //将返回 DataItem//单值:return="String" //将返回 String (或别的任何单职类型)
【4.1】开始注解sql的使用
使用约定***
- 1.项目开启编译参数:
-parameters
先来个demo
- 1.申明一个mapper
public interface DbMapper1{ @Sql(value = "select * from ${tb} where app_id = @{app_id} limit 1", caching = "test", cacheTag = "app_${app_id}") AppxModel appx_get(String tb, int app_id) throws Exception;}
- 2.使用它
DbContext db = new DbContext(...);DbMapper1 dm = db.mapper(DbMapper1.class);AppxModel m = dm.appx_get("appx",1);
两种变量形式 + 缓存控制
两种变量形式
${}
替代变量(相当于占位符,进行字符串拼接)@{}
编译变量(会编译为?,通过变量传递给jdbc)
缓存控制
caching
缓存服务cacheTag
缓存标签(在key之上,建立的虚拟tag;为便于清理)usingCache
缓存使用时间cacheClear
缓存清理(通过cacheTag形式清理)
再来一个demo2
更新之后,清掉缓存:app_${app_id}
public interface DbMapper2{ @Sql(value = "update appx set [email protected]{name} where app_id = @{app_id}", caching = "test", cacheClear = "app_${app_id}") void appx_set(int app_id, String name) throws Exception;}
再来一个demo3
使用查询结果构建cahce tag:app_type${type}
public interface DbMapper3{ @Sql(value = "select * from appx where app_id = @{app_id} limit 1", caching = "test", cacheTag = "app_${app_id},app_type${type}") AppxModel appx_get(int app_id) throws Exception;}
补充:构建一个缓存服务
//随便写在哪里//1.初始化一个ICacheServiceEx//2.通过nameSet("test") 注册到缓存库//3.之后就可以被 @sql的 caching 使用(xml sql 的 caching 同样如此)//new LocalCache("test",60).nameSet("test");
【5】事务和事务队列
之前讲过插入和更新
这次讲事务(写操作总会傍随事务嘛...)
- weed3 支持两种方式的事务
- 1.事务(主要用于单个库)
//demo1:: //事务组 // 在一个事务里,做4个插入//如果出错了,自动回滚DbUserApi dbUserApi = XmlSqlProxy.getSingleton(DbUserApi.class);db.tran((t) -> { // // 此表达式内的操作,会自动加入事务 ////sql接口 db.sql("insert into test(txt) values(?)", "cc").insert(); db.sql("update test set txt='1' where id=1").execute();//call接口 db.call("user_del").set("_user_id",10).execute();//table()接口 db.table("a_config").set("cfg_id",1).insert();//xml mapper dbUserApi.user_add(12);//大家使用统一的事务模式});
- 2.事务队列(主要用于多个库的情况)
//demo2:: //事务队列////假如,要跨两个数据库操作(一个事务对象没法用了)//DbContext db = DbConfig.pc_user;DbContext db2 = DbConfig.pc_base;//创建个事务队列(和传统概念的队列不一样)DbTranQueue queue = new DbTranQueue();//数据库1的事务db.tran().join(queue).execute((t) => { // // 在这个表达示内,会自动加入事物 // db.sql("insert into test(txt) values(?)", "cc").execute(); db.sql("insert into test(txt) values(?)", "dd").execute(); db.sql("insert into test(txt) values(?)", "ee").execute();});//数据库2的事务db2.tran().join(queue).execute((t) => { // // 在这个表达示内,会自动加入事物 // db2.sql("insert into test(txt) values(?)", "gg").execute();});//队列结组完成(即开始跑事务)queue.complete();
- 3.事务队列的加强版,跨函数或模块跑事务
public void test_main(){ DbTranQueue queue = new DbTranQueue(); test1(queue); test2(queue);}public void test1(DbTranQueue queue){ DbTran tran = DbConfig.db1.tran();//生成个事务对象 tran.join(queue).execute((t) -> { // // 在这个表达示内,会自动加入事物 // t.db().sql("insert into $.test(txt) values(?)", "cc").insert(); t.db().sql("insert into $.test(txt) values(?)", "dd").execute(); t.db().sql("insert into $.test(txt) values(?)", "ee").execute(); t.result = t.db().sql("select name from $.user_info where user_id=3").getValue(""); });}public void test2(DbTranQueue queue){ //...test2就不写了}
【6】对所有执行进行监视
通过WeedConfig开放了一些监听接口
比如:异常监听,慢SQL监听
//监听异常,以便统一的打印或记录WeedConfig.onException((cmd, ex) -> { if (cmd.text.indexOf("a_log") < 0 && cmd.isLog >= 0) { System.out.println(cmd.text); }});//监听SQL性能,以便统一记录WeedConfig.onExecuteAft((cmd)->{ if(cmd.timespan()>1000){ //执行超过1000毫秒的.. System.out.println(cmd.text + "::" + cmd.timespan() +"ms"); }});
具体的可监听事件
//异常事件WeedConfig.onException(Act2<Command, Exception> listener);//日志事件(可以把命令信息记录下来)WeedConfig.onLog(Act1<Command> listener);//执行前事件WeedConfig.onExecuteBef(Fun1<Boolean, Command> listener);//执行中事件(可以监听,Statement)WeedConfig.onExecuteStm(Act2<Command, Statement> listener);//执行后事件WeedConfig.onExecuteAft(Act1<Command> listener);
【7】嵌入到脚本或模板
嵌入到脚本引擎
- 嵌入到javascript引擎(nashorn)
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();ScriptEngine _eng = scriptEngineManager.getEngineByName("nashorn");Invocable _eng_call = (Invocable)_eng;_eng.put("db", db);
var map = db.table("test").where('id=?',1).getMap();
- 嵌入到groovy引擎
ScriptEngineManager scriptEngineManager = new ScriptEngineManager();ScriptEngine _eng = scriptEngineManager.getEngineByName("groovy");Invocable _eng_call = (Invocable)_eng;_eng.put("db", db);
def map = db.table("test").where('id=?',1).getMap();
嵌入到模板引擎
- 嵌入到Freemarker引擎
<#assign tag_name=ctx.param('tag_name','') /><#assign tags=db.table("a_config").where('label=?',label).groupBy('edit_placeholder').select("edit_placeholder as tag").getMapList() /><!DOCTYPE HTML><html><head>...
结束语:希望你能喜欢:)
边栏推荐
- Download and use of font icons
- Listview is added and deleted at the index
- 小程序直播+电商,想做新零售电商就用它吧!
- PMP考试敏捷占比有多少?解疑
- stm32Cubemx(8):RTC和RTC唤醒中断
- Web APIs DOM节点
- Research and forecast report on China's solution polymerized styrene butadiene rubber (SSBR) industry (2022 Edition)
- PMP考生,请查收7月PMP考试注意事项
- Lua GBK and UTF8 turn to each other
- Animation
猜你喜欢
2022/7/2做题总结
Unity3d learning notes
Web APIs DOM节点
Bucket sort
2022年上半年国家教师资格证考试
Reverse one-way linked list of interview questions
[转]MySQL操作实战(一):关键字 & 函数
Use of snippets in vscode (code template)
PostgreSQL surpasses mysql, and the salary of "the best programming language in the world" is low
[trans]: spécification osgi
随机推荐
嵌入式数据库开发编程(零)
China polyurethane rigid foam Market Research and investment forecast report (2022 Edition)
2022/7/1 learning summary
Unity writes timetables (without UI)
Es module and commonjs learning notes -- ESM and CJS used in nodejs
cocos2dx_ Lua particle system
China as resin Market Research and investment forecast report (2022 Edition)
When will Wei Lai, who has been watched by public opinion, start to "build high-rise buildings" again?
Bubble sort summary
Personal required code
Collapse of adjacent vertical outer margins
PostgreSQL surpasses mysql, and the salary of "the best programming language in the world" is low
[转]MySQL操作实战(三):表联结
Common database statements in unity
PMP考试敏捷占比有多少?解疑
Count sort
Detailed explanation of the ranking of the best universities
Chinese notes of unit particle system particle effect
中国聚氨酯硬泡市场调研与投资预测报告(2022版)
2021-10-29