近期有項目須要,須要把update轉化成insert,網上搜索了下發現mybatis的攔截器能夠實現該功能。java
實現一個攔截器攔截全部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; } } }
在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>
將update轉換成insert會面臨主鍵重複的問題,所以目標表上須要處理這個問題,能夠考慮新增將自增主鍵一列去掉,用業務序列號來作主鍵。apache