tk.mybatis如何擴(kuò)展自己的通用mapper
tk.mybatis擴(kuò)展自己的通用mapper
目的:tk.mybatis 提供的通用mapper,雖然使用方便,不過在有些sql還是不能滿足我們的需要的,而且我們希望對于deleted語句進(jìn)行管控(因為通用mapper提供的都是物理刪除,有時我們對一些敏感數(shù)據(jù)只能進(jìn)行邏輯刪除),因此我們將基于原有的通用mapper進(jìn)行自己的擴(kuò)展
1 jar引入
<!-- 非springboot -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper</artifactId>
<version>3.4.0</version>
</dependency>
非springboot項目引這個包
<!-- springboot -->
<dependency>
<groupId>tk.mybatis</groupId>
<artifactId>mapper-spring-boot-starter</artifactId>
<version>2.0.2</version>
</dependency>
springboot項目直接引這個包,本次我們將基于springboot進(jìn)行開發(fā),這里注意jar包版本,過低的版本將會出現(xiàn)問題,下面會介紹
2 編寫配置類
package com.example.configuration;
import com.example.common.mapper.commonMapper.CommonMapper;
import org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureAfter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import tk.mybatis.spring.mapper.MapperScannerConfigurer;
import java.util.Properties;
@Configuration
@AutoConfigureAfter(MybatisAutoConfiguration.class)
public class MyBatisConfiguration {
@Bean
public MapperScannerConfigurer mapperScannerConfigurer() {
MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
mapperScannerConfigurer.setSqlSessionFactoryBeanName("sqlSessionFactory");
mapperScannerConfigurer.setBasePackage("com.example.dao.mapper");
Properties properties = new Properties();
properties.setProperty("mappers", CommonMapper.class.getName());
properties.setProperty("notEmpty", "false");
properties.setProperty("identity", "MYSQL");
properties.setProperty("order","BEFORE");
mapperScannerConfigurer.setProperties(properties);
return mapperScannerConfigurer;
}
}
這里要注意兩點:
1>CommonMapper這個是我們自己定義的通用mapper,它放置一個單獨的包下面。也就是說這個包下面只有這個一個mapper,否則我們在實用泛型時會出現(xiàn)類型轉(zhuǎn)換的問題。(我在這里糾結(jié)了好久)
2>這里我們配置了自定義mapper的掃描路徑。注意springboot項目一定要注意jar包版本,版本過低的話,在配置類中的定義是沒有用的。還是掃描不到,我之前使用的版本1.2.3就會出現(xiàn)這個問題,更換更高的版本就可以解決了。
3 常規(guī)方法接口整合到自己的自定義Mapper接口上
因為我們使用的是mysql數(shù)據(jù)庫,所以在使用通用功能的時候就選擇性引入一些經(jīng)常使用的方法,下面是自己定義的常用mapper類的整合(這里會拋棄一些不常用的類或不是mysql的方法類)。這些基類不要放在 BasePackage的路徑下,這個路徑下的mapper都是要指定明確的泛型類型的。
1 > 提供查詢功能Mapper:
package com.example.common.mapper.basics;
import tk.mybatis.mapper.common.Marker;
import tk.mybatis.mapper.common.base.select.*;
import tk.mybatis.mapper.common.condition.SelectByConditionMapper;
import tk.mybatis.mapper.common.condition.SelectCountByConditionMapper;
import tk.mybatis.mapper.common.example.SelectByExampleMapper;
import tk.mybatis.mapper.common.ids.SelectByIdsMapper;
/**
* @desc 基礎(chǔ)查詢功能mapper
*
*/
public interface SelectMapper<T> extends Marker,
SelectOneMapper<T>,
tk.mybatis.mapper.common.base.select.SelectMapper<T>,
SelectAllMapper<T>,
SelectCountMapper<T>,
SelectByPrimaryKeyMapper<T>,
ExistsWithPrimaryKeyMapper<T>,
SelectByIdsMapper<T>,
SelectByConditionMapper<T>,
SelectCountByConditionMapper<T>,
SelectByExampleMapper<T> {
}
2>提供新增功能mapper
package com.example.common.mapper.basics;
import tk.mybatis.mapper.common.Marker;
import tk.mybatis.mapper.common.MySqlMapper;
import tk.mybatis.mapper.common.base.insert.InsertSelectiveMapper;
/**
* @desc 基礎(chǔ)插入功能mapper
*/
public interface InsertMapper<T> extends Marker,
tk.mybatis.mapper.common.base.insert.InsertMapper<T>,
InsertSelectiveMapper<T>,
MySqlMapper<T>{
}
3>提供更新功能mapper
package com.example.common.mapper.basics;
import tk.mybatis.mapper.common.Marker;
import tk.mybatis.mapper.common.base.update.UpdateByPrimaryKeyMapper;
import tk.mybatis.mapper.common.base.update.UpdateByPrimaryKeySelectiveMapper;
import tk.mybatis.mapper.common.condition.UpdateByConditionMapper;
import tk.mybatis.mapper.common.condition.UpdateByConditionSelectiveMapper;
import tk.mybatis.mapper.common.example.UpdateByExampleSelectiveMapper;
/**
* @desc 基礎(chǔ)更新功能mapper
*
*/
public interface UpdateMapper<T> extends Marker,
UpdateByPrimaryKeyMapper<T>,
UpdateByPrimaryKeySelectiveMapper<T>,
UpdateByConditionMapper<T>,
UpdateByConditionSelectiveMapper<T>,
UpdateByExampleSelectiveMapper<T> {
}
4>提供刪除功能mapper
package com.example.common.mapper.basics;
import com.example.common.mapper.defined.DeleteShamByIdsMapper;
import tk.mybatis.mapper.common.Marker;
import tk.mybatis.mapper.common.base.delete.DeleteByPrimaryKeyMapper;
import tk.mybatis.mapper.common.condition.DeleteByConditionMapper;
import tk.mybatis.mapper.common.ids.DeleteByIdsMapper;
/**
* @desc 基礎(chǔ)刪除功能mapper
*
*/
public interface DeleteMapper<T> extends Marker,
tk.mybatis.mapper.common.base.delete.DeleteMapper<T>,
DeleteByPrimaryKeyMapper<T>,
DeleteByConditionMapper<T>,
DeleteByIdsMapper<T>{
}
5>提供增刪改查的基類 CommonMapper
package com.example.common.mapper.commonMapper;
import com.example.common.mapper.basics.DeleteMapper;
import com.example.common.mapper.basics.InsertMapper;
import com.example.common.mapper.basics.SelectMapper;
import com.example.common.mapper.basics.UpdateMapper;
/**
* @param <T>
* @desc 基礎(chǔ)增刪改查功能mapper
*/
public interface CommonMapper<T> extends
DeleteMapper<T>,
InsertMapper<T>,
SelectMapper<T>,
UpdateMapper<T> {
}
6>實體對象
package com.example.dao.entity;
import lombok.Data;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
@Table(name="user")
@Data
public class User implements Serializable {
@Id
private Integer id;
private String trueName;
private String userName;
private Integer isDeleted;
}
7>讓自定義mapper繼承CommenMapper,就可以使用這些語句了
package com.example.dao.mapper;
import com.example.common.mapper.commonMapper.CommonMapper;
import com.example.dao.entity.User;
public interface UserMapper extends CommonMapper<User> {
}
接下來我們會對 CommonMapper中提供刪除方法的DeleteMapper進(jìn)行增強(qiáng),來給他添加邏輯刪除的語句
1>我們自定義的Provider需要去繼承 MapperTemplate并覆寫構(gòu)造方法
這個是源碼中的批量刪除
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package tk.mybatis.mapper.provider;
import java.util.Set;
import org.apache.ibatis.mapping.MappedStatement;
import tk.mybatis.mapper.MapperException;
import tk.mybatis.mapper.entity.EntityColumn;
import tk.mybatis.mapper.mapperhelper.EntityHelper;
import tk.mybatis.mapper.mapperhelper.MapperHelper;
import tk.mybatis.mapper.mapperhelper.MapperTemplate;
import tk.mybatis.mapper.mapperhelper.SqlHelper;
public class IdsProvider extends MapperTemplate {
public IdsProvider(Class<?> mapperClass, MapperHelper mapperHelper) {
super(mapperClass, mapperHelper);
}
public String deleteByIds(MappedStatement ms) {
Class<?> entityClass = this.getEntityClass(ms);
StringBuilder sql = new StringBuilder();
sql.append(SqlHelper.deleteFromTable(entityClass, this.tableName(entityClass)));
Set<EntityColumn> columnList = EntityHelper.getPKColumns(entityClass);
if (columnList.size() == 1) {
EntityColumn column = (EntityColumn)columnList.iterator().next();
sql.append(" where ");
sql.append(column.getColumn());
sql.append(" in (${_parameter})");
return sql.toString();
} else {
throw new MapperException("繼承 deleteByIds 方法的實體類[" + entityClass.getCanonicalName() + "]中必須只有一個帶有 @Id 注解的字段");
}
}
public String selectByIds(MappedStatement ms) {
Class<?> entityClass = this.getEntityClass(ms);
this.setResultType(ms, entityClass);
StringBuilder sql = new StringBuilder();
sql.append(SqlHelper.selectAllColumns(entityClass));
sql.append(SqlHelper.fromTable(entityClass, this.tableName(entityClass)));
Set<EntityColumn> columnList = EntityHelper.getPKColumns(entityClass);
if (columnList.size() == 1) {
EntityColumn column = (EntityColumn)columnList.iterator().next();
sql.append(" where ");
sql.append(column.getColumn());
sql.append(" in (${_parameter})");
return sql.toString();
} else {
throw new MapperException("繼承 selectByIds 方法的實體類[" + entityClass.getCanonicalName() + "]中必須只有一個帶有 @Id 注解的字段");
}
}
}
接下來我們對IdsProvider 進(jìn)行擴(kuò)展使他支持批量邏輯刪除
1>創(chuàng)建IdsProvider的子類
package com.example.common.mapper.defined;
import org.apache.ibatis.mapping.MappedStatement;
import tk.mybatis.mapper.MapperException;
import tk.mybatis.mapper.entity.EntityColumn;
import tk.mybatis.mapper.mapperhelper.EntityHelper;
import tk.mybatis.mapper.mapperhelper.MapperHelper;
import tk.mybatis.mapper.mapperhelper.SqlHelper;
import tk.mybatis.mapper.provider.IdsProvider;
import java.util.Set;
public class IdsProviderDefined extends IdsProvider {
public IdsProviderDefined(Class<?> mapperClass, MapperHelper mapperHelper) {
super(mapperClass, mapperHelper);
}
public String deleteShamByIds(MappedStatement ms) {
Class<?> entityClass = this.getEntityClass(ms);
StringBuilder sql = new StringBuilder();
sql.append(SqlHelper.updateTable(entityClass, this.tableName(entityClass)));
sql.append("<set> is_deleted='1' </set>");
Set<EntityColumn> columnList = EntityHelper.getPKColumns(entityClass);
if (columnList.size() == 1) {
EntityColumn column = (EntityColumn)columnList.iterator().next();
sql.append(" where ");
sql.append(column.getColumn());
sql.append(" in (${_parameter})");
return sql.toString();
} else {
throw new MapperException("繼承 deleteByIds 方法的實體類[" + entityClass.getCanonicalName() + "]中必須只有一個帶有 @Id 注解的字段");
}
}
}
deleteShamByIds就是我們門批量邏輯刪除的方法,這個方法其實就是再拼裝sql,tk中提供了很多幫助我們拼裝sql,和參數(shù)的類(SqlHelper,EntityHelper),我們可以使用
2>創(chuàng)建DeleteShamByIdsMapper接口
package com.example.common.mapper.defined;
import org.apache.ibatis.annotations.DeleteProvider;
public interface DeleteShamByIdsMapper<T> {
@DeleteProvider(
type = IdsProviderDefined.class,
method = "dynamicSQL"
)
int deleteShamByIds(String var1);
//這里的抽象方法的名稱必須和IdsProviderDefined 中的方法一致
}
3>在DeleteMapper中繼承我們自定義的邏輯刪除接口DeleteShamByIdsMapper
package com.example.common.mapper.basics;
import com.example.common.mapper.defined.DeleteShamByIdsMapper;
import tk.mybatis.mapper.common.Marker;
import tk.mybatis.mapper.common.base.delete.DeleteByPrimaryKeyMapper;
import tk.mybatis.mapper.common.condition.DeleteByConditionMapper;
import tk.mybatis.mapper.common.ids.DeleteByIdsMapper;
/**
* @desc 基礎(chǔ)刪除功能mapper
*
*/
public interface DeleteMapper<T> extends Marker,
tk.mybatis.mapper.common.base.delete.DeleteMapper<T>,
DeleteByPrimaryKeyMapper<T>,
DeleteByConditionMapper<T>,
DeleteByIdsMapper<T>,
DeleteShamByIdsMapper<T>{
}
到這里我們自定義的批量邏輯刪除就定義好了,我們可以通過這種方式擴(kuò)展通用mapper。
TK mybatis插件通用mapper與oracle的幾個坑
最近公司有幾個項目的數(shù)據(jù)庫用的oracle,有段時間沒用,然后果斷就掉坑里面了,記錄幾個比較有代表性的。
1:關(guān)于oracle的批量數(shù)據(jù)插入,我用TK通用mapper的insertList方法自動拼接出來的sql是這樣的
insert into table(column1,column2)values (value1,value2), (value3,value4)。。。
咋一看沒啥問題啊,然后就一直報 sql未正確結(jié)束,出于對自己sql的過于自信,導(dǎo)致一直都在思考是否是參數(shù)問題,浪費了好幾個小時,后來把sql拼好直接丟到數(shù)據(jù)庫跑了跑才發(fā)現(xiàn),這種sql確實是不能在oracle跑的,oracle批量插入的語法應(yīng)該是:
INSERT ALL INTO A(field_1,field_2) VALUES (value_1,value_2) INTO A(field_1,field_2) VALUES (value_3,value_4) INTO A(field_1,field_2) VALUES (value_5,value_6)
粗略的瞅了下TK的代碼,貌似確實沒有校驗數(shù)據(jù)庫驅(qū)動就直接開始拼sql了
public String insertList(MappedStatement ms) {
Class<?> entityClass = this.getEntityClass(ms);
StringBuilder sql = new StringBuilder();
sql.append(SqlHelper.insertIntoTable(entityClass, this.tableName(entityClass)));
sql.append(SqlHelper.insertColumns(entityClass, true, false, false));
sql.append(" VALUES ");
sql.append("<foreach collection=\"list\" item=\"record\" separator=\",\" >");
sql.append("<trim prefix=\"(\" suffix=\")\" suffixOverrides=\",\">");
Set<EntityColumn> columnList = EntityHelper.getColumns(entityClass);
Iterator var5 = columnList.iterator();
while(var5.hasNext()) {
EntityColumn column = (EntityColumn)var5.next();
if (!column.isId() && column.isInsertable()) {
sql.append(column.getColumnHolder("record") + ",");
}
}
sql.append("</trim>");
sql.append("</foreach>");
return sql.toString();
}
不過我覺得這種插件肯定是有兼容方案的,并且使用了這個插件跟使用源生mybatis并不沖突,所以實際對開發(fā)并沒有影響,算是復(fù)習(xí)一下oracle的基礎(chǔ)并提醒下自己,程序員還是老老實實先run起來,自以為是的經(jīng)驗有時候也很坑的。
2:關(guān)于oracle的比較符號(<> =)和null
先復(fù)習(xí)一下
一般數(shù)據(jù)庫查詢某某字段為空或者不為空的時候都是用is null或者is not null
如果查詢條件使用=null或者<>null是查不出來數(shù)據(jù)的
但是賦值卻可以用=null如
update BUS_PLATFORM.bus_trans set return_code = null where return_msg= 'tac驗證通過';
這個是可以執(zhí)行成功的,不過這個都是表外的值操作,那么當(dāng)表內(nèi)的數(shù)據(jù)有null時,又有啥坑呢,比如
select * from table1 where name <>'zhansan'
查詢表中name字段不為zhangsan的數(shù)據(jù),當(dāng)表中有name為null的數(shù)據(jù)時,這些數(shù)據(jù)也是查不出來的.比如表中有100條數(shù)據(jù),一條數(shù)據(jù)的name值為zhangsan ,九條數(shù)據(jù)的name值為null,那么這條sql只能查出90條數(shù)據(jù),所以如果確切的需求要查詢name值不為zhangsan的數(shù)據(jù)且包括name值為null的數(shù)據(jù),sql應(yīng)該為
select * from table1 where name <>'zhansan' or name is null
3:這個是mybatis的一個規(guī)范,基本是湊數(shù)的,不過也坑過我
先上內(nèi)容:mybatis插入null值時需要指定該值的類型(jdbctype),不然會報錯。
因為不指定類型的話mybatis會自己去適配匹配的數(shù)據(jù)庫字段類型,null適配不了。搞不定就報錯,很穩(wěn)。不過一般使用xml配置sql的話一般都不會有這個問題,寫的時候順手都會寫上。這個也是我使用TK的時候暴露出來的問題,在使用insert方法的時候有null就掛了,不過TK有個insertSelective方法做插入的時候會自動過濾掉空值。
以上為個人經(jīng)驗,希望能給大家一個參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java實現(xiàn)的文本字符串操作工具類實例【數(shù)據(jù)替換,加密解密操作】
這篇文章主要介紹了Java實現(xiàn)的文本字符串操作工具類,可實現(xiàn)數(shù)據(jù)替換、加密解密等操作,涉及java字符串遍歷、編碼轉(zhuǎn)換、替換等相關(guān)操作技巧,需要的朋友可以參考下2017-10-10
Java如何根據(jù)不同系統(tǒng)動態(tài)獲取換行符和盤分割符
這篇文章主要介紹了Java如何根據(jù)不同系統(tǒng)動態(tài)獲取換行符和盤分割符,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-12-12
JDK9為何要將String的底層實現(xiàn)由char[]改成了byte[]
String 類的源碼已經(jīng)由?char[]?優(yōu)化為了?byte[]?來存儲字符串內(nèi)容,為什么要這樣做呢?本文就詳細(xì)的介紹一下,感興趣的可以了解一下2022-03-03

