Mybatis进阶-类型处理器和SQL拦截器

Mybatis进阶-类型处理器和SQL拦截器

1.类型处理器

  1. Mybatis之所以可以完成数据库类型和Java类型之间的转换,是通过很多的类型处理器来实现的。如IntegerTypeHandlerStringTypeHandler
  2. 如果数据库中保存的是1,2,3,Java类型是List。即通过,分割后转换为List,就需要使用自定义的TypeHandler来完成了。
  3. 实体类和自定义TypeHandler
@Data
public class UserEntity {

    private Integer id;
    private String age;
    private String name;
    private String phone;

    // 映射数据库的address列,值为 1,2,3
    private List<String> address;
}

@Slf4j
@MappedTypes(value = {List.class}) // Java类型
@MappedJdbcTypes(value = {JdbcType.VARCHAR}) // 数据库类型
public class AddressTypeHandler implements TypeHandler<List<String>> {

    // 入参会调用setParameter()方法,需要结合parameterMap使用,但是parameterMap官方已经不推荐使用了。
    @Override
    public void setParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException {
        ps.setString(i, String.join(",", parameter));
    }

    // 查询结果映射时会调用getResult()方法。
    @Override
    public List<String> getResult(ResultSet rs, String columnName) throws SQLException {

        String value = rs.getString(columnName);
        return Arrays.asList(value.split(","));
    }

    @Override
    public List<String> getResult(ResultSet rs, int columnIndex) throws SQLException {

        return null;
    }

    @Override
    public List<String> getResult(CallableStatement cs, int columnIndex) throws SQLException {

        return null;
    }
}
  1. 自定义TypeHandler使用的两种方式。

    1. 全局引入。全局引入后实体类中的所有的List类型都会使用TypeHandler来处理,即如果有另一个实体类PersonEntity,PersonEntity中也有List属性,依然会使用TypeHandler来处理。
    mybatis:
      # 全局引入HandlerType
      type-handlers-package: com.mybatis.handler
    
    1. 局部引入。
    @Repository
    public interface UserMapper {
    
        // 在查询时使用@Result引入类型处理器,column为数据库的列,
        // typeHandler定义类型处理器。
        @Select("select * from tb_user")
        @Result(column = "address", typeHandler = AddressTypeHandler.class)
        List<UserEntity> selectAll();
    }
    
    <!-- 如果是xml配置文件,则需要使用resultMap来引入类型处理器 -->
    <resultMap id="userEntityResultMap" type="com.rainbow.mybatis.entity.UserEntity">
        <result column="address" property="address" typeHandler="com.rainbow.mybatis.handler.AddressTypeHandler"/>
    </resultMap>
    <select id="selectById" resultMap="userEntityResultMap">
        select * from tb_user where id = #{id}
    </select>
    
  2. 使用类型处理器将Java类型转换为数据库类型,即List转换为String。

<!-- 如果需要将Java类型的参数转换为数据库类型,则需要使用parameterMap,指定列address添加TypeHandler。
	参数使用 (?, ?, ?, ?)来指定。但是Mybatis官方不推荐使用parameterMap,后面肯能会弃用。
	推荐使用内置方式获取,即(#{age}, #{name}, #{phone}, #{address})。对于这种内置方式
	的参数类型处理器没有找到待实现的方式。
-->
<parameterMap id="userEntityParameterMap" type="com.rainbow.mybatis.entity.UserEntity">
    <parameter property="age" />
    <parameter property="name" />
    <parameter property="phone" />
    <parameter property="address" typeHandler="com.rainbow.mybatis.handler.AddressTypeHandler" />
</parameterMap>

<insert id="insert" parameterMap="userEntityParameterMap">
    insert into tb_user (age, name, phone, address) values (?, ?, ?, ?)
</insert>

2.SQL拦截器

  1. SQL拦截器可以在SQL发送给数据库前对SQL进行修改,如分页插件的实现。
  2. 自定义SQL拦截器。
@Slf4j
// type= Executor.class,拦截Executor接口的实现类,method中指定拦截update方法,因为
// 增删改都是通过update来完成的。args中指定了Executor.update()方法的参数。
@Intercepts({@Signature(type= Executor.class, method = "update",
        args = {MappedStatement.class, Object.class})})
public class SqlInterceptor implements Interceptor {

    @Override
    public Object intercept(Invocation invocation) throws Throwable {

        // args数组的第一个参数主要携带SQL类型的信息,是MappedStatement类型。
        // agrs数组的第二个参数就是定义的Java类型。
        Object[] invocationArgs = invocation.getArgs();
        SqlCommandType sqlCommandType = null;

        for (Object o : invocationArgs) {
            if (o instanceof MappedStatement) {
                MappedStatement mappedStatement = (MappedStatement) o;
                sqlCommandType = mappedStatement.getSqlCommandType();
                log.info("SQL类型 {}", sqlCommandType);
            } else if (o instanceof UserEntity) {
                if (SqlCommandType.INSERT == sqlCommandType) {
                    log.info("insert");
                    BeanUtil.setProperty(o, "phone", "15829551759");
                } else if (SqlCommandType.UPDATE == sqlCommandType) {
                    log.info("update");
                }
            }
        }
        return invocation.proceed();
    }
}
  1. SQL拦截器的使用。
@Slf4j
@Configuration
public class MybatisConfig {

    @Autowired
    public MybatisConfig(SqlSessionFactory sqlSessionFactory) {
        log.info("Mybatis中增加自己实现的拦截器");
        sqlSessionFactory.getConfiguration().addInterceptor(new SqlInterceptor());
    }
}
posted @ 2022-06-01 15:07  行稳致远方  阅读(114)  评论(0)    收藏  举报