Welcome everyone

mybatis 自定义 typeHandler

java 汪明鑫 1497浏览 0评论

无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成 Java 类型。Mybatis默认为我们实现了许多TypeHandler, 当我们没有配置指定TypeHandler时,Mybatis会根据参数或者返回结果的不同,默认为我们选择合适的TypeHandler处理。

 

当然我们可以自定义typeHandler,本文主要做一个字段自动序列化和反序列化的typeHandler

后面抽时间学习下typeHandler的相关源码

 

比如我们的person表,有个扩展字段extra以json字符串存扩展信息,每次存都要序列化,取反序列化,比较麻烦

我们可以写一个typeHandler,实现自动序列化、反序列化

 

 

Spring Boot 项目

需要的依赖:tk-mybatis,fastJson

直接附上pom

 

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.2.1.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>pers.wmx</groupId>
    <artifactId>tk-mybatis</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>tk-mybatis</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-freemarker</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

        <!--tk mybatis依赖-->
        <dependency>
            <groupId>tk.mybatis</groupId>
            <artifactId>mapper-spring-boot-starter</artifactId>
            <version>2.1.5</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.61</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

 

写一个扩展字段基类,其他类有扩展字段可以继承

package pers.wmx.tkmybatis;

import lombok.Data;

/**
 * @author wmx
 * @date 2019-12-16
 */
@Data
public class BaseExtra {
}

 

person扩展类

package pers.wmx.tkmybatis;

import lombok.Data;

/**
 * @author wmx
 * @date 2019-12-16
 */
@Data
public class PersonExtra extends BaseExtra{
    private Integer height;
    private Integer weight;
    private String nickname;
}

 

person类

package pers.wmx.tkmybatis;

import lombok.Data;
import org.apache.ibatis.type.JdbcType;
import tk.mybatis.mapper.annotation.ColumnType;

import javax.persistence.Id;
import javax.persistence.Table;

/**
 * @author wmx
 * @date 2019-12-06
 */
@Data
@Table(name = "person")
public class Person {

    @Id
    private Integer id;

    private String name;

    private Integer age;

    @ColumnType(jdbcType = JdbcType.LONGNVARCHAR,typeHandler = ExtraTypeHandler.class)
    private PersonExtra extra;

}

jdbcType 指定数据库存储类型,typeHandler 指定该字段使用的handler,不指定会使用mybatis默认的

 

下面是重头戏,就是我们的typeHandler

package pers.wmx.tkmybatis;

import com.alibaba.fastjson.JSON;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.apache.ibatis.type.TypeHandler;

import java.sql.*;

/**
 * @author wmx
 * @date 2019-12-16
 */
@MappedJdbcTypes({JdbcType.LONGNVARCHAR})
@MappedTypes({PersonExtra.class})
public class ExtraTypeHandler implements TypeHandler<PersonExtra> {

    @Override
    public void setParameter(PreparedStatement preparedStatement, int i, PersonExtra parameter, JdbcType jdbcType) throws SQLException {
        if(parameter == null){
            preparedStatement.setNull(i, Types.LONGNVARCHAR);
        }else{
            preparedStatement.setString(i, JSON.toJSONString(parameter));  //序列化
        }
    }

    @Override
    public PersonExtra getResult(ResultSet resultSet, String columnName) throws SQLException {
        String columnValue = resultSet.getString(columnName);
        return getExtra(columnValue);
    }

    @Override
    public PersonExtra getResult(ResultSet resultSet, int i) throws SQLException {
        return null;
    }

    @Override
    public PersonExtra getResult(CallableStatement callableStatement, int i) throws SQLException {
        return null;
    }

    private PersonExtra getExtra(String columnValue) {
        if(columnValue == null){
            return null;
        }
        return  JSON.parseObject(columnValue,PersonExtra.class);  //反序列化
    }
}

 

然后我们需要配置下typeHandler

application.properties:

## typeHandler
mybatis.type-handlers-package=pers.wmx.tkmybatis

 

跑一波单测:

@Test
    void testInsert(){
        Person person = new Person();
        person.setName("niubi");
        person.setAge(100);

        PersonExtra personExtra = new PersonExtra();
        personExtra.setHeight(180);
        personExtra.setWeight(65); //kg
        personExtra.setNickname("niupi");
        person.setExtra(personExtra);
        personMapper.insertSelective(person);
    }

 

 

查询一下:

@Test
    void testSelect(){
        Example example = new Example(Person.class);
        Example.Criteria criteria = example.createCriteria();
        criteria.andEqualTo("name", "niubi");
        List<Person> people = personMapper.selectByExample(example);
        System.out.println(people);
    }

输出:

[Person(id=16, name=niubi, age=100, extra=PersonExtra(height=180, weight=65, nickname=niupi))]

实现了自动反序列化

 

问题出现了。。。

这样一个TypeHandler只能显示指定PersonExtra

我们可以根据范型来改写一个通用的序列化handler

 

Now:

@Data
@Table(name = "person")
public class Person {

    @Id
    private Integer id;

    private String name;

    private Integer age;

    @ColumnType(jdbcType = JdbcType.LONGNVARCHAR)
    private PersonExtra extra;

}
package pers.wmx.tkmybatis;

import com.alibaba.fastjson.JSON;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.TypeHandler;

import java.sql.*;

/**
   通用序列化反序列化handler
 * @author wmx
 * @date 2019-12-16
 */
@MappedJdbcTypes({JdbcType.LONGNVARCHAR})
public class GenericExtraTypeHandler<T> implements TypeHandler<T> {

    private Class<T> type;

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

    @Override
    public void setParameter(PreparedStatement preparedStatement, int i, T parameter, JdbcType jdbcType) throws SQLException {
        if(parameter == null){
            preparedStatement.setNull(i, Types.LONGNVARCHAR);
        }else{
            preparedStatement.setString(i, JSON.toJSONString(parameter));
        }
    }

    @Override
    public T getResult(ResultSet resultSet, String columnName) throws SQLException {
        String columnValue = resultSet.getString(columnName);
        return getExtra(columnValue);
    }

    @Override
    public T getResult(ResultSet resultSet, int i) throws SQLException {
        String columnValue = resultSet.getString(i);
        return getExtra(columnValue);
    }

    @Override
    public T getResult(CallableStatement callableStatement, int i) throws SQLException {
        String columnValue = callableStatement.getString(i);
        return getExtra(columnValue);
    }

    private T getExtra(String columnValue) {
        if(columnValue == null){
            return null;
        }
        return  JSON.parseObject(columnValue,type);
    }
}

 

我们需要写一个配置类把扩展字段和typehandler绑定

package pers.wmx.tkmybatis;

import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Component;

import javax.sql.DataSource;

/**
 * @author wmx
 * @date 2019-12-16
 */
@Component
public class MybatisConfiguration {
    @Bean
    public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        Configuration configuration = new Configuration();
        //注册typeHandler
        configuration.getTypeHandlerRegistry().register(PersonExtra.class,new GenericExtraTypeHandler<>(PersonExtra.class));
        factory.setConfiguration(configuration);
        return factory.getObject();
    }
}

 

跑单测:

 

ok!

转载请注明:汪明鑫的个人博客 » mybatis 自定义 typeHandler

喜欢 (0)

说点什么

您将是第一位评论人!

提醒
avatar
wpDiscuz