Mybatis进阶-类型处理器和SQL拦截器
Mybatis进阶-类型处理器和SQL拦截器
1.类型处理器
- Mybatis之所以可以完成数据库类型和Java类型之间的转换,是通过很多的类型处理器来实现的。如IntegerTypeHandler、StringTypeHandler。
- 如果数据库中保存的是1,2,3,Java类型是List
。即通过,分割后转换为List,就需要使用自定义的TypeHandler来完成了。 - 实体类和自定义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;
}
}
-
自定义TypeHandler使用的两种方式。
- 全局引入。全局引入后实体类中的所有的List
类型都会使用TypeHandler来处理,即如果有另一个实体类PersonEntity,PersonEntity中也有List 属性,依然会使用TypeHandler来处理。
mybatis: # 全局引入HandlerType type-handlers-package: com.mybatis.handler
- 局部引入。
@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>
- 全局引入。全局引入后实体类中的所有的List
-
使用类型处理器将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拦截器
- SQL拦截器可以在SQL发送给数据库前对SQL进行修改,如分页插件的实现。
- 自定义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();
}
}
- SQL拦截器的使用。
@Slf4j
@Configuration
public class MybatisConfig {
@Autowired
public MybatisConfig(SqlSessionFactory sqlSessionFactory) {
log.info("Mybatis中增加自己实现的拦截器");
sqlSessionFactory.getConfiguration().addInterceptor(new SqlInterceptor());
}
}