A new micro ORM open source framework

Keywords: Programming SQL Mobile xml Database

Weed3 a micro ORM framework (only 0.1Mb)

Source: https://github.com/noear/weed3 Source: https://gitee.com/noear/weed3

In 2005, I started to write the first generation version of this framework... In 2008, I entered into the Internet company restructuring and wrote a 2-generation version... In 2014, refactoring wrote the current generation 3 version (there are two platform versions of java and. net). Recently, I was forced to add the support of xml mapper (someone always scolds it for this problem). Finally, the package has grown to 0.1Mb...

Last time a friend in the group said it was a strange framework. This is a very interesting way of speaking.

Generally speaking, the characteristics of this framework are that it doesn't like reflection and configuration (but still can't be avoided)!! It is hoped that through a good interface design, it can completely become a simple control experience. Maybe you think it's better to click sql by hand.

For some old people, this description may be better: it is equivalent to mybatis + mybatis puls (with a reference, easy to understand). But I haven't used them. Maybe I'm not right.

Besides, it's small, it's fast, it's free (some people say it's too free to control)

[1] Hello world

  • Build a java project of any type and introduce the framework package
<dependency>
    <groupId>org.noear</groupId>
    <artifactId>weed3</artifactId>
    <version>3.2.1.3</version>
</dependency>

<!-- This is a byproduct. The database connector must have one -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.47</version>
</dependency>
  • Three lines of code can run without any configuration
// hello world starts... (database link changes to match...)
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();//Get value
    System.out.println(rst);
}
  • It should be simple.

Things that can't be hello world are not good things. Ha ha: -P

Web3 supports pure java chaining or xml mapper writing. At the meeting, I will introduce the pure java writing method first... Let's talk about it slowly.

[2.1] start using pure java

When using pure java, there are three interfaces available: db.table(..), db.call(..), db.sql(). Generally, db.table(..) interface is used for chain operation. Its interface is named by mapping with SQL. It's easy for users to think about the chain interfaces. Like:. Where (..). And (..). Innerjoin (..)...

Chain operation routine: Start with db.table(..). Use. update(..) or. insert(..) or. delete(..) or. select(..). Among them. select(..) will return to the IQuery interface, providing various types of results.

First, add a meven dependency
<dependency>
  <groupId>org.noear</groupId>
  <artifactId>weed3</artifactId>
  <version>3.2.1.3</version>
</dependency>

<!-- Database connector, I don't care -->
Then, instantiate the database context object
  • All operations of weed3 are based on DbContext. So we need to implement it first...
  1. Configuration is required, which can be obtained in application.properties, configuration service and temporary handwriting.

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 by annotation or Aop.prop().get("xxx")

2. After configuration, DbContext is implemented. Write it here temporarily.

//An example of using proxool thread pool configuration (seems to be out of fashion now)
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.
DbContext db  = new DbContext("user",new HikariDataSource(...)); 

//The other is to use url,username,password.
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. */
Now, start doing simple data operations
  • General query operation
//Count the number of users less than 10
long num = db.table("user_info").where("user_id<?", 10).count();

//Check if the user exists
bool rst = db.table("user_info").where("user_id=?", 10).exists();

//Get user gender
int sex = db.table("user_info").where("user_id=?", 10)
            .select("sex").getValue();

//Get a user information
UserModel mod = db.table("user_info").where("user_id=?", 10).and("sex=1")
                  .select("*").getItem(UserModel.class);
  • And a full set of "add, delete, change and check"
//Simple increase
db.table("test").set("log_time", "$DATE(NOW())").insert();

//Easy to delete.
db.table("test").where("id=?",1).delete();

//Simple.
db.table("test").set("log_time", "$DATE(NOW())").where("id=?",1).update();

//Easy to find.
var map = db.table("test").where("id=?",1).select("*").getMap();

This is a simple start, I hope to have a good impression.

[2.2] insert and update

This section focuses on inserting and updating assignments
  • Support general assignment
String mobile="xxx"; //My cell phone number can't be written

db.table("test")
  .set("mobile",mobile) //Assignment of variables
  .set("sex",1) //Constant assignment
  .insert();
  • Support sql attachment (this can bring convenience) If the value starts with: $, it means that it is followed by SQL code (no space and no more than 100 characters). Otherwise, it is regarded as a normal string value), as follows:
//For example: current time assignment
db.table("test").set("log_time","$NOW()").insert();

//Another example: add 1 value to the field
db.table("test").set("num","$num+1")
  .where("id=?",1).update();

