Executor(update, query, flashStatements, commit, Rollback, getTransaction, close, isClosed)
ParameterHandler(getParameterObject, setParameters)
ResultSetHandler( ResultSets, handlerOutputParameters 처리 )
StatementHandler(준비, 매개변수화, 배치, 업데이트, 쿼리)
전체 요약은 다음과 같습니다.
실행기를 가로채는 방법
가로채기 처리 이온 매개변수
집합 결과 차단
SQL 구문 구성 처리 차단
이 네 가지 메소드는 MyBatis의 작업(추가, 삭제, 수정, 쿼리)에서 실행됩니다. 실행 순서는 Executor, ParameterHandler입니다. , ResultSetHandler, StateHandler.
package org.apache.ibatis.plugin; import java.util.Properties; public interface Interceptor { //intercept方法就是要进行拦截的时候要执行的方法。 Object intercept(Invocation invocation) throws Throwable; //plugin方法是拦截器用于封装目标对象的,通过该方法我们可以返回目标对象本身,也可以返回一个它的代理。当返回的是代理的时候我们可以对其中的方法进行拦截来调用intercept方法,当然也可以调用其他方法。 Object plugin(Object target); //setProperties方法是用于在Mybatis配置文件中指定一些属性的。 void setProperties(Properties properties); }
하위 테이블의 테이블 구조가 미리 설정되어 있으므로 이제 추가, 삭제 시 대상 테이블을 한 번만 직접 잠그면 됩니다. , 수정, 쿼리를 수행한 다음 대상 SQL을 교체합니다.
Mybatis는 인터셉터 인터페이스를 제공합니다. 앞서 언급했듯이 우리는 이 인터페이스를 구현하여 자체 인터셉터를 정의할 수 있습니다. 사용자 정의 인터셉터는 Mybatis에 의해 관리되어야 합니다. 그래야 Mybatis 실행이 인터셉터 실행과 결합될 수 있습니다. 즉, springboot를 사용하여 사용자 정의 인터셉터를 주입할 수 있습니다.
package com.shinemo.insurance.common.config; import org.apache.ibatis.plugin.Interceptor; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class TableShardConfig { /** * 注册插件 */ @Bean public Interceptor tableShardInterceptor() { return new TableShardInterceptor(); } }
인터셉터가 전역적으로 가로채기 때문에 가로채야 하는 매퍼만 가로채면 되므로 주석을 사용하여 표시해야 합니다.
package com.shinemo.insurance.common.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(value = { ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) public @interface TableShard { // 表前缀名 String tableNamePrefix(); // 值 String value() default ""; // 是否是字段名,如果是需要解析请求参数改字段名的值(默认否) boolean fieldFlag() default false; }
이 주석만 표시하면 됩니다. 가로채고 싶은 객체 mapper
@Mapper @TableShard(tableNamePrefix = "t_insurance_video_people_", value = "deviceId", fieldFlag = true) public interface InsuranceVideoPeopleMapper { //VideoPeople对象中包含deviceId字段 int insert(VideoPeople videoPeople); }
package com.shinemo.insurance.common.config; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.sql.Connection; import java.util.Map; import com.shinemo.insurance.common.annotation.TableShard; import com.shinemo.insurance.common.util.HashUtil; import org.apache.ibatis.binding.MapperMethod; import org.apache.ibatis.executor.statement.StatementHandler; import org.apache.ibatis.mapping.BoundSql; import org.apache.ibatis.mapping.MappedStatement; 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; import org.apache.ibatis.reflection.DefaultReflectorFactory; import org.apache.ibatis.reflection.MetaObject; import org.apache.ibatis.reflection.ReflectorFactory; import org.apache.ibatis.reflection.SystemMetaObject; @Intercepts({ @Signature(type = StatementHandler.class, method = "prepare", args = { Connection.class, Integer.class }) }) public class TableShardInterceptor implements Interceptor { private static final ReflectorFactory DEFAULT_REFLECTOR_FACTORY = new DefaultReflectorFactory(); @Override public Object intercept(Invocation invocation) throws Throwable { // MetaObject是mybatis里面提供的一个工具类,类似反射的效果 MetaObject metaObject = getMetaObject(invocation); BoundSql boundSql = (BoundSql) metaObject.getValue("delegate.boundSql"); MappedStatement mappedStatement = (MappedStatement) metaObject .getValue("delegate.mappedStatement"); // 获取Mapper执行方法 Method method = invocation.getMethod(); // 获取分表注解 TableShard tableShard = getTableShard(method, mappedStatement); // 如果method与class都没有TableShard注解或执行方法不存在,执行下一个插件逻辑 if (tableShard == null) { return invocation.proceed(); } //获取值,此值就是拿的注解上value值,注解上value设定的值,并在传入对象中获取,根据业务可以选择适当的值即可,我选取此值的目的是同一台设备的值存入一张表中,有hash冲突的值也存在一张表中 String value = tableShard.value(); //value是否字段名,如果是,需要解析请求参数字段名的值 boolean fieldFlag = tableShard.fieldFlag(); if (fieldFlag) { //获取请求参数 Object parameterObject = boundSql.getParameterObject(); if (parameterObject instanceof MapperMethod.ParamMap) { // ParamMap类型逻辑处理 MapperMethod.ParamMap parameterMap = (MapperMethod.ParamMap) parameterObject; // 根据字段名获取参数值 Object valueObject = parameterMap.get(value); if (valueObject == null) { throw new RuntimeException(String.format("入参字段%s无匹配", value)); } //替换sql replaceSql(tableShard, valueObject, metaObject, boundSql); } else { // 单参数逻辑 //如果是基础类型抛出异常 if (isBaseType(parameterObject)) { throw new RuntimeException("单参数非法,请使用@Param注解"); } if (parameterObject instanceof Map) { Map<String, Object> parameterMap = (Map<String, Object>) parameterObject; Object valueObject = parameterMap.get(value); //替换sql replaceSql(tableShard, valueObject, metaObject, boundSql); } else { //非基础类型对象 Class<?> parameterObjectClass = parameterObject.getClass(); Field declaredField = parameterObjectClass.getDeclaredField(value); declaredField.setAccessible(true); Object valueObject = declaredField.get(parameterObject); //替换sql replaceSql(tableShard, valueObject, metaObject, boundSql); } } } else {//无需处理parameterField //替换sql replaceSql(tableShard, value, metaObject, boundSql); } //把原有的简单查询语句替换为分表查询语句了,现在是时候将程序的控制权交还给Mybatis下一个拦截器处理 return invocation.proceed(); } /** * @description: * @param target * @return: Object */ @Override public Object plugin(Object target) { // 当目标类是StatementHandler类型时,才包装目标类,否者直接返回目标本身, 减少目标被代理的次数 if (target instanceof StatementHandler) { return Plugin.wrap(target, this); } else { return target; } } /** * @description: 基本数据类型验证,true是,false否 * @param object * @return: boolean */ private boolean isBaseType(Object object) { if (object.getClass().isPrimitive() || object instanceof String || object instanceof Integer || object instanceof Double || object instanceof Float || object instanceof Long || object instanceof Boolean || object instanceof Byte || object instanceof Short) { return true; } else { return false; } } /** * @description: 替换sql * @param tableShard 分表注解 * @param value 值 * @param metaObject mybatis反射对象 * @param boundSql sql信息对象 * @return: void */ private void replaceSql(TableShard tableShard, Object value, MetaObject metaObject, BoundSql boundSql) { String tableNamePrefix = tableShard.tableNamePrefix(); // // 获取策略class // Class<? extends ITableShardStrategy> strategyClazz = tableShard.shardStrategy(); // // 从spring ioc容器获取策略类 // ITableShardStrategy tableShardStrategy = SpringBeanUtil.getBean(strategyClazz); // 生成分表名 String shardTableName = generateTableName(tableNamePrefix, (String) value); // 获取sql String sql = boundSql.getSql(); // 完成表名替换 metaObject.setValue("delegate.boundSql.sql", sql.replaceAll(tableNamePrefix, shardTableName)); } /** * 生成表名 * * @param tableNamePrefix 表名前缀 * @param value 价值 * @return {@link String} */ private String generateTableName(String tableNamePrefix, String value) { //我们分了1024张表 int prime = 1024; //hash取模运算过后,锁定目标表 int rotatingHash = HashUtil.rotatingHash(value, prime); return tableNamePrefix + rotatingHash; } /** * @description: 获取MetaObject对象-mybatis里面提供的一个工具类,类似反射的效果 * @param invocation * @return: MetaObject */ private MetaObject getMetaObject(Invocation invocation) { StatementHandler statementHandler = (StatementHandler) invocation.getTarget(); // MetaObject是mybatis里面提供的一个工具类,类似反射的效果 MetaObject metaObject = MetaObject.forObject(statementHandler, SystemMetaObject.DEFAULT_OBJECT_FACTORY, SystemMetaObject.DEFAULT_OBJECT_WRAPPER_FACTORY, DEFAULT_REFLECTOR_FACTORY); return metaObject; } /** * @description: 获取分表注解 * @param method * @param mappedStatement * @return: TableShard */ private TableShard getTableShard(Method method, MappedStatement mappedStatement) throws ClassNotFoundException { String id = mappedStatement.getId(); // 获取Class final String className = id.substring(0, id.lastIndexOf(".")); // 分表注解 TableShard tableShard = null; // 获取Mapper执行方法的TableShard注解 tableShard = method.getAnnotation(TableShard.class); // 如果方法没有设置注解,从Mapper接口上面获取TableShard注解 if (tableShard == null) { // 获取TableShard注解 tableShard = Class.forName(className).getAnnotation(TableShard.class); } return tableShard; } }에 맞춤 인터셉터 구현
위 내용은 springboot+mybatis 인터셉터를 사용하여 수평 테이블 분할을 구현하는 방법의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!