前言

在工作中有时候会遇到一种场景,比如,一个Java数据对象实体的某个字段是一个数组或者对象,如果映射到数据库,那么会面临的是,这个字段要不要新建一张表来存储,比如Java数据对象这个字段是数组,存到数据库中,这个字段怎么存?是否创建一个新的数据表来进行一对多存储?又或者Java数据对象这个字段是一个对象,那么这个字段又怎么存?存到数据库中是否需要创建一个新的表来进场id一对一关联?
下面是介绍遇到上述情况不采用新表来对应,而是直接将数组的元素以任意符号来拼接成字符串存到varchar或者text类型的字段中(前提是要合理评估业务中改数组的长度,如果很长,建议采用新表来进行一对多关联存储),如果Java数据对象的字段是一个对象,那么将改对象的json字符串存储到varchar或者text类型的字段中。

1. 应对存储Java数据对象中的属性是一个对象

这个很简单,在所对应的字段中添加注解@TableField(value = "yourFieldName", typeHandler = JacksonTypeHandler.class)就可以做到插入的时候属性的对象转成json字符串存到数据库字段中,在查询时json字符串自动转换成对象映射到属性中。
示例(具体参考下面extra属性):

@Data
@EqualsAndHashCode(callSuper = false)
@TableName(value = "message", autoResultMap = true)
@AllArgsConstructor
@NoArgsConstructor
public class Message implements Serializable {

    private static final long serialVersionUID = 1L;

    @TableId(value = "id", type = IdType.AUTO)
    private Long id;
    
    @TableField("content")
    private String content;

    @TableField(value = "extra", typeHandler = JacksonTypeHandler.class)
    private MessageExtra extra;

}

2. 应对Java对象的属性是一个数组

这个就相对复杂一点,但是只要实现了就可以一劳永逸了。
下面的示例主要是使用逗号来连接数组的元素成字符串然后存到数据表的字段中。

2.1 创建一个类并实现org.apache.ibatis.type.TypeHandler接口。

MyBatis-Plus框架支持自定义实现TypeHandler来处理数据库字段与Java对象属性之间的类型转换。TypeHandler是一个接口,用于定义数据库类型与Java类型之间的转换规则。

public class CustomTypeHandler implements TypeHandler<YourJavaType> {
    // 实现接口方法
}

2.2 在实现类中覆写TypeHandler接口的方法

包括:

  • setParameter:将Java类型转换为对应的数据库类型,并将其作为预处理语句的参数设置。
  • getResult:将从数据库里获取的值转换为Java类型。

2.3在自定义TypeHandler类上添加@MappedTypes注解指定该TypeHandler所映射的Java类型

@MappedTypes(YourJavaType.class)
public class CustomTypeHandler implements TypeHandler<YourJavaType> {
    // ...
}

2.4 在MyBatis的配置文件中注册自定义TypeHandler(2种配置方式,根据自己的项目来选择其一方式来配置)

2.4.1 在MyBatis的配置文件中配置(一般为mybatis-config.xml)

在MyBatis的配置文件中注册自定义TypeHandler。在<typeHandlers>标签中添加如下配置:

<typeHandlers>
    <typeHandler handler="com.example.CustomTypeHandler"/>
</typeHandlers>

通过以上步骤,您可以自定义TypeHandler来处理特定类型的字段与Java对象属性之间的转换。这样,在使用MyBatis-Plus进行数据库操作时,框架会自动调用您自定义的TypeHandler完成类型转换的工作。

2.4.2 在application.yml或application.properties中添加自定义TypeHandler的配置信息(适合SpringBoot项目)

在Spring Boot中,可以使用@ConfigurationProperties注解将自定义TypeHandler的配置与application.ymlapplication.properties文件中的配置进行绑定。以下是在SpringBoot中配置自定义TypeHandler的示例:

  1. 创建一个用于配置自定义TypeHandler的类,并使用@ConfigurationProperties注解将配置信息与属性进行绑定。
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "mybatis.type-handlers")
public class TypeHandlerProperties {
    private String packagePath;
    private List<TypeHandlerConfig> configs;

    // getters and setters

    public static class TypeHandlerConfig {
        private String handler;
        private String javaType;

        // getters and setters
    }
}
  1. application.ymlapplication.properties中添加自定义TypeHandler的配置信息。
mybatis:
  type-handlers:
    package-path: com.example.handler
    configs:
      - handler: com.example.StringListTypeHandler
        java-type: java.util.List
  1. 在自定义TypeHandler类上添加@MappedTypes注解,指定该TypeHandler所映射的Java类型为List<String>
@MappedTypes(List.class)
public class StringListTypeHandler implements TypeHandler<List<String>> {
    // ...
}
  1. 创建一个配置类,使用@EnableConfigurationProperties注解启用自定义TypeHandler的配置类,并将TypeHandlerProperties作为参数传入。
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;

