【Java代码】使用 org.apache.ibatis.plugin.Interceptor 拦截器实现全局 mapper.xml 参数注入(可用于切换数据库实例schema+Demo举例源码)
1. why
??項目沒有使用MyBatis,進行數(shù)據(jù)操作時使用的是jdbc中默認的schema,現(xiàn)在項目要加入多租戶,同一個數(shù)據(jù)庫下不同租戶使用不同的實例schema,這就要在mapper文件內所有的表名稱前加上schema,并動態(tài)傳遞其參數(shù)值,這樣每個SQL都要添加這個參數(shù),如果傳遞的是對象,也要給對象加相應的屬性,這個工作量可想而知。
??必須想辦法,將schema參數(shù)傳遞給所有的SQL,辦法就是全局配置。
2. code
2.1 實現(xiàn)攔截器接口并重寫方法
攔截器【 1?? SchemaInterceptor】實現(xiàn)(如果用戶調用的SQL接口里沒有傳 schemaName 就用默認值):
@Intercepts({@Signature(method = "update",args = {MappedStatement.class, Object.class}, type = Executor.class),@Signature(method = "query",args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class}, type = Executor.class) }) public class SchemaInterceptor implements Interceptor {/*** 這里沒有打印日志*/private static Log LOG = LogFactory.getLog(SchemaInterceptor.class);/*** mapper.xml 使用SCHEMA時的參數(shù)名稱*/private static final String SCHEMA = "schemaName";/*** 設置默認的schema*/private String schema = "public";/*** 攔截到的動態(tài)SQL處理后放入此對象*/private Set<Integer> sourceStorage = new HashSet<>();@Overridepublic Object intercept(Invocation invocation) throws Throwable {Object[] args = invocation.getArgs();MappedStatement mappedStatement = (MappedStatement) args[0];SqlSource sqlSource = mappedStatement.getSqlSource();// 只攔截動態(tài)SQLif (sqlSource instanceof DynamicSqlSource) {// 獲取到sqlNode對象Field field = DynamicSqlSource.class.getDeclaredField("rootSqlNode");field.setAccessible(true);SqlNode sqlNode = (SqlNode) field.get(sqlSource);if (!sourceStorage.contains(sqlSource.hashCode())) {// 獲取動態(tài)代理對象Map<String, Object> argMap = (HashMap<String, Object>) args[1];// 判斷是否傳遞 schemaName或schema 如果已經傳遞則使用用戶傳遞的值 否則使用默認值String schemaNameStr = "schemaName", schemaStr = "schema";if (StringUtils.isEmpty(MapUtils.getString(argMap, schemaNameStr)) && StringUtils.isEmpty(MapUtils.getString(argMap, schemaStr))) {SqlNode proxyNode = proxyNode(sqlNode);field.set(sqlSource, proxyNode);}sourceStorage.add(sqlSource.hashCode());}}return invocation.proceed();}/*** 通過動態(tài)代理對象 添加schema參數(shù)** @param sqlNode SQL節(jié)點* @return SqlNode*/private SqlNode proxyNode(SqlNode sqlNode) {return (SqlNode) Proxy.newProxyInstance(sqlNode.getClass().getClassLoader(),new Class[]{SqlNode.class}, new SqlNodeInvocationHandler(sqlNode));}@Overridepublic Object plugin(Object target) {return Plugin.wrap(target, this);}@Overridepublic void setProperties(Properties properties) {LOG.debug("setProperties====>" + properties);}private class SqlNodeInvocationHandler implements InvocationHandler {private SqlNode target;SqlNodeInvocationHandler(SqlNode target) {super();this.target = target;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {DynamicContext context = (DynamicContext) args[0];// 給schema添加.setSchema(schema);// 設置schemacontext.getBindings().put(SCHEMA, schema);return method.invoke(target, args);}}/*** 給schema 添加.** @param schema schemaName*/private void setSchema(String schema) {String pointStr = ".";if (StringUtils.isNotBlank(schema)) {if (!schema.endsWith(pointStr)) {schema += pointStr;}}this.schema = schema;} }2.2 攔截器注冊
攔截器【 2?? 注冊】給 SqlSessionFactoryBean 添加插件:
@Bean(name = "sqlSessionFactory_greenplum")@Primarypublic SqlSessionFactory sqlSessionFactory(@Qualifier("dataSource_greenplum") DataSource dataSource)throws Exception {final SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();// 設置數(shù)據(jù)源參數(shù)sessionFactory.setDataSource(dataSource);// mapper路徑和mybatis配置文件路徑sessionFactory.setMapperLocations(new PathMatchingResourcePatternResolver().getResources(GreenplumConfiguration.MAPPER_LOCATION));sessionFactory.setConfigLocation(new ClassPathResource(mybatisConfigPath));// 添加自定義的schema攔截器【此次配置的重點】sessionFactory.setPlugins(new Interceptor[]{new SchemaInterceptor()});return sessionFactory.getObject();}2.3 攔截器注入參數(shù)使用
注入【 3?? mapper.xml】這里使用 ${schemaName} 獲取配置的 schema 的值:
<!--執(zhí)行插入文件數(shù)據(jù)SQL--> <insert id="insertFileData" parameterType="map">insert into ${schemaName}${target_table}( ${table_field} ) VALUES ( ${field_value} ) </insert>3. 總結
首先要注意的是,這里用的不是SpringMVC里的攔截器,而是mybatis的攔截器,攔截器是在執(zhí)行mapper文件內的SQL前觸發(fā)的,此時,如果你傳遞了schema參數(shù),攔截器就不會覆蓋schema的值,如果沒有配置,則使用配置的默認值。
與50位技術專家面對面20年技術見證,附贈技術全景圖總結
以上是生活随笔為你收集整理的【Java代码】使用 org.apache.ibatis.plugin.Interceptor 拦截器实现全局 mapper.xml 参数注入(可用于切换数据库实例schema+Demo举例源码)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 【Linux病毒】阿里云+腾讯云服务器的
- 下一篇: 【Linux病毒】腾讯云 cron、ss