天天看點

mybatis mysql merge_Mybatis深度整合Mysql的Json字段

概述

以前當業務資料結構變化時,往往需要采用的方案是:

?修改表結構增加字段

?遇到資料結構有list結構時,建立1對多的關聯子表

?用字典表表示字段的增加

以上方案對代碼侵入性很強,同時與舊業務資料結構不相容。導緻代碼從實體類

、Dao、Service、Controller層都要修改。

随着NOSQL資料庫的廣泛應用,可擴充的存儲方式在關系型資料庫中也有了很好的支援,最新的MySQL5.7中就新增加了一個資料類型JSON,使用mysql的json類型字段做擴充字段,可以以json串形式動态的存儲任意結構的資料,包括list結構的資料也不必再建立子表。代碼的實體類和Dao層不必修改,其他層代碼修改量也能夠減少。

mybatis mysql merge_Mybatis深度整合Mysql的Json字段

Mysql常見json字段操作

Mysql5.7開始支援json字段

建立帶有json字段的表micro_test,其中extcol為json類型字段

CREATE TABLE `micro_test` (

`id` int(11) NOT NULL AUTO_INCREMENT,

`meta_name` varchar(100) DEFAULT NULL COMMENT ‘中繼資料名稱‘,

`create_time` datetime DEFAULT NULL COMMENT ‘建立時間‘,

`update_time` datetime DEFAULT NULL COMMENT ‘更新時間‘,

`extcol` json DEFAULT NULL,

PRIMARY KEY (`id`)

) ENGINE=InnoDB CHARSET=utf8;

插入json字段

可按照json字元串插入json字段

Insert into micro_test (extcol,meta_name,create_time)

values(‘{"name":"tomcat","age":15}‘,’123’,now());

查詢json字段

可以根據path查詢json字段中全部或部分資料

Select meta_name,extcol->>‘$.name‘ as name,extcol->>‘$.age‘ as age from micro_test;

修改json字段

可以根據path局部更新json字段中資料

Update micro_test set extcol=json_set(extcol,‘$.name‘,‘jeffrey‘) where meta_name=‘123‘

Mysql5.7.22版本以後支援JSON_MERGE_PATCH

可以省略path參數,全面更新json字段中資料

Update micro_test set extcol=json_set(extcol,‘{“name”:”n1”,”age”:30}‘) where meta_name=‘123‘

Mybatis使用Json字段

按照mybatis正常方式把json函數寫入到xml檔案中的sql中,即可支援json字段增删改查。但查詢出的json字段為字元串類型,需要手工轉成bean,插入時需手工把bean轉成json字元串,這樣做不利于面向對象程式設計。

mybatis mysql merge_Mybatis深度整合Mysql的Json字段

Mybatis深度整合Json字段

實作bean與json串在mybatis内部轉換,這樣做的優點是dao層代碼和sql不變,service層可以增删改查不同的動态Entity對象。更符合面向對象程式設計習慣提高開發效率。

mybatis mysql merge_Mybatis深度整合Mysql的Json字段

Extcol開源項目實作Mybatis與mysql的json字段深度整合

項目位址為:

https://github.com/jeffreyning/extcol.git

pom引用extcol的jar

com.github.jeffreyning

extcol

0.0.1-RELEASE

Extcol包中TypeHandler子類TagToJsonTypeHandler 實作mybatis在資料庫操作過程中的參數輸入和結果轉換的攔截。攔截父類為ExtBeanWrapper的對象。

使TagToJsonTypeHandler生效需要配置

mybatis-plus:

typeHandlersPackage: com.nh.micro.ext.th

Extcol包中ExtBeanWrapper類,作為json對象轉換的目标對象,内有map成員變量儲存實際資料,getobj和setobj方法是使用fastjson做對象與map的轉換。

使用Extcol的Demo

引入和配置好extcol後,在demo業務系統工程中編寫對應micro_test表的實體類TestDto,其中json字段的成員變量類型是ExtBeanWrapper。