@org.springframework.context.annotation.Configuration
@EnableConfigurationProperties(TypeHandlerProperties.class)
public class MyBatisConfig {
    private final TypeHandlerProperties typeHandlerProperties;

    public MyBatisConfig(TypeHandlerProperties typeHandlerProperties) {
        this.typeHandlerProperties = typeHandlerProperties;
    }

    // 在这里进行自定义TypeHandler的注册和配置
    // ...
}
  1. 在MyBatis的配置类中,读取TypeHandlerProperties的配置信息,并将自定义TypeHandler注册到Configuration对象中
import org.apache.ibatis.session.Configuration;
import org.springframework.context.annotation.Bean;

@Configuration
@EnableConfigurationProperties(TypeHandlerProperties.class)
public class MyBatisConfig {
    private final TypeHandlerProperties typeHandlerProperties;

    public MyBatisConfig(TypeHandlerProperties typeHandlerProperties) {
        this.typeHandlerProperties = typeHandlerProperties;
    }

    @Bean
    public Configuration mybatisConfiguration() {
        Configuration configuration = new Configuration();

        // 获取TypeHandler的配置信息
        String packagePath = typeHandlerProperties.getPackagePath();
        List<TypeHandlerProperties.TypeHandlerConfig> configs = typeHandlerProperties.getConfigs();

        // 注册自定义TypeHandler
        if (packagePath != null && !packagePath.isEmpty()) {
            configuration.getTypeHandlerRegistry().register(packagePath);
        }

        // 根据配置信息逐个注册自定义TypeHandler
        if (configs != null) {
            for (TypeHandlerProperties.TypeHandlerConfig config : configs) {
                String handler = config.getHandler();
                String javaType = config.getJavaType();
                configuration.getTypeHandlerRegistry().register(Class.forName(javaType), Class.forName(handler));
            }
        }

        return configuration;
    }
}

2.5 实现具体转换逻辑(以逗号分隔将数组转成字符串存储,反正查询出来的对象属性字段转成数组)

要将List<String>数组以逗号分隔的字符串方式插入数据库,并在查询时将字符串映射为数组,可以通过自定义TypeHandler来实现。以下是具体的实现:

public class StringListTypeHandler implements TypeHandler<List<String>> {
    @Override
    public void setParameter(PreparedStatement ps, int i, List<String> parameter, JdbcType jdbcType) throws SQLException {
        if (parameter != null && !parameter.isEmpty()) {
            String delimitedString = String.join(",", parameter);
            ps.setString(i, delimitedString);
        } else {
            ps.setString(i, null);
        }
    }

    @Override
    public List<String> getResult(ResultSet rs, String columnName) throws SQLException {
        String delimitedString = rs.getString(columnName);
        return convertToStringList(delimitedString);
    }

    @Override
    public List<String> getResult(ResultSet rs, int columnIndex) throws SQLException {
        String delimitedString = rs.getString(columnIndex);
        return convertToStringList(delimitedString);
    }

    @Override
    public List<String> getResult(CallableStatement cs, int columnIndex) throws SQLException {
        String delimitedString = cs.getString(columnIndex);
        return convertToStringList(delimitedString);
    }

    private List<String> convertToStringList(String delimitedString) {
        if (delimitedString != null && !delimitedString.isEmpty()) {
            return Arrays.asList(delimitedString.split(","));
        }
        return Collections.emptyList();
    }
}

通过以上步骤,您可以在插入和查询时将List<String>数组以逗号分隔的字符串方式存储和映射到数据库中。使用自定义TypeHandler,MyBatis-Plus框架会自动调用TypeHandler中的方法来完成转换工作。这样,您就可以方便地处理List<String>类型字段与数据库之间的映射。

总结

TypeHandler是MyBatis框架中的一个重要概念,它用于将 Java 对象和数据库字段进行相互转换。当从数据库中查询数据时,TypeHandler将数据库字段的值转换为对应的 Java 对象;当将 Java 对象插入或更新到数据库时,TypeHandler将 Java 对象的值转换为对应的数据库字段。

在这个具体的例子中,JacksonTypeHandler是一个基于Jackson的JSON序列化/反序列化库的自定义类型处理器。它用于将Java对象以JSON格式存储在数据库中的extra字段中。当从数据库查询extra字段的值时,JacksonTypeHandler将该JSON字符串反序列化为Java对象;当向数据库插入Java对象时,JacksonTypeHandler将Java对象序列化为JSON字符串。

通过使用自定义的TypeHandler,我们可以实现更灵活的类型转换,将数据库字段与Java对象之间进行复杂的映射关系。这样可以方便地处理一些特殊需求,比如将对象序列化为JSON、将字符串转换为枚举等。

总结来说,@TableField 注解中的 typeHandler 属性用于指定一个 TypeHandler 处理器,而 JacksonTypeHandler 的作用是在数据库字段和 Java 对象之间进行 JSON 格式的序列化和反序列化转换。

Logo

瓜分20万奖金 获得内推名额 丰厚实物奖励 易参与易上手

更多推荐