1. 程式人生 > 實用技巧 >MyBatis中TypeHandler的使用

MyBatis中TypeHandler的使用

最終遇到一個問題,就是在使用MyBatis儲存資料的時候裡面的javabean得欄位不是單純的欄位,而是包含了物件(也是javaBean)。這種方式並不奇怪,但是以為我這次遇到的是四次巢狀。所以我就使用了TypeHandler來處理試試,測試的時候還是以雙層巢狀為例子。

基本環境的準備

實體類程式碼:

public class Person {
    private int id;
    private Student student;

    public int getId() {
        return id;
    }

    public void setId(int
id) { this.id = id; } public Student getStudent() { return student; } public void setStudent(Student student) { this.student = student; } @Override public String toString() { return "Person{" + "id=" + id + ", student=" + student + '}'; } }
public class Student {
    private int id;
    private String name;

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}

Mapper介面

public interface PersonMapper {
    /**
     * 新增一條記錄
     *
     * @param p
     * @return
     */
    Integer insertOne(Person p);

    /**
     * 查詢一條記錄
     *
     * @param p
     * @return
     */
    Person query();


}

Mapper.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mybatistypehandler.mapper.PersonMapper">

    <resultMap id="BaseResultMap" type="com.example.mybatistypehandler.domain.Person">
        <id column="id" jdbcType="INTEGER" property="id"></id>
        <result column="student" jdbcType="VARCHAR" property="student"
                typeHandler="com.example.mybatistypehandler.util.JsonTypeHandler"/>
    </resultMap>


    <insert id="insertOne">

    INSERT INTO
        person(student)
    VALUES(#{student,typeHandler=com.example.mybatistypehandler.util.JsonTypeHandler})

    </insert>


    <select id="query" resultMap="BaseResultMap">
        select id,student from person limit 1
    </select>

</mapper>

service以及實現類

public interface PersonService {
    /**
     * 新增一條記錄
     *
     * @param p
     * @return
     */
    Integer insert(Person p);

    /**
     * 查詢一條記錄
     *
     * @param p
     * @return
     */
    Person query();
}
@Service
public class PersonServiceImpl implements PersonService {
    @Autowired(required = false)
    PersonMapper personMapper;

    @Override
    public Integer insert(Person p) {
        return personMapper.insertOne(p);
    }

    @Override
    public Person query() {
        return personMapper.query();
    }
}

Controller

@RestController
@RequestMapping("t")
public class MyTypeHandlerController {

    @Autowired
    PersonService personService;


    @RequestMapping("save")
    public String save(@RequestBody Person person) {
        personService.insert(person);
        return "SUCCESS";
    }

    @RequestMapping("query")
    public String query() {
        Person query = personService.query();
        System.out.println(query);
        System.out.println(query.getStudent());
        return "SUCCESS";
    }


}

yml配置檔案

server:
  port: 8899
spring:
  application: TypeHandlerTest
  datasource:
    druid:
      #指定資料庫型別
      db-type: com.alibaba.druid.pool.DruidDataSource
      driver-class-name: com.mysql.cj.jdbc.Driver
      url: jdbc:mysql://192.168.20.152:3306/TypeHandlerTest?useUnicode=true&characterEncoding=utf8&useSSL=false
      username: root
      password: 111111


mybatis:
  mapper-locations: classpath:mapper/**.xml

工具類

public class JsonTypeHandler<T extends Object> extends BaseTypeHandler<T> {

    private Class<T> clazz;


    public JsonTypeHandler(Class<T> clazz) {
        if (clazz == null) {
            throw new IllegalArgumentException("Type argument cannot be null");
        }
        this.clazz = clazz;
    }

    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, T t, JdbcType jdbcType) throws SQLException {
        preparedStatement.setString(i, this.toJson(t));
    }

    @Override
    public T getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
        return this.toObject(resultSet.getString(columnName), clazz);
    }

    @Override
    public T getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {
        return this.toObject(resultSet.getString(columnIndex), clazz);
    }

    @Override
    public T getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {
        return this.toObject(callableStatement.getString(columnIndex), clazz);
    }

    private String toJson(T object) {
        try {
            return JSON.toJSONString(object);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }

    private T toObject(String content, Class<?> clazz) {
        if (content != null && !content.isEmpty()) {
            try {
                return (T) JSON.parseObject(content, clazz);
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        } else {
            return null;
        }
    }
}

啟動類

@SpringBootApplication
@MapperScan(basePackages = "com.example.mybatistypehandler.mapper")
public class MybatisTypehandlerApplication {

    public static void main(String[] args) {
        SpringApplication.run(MybatisTypehandlerApplication.class, args);
    }

}

測試

http://localhost:8899/t/save

{
  "student":{
     "name":"小明"
   }
}

物件資料被成功轉成json儲存到資料庫了

那麼查詢的時候能否將json轉成物件儲存起來呢?

http://localhost:8899/t/query

json被成功的封裝到物件中了。

原理

在儲存的時候,實際上走了這個方法:setNonNullParameter

斷點除錯下看看是什麼東西

最後一個是資料庫中的欄位型別。

查詢的除錯

這就是為什麼資料庫中是json,但是查出來卻能直接轉成物件的原因。

另外注意JsonTypeHandler是需要繼承BaseTypeHandler(或者是)實現TypeHandler介面。只要這樣MyBatis在處理型別的時候才會去執行相應的方法。

MyBatis中自帶的型別處理

不畏艱險,踐踏實地!