public class TestDto {

private Integer id;

private String metaKey;

private String metaName;

private String metaType;

private Date createTime;

private ExtBeanWrapper extcol;

public ExtBeanWrapper getExtcol() {

return extcol;

}

public void setExtcol(ExtBeanWrapper extcol) {

}

擴充字段業務bean

例如擴充bean為ExtEntity有兩個在資料庫中json字段動态存儲的字段t1和t2

public class ExtEntity {

private String t1;

private String t2;

public String getT1() {

return t1;

}

public void setT1(String t1) {

this.t1 = t1;

}

public String getT2() {

return t2;

}

public void setT2(String t2) {

this.t2 = t2;

}

}

在以TestDto為更新和插入時的參數操作時,mybatis将負責将bean轉為json串

insert into micro_test(meta_name,extcol,create_time) values (#{metaName},#{extcol},now())

當執行查詢語句時,傳回的結果映射到ExtBeanWrapper 類型的字段時,mybatis将負責将json串轉為ExtBeanWrapper ,且這個ExtBeanWrapper 可以按照不同的業務bean自适應轉化。

SELECT * from micro_test where meta_name=#{name}

取查詢結果中JSON字段中存儲的業務類ExtEntity 的代碼

public List testQuery4JsonXml(String name){

List retList=testDao.getInfo4JsonXml(name);

if(retList!=null){

for(TestDto testDto:retList){

ExtBeanWrapper extBeanWrapper=testDto.getExtcol();

ExtEntity extEntity=(ExtEntity) extBeanWrapper.getObj(ExtEntity.class);

System.out.println(extEntity.getT1());

}

}

return retList;

}

Mybatisplus的bean更新時使用JSON_MERGE_PATCH

修改mybatisplus的AutoSqlInjector

private String getPlaceTag(String row){

int start=row.indexOf("#{");

int end=row.indexOf("}")+1;

String temp=row.substring(start,end);

System.out.println(temp);

return temp;

}

private String getColTag(String row){

int end=row.indexOf("=#{");

int start=0;

if(row.contains("

start=row.indexOf(">")+1;

}

String temp=row.substring(start,end);

System.out.println(temp);

return temp;

}

private String createNewPlace(String colTag,String placeTag){

String temp="json_merge_patch("+colTag+","+placeTag+")";

return temp;

}

protected void injectUpdateByIdSql(boolean selective, Class> mapperClass, Class> modelClass, TableInfo table) {

SqlMethod sqlMethod = selective ? SqlMethod.UPDATE_BY_ID : SqlMethod.UPDATE_ALL_COLUMN_BY_ID;

String temp=sqlSet(selective, table, "et.");

String osql=temp;

if(selective){

String[] tempArray=temp.split("\n\t");

StringBuilder sb=new StringBuilder("");

for(String row:tempArray){

if(row.contains("typeHandler")){

System.out.println(getPlaceTag(row));

String placeTag=getPlaceTag(row);

System.out.println(getColTag(row));

String colTag=getColTag(row);

String nPlaceTag=createNewPlace(colTag, placeTag);

System.out.println(nPlaceTag);

row=row.replace(placeTag, nPlaceTag);

sb.append(row).append("\n\t");

}else{

sb.append(row).append("\n\t");

}

}

osql=sb.toString();

}

String sql = String.format(sqlMethod.getSql(), table.getTableName(), osql, table.getKeyColumn(),

"et." + table.getKeyProperty(),

""

+ ""

+ "and ${et.MP_OPTLOCK_VERSION_COLUMN}=#{et.MP_OPTLOCK_VERSION_ORIGINAL}"

+ "

"

+ ""

);

System.out.println(sql);

SqlSource sqlSource = languageDriver.createSqlSource(configuration, sql, modelClass);

this.addUpdateMappedStatement(mapperClass, modelClass, sqlMethod.getMethod(), sqlSource);

}

原文:https://blog.51cto.com/13442277/2387244