Mybatis sql改寫

Mybatis sql改寫

    近期有項目須要,須要把update轉化成insert,網上搜索了下發現mybatis的攔截器能夠實現該功能。java

1、mybatis攔截器

    實現一個攔截器攔截全部update方法spring

import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.ParameterMapping;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.mapping.SqlSource;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;

/**
* 實現接口Interceptor
* 加入註解 @Intercepts 該註解的使用要注意,其中的@Signature 有三個參數,具體是使用情搜索
**/
@Intercepts(@Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }))
public class UpdateToInsertInterceptor implements Interceptor{
    

	// 爲了在update轉成insert時,主鍵重複的問題,這裏去掉update時的自增主鍵
	private List<String> excludeField = new ArrayList<String>(Arrays.asList("id"));

	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		//mapper.update(invocation.getArgs()[]);
		// 攔截sql
		Object[] args = invocation.getArgs();
		MappedStatement statement = (MappedStatement) args[0];
		if (!isUpdate(statement)) {
			return invocation.proceed();
		}
		Object parameterObject = args[1];
		BoundSql boundSql = statement.getBoundSql(parameterObject);
		String sql = boundSql.getSql();
		if (StringUtils.isBlank(sql)) {
			return invocation.proceed();
		}
		// update轉換成insert
		// sql交由處理類處理 對sql語句進行處理 此處是範例 不作任何處理
		String tableName = findTable(sql);
		List<String> paramNames = findParams(boundSql.getParameterMappings());
		String sql2Reset = buildSql(tableName, paramNames);
		// 包裝sql後,重置到invocation中
		resetSql2Invocation(invocation, sql2Reset);

		return invocation.proceed();

	}

	public boolean isExcludeField(String fieldName) {
		return excludeField.contains(fieldName.toLowerCase());
	}

	private String buildSql(String tableName, List<String> paramNames) {
		StringBuilder sb = new StringBuilder("INSERT INTO ");
		sb.append(tableName);
		sb.append("(");
		int excludeCounter = 0;
		for (int i = 0; i < paramNames.size(); i++) {
			String paramName = paramNames.get(i);
			if (isExcludeField(paramName)) {
				excludeCounter++;
				break;
			}
			if (i == 0) {
				sb.append(paramName);
			} else {
				sb.append(",").append(paramName);
			}
		}
		sb.append(") values (");
		int counter = paramNames.size() - excludeCounter;
		for (int i = 0; i < counter; i++) {
			if (i == 0) {
				sb.append("?");
			} else {
				sb.append(",").append("?");
			}
		}
		sb.append(")");
		return sb.toString();

	}

	private List<String> findParams(List<ParameterMapping> list) {
		List<String> params = new ArrayList<>();
		for (ParameterMapping parameterMapping : list) {
			params.add(parameterMapping.getProperty());
		}
		return params;
	}

	private String findTable(String sql) {
		String tmp = sql.trim().toUpperCase();
		String table = tmp.substring(tmp.indexOf("UPDATE") + 6, tmp.indexOf("SET")).trim();
		return table;
	}

	private void resetSql2Invocation(Invocation invocation, String sql) throws SQLException {
		final Object[] args = invocation.getArgs();
		MappedStatement statement = (MappedStatement) args[0];
		Object parameterObject = args[1];
		final BoundSql boundSql = statement.getBoundSql(parameterObject);

		// 從新new一個查詢語句對像
		BoundSql newBoundSql = new BoundSql(statement.getConfiguration(), sql, boundSql.getParameterMappings(),
				parameterObject);
		// 把新的查詢放到statement裏
		MappedStatement newStatement = copyFromMappedStatement(statement, new BoundSqlSqlSource(newBoundSql));

		for (ParameterMapping mapping : boundSql.getParameterMappings()) {
			String prop = mapping.getProperty();
			if (isExcludeField(prop)) {
				break;
			}
			if (boundSql.hasAdditionalParameter(prop)) {
				newBoundSql.setAdditionalParameter(prop, boundSql.getAdditionalParameter(prop));
			}
		}

		// 去除指定的列
		newBoundSql.getParameterMappings().removeIf(e -> isExcludeField(e.getProperty().toLowerCase()));
		args[0] = newStatement;
	}

	private MappedStatement copyFromMappedStatement(MappedStatement ms, SqlSource newSqlSource) {
		MappedStatement.Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms.getId(), newSqlSource,
				ms.getSqlCommandType());
		builder.resource(ms.getResource());
		builder.fetchSize(ms.getFetchSize());
		builder.statementType(ms.getStatementType());
		builder.keyGenerator(ms.getKeyGenerator());
		if (ms.getKeyProperties() != null && ms.getKeyProperties().length != 0) {
			StringBuilder keyProperties = new StringBuilder();
			for (String keyProperty : ms.getKeyProperties()) {
				keyProperties.append(keyProperty).append(",");
			}
			keyProperties.delete(keyProperties.length() - 1, keyProperties.length());
			builder.keyProperty(keyProperties.toString());
		}
		builder.timeout(ms.getTimeout());
		builder.parameterMap(ms.getParameterMap());
		builder.resultMaps(ms.getResultMaps());
		builder.resultSetType(ms.getResultSetType());
		builder.cache(ms.getCache());
		builder.flushCacheRequired(ms.isFlushCacheRequired());
		builder.useCache(ms.isUseCache());

		return builder.build();
	}

	private boolean isUpdate(MappedStatement ms) {
		SqlCommandType commondType = ms.getSqlCommandType();
		if (commondType.compareTo(SqlCommandType.UPDATE) == 0) {
			return true;
		}
		return false;
	}

	@Override
	public Object plugin(Object target) {
		return Plugin.wrap(target, this);
	}

	@Override
	public void setProperties(Properties properties) {
		// TODO Auto-generated method stub

	}

	class BoundSqlSqlSource implements SqlSource {
		private BoundSql boundSql;

		public BoundSqlSqlSource(BoundSql boundSql) {
			this.boundSql = boundSql;
		}

		@Override
		public BoundSql getBoundSql(Object parameterObject) {
			return boundSql;
		}
	}


}

 

2、配置攔截器

    在sqlsessionfactorybean 中配置攔截器sql

<!--3 會話工廠bean sqlSessionFactoryBean -->
	<bean id="sqlSessionFactoryMybatis"
		class="org.mybatis.spring.SqlSessionFactoryBean">
		<!-- 數據源 -->
		<property name="dataSource" ref="dataSource"></property>
		<!-- 別名 -->
		<property name="typeAliasesPackage" value="com.ck.demo.model"></property>
		<!-- sql映射文件路徑 -->
		<property name="mapperLocations"
			value="classpath:conf/mapper/*.xml"></property>
		<property name="configLocation"
			value="classpath:conf/db/mybatis-config.xml" />
        
        <!-- 在這裏加入sql攔截器 -->
		<property name="plugins">
			<list>
				<bean
					class="com.ck.demo.dao.interceptor.UpdateToInsertInterceptor"></bean>
			</list>
		</property>
	</bean>

3、遺留問題

    將update轉換成insert會面臨主鍵重複的問題,所以目標表上須要處理這個問題,能夠考慮新增將自增主鍵一列去掉,用業務序列號來作主鍵。apache

相關文章
相關標籤/搜索