Location>code7788 >text

Solution] Database-driven custom TypeHandler-based handlers

Popularity:963 ℃/2024-10-09 08:32:43

catalogs
  • preamble
  • I. Introduction to TypeHandler
    • 1.1 Conversion steps
    • 1.2 Conversion rules
  • JSON Conversion
  • III. Enumeration conversion
  • IV. Summary of articles

preamble

In my recent project development, I frequently encountered 2 conversion problems between Java types and JDBC types:

  • Database varchar type field that needs to store a JSON string in a Java entity
  • int type field of the database, which is required to store the Enum enumeration of the Java entity.

It's not a big deal to deal with it, you can manually call the Java bean to JSON each time it's in the repository.toJSONString() You can use JSON to retrieve the data from the database.parseObject()Parsing. Besides, it's not that hard to handle enumeration types, it's just a matter of manually taking out the int-type attributes of the enumeration and setting them to the int of the database.

And the custom TypeHandler handler that we're going to cover in this article is the role of theAutomatic handling of Java bean to database type conversionThe coding efficiency is improved, and tedious manual conversions are eliminated by globally harmonized processing.


I. Introduction to TypeHandler

If you are using Mybatis or Mybatis Plus, you will need to use the TypeHandler to convert types during the execution of the SQL statement, either to set parameters or to get the result set.

MyBatis provides rich built-in TypeHandler implementations to support common data type conversions such as the following:

Table 1-1

1.1 Conversion steps

When MyBatis executes a pre-compiled SQL statement (e.g., INSERT, UPDATE, etc.), it needs to set the value of a property in a Java object to the corresponding placeholder in the SQL statement. This is accomplished through the TypeHandler.

The specific steps are as follows:

  • MyBatis will find the corresponding TypeHandle r instance based on the mapping configuration, which can be defined in the MyBatis configuration file or the Mapper XML file;
  • A TypeHandler instance takes the value of a property in a Java object and converts it to a type that JDBC recognizes, based on a mapping relationship between the two;
  • The converted value is set to the corresponding placeholder in the PreparedStatement object so that the database can parse and execute the SQL statement correctly.

1.2 Conversion rules

Again, the core function of TypeHandler is to realize the mapping and conversion between Java types and JDBC types, and the mapping and conversion rules are defined according to the characteristics and semantics of Java types and JDBC types.

  • For basic data types (e.g., int, long, float, etc.), MyBatis provides built-in TypeHandler implementations that can directly convert Java basic data types to the corresponding JDBC basic data types and vice versa.
  • For complex data types (e.g., custom objects, collections, etc.), MyBatis allows developers to customize TypeHandler to implement complex type conversion logic. For example, developers can define a custom TypeHandler to convert a JSON string in the database to an object in Java, or to convert a Java object to a JSON string for storage in the database.

Two examples are illustrated in the following two chapters.


JSON Conversion

The following is added to the application's .yml configuration file:

mybatis-plus.
  type-handlers-package: #Package path where the custom handler class resides.
/**
 * <p> Role: i.e., Java entity attributes can be directly mapped to database varchar using JSONObject, for easy inbound and outbound storage</p>.
 * <p> Note: You need to add {@code mybatis:type-handlers-package: path to the package where this class resides}</p> to the .yml configuration file.
 *
 * @param <T> This generic type is the Java object to be converted to varchar.
 * The @MappedTypes annotation is critical, and specifies the mapped types.
 */
@MappedTypes({, })
public class JSONTypeHandler <T> extends BaseTypeHandler<T> {

    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, T param, JdbcType jdbcType) throws SQLException {
        // Set the specified parameter to the given Java String value, the database driver and its conversion to varchar type
        (i, (param));

    }

    @Override
    public T getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
        //Here we get the jsonStr value we put in based on the field name.
        String jsonStr = (columnName); return (jsonStr) ?
        return (jsonStr) ? (jsonStr, getRawType()) : null; }
    }

    @Override
    public T getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {
        // Here is to determine the field based on the position, and then get the value of the field (jsonStr previously put in)
        String jsonStr = (columnIndex); return (jsonStr) ?
        return (jsonStr) ? (jsonStr, getRawType()) : null;
    }

    @Override
    public T getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {
        //Here is the value of the field based on the position of the field in the SQL procedure.
        String jsonStr = (columnIndex); return (jsonStr) ?
        return (jsonStr) ? (jsonStr, getRawType()) : null;
    }

}

