SpringBoot series Mybatis custom type conversion TypeHandler

SpringBoot series Mybatis custom type conversion TypeHandler

When using mybatis for db operations, one of the things we often do is to map the fields in the db to java beans. Usually we use ResultMapto implement the mapping. Through this tag, the binding relationship between the two can be specified, then if the java bean The field type in db is different from that in db, what should I do?

For example, it is timestamp in db, but long is defined in java bean

  • BaseTypeHandlerImplement custom type conversions by

<!-- more -->

I. Environmental Preparation

1. Database preparation

Use mysql as the instance database of this article and add a new table

CREATE TABLE `money` (
  `id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `name` varchar(20) NOT NULL DEFAULT '' COMMENT '用户名',
  `money` int(26) NOT NULL DEFAULT '0' COMMENT '钱',
  `is_deleted` tinyint(1) NOT NULL DEFAULT '0',
  `create_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  `update_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (`id`),
  KEY `name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;

2. Project Environment

This article is developed with the help of SpringBoot 2.2.1.RELEASE+ maven 3.5.3+IDEA

pom dependencies are as follows

<dependencies>
    <dependency>
        <groupId>org.mybatis.spring.boot</groupId>
        <artifactId>mybatis-spring-boot-starter</artifactId>
        <version>2.2.0</version>
    </dependency>
    <dependency>
        <groupId>mysql</groupId>
        <artifactId>mysql-connector-java</artifactId>
    </dependency>
</dependencies>

db configuration informationapplication.yml

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/story?useUnicode=true&characterEncoding=UTF-8&useSSL=false&serverTimezone=Asia/Shanghai
    username: root
    password:

II. Example Demonstration

1. entity definition

Note that thecreate_at types of and in the above case are both , and the Entity we define is as followsupdate_attimestmap

@Data
public class MoneyPo {
    private Integer id;

    private String name;

    private Long money;

    private Integer isDeleted;

    private Timestamp createAt;

    private Long updateAt;
}

2. Mapper test interface

Define a simple query interface, where the annotation method is used directly (as for the way of writing xml, there is not much difference)

/**
 * 主键查询
 *
 * @param id id
 * @return {@link MoneyPo}
 */
@Select("select * from money where id = #{id}")
@Results(id = "moneyResultMap", value = {
        @Result(property = "id", column = "id", id = true, jdbcType = JdbcType.INTEGER),
        @Result(property = "name", column = "name", jdbcType = JdbcType.VARCHAR),
        @Result(property = "money", column = "money", jdbcType = JdbcType.INTEGER),
        @Result(property = "isDeleted", column = "is_deleted", jdbcType = JdbcType.TINYINT),
        @Result(property = "createAt", column = "create_at", jdbcType = JdbcType.TIMESTAMP),
//            @Result(property = "updateAt", column = "update_at", jdbcType = JdbcType.TIMESTAMP)})
        @Result(property = "updateAt", column = "update_at", jdbcType = JdbcType.TIMESTAMP, typeHandler = Timestamp2LongHandler.class)})
MoneyPo getById(@Param("id") int id);

// 关于 SelectProvider 的使用,后面再说,主要是动态sql的演示
@SelectProvider(type = MoneyService.class, method = "getByIdSql")
@ResultMap(value = "moneyResultMap")
MoneyPo getByIdForProvider(@Param("id") int id);

illustrate:

  • @Results: This annotation has the same effect as the ResultMap label, and is mainly used to define the mapping relationship between the fields of the db and the java bean
  • id = "moneyResultMap"This id definition can realize the reuse of @Results
  • @Result: Pay attention to updateAtthe typeHandler below, where a custom TypeHandler is specified to realize JdbcType.TEMSTAMPthe conversion with the long in Java Bean

3. Type conversion

Custom type conversion, mainly inheritance BaseTypeHandlerclass, the type of generic is the type in Java Bean

/**
 * 自定义类型转换:将数据库中的日期类型,转换成long类型的时间戳
 *
 * 三种注册方式:
 * 1.直接在 result标签中,指定typeHandler,如@Result(property = "updateAt", column = "update_at", jdbcType = JdbcType.TIMESTAMP, typeHandler = Timestamp2LongHandler.class)
 * 2.在SqlSessionFactory实例中,注册 在SqlSessionFactory实例中.setTypeHandlers(new Timestamp2LongHandler());
 * 3.xml配置,<typeHandler handler="com.git.hui.boot.mybatis.handler.Timestamp2LongHandler"/>
 *
 * @author yihui
 * @date 2021/7/7
 */
@MappedTypes(value = Long.class)
@MappedJdbcTypes(value = {JdbcType.DATE, JdbcType.TIME, JdbcType.TIMESTAMP})
public class Timestamp2LongHandler extends BaseTypeHandler<Long> {

    /**
     * 将java类型,转换为jdbc类型
     *
     * @param preparedStatement
     * @param i
     * @param aLong             毫秒时间戳
     * @param jdbcType          db字段类型
     * @throws SQLException
     */
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, Long aLong, JdbcType jdbcType) throws SQLException {
        if (jdbcType == JdbcType.DATE) {
            preparedStatement.setDate(i, new Date(aLong));
        } else if (jdbcType == JdbcType.TIME) {
            preparedStatement.setTime(i, new Time(aLong));
        } else if (jdbcType == JdbcType.TIMESTAMP) {
            preparedStatement.setTimestamp(i, new Timestamp(aLong));
        }
    }

    @Override
    public Long getNullableResult(ResultSet resultSet, String s) throws SQLException {
        return parse2time(resultSet.getObject(s));
    }

    @Override
    public Long getNullableResult(ResultSet resultSet, int i) throws SQLException {
        return parse2time(resultSet.getObject(i));
    }

    @Override
    public Long getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        return parse2time(callableStatement.getObject(i));
    }

    private Long parse2time(Object value) {
        if (value instanceof Date) {
            return ((Date) value).getTime();
        } else if (value instanceof Time) {
            return ((Time) value).getTime();
        } else if (value instanceof Timestamp) {
            return ((Timestamp) value).getTime();
        }
        return null;
    }
}
  • setNonNullParameter: Convert java type to jdbc type
  • getNullableResult: Convert jdbc type to java type

