Mybatis插件动态数据库链接
场景:
springboot+mybatis-plus+mysql+clickhouse
同时整合了mysql和clickhouse 并且clickhouse设置了集群
要求对clickhouse数据库链接做负载均衡
算法采用当前线程Id的hash算法
解决方案:
- 自定义 mybatis Interceptor 拦截Executor
package io.renren.modules.ck.config;
import com.alibaba.druid.pool.DruidPooledConnection;
import com.baomidou.mybatisplus.core.executor.MybatisSimpleExecutor;
import io.renren.common.utils.CkMybatisUtil;
import io.renren.modules.ck.dao.CkMybatisDao;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.plugin.*;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;
import java.lang.reflect.Field;
import java.util.Properties;
@Intercepts({@Signature(type = Executor.class, method = "query", args = {MappedStatement.class, Object.class, RowBounds.class, ResultHandler.class})})
public class CkDataSourceInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
Object[] args = invocation.getArgs();
if (args[0] instanceof MappedStatement) {
MappedStatement ms = (MappedStatement) args[0];
String id = ms.getId();
if (id.contains(CkMybatisDao.class.getName())) {
Object target = invocation.getTarget();
MybatisSimpleExecutor executor=(MybatisSimpleExecutor) target;
Transaction transaction = executor.getTransaction();
DruidPooledConnection connection = CkMybatisUtil.getDataSource().getConnection();
Class<? extends Transaction> transactionClass = transaction.getClass();
Field connectionField = transactionClass.getDeclaredField("connection");
connectionField.setAccessible(true);
connectionField.set(transaction,connection);
}
}
return invocation.proceed();
}
@Override
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
@Override
public void setProperties(Properties properties) {
}
}
2.注册CkDataSourceInterceptor
package io.renren.modules.ck.config;
import com.baomidou.mybatisplus.autoconfigure.ConfigurationCustomizer;
import org.springframework.context.annotation.Bean;
@org.springframework.context.annotation.Configuration
public class MybatisInterceptorConfig {
//将插件加入到mybatis插件拦截链中
@Bean
public ConfigurationCustomizer configurationCustomizer() {
return configuration -> {
//插件拦截链采用了责任链模式,执行顺序和加入连接链的顺序有关
CkDataSourceInterceptor interceptor = new CkDataSourceInterceptor();
configuration.addInterceptor(interceptor);
};
}
}
3.CkMybatisUtil 获取数据源方法 在这里可以写负载均衡算法处理数据源
package io.renren.common.utils;
import com.alibaba.druid.pool.DruidDataSource;
public class CkMybatisUtil {
public static DruidDataSource getDataSource(){
DruidDataSource druidDataSource = new DruidDataSource();
druidDataSource.setUrl("jdbc:clickhouse://localhost:8123/clickhouse");
druidDataSource.setUsername("default");
druidDataSource.setDriverClassName("ru.yandex.clickhouse.ClickHouseDriver");
return druidDataSource;
}
}
4.clickhouse mapper映射的Dao接口
package io.renren.modules.ck.dao;
import io.renren.modules.ck.entity.CkTableColumnEntity;
import io.renren.modules.ck.entity.CkTableInfoEntity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import java.util.List;
@Mapper
public interface CkMybatisDao {
void createCkTable(@Param("table") CkTableInfoEntity table, @Param("columns") List<CkTableColumnEntity> columns);
void insert(@Param("tableName") String tableName, @Param("columnDataList") List<List<Object>> columnDataList,@Param("columns") List<CkTableColumnEntity> columns);
}
4.sql的xml文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="io.renren.modules.ck.dao.CkMybatisDao">
<select id="createCkTable">
create table clickhouse.${table.tableName}(
<foreach collection="columns" item="column" separator=",">
${column.columnName} ${column.dataType}
</foreach>
)
Engine = ReplacingMergeTree primary key ${table.pkColumn}
order by
${table.pkColumn}
</select>
<select id="insert">
insert into clickhouse.${tableName}
<foreach collection="columns" item="column" separator="," open="(" close=")">
${column.columnName}
</foreach>
values
<foreach collection="columnDataList" item="columnData" separator="," >
<foreach collection="columnData" item="value" separator="," open="(" close=")">
${value}
</foreach>
</foreach>
</select>
</mapper>
所有准备工作完成 直接在Service中注入CkMybatisDao 就可操作clickhouse了 并且 其他Dao依然是操作mysql