Java?mybatis?開(kāi)發(fā)自定義插件
介紹
MyBatis 允許你在映射語(yǔ)句執(zhí)行過(guò)程中的某一點(diǎn)進(jìn)行攔截調(diào)用。比如執(zhí)行前、執(zhí)行后或者對(duì)SQL結(jié)果集處理、sql入?yún)⑻幚淼?,這樣就可以在不修改mybatis源碼的情況下對(duì)sql執(zhí)行的過(guò)程或結(jié)果進(jìn)行修改,實(shí)現(xiàn)了解耦。mybatis 是在動(dòng)態(tài)代理的基礎(chǔ)上實(shí)現(xiàn)的。
使用場(chǎng)景
如果業(yè)務(wù)中需要設(shè)置一些通用數(shù)據(jù)庫(kù)操作,比如創(chuàng)建時(shí)間、創(chuàng)建人等通用字段又或者是分頁(yè)操作等,這類都可以使用插件開(kāi)發(fā)方式,PageHelper就是基于Interceptor的一個(gè)mybatis插件。
Interceptor攔截器
public interface Interceptor {
/**
* 子類攔截器必須要實(shí)現(xiàn)的方法,
* 在該方法對(duì)內(nèi)自定義攔截邏輯
* @param invocation
* @return
* @throws Throwable
*/
Object intercept(Invocation invocation) throws Throwable;
/**
生成目標(biāo)類的代理對(duì)象
* 也可以根據(jù)需求不返回代理對(duì)象,這種情況下這個(gè)攔截器將不起作用
* 無(wú)特殊情況使用默認(rèn)的即可
* @param target
* @return
*/
default Object plugin(Object target) {
return Plugin.wrap(target, this);
}
/**
* 設(shè)置變量
* 在注冊(cè)攔截器的時(shí)候設(shè)置變量,在這里可以獲取到
* @param properties
*/
default void setProperties(Properties properties) {
// NOP
}
}InterceptorChain攔截器鏈
在org.apache.ibatis.plugin包下有個(gè)InterceptorChain類,該類有個(gè)interceptors屬性,所有實(shí)現(xiàn)了Interceptor接口的攔截器都會(huì)被存儲(chǔ)到interceptors中。
源碼如下:
public class InterceptorChain {
private final List<Interceptor> interceptors = new ArrayList<>();
/**
* 讓目標(biāo)類在所有的攔截器中生成代理對(duì)象,并返回代理對(duì)象
* @param target
* @return
*/
public Object pluginAll(Object target) {
for (Interceptor interceptor : interceptors) {
target = interceptor.plugin(target);
}
return target;
}
/**
* 添加過(guò)濾器
* @param interceptor
*/
public void addInterceptor(Interceptor interceptor) {
interceptors.add(interceptor);
}
public List<Interceptor> getInterceptors() {
return Collections.unmodifiableList(interceptors);
}
}攔截方法
默認(rèn)情況下,MyBatis 允許使用插件來(lái)攔截Executor 、ParameterHandler 、ResultSetHandler 、StatementHandler 接口下面的方法。如果系統(tǒng)中有配置自定義插件,默認(rèn)情況下,系統(tǒng)會(huì)把上面四個(gè)類的默認(rèn)子類都作為目標(biāo)類來(lái)讓所有的攔截器進(jìn)行攔截, 以保證所有的攔截器都能對(duì)Executor 、ParameterHandler 、ResultSetHandler 、StatementHandler子類進(jìn)行攔截。
源碼如下: 在org.apache.ibatis.session.Configuration類中
public ParameterHandler newParameterHandler(MappedStatement mappedStatement, Object parameterObject, BoundSql boundSql) {
ParameterHandler parameterHandler = mappedStatement.getLang().createParameterHandler(mappedStatement, parameterObject, boundSql);
// 使用攔截器進(jìn)行攔截
parameterHandler = (ParameterHandler) interceptorChain.pluginAll(parameterHandler);
return parameterHandler;
}
public ResultSetHandler newResultSetHandler(Executor executor, MappedStatement mappedStatement, RowBounds rowBounds, ParameterHandler parameterHandler,
ResultHandler resultHandler, BoundSql boundSql) {
ResultSetHandler resultSetHandler = new DefaultResultSetHandler(executor, mappedStatement, parameterHandler, resultHandler, boundSql, rowBounds);
// 使用攔截器進(jìn)行攔截
resultSetHandler = (ResultSetHandler) interceptorChain.pluginAll(resultSetHandler);
return resultSetHandler;
}
public StatementHandler newStatementHandler(Executor executor, MappedStatement mappedStatement, Object parameterObject, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) {
StatementHandler statementHandler = new RoutingStatementHandler(executor, mappedStatement, parameterObject, rowBounds, resultHandler, boundSql);
// 使用攔截器進(jìn)行攔截
statementHandler = (StatementHandler) interceptorChain.pluginAll(statementHandler);
return statementHandler;
}
public Executor newExecutor(Transaction transaction) {
return newExecutor(transaction, defaultExecutorType);
}
public Executor newExecutor(Transaction transaction, ExecutorType executorType) {
executorType = executorType == null ? defaultExecutorType : executorType;
executorType = executorType == null ? ExecutorType.SIMPLE : executorType;
Executor executor;
if (ExecutorType.BATCH == executorType) {
executor = new BatchExecutor(this, transaction);
} else if (ExecutorType.REUSE == executorType) {
executor = new ReuseExecutor(this, transaction);
} else {
executor = new SimpleExecutor(this, transaction);
}
if (cacheEnabled) {
executor = new CachingExecutor(executor);
}
// 使用攔截器進(jìn)行攔截
executor = (Executor) interceptorChain.pluginAll(executor);
return executor;
}注解
Intercepts
Intercepts的作用是攔截Signature注解數(shù)組中指定的類的方法。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface Intercepts {
/**
* Returns method signatures to intercept.
* Signature注解列表
* @return method signatures
*/
Signature[] value();
}Signature
Signature注解作用是攔截指定類的方法。
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({})
public @interface Signature {
/**
* Returns the java type.
* 要攔截的類
* @return the java type
*/
Class<?> type();
/**
* Returns the method name.
* 要攔截的類的方法
* @return the method name
*/
String method();
/**
* Returns java types for method argument.
* 要攔截的類的方法的參數(shù)列表
* @return java types for method argument
*/
Class<?>[] args();
}示例
步驟
- 1、實(shí)現(xiàn)org.apache.ibatis.plugin.Interceptor接口
- 2、添加Intercepts和Signature注解
- 3、根據(jù)需求實(shí)現(xiàn)Interceptor方法邏輯
入門使用
這里會(huì)寫兩個(gè)使用示例,一個(gè)是動(dòng)態(tài)給屬性賦值,一個(gè)是打印SQL。
表結(jié)構(gòu):
CREATE TABLE `users` ( `id` bigint(20) NOT NULL AUTO_INCREMENT, `gender` varchar(20) DEFAULT NULL, `userName` text NOT NULL, `create_date` datetime DEFAULT NULL COMMENT '創(chuàng)建日期', PRIMARY KEY (`id`) ) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;
實(shí)體類:
public class UserInfo {
private Long id;
private String gender;
private String userName;
private Date createDate;
// 省略get、set方法
}動(dòng)態(tài)給屬性賦值
在創(chuàng)建表時(shí),有些是每個(gè)表都有的參數(shù),比如創(chuàng)建時(shí)間、修改時(shí)間等,這類參數(shù)如果在每個(gè)類進(jìn)行保存或修改的時(shí)候都進(jìn)行設(shè)值的話就有點(diǎn)重復(fù)操作了,所以可以通過(guò)mybatis插件進(jìn)行處理。
1、Interceptor 實(shí)現(xiàn)類InsertInterceptor:
@Intercepts({
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class InsertInterceptor implements Interceptor {
private Properties properties;
@Override
public Object intercept(Invocation invocation) throws Throwable {
final Object[] args = invocation.getArgs();
MappedStatement mappedStatement= (MappedStatement) args[0];
Object parameter = args[1];
Executor executor = (Executor) invocation.getTarget();
final Class<?> parameterClass = parameter.getClass();
final String createDate = properties.getProperty("createDate");
//獲取createDate 屬性描述器
final PropertyDescriptor propertyDescriptor = new PropertyDescriptor(createDate , parameterClass);
//獲取createDate 寫方法
final Method writeMethod = propertyDescriptor.getWriteMethod();
//調(diào)用createDate 寫方法
writeMethod.invoke(parameter , new Date());
return executor.update(mappedStatement, parameter);
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target , this);
}
/**
* 設(shè)置變量
*
* @param properties
*/
@Override
public void setProperties(Properties properties) {
this.properties = properties;
}
}2、mybatis配置文件中注冊(cè)InsertInterceptor
<plugins>
<plugin interceptor="plugin.PrintSqlPlugin"/>
<plugin interceptor="plugin.InsertInterceptor">
<property name="createDate" value="createDate"/>
</plugin>
</plugins>3、測(cè)試
public class UserTest {
private final static SqlSessionFactory sqlSessionFactory;
static {
String resource = "mybatis-config.xml";
Reader reader = null;
try {
reader = Resources.getResourceAsReader(resource);
} catch (IOException e) {
System.out.println(e.getMessage());
}
sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
}
@Test
public void insert(){
SqlSession sqlSession = sqlSessionFactory.openSession();
UserInfo userInfo = new UserInfo();
userInfo.setUserName("test1");
userInfo.setGender("male");
UserInfoMapper mapper = sqlSession.getMapper(UserInfoMapper.class);
mapper.insertUser(userInfo);
sqlSession.commit();
sqlSession.close();
}
}查看數(shù)據(jù)庫(kù),可以看到在沒(méi)有給createDate屬性收到賦值的情況下,通過(guò)攔截器進(jìn)行賦值,最后是保存到數(shù)據(jù)庫(kù)中了。

打印SQL
Interceptor 實(shí)現(xiàn)類PrintSqlPlugin:
@Intercepts({
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}),
@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class, CacheKey.class, BoundSql.class}),
@Signature(type = Executor.class, method = "update", args = {MappedStatement.class, Object.class})
})
public class PrintSqlPlugin implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
//被代理對(duì)象
Object target = invocation.getTarget();
//代理方法
Method method = invocation.getMethod();
//方法參數(shù)
Object[] args = invocation.getArgs();
MappedStatement mappedStatement= (MappedStatement) args[0];
Object parameter = args[1];
final BoundSql mappedStatementBoundSql = mappedStatement.getBoundSql(parameter);
System.err.println("BoundSql="+mappedStatementBoundSql.getSql());
final Configuration configuration = mappedStatement.getConfiguration();
final String showSql = showSql(configuration, mappedStatementBoundSql);
System.err.println("sql="+showSql);
//方法執(zhí)行
final Object returnValue = invocation.proceed();
return returnValue;
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
/**
* 獲取參數(shù)
* @param obj
* @return
*/
private static String getParameterValue(Object obj) {
String value = null;
if (obj instanceof String) {
value = "'" + obj.toString() + "'";
value = value.replaceAll("\\\\", "\\\\\\\\");
value = value.replaceAll("\\$", "\\\\\\$");
} else if (obj instanceof Date) {
DateFormat formatter = DateFormat.getDateTimeInstance(DateFormat.DEFAULT, DateFormat.DEFAULT, Locale.CHINA);
value = "'" + formatter.format(obj) + "'";
} else {
if (obj != null) {
value = obj.toString();
} else {
value = "";
}
}
return value;
}
/**
* 打印SQL
* @param configuration
* @param boundSql
* @return
*/
public static String showSql(Configuration configuration, BoundSql boundSql) {
Object parameterObject = boundSql.getParameterObject();
List<ParameterMapping> parameterMappings = boundSql.getParameterMappings();
String sql = boundSql.getSql().replaceAll("[\\s]+", " ");
if (parameterMappings.size() > 0 && parameterObject != null) {
TypeHandlerRegistry typeHandlerRegistry = configuration.getTypeHandlerRegistry();
if (typeHandlerRegistry.hasTypeHandler(parameterObject.getClass())) {
sql = sql.replaceFirst("\\?", getParameterValue(parameterObject));
} else {
MetaObject metaObject = configuration.newMetaObject(parameterObject);
for (ParameterMapping parameterMapping : parameterMappings) {
String propertyName = parameterMapping.getProperty();
if (metaObject.hasGetter(propertyName)) {
Object obj = metaObject.getValue(propertyName);
sql = sql.replaceFirst("\\?", getParameterValue(obj));
} else if (boundSql.hasAdditionalParameter(propertyName)) {
Object obj = boundSql.getAdditionalParameter(propertyName);
sql = sql.replaceFirst("\\?", getParameterValue(obj));
}
}
}
}
return sql;
}
}使用同樣的方法進(jìn)行測(cè)試,查看控制臺(tái)打印結(jié)果:
BoundSql=insert into users (gender, userName ,create_date) values(? , ?, ?)
sql=insert into users (gender, userName ,create_date) values('male' , 'test2', '2022-1-14 18:40:08')
mybatis自定義插就到這里了,其實(shí)操作也簡(jiǎn)單,用好了也很強(qiáng)大。
到此這篇關(guān)于Java mybatis 開(kāi)發(fā)自定義插件的文章就介紹到這了,更多相關(guān)Java mybatis 內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java系統(tǒng)運(yùn)行緩慢等問(wèn)題的排查思路
這篇文章主要介紹了Java系統(tǒng)運(yùn)行緩慢等問(wèn)題的排查思路,讀者可以根據(jù)具體情況具體分析,從而解決問(wèn)題2021-04-04
SpringCloud Finchley+Spring Boot 2.0 集成Consul的方法示例(1.2版本)
這篇文章主要介紹了SpringCloud Finchley+Spring Boot 2.0 集成Consul的方法示例(1.2版本),小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-08-08
javafx tableview鼠標(biāo)觸發(fā)更新屬性詳解
這篇文章主要為大家詳細(xì)介紹了javafx tableview鼠標(biāo)觸發(fā)更新屬性的相關(guān)資料,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-08-08
Java如何通過(guò)線程解決生產(chǎn)者/消費(fèi)者問(wèn)題
這篇文章主要介紹了Java如何通過(guò)線程解決生產(chǎn)者/消費(fèi)者問(wèn)題,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下2020-10-10
Springmvc國(guó)際化自動(dòng)配置代碼實(shí)現(xiàn)
這篇文章主要介紹了Springmvc國(guó)際化自動(dòng)配置代碼實(shí)現(xiàn),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-04-04
解決String字符串轉(zhuǎn)JSONObject順序不對(duì)的問(wèn)題
這篇文章主要介紹了解決String字符串轉(zhuǎn)JSONObject順序不對(duì)的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-12-12

