当前位置:网站首页>A new micro ORM open source framework

A new micro ORM open source framework

2022-07-05 05:19:00 Lin Xidong

Weed3 A micro ORM frame ( Only 0.1Mb Oh )

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

05 I started writing this framework in 1 Substitute version ... 08 In, I entered the Internet company restructuring and wrote 2 Substitute version ... 14 In, refactoring wrote the present 3 Substitute version ( Yes java and .net Two platform versions of )... Recently forced to add xml sql mapper Support for ... And by the way sql annotation ... Finally, the bag has grown to 0.1Mb 了 ...

Last time a friend in the group said , This is a strange frame . This is a very interesting way of speaking ..

On the whole , The characteristic of this framework is Don't like reflection 、 Don't like configuration ( But still can't avoid )!!! It's through good interface design , To become a simple control experience . Maybe you feel free to write sql Better than that ( How could it be , ha-ha ~~)

For some old people , This description may be better : It is equivalent to mybatis + mybatis-puls ( There's a benchmark , Easy to understand )... But I didn't use them , Maybe it's not right .

in addition , It's very small , It's fast. , It's free ( Others say , Too free to control )

【1】 First Hello world once

  • Build a... Of any kind java project , Introduce framework package
<dependency>    <groupId>org.noear</groupId>    <artifactId>weed3</artifactId>    <version>3.2.9</version></dependency><!--  This is by the way , Database connector always has one  --><dependency>    <groupId>mysql</groupId>    <artifactId>mysql-connector-java</artifactId>    <version>5.1.47</version></dependency>
  • No configuration , Three lines of code to run
// hello world  Walk up ...( Change the database link to the right one ...)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);}////  If it is  mybatis , It is estimated that we have to be busy configuring ...//
  • It should be simple ( Introduce two outsourcing ; Write three lines of code )

You can't hello world It's not a good thing . ha-ha :-P

weed3 Support pure java Chain writing perhaps xml mapper How to write it . First of all, I will introduce Chun java How to write it ... Let's talk about it slowly .

【2.1】 Start pure java Use

pure java When using , There are three interfaces available :db.table(..), db.call(..), db.sql(). In general use db.table(..) Most of the interfaces are chain operated . Its interface is used with SQL How to name the map ... People who use , It's easy to think of the chain interfaces . image :.where(..) .and(..) .innerJoin(..) etc. ...

Chain operation routine : With db.table(..) Start . With .update(..) or .insert(..) or .delete(..) or .select(..). among .select(..) Returns the IQuery Interface , Provides options for various types of results .

First , add to meven rely on
<dependency>  <groupId>org.noear</groupId>  <artifactId>weed3</artifactId>  <version>3.2.9</version></dependency><!--  Database connector , I don't care  -->
then , Instantiate database context object
  • all weed3 The operation of , It's all based on DbContext. So let's make it a reality ...
  1. It needs to be configured , Can be in application.properties obtain , You can get... By configuring the service , You can write it temporarily ..

If it is Spring frame , The configuration can be obtained by annotation If it is solon frame , You can annotate or Aop.prop().get("xxx") Get configuration

2. After configuration, the implementation begins DbContext. Here is a temporary handwritten .

// Use proxool Example of thread pool configuration ( Seems to be out of fashion now )DbContext db  = new DbContext("user","proxool.xxx_db"); // Use DataSource Example of configuration ( It is generally used to connect the pool frame ; recommend  Hikari  Connection pool )// The downside demo It's just  Hikari  Connection pool DbContext db  = new DbContext("user",new HikariDataSource(...)); // There is also the use of url,username,passwordDbContext db  = new DbContext("user","jdbc:mysql://x.x.x:3306/user","root","1234");/*  I usually use configuration services , So the database context object is provided directly by the configuration . */
Now? , Start doing simple data operations
  • General query operations
// Statistics less than 10 Of users 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);
  • One more full set " Additions and deletions "
// Simple and easy . increase db.table("test").set("log_time", "$DATE(NOW())").insert();// Simple and easy . Delete db.table("test").where("id=?",1).delete();// Simple and easy . Change db.table("test").set("log_time", "$DATE(NOW())").where("id=?",1).update();// Simple and easy . check var map = db.table("test").where("id=?",1).select("*").getMap();
Another set of interfaces for conditions
//where  Group whreEq(filed,val)  //filed be equal to valwhereLt(filed,val) // Less than whereLte(filed,val) // Less than or equal to whereGt(filed,val) // Greater than whereGte(filed,val) // Greater than or equal to whereLk(filed,val) // LIKEwhereIn(filed,ary) // INwhereNin(filed,ary) // NOT IN//and  Group andEq(filed,val)  //filed be equal to valandLt(filed,val) // Less than andLte(filed,val) // Less than or equal to andGt(filed,val) // Greater than andGte(filed,val) // Greater than or equal to andLk(filed,val) // LIKEandIn(filed,ary) // INandNin(filed,ary) // NOT IN//or  Group orEq(filed,val)  //filed be equal to valorLt(filed,val) // Less than orLte(filed,val) // Less than or equal to orGt(filed,val) // Greater than orGte(filed,val) // Greater than or equal to 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();

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

【2.2】 Go into insert and update

This section focuses on inserting and updating assignments
  • Supports normal assignment
String mobile="xxx"; // My cell phone number can't write db.table("test")  .set("mobile",mobile) // Variable assignment   .set("sex",1) // Constant assignment   .insert();
  • Support sql Value added ( This can bring convenience ***) If it's worth :$ start , Means followed by SQL Code ( There can be no gaps , And 100 Within characters . Otherwise, it will be treated as a normal string value ), as follows :
// such as : Current time assignment db.table("test").set("log_time","$NOW()").insert();// Another example : Field plus 1 Value added db.table("test").set("num","$num+1")  .where("id=?",1).update();// Another example : According to... Of another field md5, Batch update db.table("test").set("txt_md5","$MD5(txt)")  .where("id>? AND id<?",1000,2000).update();/*  How to turn the function on or off ?( Actually , It's quite safe )*///1. Only control whether this operation uses this function db.table("test").usingExpr(false) // true  Turn on ,false  close //2. Global configuration turns this function on or off :WeedConfig.isUsingValueExpression=false; // Global default off 
  • Support map Value added ( A 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 added ( A 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 ( If not, insert , There is update ) Simplified operation of
// Simplify the plan 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 value added according to the situation ( It's strange to speak ..)
//1. Old school running 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();  

About the conditions of update and deletion , Refer to the section of the inquiry . The conditions are the same

【2.3.1】 Query output

Query is a complicated topic , Maybe we 80% The database processing of is querying .
Let's talk about weed3 What can be output from the query of ?
  • 1.1. Quick query quantity
db.table("user_info").where("user_id<?", 10).count();
  • 1.2. Quick query exists
db.table("user_info").where("user_id<?", 10).exists();
  • 2.1. Query a field in a row , Output single value
bool val = db.table("user_info")             .where("user_id=?", 10)             .select("sex").getValue(false); // Set the default value to :false
  • 2.2. Query a field with multiple lines , The output array
List<String> ary = db.table("user_info")             .where("user_id=?", 10)             .select("mobile").getArray("mobile");
  • 3.1. Look up a line , Output map
Map<String,Object> map = db.table("user_info")             .where("user_id=?", 10)             .select("*").getMap(); 
  • 3.2. Query multiple lines , Output map list
List<Map<String,Object>> list = db.table("user_info")             .where("user_id>?", 10).limit(20) // limit 20 Bar record              .select("*").getMapList(); 
  • 4.1. Look up a line , Output entity
UserModel m = db.table("user_info")             .where("user_id=?", 10)             .select("*").getItem(UserModel.class); // The user model ( I call it model )// Here's the simplest format , It can be changed to bean style public class UserModel{    public String name;    public String mobile;    public int sex;}
  • 4.2. Query multiple lines , Output entity list
List<UserModel> list = db.table("user_info")             .where("user_id>?", 10).limit(0,20) // Page by page 20 That's ok              .select("*").getList(UserModel.class); 
Then what else can be output ?
  • 1.select("...") Back to a :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. among getDataList() Back to Canada is DataList, It has some type conversion interfaces :
/**  Turn all columns into classes as data for arrays ( Class is :IBinder  Subclass ) */List<T> toList(T model);/**  Turn all columns into classes as data for arrays  */List<T> toEntityList(Class<T> cls);/**  choose 1 Column as MAP Of key, And make the line data as val */Map<String,Object> toMap(String keyColumn);/**  Choose two columns as MAP The data of  */Map<String,Object> toMap(String keyColumn,String valColumn);/**  Choose a column as SET The data of  */Set<T> toSet(String column)/**  Choose a column as the data of the array  */List<T> toArray(String columnName)/**  Choose a column as the data of the array  */List<T> toArray(int columnIndex)/**  To json character string  */String toJson();
    1. among getVariate() The return is Variate, There are also some adapters
T value(T def);double doubleValue(double def);long longValue(long def);int intValue(int def);String stringValue(String def);

【2.3.2】 Conditions of inquiry

Searching is a troublesome topic ...
Fortunately, the conditions in this section will be relatively simple
  • Single table condition query ( With simple nature, you can spell complex )
//weed3  The condition construction of , It's 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("*")// Three of the above , The effect is the same ... Because it's free , So it's easy to use ( Also with a view : So it's hard to control )
  • Sometimes some conditions need dynamic control
// This example , Managing the back office is very common 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, therefore ...db.table("user u")  .append("FULL JOIN user_book b").on("u.id = b.user_id")  .select("u.name,b.*")//.append(..)  Interface that can add anything 

【2.3.3】 Query cache control

Cache control , Is the key point in the query

The framework provides control services . Not the caching service itself , It's important to understand this .

Cache control requires two important interface definitions :
  • 1. Cache service adapter ICacheService( Usually use its enhanced version ICacheServiceEx )
// It can be used to wrap all kinds of caching 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 ( You can put two  ICacheService  Put it together , Become a L2 cache service ; More nesting is the three-level cache service ) */
  • 2. Control interface on Cache Service :ICacheController ( be-all weed The control objects are all realized ICacheController)
public interface ICacheController<T> {    // Which caching service to use     T caching(ICacheService service);    // Whether to use cache     T usingCache(boolean isCache);    // Use cache and set time     T usingCache(int seconds);    // Tag the cache     T cacheTag(String tag);}
With the above foundation , Now start using cache control
  • 1. Start with a service instance
// cache key header by  test ,  The default cache time is  60sICacheService cache = new LocalCache("test",60); 
  • 2. To use

Use the cache , Time is the default ( It will automatically generate a stable cache key)

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

Use the cache , And cache 30s

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

Add... To the cache tag(tag amount to cache key The virtual folder of )

db.table("test")  .caching(cache)  .usingCache(30).cacheTag('test_all') // This is a tag, No 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 minute     }else{        uc.usingCache(30);    }  });
  • 4. Cache clear

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 , One tag Clear it out cache.clear("test_all");
  • 5. Cache update

This is rarely used ( A cache that requires a single update , Suggest using 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;});

Cache control of the framework , It's also extremely free . It should be ? Huhe .

Availability of cache service
1. Built in cache service
  • org.noear.weed.cache.EmptyCache // Empty cache
  • org.noear.weed.cache.LocalCache // Lightweight local cache ( be based on Map Realization )
  • org.noear.weed.cache.SecondCache // Second level cache ( Assemble two ICacheServiceEx Realization )
2. Extended cache service
  • org.noear.weed.cache.ehcache.EhCache // be based on ehcache encapsulation
<dependency>  <groupId>org.noear</groupId>  <artifactId>weed3.cache.ehcache</artifactId>  <version>3.2.3.4</version></dependency>
  • org.noear.weed.cache.j2cache.J2Cache // Based on Chinese development J2Cache encapsulation
<dependency>  <groupId>org.noear</groupId>  <artifactId>weed3.cache.j2cache</artifactId>  <version>3.2.3.4</version></dependency>
  • org.noear.weed.cache.memcached.MemCache // be based on memcached encapsulation
<dependency>  <groupId>org.noear</groupId>  <artifactId>weed3.cache.memcached</artifactId>  <version>3.2.3.4</version></dependency>
  • org.noear.weed.cache.redis.RedisCache // be based on redis encapsulation
<dependency>  <groupId>org.noear</groupId>  <artifactId>weed3.cache.redis</artifactId>  <version>3.2.3.4</version></dependency>
  • You can also package it yourself ICacheServiceEx ...
Do you want to package it yourself ?

【2.3.4】 Other aspects of inquiry

Add some more information about the query
  • 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");
  • grouping + Sort ( Or mix it up )
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】 Stored procedure and query procedure

Support for stored procedures , Two schemes have been designed
  • 1. The stored procedure call to the database
db.call("user_get").set("_user_id",1).getMap();
  • 2.SQL The query process ( I call it : The query process )

Looks like mybatis Of SQL The annotation code is a bit like

// from SQL Build a query db.call("SELECT * FROM user WHERE [email protected]{user_id}").set("user_id",1).getMap();
They can also be materialized ( Become a separate class )

The function of substantiation is , Data processing can be arranged to other modules ( Or folder )

  • 1. The stored procedure of docking database is materialized
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 the way 
  • 2. Materialization of the query process
public class user_get2 extends DbQueryProcedure {    public user_get2() {        super(db);        sql("select * from user where [email protected]{type} AND [email protected]{sex}");        //  This binding method , It took a long time to come up with ( I just don't want to reflect !)        set("type", () -> type);        set("sex", () -> sex);    }    public int type;    public int sex;}//DbQueryProcedure  Provided with  DbStoredProcedure  Same interface user_get2 sp  =new user_get2();sp.userID=10;Map<String,Object> map = sp.caching(cache).getMap();

【2.5】 Solve the database keyword problem

weed3 Provides support for formatting fields and objects , adopt DbContext To set
// With mysql For example DbContext db = new DbContext(...).fieldFormatSet("`%`")// Set field format                                  .objectFormatSet("`%`");// Set object formatter //% The number is a placeholder ,`%` Your fields will be converted to :` Field name `

Field formatter pairs will be true to : .set(..), .select(..), .orderBy(..), .groupBy(..) The fields in the

Object formatter pairs : .table(..), innerJoin(..), leftJoin(..), rightJoin(..) The name of the table in

If not set , You need to handle the keywords by yourself ; Manual processing does not conflict with automatic processing .
// Handle by hand db.table("`user`").where("`count`<10 AND sex=1").count();
Formatting is done by IDbFormater To deal with , If you think the implementation is not good , You can also write a replacement for it :)
IDbFormater df = new DfNew(); //DfNew  I wrote it myself db.formaterSet(df); // Get it done // attach :public interface IDbFormater {    /**  Field formatter settings  */    void fieldFormatSet(String format);    /**  Object formatter settings  */    void objectFormatSet(String format);    /**  Format fields ( be used for :set(..,v)) */    String formatField(String name);    /**  Format multiple columns ( be used for :select(..) orderBy(..) groupBy(..)) */    String formatColumns(String columns);    /**  Formatting conditions ( be used for :where(..) and(..) or(..))  */    String formatCondition(String condition);    /**  Formatting objects ( be used for :from(..), join(..)) */    String formatObject(String name);}

【2.5】 Take stock of the top three java Use interface (table,call,sql)

1.table() perform : The chain ORM operation

Here slightly ( I'll talk about this interface )

2.call(..) perform : stored procedure or The query process
// Execute stored procedures db.call("user_get").set("_user_id",1).getMap();// Execute the query process ( I call it that for the time being )db.call("select * from user where [email protected]{user_id}").set("user_id",1).getMap();
3.sql(..) perform :SQL sentence
db.sql("select * from user where id=?",1).getMap();

db.sql(..) There is also a quick version :db.exec(..). amount to :db.sql(...).execute(); // In batch processing , Can be written quickly 、 Delete 、 Change the action example :db.exec("DELETE FROM test where a=1")

Finally, return to :IQuery ( It ensures the unity of experience )

db.table(..).select(..) -> IQuerydb.call(..) -> IQuerydb.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】 Start Xml Mapper Use

Usage conventions ***
  • 1. Appointment resources/weed3/ by xml sql root directory
  • 2. Project start compilation parameters :-parameters
Ready to start with a simple example

You need to quote a meven plug-in unit ( Yes mybatis We all know that )

Frame reference

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

meven Plug-in reference ( Used to generate mapper class )

<!--  Put it in  build / plugins /  below  --><plugin>    <groupId>org.noear</groupId>    <artifactId>weed3-maven-plugin</artifactId>    <version>3.2.3.4</version></plugin>
( One ) Now? , Write a simple one first 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 [email protected]{user_id}    </sql></mapper>
( Two ) There are two ways to call the xml sql
adopt db.call("@...") call
DbContext db = new DbContext(...);UserModel um = db.call("@weed3demo.xmlsql.user_get")                 .set("user_id")                 .getItem(UserModel.class);
Generate Mapper Interface , Use... Through dynamic agents
1. use meven Plugins make it ( double-click :weed3:generator)

WX20191015-224938@2x.png

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. Have a try
// overall situation public static void main(String[] args){  // Configure a context , And add the name ( to xml mapper  use )  DbContext db = new DbContext(...).nameSet("testdb");  // Get... By proxy xml mapper  DbUserApi dbUserApi = XmlSqlMapper.get(DbUserApi.class);  // Use it   UserModel tmp = dbUserApi.user_get(10);}

【3.2】Xml Mapper The instructions and grammar of

Five instructions + Three variable forms . First come xml

In this example, various 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 users ">        INSERT user(user_id,mobile,sex) VALUES(@{m.user_id},@{m.mobile},@{sex})    </sql>    <sql id="user_add2" :return="long" :note=" Add users ">        INSERT user(user_id) VALUES(@{user_id:int})    </sql>    <sql id="user_add_for" :return="long" :note=" Batch add users 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 [email protected]{m.user_id:long}        <if test="sex gt 0">            AND [email protected]{sex:int}        </if>    </sql>    <sql id="user_set"         :note=" Update a user , And clean up the associated "         :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=" 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 instructions   :require( attribute : Import package or class )  :param?( attribute : External input variable declaration ; By default, it will automatically generate :: newly added ***)  :declare( attribute : Internal variable type pre declaration )  :return( attribute : Return type )  :db ( attribute : Database context name)  :note( attribute : describe 、 explain 、 annotation )  :caching( attribute : Caching services name) // It's right  ICacheController  Mapping of interfaces   :cacheClear?( attribute : Clear cache )  :cacheTag?( attribute : Cache tags , It supports value substitution in the input parameter or result )  :usingCache?( attribute : Cache time ,int)if  Judgment control command ( No, else)  test ( attribute : Determine the detection code )     //xml Avoid grammar enhancements :     //lt(<) lte(<=) gt(>) gte(>=) and(&&) or(||)        // example :m.sex gt 12 :: m.sex >=12     // Simplified grammar enhances :     //??( Not null,var!=null) ?!( Non empty string ,StringUtils.isEmpty(var)==false)        // example :m.icon??  ::m.icon!=null        // example :m.icon?!  ::StringUtils.isEmpty(m.icon)==falsefor  Cycle control command  ( adopt  ${var}_index  You can get the serial number , example :m_index:: newly added ***)  var ( attribute : Circular variable declaration )  items ( attribute : Set variable name )  sep? ( attribute : Separator :: newly added ***)trim  Trim command   trimStart( attribute : Start bit removal )  trimEnd( attribute : End bit removal )  prefix( attribute : add prefix )  suffix( attribute : Add Suffix )ref  Reference block instructions   sql ( attribute : Code block id)
Three variable forms
name:type    =  variable declaration ( Only used for var , or :declare)@{name:type} =  Variable injection ( For code blocks only )${name:type} =  Variable substitution ( For code blocks , or :cacheTag, or :cacheClear)
About several forms of return value
// Multiple lines , list ( use [] replace <>):return="List[weed3demo.mapper.UserModel]" // Will return  List<UserModel>:return="List[String]" // Will return  List<String> (Date,Long,... Single valued types starting with uppercase ):return="MapList" // Will return  List<Map<String,Object>>:return="DataList" // Will return  DataList// a line :return="weed3demo.mapper.UserModel" // Will return  UserModel:return="Map" // Will return  Map<String,Object>:return="DataItem" // Will return  DataItem// Single value :return="String" // Will return  String ( Or any other single type )

【4.1】 Start annotation sql Use

Usage conventions ***
  • 1. Project start compilation parameters :-parameters
A first demo
  • 1. Make a statement 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. Use it
DbContext db = new DbContext(...);DbMapper1 dm = db.mapper(DbMapper1.class);AppxModel m = dm.appx_get("appx",1); 
Two variable forms + Cache control
Two variable forms
  • ${} Alternative variables ( Equivalent to placeholder , String splicing )
  • @{} Compile variables ( Will be compiled as ?, Pass variables to jdbc)
Cache control
  • caching Caching services
  • cacheTag Cache tags ( stay key above , Build a virtual tag; For the convenience of cleaning )
  • usingCache Cache usage time
  • cacheClear Cache cleanup ( adopt cacheTag Form cleaning )
One more demo2

After the update , Clear cache :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;}
One more demo3

Build... Using query results 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;}
Add : Build a caching service
// Write anywhere //1. Initialize a ICacheServiceEx//2. adopt nameSet("test")  Register to cache //3. Then you can be  @sql Of  caching  Use (xml sql  Of  caching  The same is true of )//new LocalCache("test",60).nameSet("test");

【5】 Transactions and transaction queues

I talked about inserting and updating
This time on business ( Write operations always follow the business ...)
  • weed3 Support two kinds of transactions
  • 1. Business ( Mainly used for a single library )
//demo1:: // Transaction group  //  In a business , do 4 An insert // If something goes wrong , Auto rollback DbUserApi dbUserApi = XmlSqlProxy.getSingleton(DbUserApi.class);db.tran((t) -> {    //    //  Operation in 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 in the case of multiple libraries )
//demo2:: // Transaction queue //// If , To operate across two databases ( A transaction object is useless )//DbContext db = DbConfig.pc_user;DbContext db2 = DbConfig.pc_base;// Create a transaction queue ( Unlike the traditional concept of queues )DbTranQueue queue = new DbTranQueue();// database 1 The business of db.tran().join(queue).execute((t) => {    //    //  In this expression , Will automatically add things     //    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();});// database 2 The business of db2.tran().join(queue).execute((t) => {    //    //  In this expression , Will automatically add things     //    db2.sql("insert into test(txt) values(?)", "gg").execute();});// Queue formation complete ( Start running business )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 , Will automatically add things             //            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 Don't write }

【6】 Monitor all executions

adopt WeedConfig Open some monitor interfaces

such as : Abnormal monitoring , slow SQL monitor

// Abnormal monitoring , To print or record uniformly WeedConfig.onException((cmd, ex) -> {    if (cmd.text.indexOf("a_log") < 0 && cmd.isLog >= 0) {        System.out.println(cmd.text);    }});// monitor SQL performance , In order to keep a uniform record of WeedConfig.onExecuteAft((cmd)->{    if(cmd.timespan()>1000){ // To perform more than 1000 ms ..        System.out.println(cmd.text + "::" + cmd.timespan() +"ms");    }});
Specific events that can be monitored
// Unusual events WeedConfig.onException(Act2<Command, Exception> listener);// Log event ( You can record the command information )WeedConfig.onLog(Act1<Command> listener);// Event before execution WeedConfig.onExecuteBef(Fun1<Boolean, Command> listener);// Events in execution ( Can monitor ,Statement)WeedConfig.onExecuteStm(Act2<Command, Statement> listener);// Post execution events WeedConfig.onExecuteAft(Act1<Command> listener);

【7】 Embed into script or template

Embedded in the 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 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();
Embedded in the 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 :)
原网站

版权声明
本文为[Lin Xidong]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/186/202207050513075009.html