//Another example: batch update according to md5 of another field
db.table("test").set("txt_md5","$MD5(txt)")
  .where("id>? AND id<?",1000,2000).update();


/* How do I turn on or disable features? (actually, it's quite safe.)*/

//1. Only control whether this operation uses this function
db.table("test").usingExpr(false) // true on, false off

//2. Global configuration enables or disables this function:
WeedConfig.isUsingValueExpression=false; //Global default off
  • map attachment is supported (the field cannot be a field that does not exist in the data table...)
Map<String,Object> map = new HashMap<>();
...

//insert
db.table("test").setMap(map).insert();

//To update
db.table("test").setMap(map).where("id=?",1).update();
  • Support entity value attachment (field cannot be a field that does not exist in the data table...)
UserModel user = new UserModel();

//insert
db.table("test").setEntity(user).insert();

//To update
db.table("test").setEntity(user).where("id=?",1).update();
  • Support (insert if not, update if there is)
//Simplified scheme
db.table("test")
  .set("mobile","111")
  .set("sex",1)
  .set("icon","http://xxxx")
  .updateExt("mobile");

//This code is equivalent to: (this one is a lot of trouble)
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();  
}
  • Support attaching value according to the situation.
//1. old run
var qr = db.table("test").set("sex",1);
if(icon!=null){
  qr.set("icon",icon);
}
qr.where("mobile=?","111").update();  
//2. Chain operation routine
db.table("test").set("sex",1).expre((tb)->{ //Add an expression
  if(icon!=null){
    tb.set("icon",icon);
  }
}).where("mobile=?","111").update();  

For the conditions of update and deletion, refer to the section of query. The conditions are the same

[2.3.1] query output

Query is a complex topic. Maybe 80% of our database processing is query.
Today, let's talk about the output of the query of weed3.
  • 1.1. Quick query quantity
db.table("user_info").where("user_id<?", 10).count();
  • 1.2. Whether quick query exists
db.table("user_info").where("user_id<?", 10).exists();
  • 2.1. Query a field in a row and output a single value
bool val = db.table("user_info")
             .where("user_id=?", 10)
             .select("sex").getValue(false); //Set a default value to: false
  • 2.2. Query a field with multiple lines and output an array
List<String> ary = db.table("user_info")
             .where("user_id=?", 10)
             .select("mobile").getArray("mobile");
  • 3.1. Query a line and output map
Map<String,Object> map = db.table("user_info")
             .where("user_id=?", 10)
             .select("*").getMap(); 
  • 3.2. Query multiple lines and output map list
List<Map<String,Object>> list = db.table("user_info")
             .where("user_id>?", 10).limit(20) //Limited to 20 records
             .select("*").getMapList(); 
  • 4.1. Query a line and output entity
UserModel m = db.table("user_info")
             .where("user_id=?", 10)
             .select("*").getItem(UserModel.class); 

//User model (I call it the model)
//The simplest format is written here, which can be changed to bean style.
public class UserModel{
    public String name;
    public String mobile;
    public int sex;
}
  • 4.2. Query multiple lines and output entity list
List<UserModel> list = db.table("user_info")
             .where("user_id>?", 10).limit(0,20) //Page 20 lines
             .select("*").getList(UserModel.class); 
What else can it output?
  • 1.select("...") returns an: 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() returns DataList, which has some type conversion interfaces:
/** Turn all columns into data of class as array (class: IBinder subclass) */
List<T> toList(T model);
/** Convert all columns to classes as data of arrays */
List<T> toEntityList(Class<T> cls);
/** Select 1 column as the key of MAP and the row data as val */
Map<String,Object> toMap(String keyColumn);
/** Select two columns as MAP data */
Map<String,Object> toMap(String keyColumn,String valColumn);
/** Select a column as SET data */
Set<T> toSet(String column)
/** Select a column as the data of the array */
List<T> toArray(String columnName)
/** Select a column as the data of the array */
List<T> toArray(int columnIndex)
/** Convert to json string */
String toJson();
    1. Getvariable() returns variable and provides some transformation interfaces.
T value(T def);
double doubleValue(double def);
long longValue(long def);
int intValue(int def);
String stringValue(String def);

[2.3.2] query criteria

Searching is a troublesome topic...
Fortunately, the conditions in this section will be relatively simple
  • Single table condition query (with simple nature, it can be combined into complex)
//The conditional construction of weed3 is quite free
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("*")

//The above three, the effect is the same... It's easy to use because it's very free (some people think it's hard to control)
  • Sometimes some conditions need dynamic control
//In this example, it is common to manage the background
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();
  • Multi table associated query: 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.*")
  • How about other related queries? (such as: full join)