III. Enumeration conversion

/**
 * <p> Function: maps enum code in entity class to int in database</p>.
 * <p> Note: You need to add {@code mybatis:type-handlers-package: path to the package where this class is located} </p> to the .yml configuration file.
 *
 * @param <E> This generic is the enum object to be handled, using upper bound wildcards for type safety.
 */
@MappedTypes()
public class EnumCodeTypeHandler <E extends MyEnum> extends BaseTypeHandler<E> {

    private final Class<E> type.

    /**
     * Record the correspondence between enumeration values and enumerations
     */
    private final Map<Integer, E> enumMap = new ConcurrentHashMap<>();

    public EnumCodeTypeHandler(Class<E> type) {
        (type, "argument cannot be null");
         = type;
        E[] enums = ();
        if ((enums)) {
            // Here the enumeration value and enumeration type are stored into enumMap
            for (E e : enums) {
                ((), e);
            }
        }
    }

    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int index, E e, JdbcType jdbcType) throws SQLException {
        // Here the enumeration code is converted to an int type of the field in the database.
        (index, ());
    }

    @Override
    public E getNullableResult(ResultSet resultSet, String columnName) throws SQLException {
        // Here we convert database int to Java Integer based on the field name.
        Integer code = (columnName);
        if (()){
            return null; }else {
        }else {
            // Take out the corresponding enumeration value
            return (code);
        }
    }

    @Override
    public E getNullableResult(ResultSet resultSet, int columnIndex) throws SQLException {
        // Here the database int is converted to a Java Integer based on the position of the field.
        Integer code = (columnIndex);
        if (()){
            return null; }else {
        }else {
            // Take out the corresponding enumeration value
            return (code);
        }
    }

    @Override
    public E getNullableResult(CallableStatement callableStatement, int columnIndex) throws SQLException {
        // Here the int of the field is converted to a Java Integer based on the position of the field in the SQL procedure.
        Integer code = (columnIndex); if (()){
        if (()){
            return null; }else {
        }else {
            // Take out the corresponding enumeration value
            return (code);
        }
    }

}

/**
 * <p> Role: This interface contains two abstract methods for enumeration operations </p>
 */
public interface MyEnum {

    /**
     * Get enum instance based on code.
     * @param code
     */
    MyEnum fromCode(int code).

    /**
     * Get the code from the enumeration.
     */
    int toCode();

}
@Getter
@RequiredArgsConstructor
public enum StudyStatusEnum implements MyEnum{
    ONE(1, "enumeration1"),
    TWO(2, enumeration2"),
    THREE(3, "enumeration3"),
    FOUR(4, "enumeration4"),
    FIVE(5, "enumeration5");

    private final Integer code;

    private final String desc;

    /**
     * according to code 获取enumeration实例
     */
    @Override
    public MyEnum fromCode(int code) {
        return (())
                .filter(val -> ().equals(code))
                .findFirst().orElse(null);
    }

    /**
     * 获取enumeration中的 code
     */
    @Override
    public int toCode() {
        return ();
    }

}

IV. Summary of articles

With built-in and customized TypeHandlers, we can easily handle various data type conversion needs, improving development efficiency and code maintainability.

Using a custom TypeHandler in a Spring Boot environment simplifies the configuration and registration process even more, allowing us to focus more on the implementation of the business logic.

Finally, please correct any shortcomings or errors in the article. Or if you have something else to say, please feel free to share it in the comment section!