New micro ORM framework of Weed3 for java

Keywords: Programming SQL xml Attribute Database

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

Posted by adamp1 on Tue, 17 Dec 2019 20:08:00 -0800