4. TypeHandler registration

It's fine for us to define a TypeHandler ourselves, and then we need it to take effect. Generally speaking, there are the following ways

4.1 Specified in result tag

Specified by typeHandler in the result tag

The way to use xml is like

<result column="update_at" property="updateAt" jdbcType="TIMESTAMP" typeHandler="com.git.hui.boot.mybatis.handler.Timestamp2LongHandler"/>

The way to annotate @Result is as follows

@Result(property = "updateAt", column = "update_at", jdbcType = JdbcType.TIMESTAMP, typeHandler = Timestamp2LongHandler.class)

4.2 SqlSessionFactory global configuration

The above usage poses are precisely specified. If we want to apply to all scenes, we can SqlSessionFactoryachieve this by

@Bean(name = "sqlSessionFactory")
public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
    bean.setDataSource(dataSource);
    bean.setMapperLocations(
            // 设置mybatis的xml所在位置,这里使用mybatis注解方式,没有配置xml文件
            new PathMatchingResourcePatternResolver().getResources("classpath*:mapping/*.xml"));
    // 注册typehandler,供全局使用
    bean.setTypeHandlers(new Timestamp2LongHandler());
    return bean.getObject();
}

4.3 Global xml configuration

In addition to the above case, there is another way mybatis-config.xmlto register with the help of configuration files, such as

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
        PUBLIC "-//ibatis.apache.org//DTD Config 3.1//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <settings>
        <!-- 驼峰下划线格式支持 -->
        <setting name="mapUnderscoreToCamelCase" value="true"/>
    </settings>

    <typeHandlers>
        <typeHandler handler="com.git.hui.boot.mybatis.handler.Timestamp2LongHandler"/>
    </typeHandlers>
</configuration>

Note that using the above configuration file, you need to specify the following configuration in SpringBoot, otherwise it will not take effect

mybatis:
  config-location: classpath:mybatis-config.xml

4.4 SpringBoot configuration

type-handlers-packagespringboot configuration file, you can register TypeHandler by specifying

mybatis:
  type-handlers-package: com.git.hui.boot.mybatis.handler

5. Summary

This article mainly introduces the mapping adaptation strategy of types in db and types in java beans, mainly through inheritance BaseTypeHandlerto achieve custom type conversion

To use a custom TypeHandler, there are two ways: global effect and precise specification

  • @Result/ <result>tag, specified by typeHandler
  • SqlSessionFactory sets typeHandler globally
  • mybatis-config.xmlProfile settingstypeHandlers

In addition, in the configuration of this article, the inter-conversion configuration of hump and underscore is also supported, which is also a common configuration. You can configure mybatis-configit as follows in

<setting name="mapUnderscoreToCamelCase" value="true"/>

The next question is, hump can be interchanged with underscore, so is there a way to implement custom name mapping?

III. Source code and related knowledge points that cannot be missed

0. Project

mybatis series of blog posts

1. A gray blog

It is not as good as a letter. The above content is purely from the family. Due to limited personal ability, it is inevitable that there will be omissions and mistakes. If you find bugs or have better suggestions, you are welcome to criticize and correct them. Thank you

The following is a gray personal blog, recording all blog posts in study and work, welcome everyone to visit

a grey blog

{{o.name}}
{{m.name}}

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=324030185&siteId=291194637