MybatisPlus?自定義插件實現(xiàn)攔截SQL修改功能(實例詳解)
最近項目內(nèi)使用MybatisPlus整合Phoenix實現(xiàn)對HBase進行操作,但是Phoenix的sql語法和MySQL不太一樣,導(dǎo)致得在列上加@TableField申明列簇名稱和列名稱,不太友好,所以自己寫了個插件攔截sql并進行修改
package org.gjw.config;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import net.sf.jsqlparser.expression.Alias;
import net.sf.jsqlparser.expression.BinaryExpression;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Function;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.delete.Delete;
import net.sf.jsqlparser.statement.insert.Insert;
import net.sf.jsqlparser.statement.select.*;
import net.sf.jsqlparser.statement.update.Update;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.List;
import java.util.stream.Collectors;
/**
* @author guojunwang
* @date 2021-11-29 17:06
*/
public class PhoenixMPPlugin extends JsqlParserSupport implements InnerInterceptor {
/**
* 查詢時處理邏輯
*/
@Override
public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
//通過 JSqlParser工具修改查詢sql后執(zhí)行
mpBs.sql(parserSingle(mpBs.sql(), null));
}
/**
* 增刪改時 處理邏輯
*/
@Override
public void beforePrepare(StatementHandler sh, Connection connection, Integer transactionTimeout) {
PluginUtils.MPStatementHandler mpSh = PluginUtils.mpStatementHandler(sh);
MappedStatement ms = mpSh.mappedStatement();
SqlCommandType sct = ms.getSqlCommandType();
//增刪改調(diào)用 JSqlParser工具修改sql后執(zhí)行
if (sct == SqlCommandType.INSERT || sct == SqlCommandType.UPDATE || sct == SqlCommandType.DELETE) {
PluginUtils.MPBoundSql mpBs = mpSh.mPBoundSql();
mpBs.sql(parserMulti(mpBs.sql(), null));
}
}
/**
* 以處理查詢sql為例,增刪改的實現(xiàn)可根據(jù)自己的業(yè)務(wù)實現(xiàn)
*/
@Override
protected void processSelect(Select select, int index, String sql, Object obj) {
//此處處理select邏輯 將字符串拼接上 雙引號
SelectBody selectBody = select.getSelectBody();
if(selectBody instanceof PlainSelect) reformatPlainSelect((PlainSelect) selectBody);
}
@Override
protected void processInsert(Insert insert, int index, String sql, Object obj) {
System.out.println( "新增前調(diào)用,可修改sql" );
}
@Override
protected void processDelete(Delete delete, int index, String sql, Object obj) {
System.out.println( "刪除前調(diào)用,可修改sql" );
}
@Override
protected void processUpdate(Update update, int index, String sql, Object obj) {
System.out.println("修改調(diào)用,可修改sql");
}
//---------------以下為處理sql操作,根據(jù)自己業(yè)務(wù)功能完善
/**
* 處理查詢字段
*/
private List<SelectItem> disposeSelectColumn(List<SelectItem> selectItems){
return selectItems.stream().map( this::resetSelectItem ).collect(Collectors.toList());
}
private SelectItem resetSelectItem( SelectItem selectItem ){
//如果不符合直接返回
if( !(selectItem instanceof SelectExpressionItem) ) return selectItem;
SelectExpressionItem item = (SelectExpressionItem)selectItem;
//如果是列
if( item.getExpression() instanceof Column ){
Column columnExp = (Column)item.getExpression();
return new SelectExpressionItem( reFormatSelectColumn( columnExp,item.getAlias() ) );
}
//如果是函數(shù)
if( item.getExpression() instanceof Function){
Function function = (Function) item.getExpression();
return new SelectExpressionItem( reFormatFunction( function ) );
}
return item;
}
/**
* 重新格式化 查詢語句
* @param plainSelect 查詢語句
* @return 格式化的查詢語句
*/
public void reformatPlainSelect(PlainSelect plainSelect){
//處理要查詢的字段
List<SelectItem> selectItems = plainSelect.getSelectItems();
//處理查詢條件
plainSelect.setSelectItems( disposeSelectColumn( selectItems ) );
//處理 where 條件
plainSelect.setWhere( disposeSelectWhere( plainSelect.getWhere() ) );
}
/**
* 重新格式化列
* @param columnExp 列
* @param alias 列別名
* @return 格式化的列
*/
private Column reFormatSelectColumn( Column columnExp,Alias alias ){
if( columnExp == null ) return columnExp;
//表名和列簇名會在一起
String tableAndCFName= columnExp.getTable() == null ? "" : columnExp.getTable().toString();
//字段名
String columnName= columnExp.getColumnName();
//根據(jù) `.` 分隔方便處理表名和列簇名
String[] tableAndCFInfo = tableAndCFName.split("\\.");
// 可能會出現(xiàn)很多情況 列名 列簇.列名 表名.列簇.列名 表名.列名
String tableName = tableAndCFInfo[0];
String cf = tableAndCFInfo[tableAndCFInfo.length - 1];
//如果表名和字段名相等,只有3種情況: 列名 表名.列名 列簇.列名
if( StrUtil.equals(tableName,cf) && StrUtil.isNotBlank(tableName) ){
//判斷前綴是表名還是列名 要求列簇必須全大寫 表名不能全大寫
//如果全大寫這是列簇名
if( StrUtil.equals(cf.toUpperCase(),cf) ) {
tableName = "";
}else cf = ""; //否則是表名
}
StringBuilder finalName = new StringBuilder();
//如果表名不為空 拼接表名
if( StrUtil.isNotBlank( tableName ) ) finalName.append( tableName ).append( "." );
//如果列簇名不為空 拼接列簇名
if( StrUtil.isNotBlank( cf ) ) finalName.append( appendPrefixAndSuffix(cf) ).append(".");
//拼接字段名
finalName.append( appendPrefixAndSuffix(columnName) );
//拼接別名: as xxx
if( alias !=null ) finalName.append(" ").append( alias.getName() );
//重新格式化列名 封裝返回
return new Column( finalName.toString() );
}
/**
* 重新格式化查詢函數(shù)
* @param function 函數(shù)
* @return 格式化的函數(shù)
*/
private Function reFormatFunction( Function function ){
List<Expression> expressions = function.getParameters().getExpressions();
//對于是列的參數(shù)進行格式化
expressions = expressions.stream().map(exp -> {
if (exp instanceof Column) return reFormatSelectColumn((Column) exp, null);
return exp;
}).collect(Collectors.toList());
//重新設(shè)置回去
function.getParameters().setExpressions(expressions);
return function;
}
/**
* 重新格式化子查詢
* @param subSelect 子查詢
* @return 格式化的函數(shù)
*/
private SubSelect reFormatSubSelect( SubSelect subSelect ){
if( subSelect.getSelectBody() instanceof PlainSelect ){
reformatPlainSelect( (PlainSelect)subSelect.getSelectBody() );
}
return subSelect;
}
public Expression disposeSelectWhere(Expression expression){
if( !(expression instanceof BinaryExpression) ) return expression;
BinaryExpression binaryExpression =(BinaryExpression)expression;
//如果左邊還是多條件的
if( binaryExpression.getLeftExpression() instanceof BinaryExpression){
disposeSelectWhere( binaryExpression.getLeftExpression() );
}
//如果右邊還是多條件的
if( binaryExpression.getRightExpression() instanceof BinaryExpression){
disposeSelectWhere( binaryExpression.getRightExpression() );
}
//如果左邊表達式是列信息 格式化
if( binaryExpression.getLeftExpression() instanceof Column ){
Column newColumn = reFormatSelectColumn((Column) binaryExpression.getLeftExpression(), null);
binaryExpression.setLeftExpression( newColumn );
}
//如果左邊表達式是 子查詢 processPlainSelect
if(binaryExpression.getLeftExpression() instanceof SubSelect){
SubSelect subSelect = (SubSelect)binaryExpression.getLeftExpression();
if( subSelect.getSelectBody() instanceof PlainSelect ){
reformatPlainSelect( (PlainSelect)subSelect.getSelectBody() );
}
}
//如果右邊是列信息 格式化
if( binaryExpression.getRightExpression() instanceof Column ){
Column newColumn = reFormatSelectColumn((Column) binaryExpression.getLeftExpression(), null);
binaryExpression.setRightExpression( newColumn );
}
//如果右邊表達式是 子查詢 processPlainSelect
if( binaryExpression.getRightExpression() instanceof SubSelect){
SubSelect subSelect = (SubSelect)binaryExpression.getRightExpression();
reFormatSubSelect( subSelect );
}
return binaryExpression;
}
private String appendPrefixAndSuffix(String str){
final String PREFIX = "\"";
final String SUFFIX = "\"";
//如果已經(jīng)有前綴了直接返回
if( str.contains(PREFIX) ) return str;
//拼接前綴和后綴
return new StringBuilder().append(PREFIX).append(str).append(SUFFIX).toString();
}
}使用: 編寫配置類配置MybatisPlus并設(shè)置插件
@MapperScan(value = "org.gjw.mapper.phoenix",sqlSessionTemplateRef = "phoenixSqlSessionTemplate",sqlSessionFactoryRef = "phoenixSqlSessionFactory")
@Configuration
public class PhoenixConfig {
@Bean
@ConfigurationProperties("spring.datasource.phoenix")
public DataSource phoenixDataSource(){
return new HikariDataSource();
}
@Bean
public SqlSessionFactory phoenixSqlSessionFactory( @Qualifier("phoenixDataSource") @Autowired DataSource phoenixDataSource) throws Exception {
SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
sqlSessionFactoryBean.setDataSource( phoenixDataSource() );
sqlSessionFactoryBean.setMapperLocations( new PathMatchingResourcePatternResolver().getResources("classpath*:/phoenixMapper/**/*.xml"));
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor( new PhoenixMPPlugin() );
sqlSessionFactoryBean.setPlugins( interceptor );
MybatisConfiguration mybatisConfiguration = new MybatisConfiguration();
mybatisConfiguration.setMapUnderscoreToCamelCase(true);
mybatisConfiguration.setLogImpl(StdOutImpl.class);
sqlSessionFactoryBean.setConfiguration(mybatisConfiguration);
return sqlSessionFactoryBean.getObject();
}
@Bean
public SqlSessionTemplate phoenixSqlSessionTemplate( @Qualifier("phoenixSqlSessionFactory") @Autowired SqlSessionFactory phoenixSqlSessionFactory){
return new SqlSessionTemplate( phoenixSqlSessionFactory );
}
@Bean
public DataSourceTransactionManager phoenixDataSourceTransactionManager(@Qualifier("phoenixDataSource") @Autowired DataSource phoenixDataSource){
return new DataSourceTransactionManager(phoenixDataSource);
}
}到此這篇關(guān)于MybatisPlus 自定義插件實現(xiàn)攔截SQL修改功能的文章就介紹到這了,更多相關(guān)MybatisPlus 攔截SQL內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MyBatis自定義映射關(guān)系和關(guān)聯(lián)查詢實現(xiàn)方法詳解
這篇文章主要介紹了MyBatis自定義映射關(guān)系和關(guān)聯(lián)查詢實現(xiàn)方法,當POJO屬性名與數(shù)據(jù)庫列名不一致時,需要自定義實體類和結(jié)果集的映射關(guān)系,在MyBatis注解開發(fā)中,使用@Results定義并使用自定義映射,使用 @ResultMap使用自定義映射2023-04-04
SpringBoot中dubbo+zookeeper實現(xiàn)分布式開發(fā)的應(yīng)用詳解
這篇文章主要介紹了SpringBoot中dubbo+zookeeper實現(xiàn)分布式開發(fā)的應(yīng)用詳解,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-11-11
解決Spring配置文件中bean的property屬性中的name出錯問題
這篇文章主要介紹了解決Spring配置文件中bean的property屬性中的name出錯問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2021-07-07
SpringBoot統(tǒng)一數(shù)據(jù)返回的幾種方式
在Web應(yīng)用程序開發(fā)中,統(tǒng)一數(shù)據(jù)返回格式對于前后端分離項目尤為重要,本文就來介紹一下SpringBoot統(tǒng)一數(shù)據(jù)返回的幾種方式,具有一定的參考價值,感興趣的可以了解一下2024-07-07
SSH框架網(wǎng)上商城項目第20戰(zhàn)之在線支付平臺
這篇文章主要為大家詳細介紹了SSH框架網(wǎng)上商城項目第20戰(zhàn)之在線支付平臺,關(guān)于第三方支付的內(nèi)容從本文開始,感興趣的小伙伴們可以參考一下2016-06-06
深入講解java線程與synchronized關(guān)鍵字
Java 中多線程的同步依靠的是對象鎖機制,synchronized關(guān)鍵字就是利用了封裝對象鎖來實現(xiàn)對共享資源的互斥訪問。下面這篇文章主要介紹了java線程與synchronized關(guān)鍵字的相關(guān)資料,需要的朋友可以參考下。2017-03-03
Java Collection和Collections的區(qū)別
本文主要介紹了Java Collection和Collections的區(qū)別,Collection?是表示集合的接口,而?Collections?是對集合進行操作的工具類,下面就來介紹一下具體用法,感興趣的可以了解一下2023-12-12

