JavaWeb框架 - Mybatis07

Mybatis中自定义类型处理器:

  1. 什么是Mybatis中的typeHandler:MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型 这也就是Mybatis能进行结果集封装的原因 Mybatis中已经是定义好了 非常多种的类型处理器 但是有些特定的需求还是不能实现
    在这里插入图片描述
  2. 实现自定义的类型处理器:有两种方式:一种是实现TypeHandler接口,还有一种简化的写法就是继承自BaseTypeHandler类
    • TypeHandler 源码:

      //此接口作用是用于指定jdbc与java的数据类型间对应关系处理。
      public interface TypeHandler<T> {
        // 保存操作,数据入库之前时数据处理
        void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
        //下面三个则是,从数据库加载数据后,vo对象封装前的数据处理
        T getResult(ResultSet rs, String columnName) throws SQLException;
        T getResult(ResultSet rs, int columnIndex) throws SQLException;
        T getResult(CallableStatement cs, int columnIndex) throws SQLException;
      }
      
    • 使用BaseTypeHandler类实现自定义类型处理器:

      @MappedJdbcTypes(JdbcType.VARCHAR)
      @MappedTypes(Date.class)
      public class MyDateTypeHandler extends BaseTypeHandler<Date> {
          // 插入数据之前进行类型转换 重新定义要写往数据库的数据
          @Override
          public void setNonNullParameter(PreparedStatement ps, int i, Date parameter, JdbcType jdbcType) throws SQLException {
              ps.setString(i, String.valueOf(parameter.getTime()));
          }
          // 下面的这三种方法就是 将从数据库读出的数据类型进行转换
          @Override
          public Date getNullableResult(ResultSet rs, String columnName) throws SQLException {
              String dateString = rs.getString(columnName);
              if(dateString != null){
                  return new Date(dateString);
              }
              return null;
          }
      
          @Override
          public Date getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
              String dateString = rs.getString(columnIndex);
              if(dateString != null){
                  return new Date(dateString);
              }
              return null;
          }
      
          @Override
          public Date getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
              String dateString = cs.getString(columnIndex);
              if(dateString != null){
                  return new Date(dateString);
              }
              return null;
          }
      }
      
      • 详解:
        • @MappedJdbcTypes定义的是JdbcType类型,这里的类型不可自己随意定义,必须要是枚举类org.apache.ibatis.type.JdbcType所枚举的数据类型
        • @MappedTypes定义的是JavaType的数据类型,描述了哪些Java类型可被拦截 传入的是class
        • 在我们启用了我们自定义的这个TypeHandler之后,数据的读写都会被这个类所过滤
        • setNonNullParameter方法中,我们重新定义要写往数据库的数据
        • 另外三个方法中我们将从数据库读出的数据类型进行转换
    • 仅仅是定义类型处理器是不能使用的 还需要在配置文件中进行配置:

      <!-- mybatis-config.xml -->
      <typeHandlers>
        <!--注意jdbcType是枚举类型所以只能是大写 VARCHAR-->
        <typeHandler javaType="Date" jdbcType="VARCHAR" handler="com.edu.typehandler.MyDateTypeHandler"/>
      </typeHandlers>
      
    • 最后在mapper.xml中使用即可

      <insert id="saveOne" parameterType="com.edu.mybatis.entity.Employee" >
            INSERT INTO employee(user_name,gender,email,createtime) 
            VALUES(#{userName},#{gender},#{email},#{createtime,typeHandler=com.edu.mybatis.typehandler.MyDateTypeHandler})
      </insert>
      

Mybatis中插件开发:

  1. Mybatis 中插件常常使用的场景:

    • 分页功能:mybatis的分页默认是基于内存分页的(查出所有,再截取),数据量大的情况下效率较低,不过使用mybatis插件可以改变该行为,只需要拦截StatementHandler类的prepare方法,改变要执行的SQL语句为分页语句即可

    • 公共字段统一赋值:一般业务系统都会有创建者,创建时间,修改者,修改时间四个字段,对于这四个字段的赋值,实际上可以在DAO层统一拦截处理,可以用mybatis插件拦截Executor类的update方法,对相关参数进行统一赋值即可;

    • 性能监控:对于SQL语句执行的性能监控,可以通过拦截Executor类的update, query等方法,用日志记录每个方法执行的时间;

    • 权限控制:基于插件机制 很好的进行权限的控制

  2. 支持拦截的方法 是叫插件但是其实就是拦截器:

    • 执行器Executor(update、query、commit、rollback等方法);
    • 参数处理器ParameterHandler(getParameterObject、setParameters方法);
    • 结果集处理器ResultSetHandler(handleResultSets、handleOutputParameters等方法);
    • SQL语法构建器StatementHandler(prepare、parameterize、batch、update、query等方法);
  3. 分页插件开发:这里只实现intercept方法

    @Intercepts({@Signature(type=StatementHandler.class,method="prepare",args={Connection.class})})
    public class PagePlugin implements Interceptor {
    
    	private static String dialect = "";	
    	private static String pageSqlId = ""; //mapper.xml中需要拦截的ID
    	
    	public Object intercept(Invocation ivk) throws Throwable {
    		// TODO Auto-generated method stub
    		if(ivk.getTarget() instanceof RoutingStatementHandler){
    			RoutingStatementHandler statementHandler = (RoutingStatementHandler)ivk.getTarget();
    			BaseStatementHandler delegate = (BaseStatementHandler) ReflectHelper.getValueByFieldName(statementHandler, "delegate");
    			MappedStatement mappedStatement = (MappedStatement) ReflectHelper.getValueByFieldName(delegate, "mappedStatement");
    			
    			if(mappedStatement.getId().matches(pageSqlId)){ //拦截需要分页的SQL
    				BoundSql boundSql = delegate.getBoundSql();
    				Object parameterObject = boundSql.getParameterObject();//分页SQL<select>中parameterType属性对应的实体参数,即Mapper接口中执行分页方法的参数,该参数不得为空
    				if(parameterObject==null){
    					throw new NullPointerException("parameterObject尚未实例化!");
    				}else{
    					Connection connection = (Connection) ivk.getArgs()[0];
    					String sql = boundSql.getSql();
    					//String countSql = "select count(0) from (" + sql+ ") as tmp_count"; //记录统计
    					String countSql = "select count(0) from (" + sql+ ")  tmp_count"; //记录统计 == oracle 加 as 报错(SQL command not properly ended)
    					PreparedStatement countStmt = connection.prepareStatement(countSql);
    					BoundSql countBS = new BoundSql(mappedStatement.getConfiguration(),countSql,boundSql.getParameterMappings(),parameterObject);
    					setParameters(countStmt,mappedStatement,countBS,parameterObject);
    					ResultSet rs = countStmt.executeQuery();
    					int count = 0;
    					if (rs.next()) {
    						count = rs.getInt(1);
    					}
    					rs.close();
    					countStmt.close();
    					//System.out.println(count);
    					Page page = null;
    					if(parameterObject instanceof Page){	//参数就是Page实体
    						 page = (Page) parameterObject;
    						 page.setEntityOrField(true);	 
    						page.setTotalResult(count);
    					}else{	//参数为某个实体,该实体拥有Page属性
    						Field pageField = ReflectHelper.getFieldByFieldName(parameterObject,"page");
    						if(pageField!=null){
    							page = (Page) ReflectHelper.getValueByFieldName(parameterObject,"page");
    							if(page==null)
    								page = new Page();
    							page.setEntityOrField(false); 
    							page.setTotalResult(count);
    							ReflectHelper.setValueByFieldName(parameterObject,"page", page); //通过反射,对实体对象设置分页对象
    						}else{
    							throw new NoSuchFieldException(parameterObject.getClass().getName()+"不存在 page 属性!");
    						}
    					}
    					String pageSql = generatePageSql(sql,page);
    					ReflectHelper.setValueByFieldName(boundSql, "sql", pageSql); //将分页sql语句反射回BoundSql.
    				}
    			}
    		}
    		return ivk.proceed();
    	}
    }
    
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

上山打卤面

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值