14第十一章:01_自定义类型处理器

一、简单类型与复杂类型

1、三个类型

User类:

@Table(name = "table_user")
public class User {

    @Id
    private Integer userId;

    private String userName;
    private Address address;
    private SeasonEnum season;

    public User() {
    }

    public User(Integer userId, String userName, Address address, SeasonEnum season) {
        this.userId = userId;
        this.userName = userName;
        this.address = address;
        this.season = season;
    }

    public Integer getUserId() {
        return userId;
    }

    public void setUserId(Integer userId) {
        this.userId = userId;
    }

    public String getUserName() {
        return userName;
    }

    public void setUserName(String userName) {
        this.userName = userName;
    }

    public Address getAddress() {
        return address;
    }

    public void setAddress(Address address) {
        this.address = address;
    }

    public SeasonEnum getSeason() {
        return season;
    }

    public void setSeason(SeasonEnum season) {
        this.season = season;
    }

    @Override
    public String toString() {
        return "User{" +
                "userId=" + userId +
                ", userName='" + userName + '\'' +
                ", address=" + address +
                ", season=" + season +
                '}';
    }
}

Address类:

public class Address {
    private String provice;
    private String city;
    private String stree;

    public Address() {
    }

    public Address(String provice, String city, String stree) {
        this.provice = provice;
        this.city = city;
        this.stree = stree;
    }

    public String getProvice() {
        return provice;
    }

    public void setProvice(String provice) {
        this.provice = provice;
    }

    public String getCity() {
        return city;
    }

    @Override
    public String toString() {
        return "Address{" +
                "provice='" + provice + '\'' +
                ", city='" + city + '\'' +
                ", stree='" + stree + '\'' +
                '}';
    }

    public void setCity(String city) {
        this.city = city;
    }

    public String getStree() {
        return stree;
    }

    public void setStree(String stree) {
        this.stree = stree;
    }
}

SeasonEnum 类:

public enum SeasonEnum {
    SPRING("Spring @_@"),
    SUMMER("summer @_@"),
    AUTUMN("autumn @_@"),
    WINTER("winter @_@");

    private String seasonName;

    SeasonEnum(String seasonName) {
        this.seasonName = seasonName;
    }

    public String getSeasonName() {
        return seasonName;
    }

    public void setSeasonName(String seasonName) {
        this.seasonName = seasonName;
    }
}

对于上面的三个类,SeasonEnum 是一个枚举类型,Address 是具有三个 String 类型的引用类型,User 具有两个 String 类型,一个Address 类型和一个枚举类型的复杂类型。

而通用Mapper进行增删改查是只能用于简单类型,对于复杂类型是不支持的。

2、简单类型和复杂类型

基本数据类型:byte、short、int、long、double、float、char、boolean

引用数据类型:类、接口、数组、枚举……

简单类型:只有一个值的类型

复杂类型:多个简单类型组合起来,有多个值的类型

3、通用Mapper处理基本类型与复杂类型

当使用通用Mapper 对 User 类进行查询时,根据查询结果看到,通用Mapper只会处理简单数据类型,忽略复杂数据类型。

注意:通用Mapper默认情况下会忽略复杂类型,对复杂类型不进行从“从类到表”的映射

4、自定义类型处理器

在实际开发中,如果需要把复杂类型也存储到数据库中,

方式一:使用关联的管理,使用多表进行关联;

方式二:把复杂类型整体作为一个字段存储到数据库中,使用类型处理器;

5、BaseTypeHandler

TypeHandler 接口
public interface TypeHandler<T> {

  void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  T getResult(ResultSet rs, String columnName) throws SQLException;

  T getResult(ResultSet rs, int columnIndex) throws SQLException;

  T getResult(CallableStatement cs, int columnIndex) throws SQLException;

}
BaseTypeHandler 抽象类:
public abstract class BaseTypeHandler<T> extends TypeReference<T> implements TypeHandler<T> {
  ....其他方法
  public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

  public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;

  public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;

  public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
}

继承树:

可以看到通用 Mapper 中的类型处理器都继承自 BaseTypeHandler,所以自定义类型处理器时,我们也继承 BaseTypeHandler ,然后重写其中的抽象方法即可。

抽象方法说明:

//将parameter对象转换为字符串存入到 ps 对象的i位置
public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;

//从结果集中获取数据库对应查询结果
//将字符串还原为原始的T类型对象
public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;


public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;


public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;

二、Address 类型处理器

1、创建 AddressTypeHandler 类型处理器

2、继承 BaseTypeHandler 并实现抽象方法