//Because not all databases support full join, so...
db.table("user u")
  .append("FULL JOIN user_book b").on("u.id = b.user_id")
  .select("u.name,b.*")

//. append(..) an interface that can add anything

[2.3.3] query cache control

Cache control is the key point in query

The framework provides control services. Rather than caching services themselves, it's important to understand this.

Cache control requires two important interface definitions:
  • 1. Cache service adaptation interface ICacheService (usually use its enhanced version of ICacheServiceEx)
//It can be used to package various cache services
public interface ICacheService {
    void store(String key, Object obj, int seconds);
    Object get(String key);
    void remove(String key);
    int getDefalutSeconds();
    String getCacheKeyHead();
}

/** weed3 Three implementation classes are built in:
 *EmptyCache,Empty cache
 *LocalCache,Local cache
 *SecondCache,Second level cache container (two icacheservices can be put together to become a second level cache service; more nesting is the third level cache service)
 */
  • 2. Operation interface on Cache Service: ICacheController
public interface ICacheController<T> {
    //Which cache service to use
    T caching(ICacheService service);
    //Use cache or not
    T usingCache(boolean isCache);
    //Use cache and set time
    T usingCache(int seconds);
    //Label cache
    T cacheTag(String tag);
}
Now that you have the foundation above, start using cache control
  • 1. Make a service instance first
ICacheService cache = new LocalCache(); 
  • 2. use up

Using cache, time is the default (stable cache key will be generated automatically)

db.table("test").select("*").caching(cache).getMapList();

Use cache and cache for 30s

db.table("test")
  .caching(cache).usingCache(30) //It can also be placed between table() and select().
  .select("*").getMapList();

Add a tag to the cache (the tag is equivalent to the virtual folder of the cache key)

db.table("test")
  .caching(cache)
  .usingCache(30).cacheTag('test_all') //This is tag, not key
  .limit(10,20)
  .select("*").getMapList();

*3. Fine control

Control cache time based on query results

db.table("test").where("id=?",10)
  .caching(cache)
  .select("*").getItem(UserModel.class,(cu,m)->{
    if(m.hot > 2){
        uc.usingCache(60 * 5); //Popular user cache 5 minutes
    }else{
        uc.usingCache(30);
    }
  });
  • 4. Cache clearing

Take a paging query as an example

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();

//No matter how many pages you have, a tag clears it
cache.clear("test_all");
  • 5. Cache update

This is rarely used (redis is recommended for single update cache)

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;
});

The cache control of the framework is also extremely free. Should it be? Ha he.

[2.3.4] other queries

Add some query related content
  • alias
db.table("user u")
  .limit(20)
  .select("u.mobile mob");
  • Duplicate removal
db.table("user")
  .limit(20)
  .select("distinct  mobile");
  • Grouping
db.table("user u")
  .groupBy("u.mobile").having("count(*) > 1")
  .select("u.mobile,count(*) num");
  • sort
db.table("user u")
  .orderBy("u.mobile ASC")
  .select("u.mobile,count(*) num");
  • Group + sort (or combine at will)
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] storage process and query process

For the support of stored procedure, two schemes are designed
  • 1. Calling stored procedures for docking database
db.call("user_get").set("_user_id",1).getMap();
  • 2.SQL query process (I call it: query process)

It looks like the SQL annotation code of mybatis.

//A query built by SQL
db.call("SELECT * FROM user WHERE id=@user_id").set("@user_id",1).getMap();
They can also be materialized (into a separate class)

The function of materialization is to arrange data processing to other modules (or folders).

  • 1. Storage process substantiation of docking database
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();//Add a cache along
  • 2. Materialization of query process
public class user_get2 extends DbQueryProcedure {
    public user_get2() {
        super(db);

        sql("select * from user where type=@type AND sex=@sex");
        // This binding method took a long time to come up with (just don't want to reflect!)
        set("@type", () -> type);
        set("@sex", () -> sex);
    }

    public int type;
    public int sex;
}
//DbQueryProcedure provides the same interface as DbStoredProcedure
user_get2 sp  =new user_get2();
sp.userID=10;
Map<String,Object> map = sp.caching(cache).getMap();

[2.5] solve the database keyword problem

Web3 provides field and object formatting support, which is set through DbContext
//Take mysql as an example
DbContext db = new DbContext(...).fieldFormatSet("`%`")//Set field formatter
                                 .objectFormatSet("`%`");//Format object
//%No. is a placeholder, and the field of `% 'will be changed to:' field name.`

The field formatter pairs: The fields in. Set (..), select (..), orderby (..), groupby (..) are processed.

