类型处理器(typeHandlers)
无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
一些常用的情景很有用,无须多次执行重复动作。如数据库字段标记为 0 代表女性,1 代表男性。可以自定义类型进行数据出入解析。还有数据库保存 json 统一处理,数据库时间戳统一转化等。下面例子处理转化枚举类型。
1.定义枚举
//用于SexTypeHandler的性别转换器枚举
public enum Sex {
//每一个类型都是一个枚举类(Sex)的实例
MALE(1, "男"),
FMALE(0, "女");
//用于保存在数据库
private int SexCode;
//用于UI展示
private String SexName;
Sex(int sexCode, String sexName) {
SexCode = sexCode;
SexName = sexName;
}
public int getSexCode() {
return SexCode;
}
//通过SexCode的值来获取Sex枚举类型,数据库只需保存code,通过代码解析成Sex类型
public static Sex getSexFromCode(int code) {
for (Sex sex : Sex.values()) {
if (sex.getSexCode() == code) {
return sex;
}
}
return null;
}
}
2.定义实体 entity
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class Typehandle extends Model<Typehandle> {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* name
*/
private String name;
/**
* 性别
*/
private Sex sex;
/**
* 配置,保存json格式
*/
private String config;
/**
* 年龄
*/
private Integer age;
}
其中注意到,性别参数类型为定义的枚举类 Sex,
3.自定义处理器
注解 @MappedTypes({Sex.class}) 表示遇到类型为 Sex 类型的数据类型统一走该方法进行处理,也可以在 XML 中指定, xml 配置 javaType。
老版本< 3.1.2 使用 el 表达式控制插入使用相应的 handler
@TableField(el = "images, typeHandler=com.zyzc.xyzy.config.db.JsonTypeHandler")
private List images;
mapper里json型字段到类的映射。
用法一: ps(mybatis-plus 使用 3.1.2 版本使用
@TableField(typeHandler = CollectionTypeHandler.class)
入库),无需扫描,局部使用,针对resultMap返回入库:#{jsonDataField, typeHandler=com.adu.spring_test.mybatis.typehandler.JsonTypeHandler}
出库:
<resultMap> <result property="jsonDataField" column="json_data_field" javaType="com.xxx.MyClass" typeHandler="com.adu.spring_test.mybatis.typehandler.JsonTypeHandler"/> </resultMap>
用法二(等于注解,springboot的typehandle扫描): 1)在mybatis-config.xml中指定handler:
<typeHandlers> <typeHandler handler="com.adu.spring_test.mybatis.typehandler.JsonTypeHandler" javaType="com.xxx.MyClass"/> </typeHandlers>
2)在MyClassMapper.xml里直接select/update/insert。
自定义 Handler 和实现 TypeHandler<T>
接口或则继承 BaseTypeHandler<T>
接口。
@MappedTypes({Sex.class})
public class SexTypeHandle implements TypeHandler<Sex> {
@Override
public void setParameter(PreparedStatement preparedStatement, int i, Sex sex, JdbcType jdbcType) throws SQLException {
//设置第i个参数的值为传入sex的code值,preparedStatement为执行数据库操纵的对象
//传值的时候是一个sex对象,但是当进行映射插入的时候就会转化为sex的code值进行存储
preparedStatement.setInt(i, sex.getSexCode());
}
@Override
public Sex getResult(ResultSet resultSet, String s) throws SQLException {
//获取数据库存储的sex的code值
int result = resultSet.getInt(s);
return Sex.getSexFromCode(result);
}
@Override
public Sex getResult(ResultSet resultSet, int i) throws SQLException {
int result = resultSet.getInt(i);
return Sex.getSexFromCode(result);
}
@Override
public Sex getResult(CallableStatement callableStatement, int i) throws SQLException {
int result = callableStatement.getInt(i);
return Sex.getSexFromCode(result);
}
}
4.配置
以下配置二选一
yaml 配置 增加配置 type-handlers-package 指定类型处理的包
mybatis-plus: check-config-location: true type-aliases-package: com.entity mapper-locations: classpath:mybatis/*/*.xml config-location: classpath:mybtais-config.xml type-handlers-package: com.typehandle
mybatis-config.xml 配置 package 标签指定包 和 typeHandler 标签指定具体类型。
<typeHandlers> <!--<package name="com.typehandle"></package>--> <typeHandler handler="com.typehandle.SexTypeHandle"></typeHandler> </typeHandlers>
5.测试
访问 insert 后在访问 select,页面显示的是枚举的值。
@RestController
@RequestMapping("/")
public class TypehandleController {
@Autowired
private TypehandleService typehandleService;
@Autowired
private TypehandleMapper typehandleMapper;
@RequestMapping("/insert")
public void test() {
Typehandle typehandle = new Typehandle();
typehandle.setAge(24).setConfig("json").setName("blin").setSex(Sex.FMALE);
typehandleService.save(typehandle);
}
@RequestMapping("/select")
public void select() {
Typehandle typehandle = typehandleMapper.selectById(2);
System.out.println(typehandle.toString());
}
}
结果:
Typehandle(id=2, name=blin, sex=FMALE, config=json, age=24)
处理 list 集合转换
ListTypeHandle
@MappedTypes({List.class})
public class ListTypeHandle extends BaseTypeHandler<List> {
@Override
public void setNonNullParameter(PreparedStatement preparedStatement, int i, List list, JdbcType jdbcType) throws SQLException {
preparedStatement.setString(i, JSON.toJSONString(list));
}
@Override
public List getNullableResult(ResultSet resultSet, String s) throws SQLException {
return JSON.parseArray(resultSet.getString(s));
}
@Override
public List getNullableResult(ResultSet resultSet, int i) throws SQLException {
// i 从1开始
return JSON.parseArray(resultSet.getString(i));
}
@Override
public List getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
return JSON.parseArray(callableStatement.getString(i));
}
}
对应的实体类修改为 list
@Data
@Accessors(chain = true)
@NoArgsConstructor
@AllArgsConstructor
public class Typehandle extends Model<Typehandle> {
private static final long serialVersionUID = 1L;
/**
* id
*/
@TableId(value = "id", type = IdType.AUTO)
private Long id;
/**
* name
*/
private String name;
/**
* 性别
*/
private Sex sex;
/**
* 配置,保存json格式
*/
private List config;
/**
* 年龄
*/
private Integer age;
}
配置已经配置为扫描 typehandle 包。所有目录下的类型处理器都会注册,此外,注解 @MappedTypes
全局声明处理 List 集合,也无需在 XML 文件指定对应的处理器,(ps:xml 文件声明的优先)