Weed3, micro ORM framework (support: java sql, xml sql, annotation sql; stored procedure; transaction; cache; monitor; etc.)
The first generation was developed in 2005; In 2008, the second generation was developed. At that time, it entered the Internet company and had a new understanding of performance; The third generation was developed in 14 years. Because I don't like abusing reflection, I don't like to have many configurations, so I've been persistent.
The first two generations are developed in. net; the third generation focuses on java. It should be considered as a fully functional and minimal ORM framework, with no other dependencies, only 0.1mb. There are not many external interfaces, mainly four interfaces on DbContext initiate all operations.
Because some of the obsessive writing is relatively small:
- Snack3 (70 KB Json framework, orderly listing, Jsonpath, format conversion mechanism; emphasis on building capacity)
- Solon (80kb Web Framework)
- A mobile browser (0.1mb, but with complete functions; a creative work)
Features and concepts of Weed3:
High performance: two years ago, a colleague tested four ORM frameworks, which are the best (I don't know if they are now).
Cross platform: it can be embedded into the JVM script engine (js, groovy, lua, python, ruby); there are. net, php versions (haven't been maintained for a long time).
Very small: only 0.1Mb (with complete functions and rich schemes, it can greatly simplify database development).
Have personality: don't like reflection, don't like configuration... (no configuration except connection).
Others: support cache control and cross database transaction (it is a kind of distributed transaction).
Weed3 components:
assembly | explain |
---|---|
org.noear:weed3-parent | Framework version management |
org.noear:weed3 | Main framework (no dependencies) |
org.noear:weed3-maven-plugin | Maven plug-in for generating Xml sql mapper |
org.noear:weed3.cache.memcached | Extended cache service based on Memcached encapsulation |
org.noear:weed3.cache.redis | Extended cache service based on Redis encapsulation |
org.noear:weed3.cache.ehcache | Extended cache service based on ehcache encapsulation |
org.noear:weed3.cache.j2cache | Extended cache service based on j2cache encapsulation |
Weed3 meven configuration:
<!-- Frame package --> <dependency> <groupId>org.noear</groupId> <artifactId>weed3</artifactId> <version>3.2.4.1</version> </dependency> <!-- maven Plug in for building Xml sql mapper Interface --> <plugin> <groupId>org.noear</groupId> <artifactId>weed3-maven-plugin</artifactId> <version>3.2.4.1</version> </plugin>
Starting process of Weed3:
- Configure DataSource information
- Materialize DbContext
- Call the interface on DbContext (need to know more about syntax...)
I. context object DbContext
All operations of weed3 are based on the operation of the interface on DbContext. That is, everything starts from instantiating DbContext:
- 1. Use application.yml to configure data source (or other format configuration, or configuration service), format example:
demodb: schema: demo url: jdbc:mysql://localdb:3306/demo?... driverClassName: com.mysql.cj.jdbc.Driver username: demo password: UL0hHlg0Ybq60xyb
-
2. After configuration, start to materialize DbContext:
If it is a Spring framework, you can get the configuration through annotation In the case of the solon framework, the configuration can be obtained through annotations or interfaces
//Example of using Properties configuration Properties properties = XApp.cfg().getProp("demodb"); //This is the interface of the solon framework DbContext db = new DbContext(properties); //Example of using Map configuration DbContext db = new DbContext(map); //Example of using proxool thread pool configuration (as if it's not popular now) / / proxool configuration through xml DbContext db = new DbContext("user","proxool.xxx_db"); //Example of using DataSource configuration (commonly used when using connection pool framework; recommended Hikari connection pool) //The Hikari connection pool is used in the down demo DataSource dataSource = new HikariDataSource(...); DbContext db = new DbContext("user", dataSource); //The other is to use url,username,password (this does not need to be configured) DbContext db = new DbContext("user","jdbc:mysql://x.x.x:3306/user","root","1234"); /* I usually use configuration services, so configuration provides database context objects directly. */ //Use configuration service to get DbContext directly DbContext db = WaterClient.Config.get("demodb").getDb();
2. Four interfaces: db.mapper(), db.table(), db.call(), db.sql()
Four interfaces are also four application schemes of DbContext in different scenarios
Core interface: db.mapper(), db.table(). Represents two completely different styles and tastes.
Supplementary interface: db.call(), db.sql(). Respond to special application scenarios.
Among them, db.table(), db.call(), db.sql() can be embedded in the JVM script engine (js, groovy, lua, python, ruby) and some grailvm languages.
Because the author also has an embedded FaaS engine. Unified implementation of initiating objects, no injection, no configuration, and weak type interfaces play an important role; they can be easily embedded in various languages, and provide a unified ORM experience.
(I) db.mapper(), providing mapper operation support
mapper style, is now a very popular one. Most people use it.
This interface provides BaseMapper mode, @ Sql injection mode and Xml sql configuration mode. Among them, the internal processing of Xml sql will be precompiled to Java class at startup; the performance should be reliable (like a bit of jsp precompiled flavor).
-
1.db.mapperBase(clz) get BaseMapper instance
Since XXX plus, if you don't have BaseMapper, it seems that you are embarrassed to say that you are an ORM framework.
This interface does bring great methods, and the simple CRUD is completely eliminated.
//Use BaseMapper directly BaseMapper<User> userDao= db.mapperBase(User.class); //increase userDao.insert(user,false); //false: exclude null value //Delete userDao.deleteById(12); //Change: change by ID userDao.updateById(user,false); //false: exclude null value //Change: change by conditions userDao.update(user,false,m->m.whereEq(User::getType,12).andEq(User::getSex,1)); //Check by ID User user = userDao.selectById(12); //Check. Check by condition (condition, can be string style; can be lambda style) User user = userDao.selectItem(m -> m.whereEq(User::getId,12));
- 2.db.mapper(clz), get mapper instance
@Namespace("demo.dso.db") public interface UserDao { //This interface can be extended from basemapper < T > @sql("select * from `user` where id=@{id}") //Variable style User getUserById(int id); @sql("select * from `user` where id=?") //Placeholder style User getUserById2(int id); long addUser(User m); //No annotation, xml sql configuration needs to be written } UserDao userDao = db.mapper(UserDao.class); User user = userDao.getUserById(12); userDao.addUser(user);
-
3.db.mapper(xsqlid, args), get Xml sql mapper results
The advantage of this interface is that DAO can be made into a middle platform: xml sql can be put in the database for unified management; and a DAO gateway can be developed to provide services in the way of RPC or REST API.
Map<String,Object> args = new HashMap<>(); args.put("id",22); //xsqlid = @{sqlid} = @{namespace}.{id} User user = db.mapper("@demo.dso.db.getUserById",args);
(II) db.table() provides pure java chain operation
This is what Weed3 looks like initially, and this is my favorite method. It is also the key capability of specific cross platform embedding.
BaseMapper is also implemented by db.table(), which is OK in a few lines.
Flexible, flexible, direct, can achieve any SQL code effect. Development management background, very cool (because the query conditions are miscellaneous and messy).
db.table() supports two styles:
1. String style: flexible, free and convenient, embeddable, easy to cross platform syntax, but it is troublesome to change the field name;
2.lambda style: strong constraint, easy to manage (it is very convenient to change the field name); but it is troublesome to write; the syntax is not good cross platform.
- Added, INSEERT
User user = new User(); .. //Single insertion db.table("user").set("name","noear").insert(); db.table("user").setEntity(user).insert(); db.table("user").setEntityIf(user, (k,v)->v!=null).insert(); //Filter null //Bulk insert db.table("user").insertList(list);
- DELETE, DELETE
//Delete records with ID < 12 db.table("user").whereLt("id",12).delete(); //Delete record with id=12 (lamdba style) db.table(User.class).whereEq(User::getId,12).delete();
- Change, UPDATE
//Change the sex field with id=23 db.table("user").set("sex",1).whereEq("id",23).update(); //Add or update according to mobile number public void saveUser(UserModel m){ db.talbe("user").setEntityIf(m, (k,v)->v!=null).upsert("mobile"); }
- Check, SELECT
//Count the number of records with ID < 100 and name length > 10 (you can freely use SQL function) db.table("user").where("id<?", 100).and("LENGTH(name)>?",10).count(); //Query 20 records with ID > 10 db.table("user").whereGte("id", 10).limit(20).select("*").getMapList(); //Associated query and output an entity (lamdba style) / / or string style is more flexible and concise 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);
- Interfaces with filtering capabilities: whereIf, andIf, orIf, setIf, setMapIf, setEntityIf
//If there is a name, add the name condition; (it is very practical to manage the background query; it saves a lot of if) db.talbe("user").whereIf(name!=null, "name=?", name).limit(10).select(""); //Insert, filter null db.table("user").setMapIf(map,(k,v)->v!=null).insert(); //Filter null //to update 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();
(III) db.call() provides call operation
- call database stored procedure
//Database stored procedure usage // User user = db.call("user_get").set("id",1).getItem(User.class);
- call query process
//Use of query stored procedure (@ sql internal implementation) // User user = db.call("select * from user where id=@{id}").set("id",1).getItem(User.class);
- call Xmlsql
//Weak type usage of Xml sql / / start with @ // User user = db.call("@demo.dso.db.getUser").set("id",1).getItem(User.class);
(IV) db.sql(), providing handwritten sql operation
//So the interface will eventually turn to db.sql(), which is the lowest interface // User user = db.sql("select * from user where id=?",12).getItem(User.class); Long total = db.sql("select count(*) from user").getValue(0l); //Shortcut version of db.sql(): db.exe(), for fast batch processing // db.exe("delete from user where id=12"); db.exe("update user sex=1 where id=12");
III. Mapper syntax
(I) BaseMapper interface
- Long insert(T entity, boolean excludeNull);
- void insertList(List<T> list);
- Integer deleteById(Object id);
- Integer deleteByIds(Iterable<Object> idList);
- Integer deleteByMap(Map<String, Object> columnMap);
- Integer delete(Act1<WhereQ> condition);
- Integer updateById(T entity, boolean excludeNull);
- Integer update(T entity, boolean excludeNull, Act1<WhereQ> condition);
- Long upsert(T entity, boolean excludeNull);
- Long upsertBy(T entity, boolean excludeNull, String conditionFields);
- boolean existsById(Object id);
- boolean exists(Act1<WhereQ> condition);
- T selectById(Object id);
- List<T> selectByIds(Iterable<Object> idList);
- List<T> selectByMap(Map<String, Object> columnMap);
- T selectItem(T entity);
- T selectItem(Act1<WhereQ> condition);
- Map<String, Object> selectMap(Act1<WhereQ> condition);
- Object selectValue(String column, Act1<WhereQ> condition);
- Long selectCount(Act1<WhereQ> condition);
- List<T> selectList(Act1<WhereQ> condition);
- List<Map<String, Object>> selectMapList(Act1<WhereQ> condition);
- List<Object> selectArray(String column, Act1<WhereQ> condition);
- List<T> selectPage(int start, int end, Act1<WhereQ> condition);
- List<Map<String, Object>> selectMapPage(int start, int end, Act1<WhereQ> condition);
(II) annotation sql
- Example
ICacheServiceEx cache = new LocalCache().nameSet("cache"); //With cache @Sql(value="select * from user where id=@{id}", caching="cache") public UserModel getUser(int id);
- Sql annotation description
@Target({ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) public @interface Sql { String value() default ""; //Code String caching() default ""; //Cache service name String cacheClear() default ""; //Cache clear label String cacheTag() default ""; //Cache label int usingCache() default 0; //Cache time }
(III) Xml sql
- Example
<?xml version="1.0" encoding="utf-8" ?> <mapper namespace="weed3demo.xmlsql2"> <sql id="getUser" :return="demo.model.UserModel" :note="Get user information"> SELECT * FROM user WHERE id = @{id:int} </sql> </mapper>
- grammar
mapper start tag Namespace (property: namespace, {namespace}.{id} = sqlid) sql code block definition instruction id (attribute: id) : require (attribute: import package or class) : param? (attribute: Declaration of external input variable; automatically generated by default:: add * * *) : declare (property: internal variable type pre declaration) : return (property: return type) : note (attribute: description, description, annotation) : caching (property: cache service name) / / is the mapping of the ICacheController interface : cacheClear? (property: clear cache) : cacheTag? (property: cache tag, value substitution in input parameter or result is supported) : usingCache? (attribute: cache time, int) if judgment control instruction (no else) test (attribute: judge detection code) //xml avoids syntax enhancements: //lt(<) lte(<=) gt(>) gte(>=) and(&&) or(||) //Example: m.sex GT 12:: m.sex > = 12 //Simplified syntax enhancements: //? (non null,var!=null)?! (non empty string, stringutilities. Isempty (VaR) = = false) //Example: m.icon?:: m.icon! = null //Example: m.icon?!: stringutils. Isempty (m.icon) = = false For loop control instruction (the sequence number can be obtained through ${var} ~ index, for example: m ~ index:: add * * *) var (attribute: circular variable declaration) items (property: Collection variable name) sep? (property: separator:: New * * *) trim instruction trimStart (attribute: start bit removal) trimEnd (attribute: end bit removal) Prefix (attribute: add prefix) Suffix (attribute: add suffix) ref reference block instruction sql (attribute: code block id) name:type = variable declaration (can be used for attribute:: param,: declare, var, or macro definition @ {..}, ${..}) @{name:type} = variable injection ${name:type} = variable substitution //List ([] replace < > :return="List[weed3demo.mapper.UserModel]" => List<UserModel> : return = "list [string]" = > List < string > (Date,Long,... Single value type starting with uppercase) :return="MapList" => List<Map<String,Object>> :return="DataList" => DataList //A row :return="weed3demo.mapper.UserModel" => UserModel :return="Map" => Map<String,Object> :return="DataItem" => DataItem //Single value : return = "string" = > string (any single position type)
IV. Table syntax
(I) conditional operation (shared with Mapper)
Method | Effect description |
---|---|
where, whereIf | |
whereEq, whereNeq | ==, != |
whereLt, whereLte | <, <= |
whereGt, whereGte | >, >= |
whereLk, whereNlk | LIKE, NOT LIKE |
whereIn, whereNin | IN(..), NOT IN(..) |
whereBtw, whereNbtw | BETWEEN, NOT BETWEEN |
and system method | Same as where |
or system method | Same as where |
begin | ( |
end | ) |
(II) Table operation (Table exclusive)
Method | Effect description |
---|---|
set, setIf | Set value |
setMap, setMapIf | Set value |
setEntity, setEntityIf | Set value |
table | Main table |
innerJoin, leftJoin, rightJoin | Association table |
on, onEq | Association conditions |
orderBy, orderByAsc, orderByDesc | sort |
groupBy | group |
having | Group condition |
limit | Limitation |
select | Query (return IQuery) |
count | Query shortcut, count quantity |
exists | Query whether the shortcut version exists |
update | to update |
insert | insert |
delete | delete |
(III) IQuery interface
- long getCount() throws SQLException;
- Object getValue() throws SQLException;
- <T> T getValue(T def) throws SQLException;
- Variate getVariate() throws SQLException;
- <T> T getItem(Class<T> cls) throws SQLException;
- <T> List<T> getList(Class<T> cls) throws SQLException;
- DataList getDataList() throws SQLException;
- DataItem getDataItem() 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;
- Wait
V. caching and transactions
- Cache (skip unnecessary)
ICacheServiceEx cache = new LocalCache().nameSet("cache"); User user = db.table("user") .where("id=?",12) .caching(cache) //Add cache. The time is the default time of cache .select("*").getItem(User.class);
- Cache control (you can skip if you don't need it)
//On query, cache User user = db.table("user") .where("id>?",12) .limit(100,20) //Paging query .caching(cache) .usingCache(60*5) //Cache for 5 minutes .cacheTag("user_all") //Cache label user? All .select("*").getList(User.class); //When updating, clear the cache / / the next time you query, you can get the latest data db.table("user").set("sex",0).where("id=101").update(); cache.clear("user_all");
- Single database transaction
db.tran(t->{ //Registered users long user_id = userDao.addUser(user); //Send 10 gold coins after registration (completed in the same transaction) userDao.addUserGold(user_id, 10); });
- Cross database transaction (whether it is a kind of distributed transaction or not)
new DbTranQueue().execute((tq) -> { //User system, add user Guan coin db1.tran().join(tq).execute(t -> { user.id = userDao.addUser(user); //id self increasing }); //banking system db2.tran().join(tq).execute(t -> { bankDao.addAccount(user.id); //New account bankDao.addAccountGold(user.id, 10); //Add account called gold coin bankDao.addJournal(user.id,10); //Add Journal }); //Broadcast message / / for subsequent horizontal expansion MsgUtil.sendMessage("user.registered",tmp.value); });
(VI) monitoring and recording
- Abnormal monitoring
WeedConfig.onException((cmd,ex)->{ //You can make a record ex.printStackTrace(); });
- Observe performance
WeedConfig.onExecuteAft((cmd)->{ //cmd.timespan() / / get execution time (MS) });
- Record behavior
WeedConfig.onLog((cmd) -> { if (cmd.isLog >= 0) { //isLog: -1, no record required; 0, default; 1, record required //cmd.text; / / execute code //cmd.paramS; / / execution parameters //cmd.paramMap(); / / perform parameter mapping } });
- Code filtering
//Example: disable DELETE operation WeedConfig.onExecuteBef((cmd)->{ if(cmd.text.indexOf("DELETE ") >=0){ return false; } return true; });
(VII) embedded in script
- Embedded in javascript engine (nashorn)
ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); ScriptEngine _eng = scriptEngineManager.getEngineByName("nashorn"); Invocable _eng_call = (Invocable)_eng; _eng.put("db", db); /* * var map = db.table("user").where('id=?',1).getMap(); * var user_id = map.id; */
- Embedded in the groovy engine
ScriptEngineManager scriptEngineManager = new ScriptEngineManager(); ScriptEngine _eng = scriptEngineManager.getEngineByName("groovy"); Invocable _eng_call = (Invocable)_eng; _eng.put("db", db); /* * def map = db.table("user").where('id=?',1).getMap(); * def user_id = map.id; */
If there is an opportunity, I will explain some details again