Object formatter matching: The table names in. table(..), innerJoin(..), leftJoin(..), rightJoin(..) are processed.

If it is not set, you need to handle the keyword manually; manual processing does not conflict with automatic processing.
//Manual processing
db.table("`user`").where("`count`<10 AND sex=1").count();
Formatting is handled by IDbFormater. If you think the implementation is not good, you can write a replacement one yourself:)
IDbFormater df = new DfNew(); //DfNew is written by itself.
db.formaterSet(df); //Settle

//Attachment:
public interface IDbFormater {
    /** Field formatter settings */
    void fieldFormatSet(String format);
    /** Object formatter settings */
    void objectFormatSet(String format);

    /** Format field (for: set(..,v)) */
    String formatField(String name);
    /** Format multiple columns (for: select(..) orderBy(..) groupBy(..)) */
    String formatColumns(String columns);
    /** Format condition (for: where(..) and(..) or(..))  */
    String formatCondition(String condition);
    /** Format object (for: from(..), join(..)) */
    String formatObject(String name);
}

[2.5] check the three java interfaces (table,call,sql)

1.table() executes: chain ORM operation

Omitted here (this interface is mainly discussed earlier)

2.call(..) execution: stored procedure or query procedure
//Execute stored procedure
db.call("user_get").set("_user_id",1).getMap();

//Execute the query process (I'll call it that for now)
db.call("select * from user where id=@user_id").set("@user_id",1).getMap();
3.sql(..) execution: SQL statement
db.sql("select * from user where id=?",1).getMap();

db.sql(..) also has a shortcut version: db.exec(..). Equivalent to: db.sql(...).execute(); / / during batch processing, you can quickly write add, delete, and modify actions. Example: db.exec("DELETE FROM test where a=1")

Finally unified return: IQuery (ensuring the unity of experience)

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;
}

[3.1] start using Xml Mapper

Ready to start with a simple example

This time, you need to refer to a meven plug-in (you know it after playing mybatis)

Frame reference

<dependency>
  <groupId>org.noear</groupId>
  <artifactId>weed3</artifactId>
  <version>3.2.1.3</version>
</dependency>

meven plug-in reference (used to generate mapper class)

<!-- put to build / plugins / Below -->
<plugin>
    <groupId>org.noear</groupId>
    <artifactId>weed3-maven-plugin</artifactId>
    <version>3.2.1</version>
</plugin>
Xml file location Convention

resources/weed3 / as xml file directory

(1) now, write a simple xml file
  • 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="Get user information">
        SELECT * FROM `user` WHERE id=@{user_id}
    </sql>
</mapper>
(2) there are two ways to call the xml sql
Called through db.call("@...)
DbContext db = new DbContext(...);

UserModel um = db.call("@weed3demo.xmlsql.user_get")
                 .set("user_id")
                 .getItem(UserModel.class);
Generate Mapper interface and use it through dynamic agent
1. Use the meven plug-in to generate it (double click: weed3:generator)

2. Generated java file (java/weed3demo/xmlsql/DbUserApi.java)
package weed3demo.xmlsql;
@Namespace("weed3demo.xmlsql")
public interface DbUserApi{
  /** Get user information*/
  weed3demo.mapper.UserModel user_get(int user_id);
}
3. try it on.
//Overall situation
public static void main(String[] args){
  //Configure a context and add a name (for xml mapper)
  DbContext db = new DbContext(...).nameSet("testdb");

  //Get xml mapper by proxy
  DbUserApi dbUserApi = XmlSqlMapper.get(DbUserApi.class);
  //Use it
  UserModel tmp = dbUserApi.user_get(10);
}

[3.2] instruction and syntax of Xml Mapper

Five instructions + three variable forms. First segment xml

In this example, all kinds of situations should be presented.

<?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="Add user">
        INSERT user(user_id,mobile,sex) VALUES(@{m.user_id},@{m.mobile},@{sex})
    </sql>

    <sql id="user_add2" :return="long" :note="Add user">
        INSERT user(user_id) VALUES(@{user_id:int})
    </sql>

    <sql id="user_add_for" :return="long" :note="Batch add user 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 a user">
        DELETE FROM user WHERE id=@{m.user_id:long}
        <if test="sex > 0">
            AND sex=@{sex:int}
        </if>
    </sql>

    <sql id="user_set"
         :note="Update a user and clean up the associated save"
         :caching="localCache"
         :cacheClear="user_${user_id},user_1">
        UPDATE user SET mobile=@{mobile:String},sex=@{sex:int}
        <if test="icon != null">
            icon=@{icon:String}
        </if>
    </sql>

    <sql id="user_get_list"
         :note="Get a batch of qualified users"
         :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="Get a batch of qualified users"
         :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>
Four instructions
sql code block definition instruction
  : 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)

  : db (property: database context name)
  : 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)
