精华内容
下载资源
问答
  • 实现目的:为了存储了公共字典表主键的其他表在查询的...实现步骤:自定义注解——自定义实现mybaits拦截器——注册mybaits拦截器 一、自定义注解 1.1 代码示例 import java.lang.annotation.Documented; i...

    实现目的:为了存储了公共字典表主键的其他表在查询的时候不用关联查询(所以拦截位置位于mybaits语句查询得出结果集后)

    项目环境 :springboot+mybaits

    实现步骤:自定义注解——自定义实现mybaits拦截器——注册mybaits拦截器

    一、自定义注解

    1.1  代码示例

    import java.lang.annotation.Documented;
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Inherited;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    @Target({ElementType.FIELD})//
    @Retention(RetentionPolicy.RUNTIME)//该注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
    @Inherited//允许子类继承父类的注解。 (子类中可以获取并使用父类注解)
    @Documented//指明修饰的注解,可以被例如javadoc此类的工具文档化,只负责标记,没有成员取值。
    /**
    * 该自定义注解用于所查询语句中字段包含字典表主键 并需要将主键同时对照成字典表对应的名称
    * 将该注解放置在名称列,参数为字典表主键存储列的名字
    * @ClassName: DictReplace 
    * 描述: TODO  用于字典名称字段默认为空,则空则认为字典id字段名为 字典名称字典.substring(0,length()-4) 若不为空则认定字典id字段名称为参数值
    * 作者cy
    * 时间 2019年3月26日 上午9:02:47 
    *
    */
    public @interface DictReplace {
    
    String dictIdFieldName() default "";    
    
    }

    @Target 注解

    功能:指明了修饰的这个注解的使用范围,即被描述的注解可以用在哪里。

    ElementType的取值包含以下几种: 

    • TYPE:类,接口或者枚举
    • FIELD:域,包含枚举常量
    • METHOD:方法
    • PARAMETER:参数
    • CONSTRUCTOR:构造方法
    • LOCAL_VARIABLE:局部变量
    • ANNOTATION_TYPE:注解类型
    • PACKAGE:包

    @Retention 注解

    功能:指明修饰的注解的生存周期,即会保留到哪个阶段。

    RetentionPolicy的取值包含以下三种:

    • SOURCE:源码级别保留,编译后即丢弃。
    • CLASS:编译级别保留,编译后的class文件中存在,在jvm运行时丢弃,这是默认值。
    • RUNTIME: 运行级别保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用。

    @Documented 注解

    功能:指明修饰的注解,可以被例如javadoc此类的工具文档化,只负责标记,没有成员取值。

    @Inherited注解

    功能:允许子类继承父类中的注解。

     

    1.2  使用场景

            @TableField("runtime_platform")
            private Integer runtimePlatform;
            
            @DictReplace//字典替换注解
            @TableField(exist = false)        
            private String runtimePlatformName;        

     

     

    二、自定义mybaits拦截器并注册

      2.1  代码示例

     

    import java.util.List;
    import java.util.Properties;
    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 com.msunsoft.base.common.factory.ConstantFactory;
    import com.msunsoft.base.common.interceptor.annotation.DictReplace;
    import com.msunsoft.base.spring.SpringContextHolder;
    import com.msunsoft.base.util.ToolUtil;
    import org.apache.ibatis.executor.resultset.ResultSetHandler;
    import java.lang.reflect.Field;
    import java.sql.Statement;
    /**
     * 字典替换拦截器,当注解方法被执行后拦截并修改查询后的结果
    * @ClassName: DictReplaceInteceptor 
    * 描述: TODO
    * 作者
    * 时间 2019年3月25日 下午7:23:41 
    *
     */
    @Intercepts({    
        @Signature(type = ResultSetHandler.class,method = "handleResultSets", args = { Statement.class }) 
        })  
    public class DictReplaceInteceptor implements Interceptor{
        private Properties properties; 
        private SpringContextHolder spring;//实现  ApplicationContextAware 接口的类包含获取spring容器中的bean的静态方法    
        
        @Override
        @SuppressWarnings(value = {"all"})
        public Object intercept(Invocation invocation) throws Throwable {
                //因为 handleResultSets  方法执行结束后可以收到一个list类型的数据结果集,所以虽然该方法的目的是用于结束本次拦截,执行预定方法(handleResultSets)方便下次拦截
                List<Object> results = (List<Object>)invocation.proceed();
                try{
              //自定义方法用于判断对象是否为空
    if(ToolUtil.isNotEmpty(results)){
                //ConstantFactory 是自定义的包含常用方法的一个类,现在用到的是它包含在其中的通过字典主键获取字典名称的方法 ConstantFactory constantFactory
    = spring.getBean(ConstantFactory.class); Class<?> cls = results.get(0).getClass(); Field[] fields = cls.getDeclaredFields();// 获取private修饰的成员变量 获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。 for(Object result:results){ for (Field field : fields) { //获取我们自定义的注解 DictReplace dictReplace = field.getAnnotation(DictReplace.class); if(dictReplace!=null){//如果存在这个注解 我们在执行后续方法 String dictIdFieldName = dictReplace.dictIdFieldName();//获取注解属性值 Field idField = null; if(ToolUtil.isNotEmpty(dictIdFieldName)){ idField = cls.getDeclaredField(dictIdFieldName);//获取实体类对应字段 }else{ String fieldName = field.getName();//获取实体类字段名称 String idFieldName = fieldName.substring(0,fieldName.length()-4); idField = cls.getDeclaredField(idFieldName); } idField.setAccessible(true);//允许我们在用反射时访问私有变量 Object dictId = idField.get(result);//从返回值中获得字段对应的 值 field.setAccessible(true); if(ToolUtil.isNotEmpty(dictId)){ field.set(result, constantFactory.getDictName( Long.valueOf(new String(dictId.toString())) ) ); //用字典id查询出字典名称 并替换结果集中的值 } } } } } }catch (Exception e) { e.printStackTrace(); }finally{ return results; } } @Override public Object plugin(Object target) { // 读取@Signature中的配置,判断是否需要生成代理类 if (target instanceof ResultSetHandler) { return Plugin.wrap(target, this);//返回代理 } else { return target; } } @Override public void setProperties(Properties properties) { this.properties = properties; } }

    2019年4月16日更新,为了使用mybaits缓存机制减少数据库负担,将部分代码改写

    @Intercepts({    
        @Signature(type = ResultSetHandler.class,method = "handleResultSets", args = { Statement.class }) 
        })  
    public class DictReplaceInteceptor implements Interceptor{
    	private Properties properties; 
    	private SpringContextHolder spring;
    	 
    	
    	@Override
    	@SuppressWarnings(value = {"all"})
    	public Object intercept(Invocation invocation) throws Throwable {
    			//
    			List<Object> results = (List<Object>)invocation.proceed();
    			SqlSessionFactory sqlSessionFactory = spring.getBean(SqlSessionFactory.class);
    			SqlSession sqlSession = sqlSessionFactory.openSession();
    			try{
    				if(ToolUtil.isNotEmpty(results)){
    					ConstantFactory constantFactory = spring.getBean(ConstantFactory.class);
    					Class<?> cls = results.get(0).getClass();
    
    					Field[] fields = cls.getDeclaredFields();// 暴力获取private修饰的成员变量  获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
    	                for(Object result:results){     	 
    	                	
    	                	 for (Field field : fields) {
    	                		
    	                    	 DictReplace dictReplace = field.getAnnotation(DictReplace.class);
    	                    	 if(dictReplace!=null){
    	                    		 String dictIdFieldName = dictReplace.dictIdFieldName();
    	                    		 Field idField = null;
    	                    		 if(ToolUtil.isNotEmpty(dictIdFieldName)){
    	                    			 idField = cls.getDeclaredField(dictIdFieldName);
    	                    		 }else{
    	                    			 String fieldName = field.getName();
    	                            	 String idFieldName = fieldName.substring(0,fieldName.length()-4);
    	                            	 idField = cls.getDeclaredField(idFieldName);
    	                    		 }
    	                    		 idField.setAccessible(true);
    	                    		 Object  dictId =  idField.get(result);
    	                    		 field.setAccessible(true);
    	                    		 if(ToolUtil.isNotEmpty(dictId)){
    
    						if (ToolUtil.isEmpty(dictId)) {
    							return "";
    						} else {
                                     //以前是直接调用方法,每次调用调用都会创建,现在通过sqlSession获取对应的mapper 避免每次都创建 DictMapper dictMapper = sqlSession.getMapper(DictMapper.class); Dict dict = dictMapper.selectById(new String(dictId.toString())); if (dict == null) { } else { field.set(result, dict.getName()); } } } } } } } }catch (Exception e) { e.printStackTrace(); }finally{ sqlSession.close(); return results; } } @Override public Object plugin(Object target) { // 读取@Signature中的配置,判断是否需要生成代理类 if (target instanceof ResultSetHandler) { return Plugin.wrap(target, this); } else { return target; } } @Override public void setProperties(Properties properties) { this.properties = properties; } }

     再举一个例子

      注解对象

    @Target({ElementType.FIELD})//
    @Retention(RetentionPolicy.RUNTIME)//该注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在;
    @Inherited//允许子类继承父类的注解。 (子类中可以获取并使用父类注解)
    @Documented//指明修饰的注解,可以被例如javadoc此类的工具文档化,只负责标记,没有成员取值。
    public @interface One2One {
        String byField();
        Class resultType();
        Class mapper();
        String methodName();
    }
    

      拦截器

    import com.msunsoft.base.common.factory.ConstantFactory;
    import com.msunsoft.base.common.interceptor.annotation.One2One;
    import com.msunsoft.base.spring.SpringContextHolder;
    import com.msunsoft.base.util.ToolUtil;
    import org.apache.ibatis.executor.resultset.ResultSetHandler;
    import org.apache.ibatis.plugin.*;
    import org.apache.ibatis.session.SqlSession;
    import org.apache.ibatis.session.SqlSessionFactory;
    import java.io.Serializable;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.sql.Statement;
    import java.util.List;
    import java.util.Properties;
    
    @Intercepts({
    @Signature(type = ResultSetHandler.class,method = "handleResultSets", args = { Statement.class })
    })
    public class One2OneInteceptor implements Interceptor {
    
    
        private Properties properties;
        private SpringContextHolder spring;
    
    
        @Override
        @SuppressWarnings(value = {"all"})
        public Object intercept(Invocation invocation) throws Throwable {
            //
            List<Object> results = (List<Object>)invocation.proceed();
            SqlSessionFactory sqlSessionFactory = spring.getBean(SqlSessionFactory.class);
            SqlSession sqlSession = sqlSessionFactory.openSession();
            try{
                if(ToolUtil.isNotEmpty(results)){
                    ConstantFactory constantFactory = spring.getBean(ConstantFactory.class);
    
                    Class<?> cls = results.get(0).getClass();
                    Field[] fields = cls.getDeclaredFields();// 暴力获取private修饰的成员变量  获得某个类的所有声明的字段,即包括public、private和proteced,但是不包括父类的申明字段。
                    for(Object result:results){
    
                        for (Field field : fields) {
                            field.setAccessible(true);
                            One2One one2One = field.getAnnotation(One2One.class);
                            if(one2One!=null){
                                String byFieldString = one2One.byField();
                                Class resultType = one2One.resultType();
                                Class mapper = one2One.mapper();
                                String methodName = one2One.methodName();
                                Object objMaper = sqlSession.getMapper(mapper);
                                Method method = mapper.getMethod(methodName, Serializable.class);
    
                                Field byField = cls.getDeclaredField(byFieldString);
                                byField.setAccessible(true);
                                field.set(result, method.invoke(objMaper,byField.get(result)));
    
                            }
                        }
                    }
                }
            }catch (Exception e) {
                e.printStackTrace();
            }finally{
                sqlSession.close();
                return results;
            }
        }
    
        @Override
        public Object plugin(Object target) {
            // 读取@Signature中的配置,判断是否需要生成代理类
            if (target instanceof ResultSetHandler) {
                return Plugin.wrap(target, this);
            } else {
                return target;
            }
        }
    
        @Override
        public void setProperties(Properties properties) {
            this.properties = properties;
        }
    }

    使用实例(实体类中)

        //项目编号
        @TableField("project_id")
        private Long projectId;
    
        //项目信息
        @TableField(exist =  false)
        @One2One(byField = "projectId",resultType = Project.class,mapper= ProjectMapper.class,methodName = "selectById")
        private Project project;

     

       2.2  拦截器部分知识点

      2.1.1  MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:

    1. Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
    2. ParameterHandler (getParameterObject, setParameters)
    3. ResultSetHandler (handleResultSets, handleOutputParameters)
    4. StatementHandler (prepare, parameterize, batch, update, query)

      2.1.2  MyBatis拦截器的接口定义

        一共有三个方法intercept 、plugin 、setProperties

        setProperties()

        方法主要是用来从配置中获取属性。

        plugin()

        方法用于指定哪些方法可以被此拦截器拦截。

           intercept()

        方法是用来对拦截的sql进行具体的操作。

        注解实现

        MyBatis拦截器用到了两个注解:@Intercepts@Signature

    @Intercepts(
            {
                    @Signature(type = Executor.class, method = "query",
                            args = {MappedStatement.class, Object.class,
                                    RowBounds.class, ResultHandler.class}),
                    @Signature(type = Executor.class, method = "query",
                            args = {MappedStatement.class, Object.class, RowBounds.class,
                                    ResultHandler.class, CacheKey.class, BoundSql.class}),
            }
    )

      type的值与类名相同,method与方法名相同,为了避免方法重载,args中指定了各个参数的类型和个数,可通过invocation.getArgs()获取参数数组。

       2.1.3  Spring Boot整合

       方法一   

        如果是使用xml式配置拦截器,可在Mybatis配置文件中添加如下节点,属性可以以如下方式传递

    <plugins>
        <plugin interceptor="tk.mybatis.simple.plugin.XXXInterceptor">
            <property name="propl" value="valuel" />
            <property name="prop2" value="value2" />
        </plugin>
    </plugins>

      方法二  

        如果在Spring boot中使用,则需要单独写一个配置类,如下:

    @Configuration
    public class MybatisInterceptorConfig {
        @Bean
        public String myInterceptor(SqlSessionFactory sqlSessionFactory) {
            ExecutorInterceptor executorInterceptor = new ExecutorInterceptor();
            Properties properties = new Properties();
            properties.setProperty("prop1","value1");
            executorInterceptor.setProperties(properties);
            return "interceptor";
        }
    }

      OR

    import com.baomidou.mybatisplus.extension.plugins.OptimisticLockerInterceptor;
    import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
    import com.msunsoft.base.common.interceptor.mybaits.DictReplaceInteceptor;
    import org.mybatis.spring.annotation.MapperScan;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    import org.springframework.transaction.annotation.EnableTransactionManagement;
    
    @Configuration
    @EnableTransactionManagement
    @MapperScan("com.msunsoft.**.mapper")//Mapper接口扫描
    public class DataSourceConfig {
        /**
         * 乐观锁mybatis插件
         */
       @Bean
        public OptimisticLockerInterceptor optimisticLockerInterceptor() {
            return new OptimisticLockerInterceptor();
        }
    
        /**
         * mybatis-plus分页插件
         */
        @Bean
        public PaginationInterceptor paginationInterceptor() {
            return new PaginationInterceptor();
        }
        
        @Bean
        public DictReplaceInteceptor dictReplaceInteceptor(){
            return new DictReplaceInteceptor();
        }
    
    }

      方法三

        在拦截器上加@Component注解

     

    ps:

    一、引用并参考

    1.《深入理解mybatis原理》 MyBatis的架构设计以及实例分析

      https://blog.csdn.net/luanlouis/article/details/40422941      

    2.关于mybatis拦截器,对结果集进行拦截

      https://www.cnblogs.com/SmallHan/articles/8127327.html

    3.Springboot2(22)Mybatis拦截器实现

      https://blog.csdn.net/cowbin2012/article/details/85256360

    二、涉及技术点

    spring(注解、AOP) ,java反射与动态代理,mybaits(以上代码示例用的是mybaits-Plus 3.0.6.jar),

     

     

     

    转载于:https://www.cnblogs.com/qingfengsuixin/p/10598394.html

    展开全文
  • 正常来说ID生成是很简单的,但是因为本人比较懒,喜欢统一处理问题,所以,决定通过mybaits插件的形式进行全局生成 /** * 自动添加ID * * @author 戚辰先生 * */ @Intercepts({ @Signature(type = Exec...

    因为最近在研究分布式项目,所以学习到了几个知识点

    1、雪花算法

    2、sharding jdbc

    3、分布式ID生成

    正常来说ID生成是很简单的,但是因为本人比较懒,喜欢统一处理问题,所以,决定通过mybaits插件的形式进行全局生成

    /**
     * 自动添加ID
     * 
     * @author 戚辰先生
     *
     */
    @Intercepts({ @Signature(type = Executor.class, method = "update", args = { MappedStatement.class, Object.class }) })
    @Component
    public class AutoGeneratePrimaryKeyMyBatisPluginInterceptor implements Interceptor {
    
    	static int MAPPED_STATEMENT_INDEX = 0;
    	static int PARAMETER_INDEX = 1;
    	static int ROWBOUNDS_INDEX = 2;
    	static int RESULT_HANDLER_INDEX = 3;
    	static String BASE_FIELD_SET_PRIMARY_KEY_FUNTION_NAME = "setPrimaryKey";
    	static String BASE_FIELD_SET_CREATE_TIME_FUNTION_NAME = "setCreateTime";
    	static String BASE_FIELD_SET_UPDATE_TIME_FUNTION_NAME = "setCreateTime";
    	static String BASE_FIELD_SET_DELETE_FUNTION_NAME = "setDelete";
    
    	@Autowired
    	private IdGenerator generator;
    
    	public AutoGeneratePrimaryKeyMyBatisPluginInterceptor() {
    		System.out.println("auto generate primaryKey mybatis plugin start!!!");
    	}
    
    	@SuppressWarnings("static-access")
    	public Object intercept(Invocation invocation) throws Throwable {
    		System.out.println(generator);
    		MappedStatement mappedStatement = (MappedStatement) invocation.getArgs()[MAPPED_STATEMENT_INDEX];
    		SqlCommandType commandType = mappedStatement.getSqlCommandType();
    		if (commandType.INSERT.equals(SqlCommandType.INSERT)) {
    			Object params = invocation.getArgs()[PARAMETER_INDEX];
    			if (params instanceof BaseEntity) {
    				Class<? extends Object> class1 = params.getClass();
    				for (Method method : class1.getMethods()) {
    					if (BASE_FIELD_SET_PRIMARY_KEY_FUNTION_NAME.equals(method.getName())) {
    						method.invoke(params, String.valueOf(generator.generateId()));
    					}
    				}
    				invocation.getArgs()[PARAMETER_INDEX] = params;
    			}
    		}
    		return invocation.proceed();
    	}
    
    	public Object plugin(Object target) {
    		return Plugin.wrap(target, this);
    	}
    
    	@Bean
    	public IdGenerator generatorId() {
    		return new CommonSelfIdGenerator();
    	}
    }

     

    展开全文
  • SqlSession下的四大对象(及拦截器拦截的对象)1 1.Executor代表执行器,其它三大对象都由它来调度 2.statementHandler 3.ParameterHandler 4.ResultHandler:时进行最后数据集的封装返回处理 StatementHandler...

    1.创建两个实体类

    CommandContent类和Command类

    public class CommandContent {
    private String id; //主键
    private String content;
    private String commandId;
    public String getId() {
    return id;
    }
    public void setId(String id) {
    this.id = id;
    }
    public String getContent() {
    return content;
    }
    public void setContent(String content) {
    this.content = content;
    }
    public String getCommandId() {
    return commandId;
    }
    public void setCommandId(String commandId) {
    this.commandId = commandId;
    }
    }

    package com.jh.bean;


    import java.util.List;


    public class Command {
    //主键
    private String id;
    //指令名称
    private String name;
    //指令描述
    private String descrption;
    //指令对应得多条回复
    private List<CommandContent> contentlist;
    public String getId() {
    return id;
    }
    public void setId(String id) {
    this.id = id;
    }
    public String getName() {
    return name;
    }
    public void setName(String name) {
    this.name = name;
    }
    public String getDescrption() {
    return descrption;
    }
    public void setDescrption(String descrption) {
    this.descrption = descrption;
    }
    public List<CommandContent> getContentlist() {
    return contentlist;
    }
    public void setContentlist(List<CommandContent> contentlist) {
    this.contentlist = contentlist;
    }
    }

    2.在数据库创建两个表,具有关联关系

    3.配置文件

    //command实体类配置文件

    <mapper namespace="Command">


      <resultMap type="com.jh.bean.Command" id="Command">
        <id column="c_id" jdbcType="VARCHAR" property="id"/>
        <result column="name" jdbcType="VARCHAR" property="name"/>
        <result column="descrption" jdbcType="VARCHAR" property="descrption"/>
        <collection property="contentlist" resultMap="CommandContent.Content"/>
      </resultMap>
      
      <select  id="getCommandList" parameterType="com.jh.bean.Command" resultMap="Command">
         select c.id c_id,c.name,c.descrption,d.id,d.content,d.commandId 
         from command c left join commandContent d on c.ID=d.commandId
         <where>
        <if test="name != null and !&quot;&quot;.equals(name.trim())"> and c.name=#{name}</if>   <!-- c:if标签里用上一个ognl表达式  xml得双引号为:&quot&quot &&在xml里是&amp;&amp -->
        <if test="descrption != null and !&quot;&quot;.equals(descrption.trim())"> and a.descrption like '%' #{descrption} '%'</if>
         </where>
      </select>
     <update id="updateMessage" parameterType="com.jh.bean.Command">
         update command c left join commandContent d on c.ID=d.commandId set c.name=#{name},c.descrption=#{descrption},d.content=#{content}
         where c.id c_id=#{id}
     </update> 
      
    </mapper>

    commandContent配置文件

    <mapper namespace="CommandContent">


      <resultMap type="com.jh.bean.CommandContent" id="Content">
        <id column="id" jdbcType="VARCHAR" property="id"/>
        <result column="content" jdbcType="VARCHAR" property="content"/>
        <result column="commandId" jdbcType="VARCHAR" property="commandId"/>
      </resultMap>
      <!-- 
      <insert id="addBacth" parameterType="java.util.List" useGeneratedKeys="true" keyProperty="id">
          insert into message(content,commandId) values
          <foreach collection="list" item="item" separator=",">
              (#{item.content},#{item.commandId})
          </foreach>
      </insert>
       -->
      </mapper>

    实现拦截的类:

    package com.jh.intercept;
    import java.sql.Connection;


    import java.sql.ResultSet;
    import java.util.Map;
    import java.util.Properties;
    import org.apache.ibatis.executor.parameter.ParameterHandler;
    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.MetaObject;
    import org.apache.ibatis.reflection.SystemMetaObject;


    import com.jh.entiy.Page;
    import com.mysql.jdbc.PreparedStatement;

    //type参数表示拦截的部分,method表示拦截的方法,args表示参数
    @Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})
    public class PageIntercept implements Interceptor {

    @Override
    /*
     * 跟踪源码
     * 查询是通过excute调度statementHandler来完成的,调度statementHandler是通过prepare方法进行与预编译SQL
     * 需拦截的方法是prepare,在此之前完成SQL的重新编写
     * 使用mybatis的拦截器进行分页
     * 实现intercept接口
     * 注意几大对象
     *1.statementHandler对象是通过接口里的参数获取.getTarget,它的作用是让数据库的statement(preparedStatement)执行操作
     *(此对象是通过routingStatementHandler对象获取)
     *2.获取metaobject对象传入的参数为上面的对象
     *8.delegate对象的适配器 他是StatementHandler接口对象
     *3.MappedStatement对象,是通过metaobject对象的getValue方法,参数为delegate.mappedStatement
     *4.Boundsql对象,通过statementHandler对象的getBoundsql方法
     *5.mappedStatement对象保存映射的节点(select insert update delete)包括配置的id,缓存信息,resultMap parameterType 等重要配置信息
     *7.ParameterHandler用于sql对参数的处理,获取的方法同MappedStatement
     *6.boundsql是建立sql语句和参数的对象,有三个常用的属性(sql parameterType parameterOject parameterMappings)
     * */
    public Object intercept(Invocation invocation) throws Throwable {
    //获取StatementHandler对象,通过getTarget方法
    StatementHandler statementHandler=(StatementHandler)invocation.getTarget();
    //获得metaObject对象
    MetaObject metaObject=SystemMetaObject.forObject(statementHandler);
    MappedStatement mappedStatement=(MappedStatement) metaObject.getValue("delegate.mappedStatement");
    //获取sql语句的id
    String id=mappedStatement.getId();
    //用正则表达式选择id
    if(id.matches(".+Bypage$")){
    //获得原始的sql语句
    BoundSql boundSql=statementHandler.getBoundSql();
    //通过boundsql获取sql
    String sql=boundSql.getSql();
    //查询总条数的语句
    String sqlCount="select count(*) from ("+sql+")a";
    //获取连接,通过invocation参数的getArgs方法
    Connection connection=(Connection)invocation.getArgs()[0];
    PreparedStatement ps= (PreparedStatement) connection.prepareStatement(sqlCount);
    //获取parameterHandler对象
    ParameterHandler parameterHandler=(ParameterHandler)metaObject.getValue("delegate.parameterHandler");
    //为ps设置参数
    parameterHandler.setParameters(ps);
    ResultSet rs=ps.executeQuery();
    //通过boundsql获取参数
    Map<?,?> parameter=(Map<?, ?>)boundSql.getParameterObject();
    //获得page参数
    Page page=(Page)parameter.get("page");
    if(rs.next()){
    //设置属性
    page.setTotleNumber(rs.getInt(1));
    //改造后分页查询的语句,sql语句由boundsql获得,boundsql由delegate获得
    String pageSpl=sql+"limit"+page.getDbIndex()+page.getDbNumber();
    metaObject.setValue("delegate.boundsql.sql",pageSpl);
    }
    }
    return invocation.proceed();
    }

    @Override
    public Object plugin(Object target) {
    return Plugin.wrap(target, this);
    }


    @Override
    public void setProperties(Properties arg0) {
    // TODO Auto-generated method stub
    }
    }

    总结:理解理解java反射机制,理解myBatis执行sql语句的过程,底层代码的实现,映射器的内部组成(三部分)1.MappedStatement  2.SqlSource:提供BoundSql的地方,是上一个的属性  3.Boundsql:它是建立sql的对象,有三个属性(sql,parameterObject,parameterMapping)

    在拦截器都需用到这些对象

    SqlSession下的四大对象(及拦截器拦截的对象)1

    1.Executor代表执行器,其它三大对象都由它来调度 2.statementHandler 3.ParameterHandler 4.ResultHandler:时进行最后数据集的封装返回处理

    StatementHandler接口是有RoutingStatementHandler对象实现,而它的delegate对象才是真正服务的statementHandler,真实的statementHandler对象有一个属性Boundsql,而BoundSql里有sql属性,所以最后的获取的sql语句:delegate.BoundSql.Sql

    查询的过程是通过Executor调度StatemnetHandler来完成的,调度StatemnetHandler的prepare方法预编译sql,所以我们需要拦截的方法便是prepare

    MyBatis的工具类-MethodObject:可以有效的读取或者修改一些重要的对象的参数,其有三个方法普遍使用1.MethodObkect.forObject(Objectobj,ObjectFactoryobjectFactory,ObjectWrapperFactor objectWrapperFactory ),2:Object. getValue(String name)方法用于获取属性的值 支持OGNL 3.void setValue(String name,Object Value)用于修改值 也支持OGNL表达式(PammedStatement,statementHandler都是由MethodObject获取,而MethodObject是由SystemMetaObject.forObject获取)

    在完成拦截后要交回给底层代码执行sql语句,invocation.proced()进入责任链下一层

    plugin方法:使用默认的MyBatis提供的类生成代理对象


    展开全文
  • 采用继承DataSourceTransactionManager控制事务读写分离,以及mybatis拦截器控制方法读写分离,通过继承AbstractRoutingDataSource获取动态数据源。 1.1、代码说明 1.1.1、本地数据源管理 创...

    1、读写分离

             采用继承DataSourceTransactionManager控制事务读写分离,以及mybatis拦截器控制方法读写分离,通过继承AbstractRoutingDataSource获取动态数据源。

    1.1、代码说明

    1.1.1、本地数据源管理

    创建本地数据源类型管理类,使用ThreadLocal保存本地数据源类型

    bodsite-common - com.bodsite.common.datasource. DataSourceHandler

    package com.bodsite.common.datasource;

    /**

     * @Description:本地线程数据源

     * @author bod

     * @date

     *

     */

    public class DataSourceHandler {

       public enum DYNAMIC_DATA_SOURCE{

          MASTER,//主库(写)

          SLAVE;//从库(读)

       }

      

       private static final ThreadLocal<DYNAMIC_DATA_SOURCE> dataSourceThreadLocal = new ThreadLocal<DYNAMIC_DATA_SOURCE>();

      

       protected static void set(DYNAMIC_DATA_SOURCE dynamic_data_source){

          dataSourceThreadLocal.set(dynamic_data_source);

       }

      

       /**

        * 设置为主库

        * @author bod

        */

       protected static void setMaster(){

          dataSourceThreadLocal.set(DYNAMIC_DATA_SOURCE.MASTER);

       }

      

       /**

        * 设置为读库

        * @author bod

        */

       protected static void setSlave(){

          dataSourceThreadLocal.set(DYNAMIC_DATA_SOURCE.SLAVE);

       }

     

       /**

        * 判断是否为主库

        * @author bod

        */

       protected static boolean isMaster(){

          return isThis(DYNAMIC_DATA_SOURCE.MASTER);

       }

      

       /**

        * 判断是否为从

        * @author bod

        */

       protected static boolean isSlave(){

          return isThis(DYNAMIC_DATA_SOURCE.SLAVE);

       }

     

       protected static boolean isThis(DYNAMIC_DATA_SOURCE dynamic_data_source){

          if(dataSourceThreadLocal.get()==null){

             return false;

          }

          return dynamic_data_source == dataSourceThreadLocal.get();

       }

      

       protected static void DataSoruceClean(){

          dataSourceThreadLocal.remove();

       }

    }

    1.1.2、mybatis拦截,设置数据源

    创建mybatis拦截器,拦截update、query方法,设置数据源,如果有事务,不做拦截,有事务的情况,在事务管理器中进行设置。

    bodsite-common - com.bodsite.common.datasource. DynamicDataSourceInterceptor

    package com.bodsite.common.datasource;

     

    import java.util.Properties;

     

    import org.apache.ibatis.executor.Executor;

    import org.apache.ibatis.executor.keygen.SelectKeyGenerator;

    import org.apache.ibatis.mapping.MappedStatement;

    import org.apache.ibatis.mapping.SqlCommandType;

    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.session.ResultHandler;

    import org.apache.ibatis.session.RowBounds;

    import org.springframework.transaction.support.TransactionSynchronizationManager;

     

    /**

     * @Description: mybatis plugin 拦截器-设置数据源

     * @author bod

     * @date

     *

     */

    @Intercepts({

          @Signature(method = "query", type = Executor.class, args = { MappedStatement.class, Object.class,

                RowBounds.class, ResultHandler.class }), // method:方法名,type:类,args:方法参数

          @Signature(method = "update", type = Executor.class, args = { MappedStatement.class, Object.class }) })

    public class DynamicDataSourceInterceptor implements Interceptor {

     

       @Override

       public Object intercept(Invocation invocation) throws Throwable {

          // 是否有事务

          boolean synchronizationActive = TransactionSynchronizationManager.isSynchronizationActive();

          if (!synchronizationActive) {

             Object[] args = invocation.getArgs();

             MappedStatement ms = (MappedStatement) args[0];

             if(ms.getSqlCommandType().equals(SqlCommandType.SELECT)){//查询

                  //!selectKey 为自增id查询主键(SELECT LAST_INSERT_ID() )方法,使用主库

                    if(ms.getId().contains(SelectKeyGenerator.SELECT_KEY_SUFFIX)) {

                    DataSourceHandler.setMaster();

                    }else{

                    DataSourceHandler.setSlave();

                    }

             }else{//其他

                DataSourceHandler.setMaster();

             }

          }

          Object result = invocation.proceed();

          DataSourceHandler.DataSoruceClean();

          return result;

       }

     

       @Override

       public Object plugin(Object target) {

          if (target instanceof Executor) {

             return Plugin.wrap(target, this);

          } else {

             return target;

          }

       }

     

       @Override

       public void setProperties(Properties properties) {

     

       }

     

    }

     

    1.1.3、spring事务管理,设置数据源

    创建动态事务管理类,继承DataSourceTransactionManager,根据读写判断,设置数据源。

    bodsite-common - com.bodsite.common.datasource. DataSourceTransactionManager

       package com.bodsite.common.datasource;

     

    import org.springframework.jdbc.datasource.DataSourceTransactionManager;

    import org.springframework.transaction.TransactionDefinition;

     

    /**

     *

     * @Description:根据事务这是数据源(必须有事务才能进入)

     * @author bod

     * @date

     *

     */

    public class  DynamicDataSourceTransactionManager extends DataSourceTransactionManager{

     

       /**

        *

        */

       private static final long serialVersionUID = 1L;

     

       @Override

       protected void doBegin(Object transaction, TransactionDefinition definition) {

          boolean readOnly = definition.isReadOnly();

          if(readOnly){//只读

             DataSourceHandler.setSlave();

          }else{//读写

             DataSourceHandler.setMaster();

          }

          super.doBegin(transaction, definition);

       }

     

       @Override

       protected void doCleanupAfterCompletion(Object transaction) {

          super.doCleanupAfterCompletion(transaction);

          DataSourceHandler.DataSoruceClean();

       }

      

    }

     

    1.1.4、动态获取数据源

    创建动态获取数据源类,继承AbstractRoutingDataSource,根据读写判断,设置数据源。

    提供两种获取数据源方式:

    1、通过重写determineCurrentLookupKey方法,返回数据源名称,走AbstractRoutingDataSource的determineTargetDataSource(根据determineCurrentLookupKey返回的名称)方法获取数据源。

    2、通过重写determineTargetDataSource。直接返回数据源。

    bodsite-common - com.bodsite.common.datasource. DynamicDataSource

    package com.bodsite.common.datasource;

     

    import java.util.HashMap;

    import java.util.List;

    import java.util.Map;

    import java.util.concurrent.ThreadLocalRandom;

    import java.util.concurrent.atomic.AtomicInteger;

     

    import javax.sql.DataSource;

     

    import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource;

     

    import com.bodsite.common.logger.Logger;

    import com.bodsite.common.logger.LoggerFactory;

     

    /**

     * @Description:动态数据源

     * @author bod

     * @date

     *

     */

    public class DynamicDataSource extends AbstractRoutingDataSource {

       private static final Logger logger = LoggerFactory.getLogger(DynamicDataSource.class);

       private DataSource master;

       private List<DataSource> slaves;

        private int slaveSize; //读数据源个数

       private Integer strategy;// 默认,0:轮询,1,随机

       private AtomicInteger counter = new AtomicInteger();

       private Map<Object, Object> targetDataSources = new HashMap<>();

     

       /******************* 1、加载数据源关系, 设置数据源名,会根据数据源名返回数据源 ************/

       /**

        * 设置数据源名

        */

       @Override

       protected Object determineCurrentLookupKey() {

          return getDataSourceName();

       }

     

       /**

        * 设置数据源映射关系

        */

       @Override

       public void afterPropertiesSet() {

          if (this.master == null) {

             throw new IllegalArgumentException("Property 'targetDataSources' is required");

          }

          setDefaultTargetDataSource(master);

          targetDataSources.put(DataSourceHandler.DYNAMIC_DATA_SOURCE.MASTER.name(), master);

          if (slaves != null && !slaves.isEmpty()) {

             this.slaveSize = slaves.size();

             for (int i = 0; i < slaveSize; i++) {

                targetDataSources.put(DataSourceHandler.DYNAMIC_DATA_SOURCE.SLAVE.name() + i, slaves.get(i));

             }

          }

          setTargetDataSources(targetDataSources);

          super.afterPropertiesSet();

       }

     

       /******************* 2、直接返回数据源 ***********************/

       /**

        * 原方法:根据数据源名称返回数据源,自定义直接返回数据源

        */

       /*@Override

       protected DataSource determineTargetDataSource() {

          return (DataSource) targetDataSources.get(getDataSourceName());

       }*/

     

       /**

        * 获取数据源名称

        * @author bod

        */

       public String getDataSourceName() {

          String dataSourceName = null;

          if (DataSourceHandler.isMaster()) {

             dataSourceName = DataSourceHandler.DYNAMIC_DATA_SOURCE.MASTER.name();

          } else if (DataSourceHandler.isSlave() && slaves != null && !slaves.isEmpty()) {

             int index = 0;

             if (strategy == null || strategy == 0) {

                int count = counter.incrementAndGet();

                index = count%slaveSize;

             }else if(strategy == 1){

                   index = ThreadLocalRandom.current().nextInt(0, slaveSize);

             }

             dataSourceName = DataSourceHandler.DYNAMIC_DATA_SOURCE.SLAVE.name()+index;

          }else{

             dataSourceName = DataSourceHandler.DYNAMIC_DATA_SOURCE.MASTER.name();

          }

          logger.info("This data source name is "+dataSourceName);

          return dataSourceName;

       }

       public DataSource getMaster() {

          return master;

       }

     

       public void setMaster(DataSource master) {

          this.master = master;

       }

     

       public List<DataSource> getSlaves() {

          return slaves;

       }

     

       public void setSlaves(List<DataSource> slaves) {

          this.slaves = slaves;

       }

     

       public Integer getStrategy() {

          return strategy;

       }

     

       public void setStrategy(Integer strategy) {

          this.strategy = strategy;

       }

     

    }

     

     

    1.1.5、application-mybatis.xml配置

    bodsite-site-service - application-mybatis.xml

    <?xml version="1.0" encoding="UTF-8"?>

    <beans xmlns="http://www.springframework.org/schema/beans"

       xmlns:context="http://www.springframework.org/schema/context"

       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:tx="http://www.springframework.org/schema/tx"

       xmlns:aop="http://www.springframework.org/schema/aop"

       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd

          http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.3.xsd

                http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.3.xsd

          http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.3.xsd">

       <!-- druid 连接池配置信息 http://www.cnblogs.com/SummerinShire/p/5828888.html -->

       <!-- druid 数据库连接池 -->

       <bean id="masterDataSource" class="com.alibaba.druid.pool.DruidDataSource"

          init-method="init" destroy-method="close">

          <!-- 连接数据库信息 -->

          <property name="driverClassName" value="${master.jdbc.driver}" />

          <property name="url" value="${master.jdbc.url}" />

          <property name="username" value="${master.jdbc.username}" />

          <property name="password" value="${master.jdbc.password}" />

     

          <!-- 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 -->

          <property name="initialSize" value="1" />

          <!-- 最大连接池数量 -->

          <property name="maxActive" value="50" />

          <!-- 最小连接池数量 -->

          <property name="minIdle" value="10" />

          <!-- 获取连接时最大等待时间,单位毫秒 -->

          <property name="maxWait" value="60000" />

          <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->

          <property name="timeBetweenEvictionRunsMillis" value="60000" />

          <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->

          <property name="minEvictableIdleTimeMillis" value="300000" />

          <!-- 用来检测连接是否有效的sq -->

          <property name="validationQuery" value="SELECT 'x' FROM DUAL" />

          <!-- 建议配置为true,不影响性能,并且保证安全性 -->

          <property name="testWhileIdle" value="true" />

          <!-- 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 -->

          <property name="testOnBorrow" value="false" />

          <!-- 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 -->

          <property name="testOnReturn" value="false" />

          <!-- 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall -->

          <property name="filters" value="stat" />

       </bean>

     

       <bean id="slaveDataSource" class="com.alibaba.druid.pool.DruidDataSource"

          init-method="init" destroy-method="close">

          <!-- 连接数据库信息 -->

          <property name="driverClassName" value="${slave1.jdbc.driver}" />

          <property name="url" value="${slave1.jdbc.url}" />

          <property name="username" value="${slave1.jdbc.username}" />

          <property name="password" value="${slave1.jdbc.password}" />

     

          <!-- 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时 -->

          <property name="initialSize" value="1" />

          <!-- 最大连接池数量 -->

          <property name="maxActive" value="50" />

          <!-- 最小连接池数量 -->

          <property name="minIdle" value="10" />

          <!-- 获取连接时最大等待时间,单位毫秒 -->

          <property name="maxWait" value="60000" />

          <!-- 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒 -->

          <property name="timeBetweenEvictionRunsMillis" value="60000" />

          <!-- 配置一个连接在池中最小生存的时间,单位是毫秒 -->

          <property name="minEvictableIdleTimeMillis" value="300000" />

          <!-- 用来检测连接是否有效的sq -->

          <property name="validationQuery" value="SELECT 'x' FROM DUAL" />

          <!-- 建议配置为true,不影响性能,并且保证安全性 -->

          <property name="testWhileIdle" value="true" />

          <!-- 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 -->

          <property name="testOnBorrow" value="false" />

          <!-- 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。 -->

          <property name="testOnReturn" value="false" />

          <!-- 属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有:监控统计用的filter:stat 日志用的filter:log4j 防御sql注入的filter:wall -->

          <property name="filters" value="stat" />

       </bean>

       <!-- 动态数据源 -->

       <bean id="dataSource" class="com.bodsite.common.datasource.DynamicDataSource">

          <property name="master" ref="masterDataSource" />

          <property name="slaves">

             <list>

                <ref bean="slaveDataSource" />

             </list>

          </property>

          <property name="strategy" value="0"/>

       </bean>

     

     

       <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">

          <property name="dataSource" ref="dataSource" />

          <property name="configLocation" value="classpath:mybatis-config.xml" />

          <property name="mapperLocations" value="classpath:/mappings/**/*.xml" />

       </bean>

       <!-- 定义事务 -->

       <bean id="transactionManager"

          class="com.bodsite.common.datasource.DynamicDataSourceTransactionManager">

          <property name="dataSource" ref="dataSource" />

       </bean>

       <!-- 扫描@Transactional注解的类定义事务 -->

       <tx:annotation-driven transaction-manager="transactionManager"

          proxy-target-class="true" />

       <!-- 自动注册mybatis mapper bean -->

       <!-- 注意,没有必要去指定SqlSessionFactory或SqlSessionTemplate, 因为MapperScannerConfigurer将会创建

          MapperFactoryBean,之后自动装配。 但是,如果你使 用了一个以上的DataSource,那 么自动装配可能会失效。 这种情况下,你可以使用

          sqlSessionFactoryBeanName或sqlSessionTemplateBeanName 属性来设置正确的 bean名称来使用。 -->

       <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">

          <property name="basePackage" value="com.bodsite.**.dao" />

       </bean>

    </beans>

    1.1.6、mybatis-generator.xml 配置-添加拦截器

    bodsite-site-service - mybatis- generator.xml

    <?xml version="1.0" encoding="UTF-8"?>

    <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd">

    <configuration>

       <settings>

          <!--对在此配置文件下的所有cache 进行全局性开/关设置。 -->

          <setting name="cacheEnabled" value="true" />

          <!-- 全局性设置懒加载。如果设为‘false’,则所有相关联的都会被初始化加载。 -->

          <setting name="lazyLoadingEnabled" value="true" />

          <!-- 允许和不允许单条语句返回多个数据集(取决于驱动需求) -->

          <setting name="multipleResultSetsEnabled" value="true" />

          <!-- 使用列标签代替列名称。 -->

          <setting name="useColumnLabel" value="true" />

          <!-- 允许JDBC 生成主键。需要驱动器支持。如果设为了true,这个设置将强制使用被生成的主键,有一些驱动器不兼容不过仍然可以执行。 -->

          <setting name="useGeneratedKeys" value="false" />

          <!-- 这是默认的执行类型 (SIMPLE: 简单; REUSE: 执行器可能重复使用prepared statements语句;BATCH:

             执行器可以重复执行语句和批量更新) -->

          <setting name="defaultExecutorType" value="SIMPLE" />

          <!-- 配置Java属性-数据库表字段对应驼峰规则 -->

          <setting name="mapUnderscoreToCamelCase" value="true" />

       </settings>

       <plugins>

       <!-- 读写分离拦截器 -->

       <plugin interceptor="com.bodsite.common.datasource.DynamicDataSourceInterceptor">

       </plugin>

       </plugins>

    </configuration>

    1.2、测试

    1、启动bodsite-site-service,在bodsite-site中的DemoConsumer类,执行查询

    142905_8Z9U_1416405.png

    142915_VYrv_1416405.png

    142922_IrdS_1416405.png

    142927_kHq1_1416405.png  

    2、在bodsite-site中的DemoConsumer类,执行插入

    142946_zSQs_1416405.png

    142952_sQ4X_1416405.png

    项目地址:https://git.oschina.net/bodsite/bodsite

    转载于:https://my.oschina.net/u/1416405/blog/837563

    展开全文
  • 写了一个sql拦截器用于打印sql语句的日志 package com.logext.sql.mybatis; import java.io.IOException; import java.io.InputStream; import java.io.Reader; import java.sql.Blob; import java.sql.Clob; ...
  • /** * @author fuwenshen * @description 拦截器 (代码模式+责任链模式) * @date 2020/3/25 */ /** * Invocation 用于保存代理对应反射嗲用需要的数据 ,方便传递 */ class Invocation{ // 代理目标 private Object...
  • spring boot mybatis拦截器

    千次阅读 2019-08-25 13:20:43
    mybaits拦截器 package com.chinamobile.scm.masterdata.interceptor; import com.alibaba.fastjson.JSON; import lombok.extern.slf4j.Slf4j; import org.apache.ibatis.executor.Executor; import org.apache....
  •  已知拦截器能够拦截四种类型:Executor、ParameterHandler、ResultSetHandler、StatementHandler。 1.1、不同类型拦截器的执行顺序 背景:不同类型 项目地址:https://github.com/bjlhx15/mybatis.git中的...
  • 实现Mybatis官方提供的拦截器,用于记录SQL语句的执行时间 package com.github.bjlhx15.mybatis; /** * @author lihongxu * @since 2018/11/15 下午4:02 */ import org.apache.ibatis.executor....
  • mybatis的分页插件底层其实使用的就是mybatis的拦截器,mybatis的拦截器可以拦截到增删改查的方法,然后在执行这些方法之前做一些操作,类似与aop面向切面编程的思想,下面我们自定义一个拦截器 1.1 自定义拦截器...
  •  Mybatis采用责任链模式,通过动态代理组织多个插件(拦截器),通过这些插件可以改变Mybatis的默认行为(诸如SQL重写之类的)  Mybatis是通过动态代理的方式实现拦截的  拦截器(Interceptor)在 Mybatis 中被...
  • 一、概述 基本项目搭建 技术框架:spring web mvc 、日志【slf4j、log4j2】、mybatis、druid、jetty插件启动、mybatis-generator逆向配置生产dao、分页插件pagehelper 项目地址:... ...
  • 关于我个人mybatis拦截器对于分页的看法 一、对于mybaits的认知 二、关于mybatis的分页 三、mybatis拦截器分页对项目各方面效率的影响 总结 一、对于mybaits的认知由于我还是一个学生,mybatis是我自学的,所以下面...
  • 该系列文章针对Mybatis3.5.1版本一、Mybatis 插件的作用Mybatis 针对 SQL 映射语句执行过程中进行拦截处理,而对应的拦截器 Mybaits 又称之为 插件(这些插件就是Mybatis的扩展点)在 Mybaits 中允许用插件来拦截的...
  • 使用mybaits框架操作数据库时每张表的增删改查都需要涉及这几个字段,因此可以自定义拦截器对公共字段进行统一处理。xml文件的sql中可以不用写公共字段,由拦截器进行赋值处理,或抛出异常。话不多说,直接上代码。 ...
  • 插件是用来改变或者扩展MyBaits原有的功能,MyBatis对外提供的扩展点共有四个,可以使用插件进行拦截的接口和方法如下: Executor update (updaye、query、flushStatement、commit、rollback、getTransaction、...
  • MyBaits是一个开源的优秀的持久层框架,SQL语句与代码分离,面向配置的编程,良好支持复杂数据映射,动态SQL;MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。MyBatis 避免了几乎所有的 JDBC ...
  • 一、概述Mybatis采用责任链模式,通过动态代理组织多个插件(拦截器),通过这些插件可以改变Mybatis的默认行为(诸如SQL重写之类的)Mybatis是通过动态代理的方式实现拦截的拦截器(Interceptor)在 Mybatis 中被当做插件...
  • 插件是用来改变或者扩展 mybatis 的原有的功能, mybaits 的插件就是通过继承Interceptor 拦截器实现的; MyBatis 中能使用插件进行拦截的接口和方法如下: 自定义插件 自定义慢查询插件,记录项目中执行慢的Sql @...
  • pageHelper分页助手

    2018-12-16 19:28:09
    今天看了看pagehelper的相关东西,发现它的原理是通过配置了一个mybaits拦截器,拦截到 PageHelper.startPage(page, rows); 之后第一个查询语句,然后将其拼接成不同的sql再进行查询   @Override public ...
  • 一、实现原理   ...在Mybatis配置xml中配置拦截器插件:  &lt;plugins&gt;  &lt;!-- com.github.pagehelper为PageHelper类所在包名 --&gt;  &lt;plugin intercepto...
  • 插件是用来改变或者扩展mybatis的原有的功能,mybaits的插件就是通过继承Interceptor拦截器实现的; mybatis中能使用插件进行拦截的接口和方法如下: Executor(update、query 、 flushStatment 、 commit 、 ...
  • 身份认证,整合Apache Shiro和自定义拦截器两种方式13.发送邮件14.消息队列,集成RabbitMQ15.生成token 集成jwt1后台gradle构建,2springboot,3,系统架构,4.模板引擎---thymeleaf或者json返回,集成fastjson,5....
  • 前景回顾第一节 从零开始手写 mybatis...下面我们一起来体验一下这有趣的灵魂带来的痛苦与快乐~插件的作用在实际开发过程中,我们经常使用的Mybaits插件就是分页插件了,通过分页插件我们可以在不用写count语句和li...

空空如也

空空如也

1 2 3 4
收藏数 64
精华内容 25
关键字:

mybaits拦截器