public class AddressTypeHandler extends BaseTypeHandler<Address> {

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, Address address, JdbcType jdbcType) throws SQLException {
        //1. 对 address 对象进行验证
        if (address == null) {
            return;
        }

        //2. 从 Address 对象中取出具体数据
        String provice = address.getProvice();
        String city = address.getCity();
        String stree = address.getStree();

        //3. 拼装成一个字符串
        //规则:各个值之间使用 "," 分开
        StringJoiner joiner = new StringJoiner(",");
        joiner.add(provice).add(city).add(stree);

        String parameterValue = joiner.toString();

        //4. 设置参数
        ps.setString(i, parameterValue);
    }

    @Override
    public Address getNullableResult(ResultSet rs, String columnName) throws SQLException {
        //1. 从结果集根据字段名从rs对象中获取字段值
        String columnValue = rs.getString(columnName);

        //2. 验证 columnValue 是否有效
        if (columnValue == null || columnValue.length() == 0 || !columnValue.contains(",")) {
            return null;
        }

        //3. 根据","对 columnValue 进行拆分
        String[] split = columnValue.split(",");

        //4.从拆分结果数组中获取 Address 需要的具体数据
        return new Address(split[0], split[1], split[2]);
    }

    @Override
    public Address getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
        //1. 从结果集根据字段名从rs对象中获取字段值
        String columnValue = rs.getString(columnIndex);

        //2. 验证 columnValue 是否有效
        if (columnValue == null || columnValue.length() == 0 || !columnValue.contains(",")) {
            return null;
        }

        //3. 根据","对 columnValue 进行拆分
        String[] split = columnValue.split(",");

        //4.从拆分结果数组中获取 Address 需要的具体数据
        return new Address(split[0], split[1], split[2]);
    }

    @Override
    public Address getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
        //1. 从结果集根据字段名从rs对象中获取字段值
        String columnValue = cs.getString(columnIndex);

        //2. 验证 columnValue 是否有效
        if (columnValue == null || columnValue.length() == 0 || !columnValue.contains(",")) {
            return null;
        }

        //3. 根据","对 columnValue 进行拆分
        String[] split = columnValue.split(",");

        //4.从拆分结果数组中获取 Address 需要的具体数据
        return new Address(split[0], split[1], split[2]);
    }
}

3、注册自定义类型处理器 AddressTypeHandler

方式一:字段级别,使用 @ColumnType 注解
    @ColumnType(typeHandler = AddressTypeHandler.class)
    private Address address;
方式二:全局级别,在 MyBatis 配置文件中配置 typeHandlers

① 在要处理的属性上面添加@Column 注解,让通用Mapper把该字段作为一个列处理

    @Column
    private Address address;

② 在全局配置文件中配置 类型处理器

    <!--配置类型转换器-->
    <typeHandlers>
        <!--
            handler属性:指定自定义类型转换器全类名
            javaType属性:指定需要使用“自定义类型转换器”进行类型处理的实体类型
        -->
        <typeHandler handler="com.njf.mapper.typeHandler.AddressTypeHandler" javaType="com.njf.mapper.bean.Address"/>
    </typeHandlers>

4、测试


设置完毕后,address 可以正常处理。

三、枚举类型处理器

1、方式一:让通用Mapper把枚举类型作为简单类型处理

增加一个通用Mapper的配置项

在Spring配置文件中找到 MapperScannerConfigurer,用于配置是否将枚举类型当成基本类型对待。

默认 simpleType 会忽略枚举类型,使用 enumAsSimpleType 配置后悔把枚举按简单类型处理,需要自己配置好 typeHandler。

配置方式如下:

    <bean class="tk.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.njf.mapper.mappers"/>
        <property name="properties">
            <value>
                enumAsSimpleType=true
            </value>
        </property>
    </bean>

本质就是使用 EnumTypeHandler 处理器

2、方式二:让枚举类型配置对应的类型处理器

(1)枚举类型处理器

内置处理器:通用Mapper 定义类内置两个枚举类型处理器:

org.apache.ibatis.type.EnumTypeHandler

org.apache.ibatis.type.EnumOrdinalTypeHandler

自定义处理器:我们也可以自定义枚举类型处理器。

(2)处理器如何注册

自定义类型处理器注册方式同上。

内置类型处理器注册:

方式一:使用@ColumnType 注解(失败)

在枚举类型这里无法使用 @ColumnType 注解注册 MyBatis 内置的枚举类型处理器

    //在枚举类型这里无法使用 @ColumnType 注解注册 MyBatis 内置的枚举类型处理器
    @ColumnType(typeHandler = EnumTypeHandler.class)
    private SeasonEnum season;

报错信息:

方式二:配置文件中进行配置

① 在字段上使用 @Column 注解

注意:加@Column 注解的作用是让通用 Mapper 不忽略枚举类型。

    @Column
    private SeasonEnum season;

② 在全局配置文件中进行配置

    <!--配置类型转换器-->
    <typeHandlers>
        <!--
            handler属性:指定自定义类型转换器全类名
            javaType属性:指定需要使用“自定义类型转换器”进行类型处理的实体类型
        -->
        <typeHandler handler="com.njf.mapper.typeHandler.AddressTypeHandler" javaType="com.njf.mapper.bean.Address"/>
        <typeHandler handler="org.apache.ibatis.type.EnumTypeHandler" javaType="com.njf.mapper.bean.SeasonEnum"/>
    </typeHandlers>

需要在 mybatis 的配置文件中配置专门的类型处理器,并在字段上使用 @Column 注解

3、测试


可以正常使用了。

扩展:是否带 Ordinal

EnumTypeHandler:在数据库中存储枚举值本身

EnumOrdinalTypeHandler:在数据库中仅仅存储枚举值索引

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值