Three variable forms
name:type = variable declaration (var only, or: declare)
@{name:type} = variable injection (code block only)
${name:type} = variable substitution (for code blocks, or: cacheTag, or: cacheClear)
Several forms of return value
//Multiline, list (replace < > with [])
:return="List[weed3demo.mapper.UserModel]" //List < usermodel > will be returned.
:return="List[String]" //Will return list < string > (Date,Long,... Single value type starting with uppercase)
:return="MapList" //List < map < string, Object > >
:return="DataList" //DataList will be returned

//A line
:return="weed3demo.mapper.UserModel" //UserModel will be returned
:return="Map" //Map < string, Object > will be returned.
:return="DataItem" //DataItem will be returned

//Single value
:return="String" //String (or any other job type) will be returned

[4] transaction and transaction queue

I talked about inserting and updating before.
This time, I will talk about business (write operations always follow the business...)
  • weed3 supports two kinds of transactions
  • 1. Transaction (mainly used for a single library)
//demo1:: / / transaction group / / in a transaction, make four inserts / / if there is an error, rollback automatically.
DbUserApi dbUserApi = XmlSqlProxy.getSingleton(DbUserApi.class);

db.tran((t) -> {
    //
    // Operations within this expression will automatically join the transaction
    //
//sql interface
    db.sql("insert into test(txt) values(?)", "cc").insert();
    db.sql("update test set txt='1' where id=1").execute();
//call interface
    db.call("user_del").set("_user_id",10).execute();
//table() interface
    db.table("a_config").set("cfg_id",1).insert();
//xml mapper
    dbUserApi.user_add(12);
//We use a unified business model.
});
  • 2. Transaction queue (mainly used for multiple databases)
//demo2:: / / transaction queue
//
//If you want to operate across two databases (one transaction object is useless)
//
DbContext db = DbConfig.pc_user;
DbContext db2 = DbConfig.pc_base;

//Create transaction queues (different from traditional concept queues)
DbTranQueue queue = new DbTranQueue();

//Transactions for database 1
db.tran().join(queue).execute((t) => {
    //
    // In this expression, things will be added automatically.
    //
    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();
});

//Transactions for database 2
db2.tran().join(queue).execute((t) => {
    //
    // In this expression, things will be added automatically.
    //
    db2.sql("insert into test(txt) values(?)", "gg").execute();
});

//Queue group completion (i.e. start running transaction)
queue.complete();
  • 3. Enhanced version of transaction queue, running transactions across functions or modules
public void test_main(){
    DbTranQueue queue = new DbTranQueue();
    test1(queue);
    test2(queue);
}

public void test1(DbTranQueue queue){
    DbTran tran = DbConfig.db1.tran();//Generate transaction objects

    tran.join(queue).execute((t) -> {
            //
            // In this expression, things will be added automatically.
            //
            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 won't write.
}

[5] monitor all execution

Some monitoring interfaces are opened through WeedConfig

For example: exception monitoring, slow SQL monitoring

//Monitor exceptions for unified printing or recording
WeedConfig.onException((cmd, ex) -> {
    if (cmd.text.indexOf("a_log") < 0 && cmd.isLog >= 0) {
        System.out.println(cmd.text);
    }
});

//Monitor SQL performance for unified recording
WeedConfig.onExecuteAft((cmd)->{
    if(cmd.timespan()>1000){ //Execute more than 1000 milliseconds..
        System.out.println(cmd.text + "::" + cmd.timespan() +"ms");
    }
});
Specific events that can be monitored
//Abnormal events
WeedConfig.onException(Act2<Command, Exception> listener);
//Log events (command information can be recorded)
WeedConfig.onLog(Act1<Command> listener);
//Pre execution events
WeedConfig.onExecuteBef(Fun1<Boolean, Command> listener);
//Events in execution (can listen, Statement)
WeedConfig.onExecuteStm(Act2<Command, Statement> listener);
//Post execution events
WeedConfig.onExecuteAft(Act1<Command> listener);

[6] embedded in script or template

Embed into script engine
  • 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("test").where('id=?',1).getMap();
  • 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("test").where('id=?',1).getMap();
Embed into template engine
  • Embedded in Freemarker engine
<#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>
...
Conclusion: I hope you like:)

Posted by silrayn on Thu, 24 Oct 2019 01:05:01 -0700