mybatis动态表名(分库分表)
//测试代码
@RestController
public class UserController {
@Resource
private UserMapper userMapper;
@Autowired
MysqlTbNameUtil mysqlTbNameUtil;
@GetMapping("/selectByAge")
@ResponseBody
public Object selectByAge() {
return mysqlTbNameUtil.getMapperPlus(UserMapper.class, "tb001").selectByAge(27);
// return userMapper.selectByAge(27);
}
@GetMapping("/selectByAge2")
@ResponseBody
public Object selectByAge2() {
return mysqlTbNameUtil.getMapperPlus(UserMapper.class, "tb001").selectByAge2(27);
// return userMapper.selectByAge2(27);
}
@GetMapping("/selectByAgeAndName")
@ResponseBody
public Object selectByAgeAndName() {
return mysqlTbNameUtil.getMapperPlus(UserMapper.class, "tb001").selectByAgeAndName(1, "ctl");
// return userMapper.selectByAgeAndName(1, "ctl");
}
@GetMapping("/selectByAgeAndName2")
@ResponseBody
public Object selectByAgeAndName2() {
return mysqlTbNameUtil.getMapperPlus(UserMapper.class, "tb001").selectByAgeAndName2(1, "ctl");
// return userMapper.selectByAgeAndName2(1, "ctl");
}
@GetMapping("/selectByUser")
@ResponseBody
public Object selectByUser() {
return mysqlTbNameUtil.getMapperPlus(UserMapper.class, "tb001").selectByUser(new User().withAge(1));
// return userMapper.selectByUser(new User().withAge(1));
}
@GetMapping("/selectByUser2")
@ResponseBody
public Object selectByUser2() {
return mysqlTbNameUtil.getMapperPlus(UserMapper.class, "tb001").selectByUser2(new User().withId(1L));
// return userMapper.selectByUser2(new User().withId(1L));
}
@GetMapping("/selectByMap")
@ResponseBody
public Object selectByMap() {
Map<String, Object> map = new HashMap<>();
map.put(User.Fields.age.name(), "27");
return mysqlTbNameUtil.getMapperPlus(UserMapper.class, "tb001").selectByMap(map);
// return userMapper.selectByMap(map);
}
@GetMapping("/update")
@ResponseBody
public Object update() {
return mysqlTbNameUtil.getMapperPlus(UserMapper.class, "tb001").update(new User().withId(26L).withAge(1).withUserName("ctl").withAddress("新乡" + System.currentTimeMillis()));
// return userMapper.update(new User().withId(26L).withAge(1).withUserName("ctl").withAddress("新乡" + System.currentTimeMillis()));
}
}
//mapper.java
@Mapper
public interface UserMapper {
List<User> selectByAgeAndName(int age,String userName);
List<User> selectByAgeAndName2(@Param("age") int age2,@Param("name") String userName2);
List<User> selectByAge(@Param("age") int age2);
List<User> selectByAge2(int age3);
List<User> selectByUser(User user);
List<User> selectByUser2(@Param("u") User user);
List<User> selectByMap(Map<String,Object> user);
long update(User user);
}
//mapper.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="com.ctl.domain.mapper.UserMapper">
<resultMap id="userResultMap" type="com.ctl.domain.model.User">
<result column="id" property="id"/>
<result column="user_name" property="userName"/>
<result column="age" property="age"/>
<result column="address" property="address"/>
</resultMap>
<select id="selectByAge" resultMap="userResultMap">
SELECT * FROM user where age=#{age}
</select>
<select id="selectByAge2" resultMap="userResultMap">
SELECT * FROM user where age=#{id}
</select>
<select id="selectByAgeAndName" resultMap="userResultMap">
SELECT * FROM user where user_name = #{param2} and age=#{param1}
</select>
<select id="selectByAgeAndName2" resultMap="userResultMap">
SELECT * FROM user where user_name = #{name} and age=#{age}
</select>
<select id="selectByUser" resultMap="userResultMap">
SELECT * FROM user
<where>
<if test="userName!=null and userName!=''">
user_name = #{userName}
</if>
<if test="age!=null ">
age = #{age}
</if>
<if test="id!=null ">
id = #{id}
</if>
</where>
</select>
<select id="selectByUser2" resultMap="userResultMap">
SELECT * FROM user
<where>
<if test="u.userName!=null and u.userName!=''">
user_name = #{u.userName}
</if>
<if test="u.age!=null ">
age = #{u.age}
</if>
<if test="u.id!=null ">
id = #{u.id}
</if>
</where>
</select>
<select id="selectByMap" resultMap="userResultMap">
SELECT * FROM user
<where>
<if test="userName!=null and userName!=''">
user_name = #{userName}
</if>
<if test="age!=null ">
age = #{age}
</if>
<if test="id!=null ">
id = #{id}
</if>
</where>
</select>
<update id="update" parameterType="com.ctl.domain.model.User">
update user
<set>
<if test="userName!=null and userName!=''">
user_name = #{userName},
</if>
<if test="age!=null ">
age = #{age},
</if>
<if test="address!=null and address!=''">
address = #{address},
</if>
</set>
where id = #{id,jdbcType=INTEGER}
</update>
</mapper>
//MysqlTbNameUtil
@Component("mysqlTbNameUtil")
public class MysqlTbNameUtil {
public static final String TB_PREFIX_PARAMKEY="tbPrefix";
private final ThreadLocal<String> holder= new ThreadLocal<>();
public String get(){
String value = holder.get();
return ObjectUtils.isEmpty(value)?"":value;
}
public void set(String prefix){
if(prefix==null){
throw new RuntimeException("tenantId not null.");
}
holder.set(prefix);
}
public void clear(){
holder.remove();
}
public <T> T getMapperPlus(Class<T> clazz, String prefix) {
try {
holder.set(prefix + "_");
return SpringContextUtils.getApplicationContext().getBean(clazz);
} catch (Exception e) {
return null;
}
}
}
//ReplaceTablePlugin
/**
* mybatis插件实现动态表名,可以拦截器新增、编辑、删除、查询等
* MyBatis插件是一种拦截器,可以在MyBatis某个行为执行时进行拦截并改变这个行为。
* 通常,MyBatis的插件可以作用于MyBatis中的四大接口,
* 分别为Executor,ParameterHandler,ResultSetHandler和StatementHandler
*/
@Component
@Intercepts({@Signature(type = StatementHandler.class, method = "prepare", args = {Connection.class, Integer.class})})
public class ReplaceTablePlugin implements Interceptor {
@Autowired
MysqlTbNameUtil mysqlTbNameUtil;
private static final Logger log = LoggerFactory.getLogger(ReplaceTablePlugin.class);
private final static Map<String, String> TABLE_MAP = new LinkedHashMap<>();
/**
* 需要替换的表(替换前的表名和替换后的表名)
* 方案一、直接使用TABLE_MAP的表替换,写死不友好
* 方案二、使用mysqlTbNameUtil获取前缀,友好且线程安全
*/
static {
// TABLE_MAP.put("user", "tb001_user");
TABLE_MAP.put("user", "");
}
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler statementHandler = realTarget(invocation.getTarget());
MetaObject metaObject = SystemMetaObject.forObject(statementHandler);
doTable(statementHandler, metaObject);
return invocation.proceed();
}
/**
* 是否为基本数据类型
*
* @param clazz
* @return
*/
public static boolean isPrimitiveType(Class<?> clazz) {
List<String> extracted = extracted();
return extracted.contains(clazz.getName());
}
private static List<String> extracted() {
List<String> list = new ArrayList<>();
list.add(Boolean.class.getName());
list.add(Byte.class.getName());
list.add(Character.class.getName());
list.add(Short.class.getName());
list.add(Integer.class.getName());
list.add(Long.class.getName());
list.add(Float.class.getName());
list.add(Double.class.getName());
list.add(String.class.getName());
list.add(Date.class.getName());
list.add(LocalDate.class.getName());
list.add(LocalDateTime.class.getName());
return list;
}
/**
* 是否为基本数据类型
*
* @param obj
* @return
*/
public static boolean isPrimitiveType(Object obj) {
return isPrimitiveType(obj.getClass());
}
private void doTable(StatementHandler handler, MetaObject metaStatementHandler) throws ClassNotFoundException {
BoundSql boundSql = handler.getBoundSql();
String originalSql = boundSql.getSql();
ParameterHandler parameterHandler = handler.getParameterHandler();
Object parameterObject = parameterHandler.getParameterObject();
boolean primitiveType = isPrimitiveType(parameterObject);
if (primitiveType) {
MapperMethod.ParamMap paramMap = new MapperMethod.ParamMap();
paramMap.put("id", parameterObject);
paramMap.put(TB_PREFIX_PARAMKEY, mysqlTbNameUtil.get());
ReflectUtil.setFieldValue(parameterHandler, "parameterObject", paramMap);
log.info("基本类型{},更改为ParamMap={}", parameterObject.getClass().getName(), paramMap);
} else {
if (parameterObject instanceof MapperMethod.ParamMap) {
MapperMethod.ParamMap paramMap = (MapperMethod.ParamMap) parameterObject;
paramMap.put(TB_PREFIX_PARAMKEY, mysqlTbNameUtil.get());
log.info("非基本类型ParamMap,更新为ParamMap={}", paramMap);
} else {
Map<String, Object> map = new HashMap<>();
BeanUtil.beanToMap(parameterObject, map, false, true);
MapperMethod.ParamMap paramMap = new MapperMethod.ParamMap();
paramMap.put(TB_PREFIX_PARAMKEY, mysqlTbNameUtil.get());
paramMap.putAll(map);
ReflectUtil.setFieldValue(parameterHandler, "parameterObject", paramMap);
log.info("非基本类型{},更改为ParamMap={}", parameterObject.getClass().getName(), paramMap);
}
}
log.info("parameterObject={}", JacksonMapper.getJsonStr(parameterHandler.getParameterObject()));
if (originalSql != null && !originalSql.equals("")) {
log.info("拦截前的sql:{}", originalSql);
originalSql= originalSql.replace(","," , ").replaceAll("\\s+", " ").replaceAll("\\n"," ");
if (isReplaceTableName(originalSql)) {
originalSql = replaceTableName(originalSql);
log.info("拦截后的sql:{}", originalSql);
metaStatementHandler.setValue("delegate.boundSql.sql", originalSql);
}
}
}
private boolean isReplaceTableName(String sql) {
TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
try {
List<String> tableList = tablesNamesFinder.getTableList(CCJSqlParserUtil.parse(sql));
String[] splits = sql.split(" ");
for (int i = 0; i < splits.length; i++) {
String split = splits[i].toLowerCase();
if (!ObjectUtils.isEmpty(TABLE_MAP.keySet().contains(split)) && tableList.contains(split)) {
return true;
}
}
} catch (Exception e) {
log.error("", e);
}
return false;
}
/**
* 替换sql中的表名得到分表表名
* @param sql
* @return
*/
private String replaceTableName(String sql) {
TablesNamesFinder tablesNamesFinder = new TablesNamesFinder();
try {
List<String> tableList = tablesNamesFinder.getTableList(CCJSqlParserUtil.parse(sql));
log.info("tableList={}", tableList);
String[] splits = sql.split(" ");
for (int i = 0; i < splits.length; i++) {
String split = splits[i].toLowerCase();
if (!ObjectUtils.isEmpty(TABLE_MAP.keySet().contains(split)) && tableList.contains(split)) {
splits[i]=mysqlTbNameUtil.get()+split;
}
}
StringJoiner stringJoiner =new StringJoiner(" ");
for (int i = 0; i < splits.length; i++) {
stringJoiner.add(splits[i]);
}
return stringJoiner.toString();
} catch (Exception e) {
log.error("", e);
}
return sql;
}
@Override
public Object plugin(Object target) {
if (target instanceof StatementHandler) {
return Plugin.wrap(target, this);
}
return target;
}
@Override
public void setProperties(Properties properties) {
}
public static <T> T realTarget(Object target) {
if (Proxy.isProxyClass(target.getClass())) {
MetaObject metaObject = SystemMetaObject.forObject(target);
return realTarget(metaObject.getValue("h.target"));
}
return (T) target;
}
}
mongo动态分表参看 mongodb动态分表_ctllin的博客-CSDN博客
CREATE TABLE `user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_name` varchar(128) NOT NULL DEFAULT '' COMMENT '用户名',
`age` int(11) NOT NULL COMMENT '年龄',
`address` varchar(128) COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';
CREATE TABLE `tb001_user` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`user_name` varchar(128) NOT NULL DEFAULT '' COMMENT '用户名',
`age` int(11) NOT NULL COMMENT '年龄',
`address` varchar(128) COMMENT '地址',
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='用户表';