Mybatis中自定义类型处理器:
- 什么是Mybatis中的typeHandler:MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型 这也就是Mybatis能进行结果集封装的原因 Mybatis中已经是定义好了 非常多种的类型处理器 但是有些特定的需求还是不能实现
- 实现自定义的类型处理器:有两种方式:一种是实现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中插件开发:
-
Mybatis 中插件常常使用的场景:
-
分页功能:mybatis的分页默认是基于内存分页的(查出所有,再截取),数据量大的情况下效率较低,不过使用mybatis插件可以改变该行为,只需要拦截StatementHandler类的prepare方法,改变要执行的SQL语句为分页语句即可
-
公共字段统一赋值:一般业务系统都会有创建者,创建时间,修改者,修改时间四个字段,对于这四个字段的赋值,实际上可以在DAO层统一拦截处理,可以用mybatis插件拦截Executor类的update方法,对相关参数进行统一赋值即可;
-
性能监控:对于SQL语句执行的性能监控,可以通过拦截Executor类的update, query等方法,用日志记录每个方法执行的时间;
-
权限控制:基于插件机制 很好的进行权限的控制
-
-
支持拦截的方法 是叫插件但是其实就是拦截器:
- 执行器Executor(update、query、commit、rollback等方法);
- 参数处理器ParameterHandler(getParameterObject、setParameters方法);
- 结果集处理器ResultSetHandler(handleResultSets、handleOutputParameters等方法);
- SQL语法构建器StatementHandler(prepare、parameterize、batch、update、query等方法);
-
分页插件开发:这里只实现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(); } }