SpringBoot-Mybatis:枚举类型自动转换

例如:

准备insert的实体类:

insert到mySql之后:

select返回的实体类:

Mybatis可以做到枚举类型的自动转换,但是结果不是我们想要的,比如:

    STUDENT("01","学生"),
    CIVIL_SERVANT("02","公务员"),
    FARMER("03","农民"),
    PROGRAMMER("04","程序员")

    如果插入时在代码里不做任何转换,插入之后,user_type=这4个类型之一(STUDENT、CIVIL_SERVANT.......),查询时会可以自动转换成枚举类型。如何实现上面的效果

引入依赖

            <dependency>
			<groupId>org.mybatis.spring.boot</groupId>
			<artifactId>mybatis-spring-boot-starter</artifactId>
			<version>1.3.1</version>
		</dependency>

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

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>5.1.46</version>
		</dependency>

		<dependency>
			<groupId>com.alibaba</groupId>
			<artifactId>druid</artifactId>
			<version>1.1.9</version>
		</dependency>

1、user_info

CREATE TABLE user_info (
  user_id bigint(20) NOT NULL COMMENT '用户id',
  name varchar(32) NOT NULL COMMENT '用户姓名',
  age int(2) NOT NULL COMMENT '用户年龄',
  gender varchar(2) NOT NULL COMMENT '性别 01:男,02女',
  user_type varchar(2) NOT NULL COMMENT '用户类型 01:学生,02:公务员,03:伟大的农民,04:程序员',
  address varchar(64) DEFAULT NULL,
  create_time datetime DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  update_time datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '修改时间',
  PRIMARY KEY (user_id)
) ENGINE=InnoDB DEFAULT CHARSET=utf8  COMMENT '用户表'

2、UserInfo

package com.cn.dl.bean;

import com.cn.dl.enums.GenderEnum;
import com.cn.dl.enums.UserTypeEnum;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;

/**
 * 用户信息
 * Created by yanshao on 2019/3/24.
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class UserInfo {
    private long userId;
    private String name;
    private int age;
    private GenderEnum gender;
    private UserTypeEnum userType;
    private String address;
}

3、定义自动转换枚举的工具类:UniversalEnumHandler,参考这篇博客

package com.cn.dl.handler;

import com.cn.dl.enums.BaseEnum;
import com.cn.dl.enums.GenderEnum;
import com.cn.dl.enums.UserTypeEnum;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedTypes;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * 枚举转换
 * Created by yanshao on 2019/3/21.
 */
@MappedTypes({GenderEnum.class, UserTypeEnum.class})
public class UniversalEnumHandler<E extends BaseEnum> extends BaseTypeHandler<E> {
    private Class<E> type;

    private E [] enums;

    /**
     * 设置配置文件设置的转换类以及枚举类内容,供其他方法更便捷高效的实现
     * @param type 配置文件中设置的转换类
     */
    public UniversalEnumHandler(Class<E> type) {
        if (type == null)
            throw new IllegalArgumentException("Type argument cannot be null");
        this.type = type;
        this.enums = type.getEnumConstants();
        if (this.enums == null)
            throw new IllegalArgumentException(type.getSimpleName()
                    + " does not represent an enum type.");
    }

    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, E parameter,
                                    JdbcType jdbcType) throws SQLException {
        //BaseTypeHandler已经帮我们做了parameter的null判断
        ps.setObject(i,parameter.getValue(), jdbcType.TYPE_CODE);
    }

    @Override
    public E getNullableResult(ResultSet rs, String columnName)
            throws SQLException {
        // 根据数据库存储类型决定获取类型,本例子中数据库中存放String类型
        String i = rs.getString(columnName);
        if (rs.wasNull()) {
            return null;
        } else {
            // 根据数据库中的value值,定位PersonType子类
            return locateEnumStatus(i);
        }
    }

    @Override
    public E getNullableResult(ResultSet rs, int columnIndex)
            throws SQLException {
        // 根据数据库存储类型决定获取类型,本例子中数据库中存放String类型
        String i = rs.getString(columnIndex);
        if (rs.wasNull()) {
            return null;
        } else {
            // 根据数据库中的value值
            return locateEnumStatus(i);
        }
    }

    @Override
    public E getNullableResult(CallableStatement cs, int columnIndex)
            throws SQLException {
        // 根据数据库存储类型决定获取类型,本例子中数据库中存放String类型
        String i = cs.getString(columnIndex);
        if (cs.wasNull()) {
            return null;
        } else {
            // 根据数据库中的value值
            return locateEnumStatus(i);
        }
    }

    /**
     * 枚举类型转换,由于构造函数获取了枚举的子类enums,让遍历更加高效快捷
     * @param value 数据库中存储的自定义value属性
     * @return value对应的枚举类
     */
    private E locateEnumStatus(String value) {
        for(E e : enums) {
            if(String.valueOf(e.getValue()).equals(value)) {
                return e;
            }
        }
        return null;
    }

}

其中@MappedTypes,指定了需要转换那些枚举

4、GenderEnum

package com.cn.dl.enums;

import lombok.Getter;

/**
 * 性别类型
 * Created by yanshao on 2019/3/24.
 */
public enum GenderEnum implements BaseEnum<GenderEnum,String>{
    MALE("01","男性"),
    FEMALE("02","女性"),
    ;
    @Getter
    private String value;
    @Getter
    private String desc;

    GenderEnum(String value, String desc) {
        this.value = value;
        this.desc = desc;
    }
    public static GenderEnum getByValue(String value){
        GenderEnum genderEnums[] = GenderEnum.values();
        for(GenderEnum genderEnum : genderEnums){
            if(genderEnum.getValue().equals(value)){
                return genderEnum;
            }
        }
        return null;
    }
}

5、UserTypeEnum

package com.cn.dl.enums;

import lombok.Getter;

/**
 * 用户类型
 * Created by yanshao on 2019/3/24.
 */
public enum UserTypeEnum implements BaseEnum<UserTypeEnum,String>{

    STUDENT("01","学生"),
    CIVIL_SERVANT("02","公务员"),
    FARMER("03","伟大的农民"),
    PROGRAMMER("04","程序员")
    ;
    @Getter
    private String value;
    @Getter
    private String desc;

    UserTypeEnum(String value, String desc) {
        this.value = value;
        this.desc = desc;
    }
    public static UserTypeEnum getByValue(String value){
        UserTypeEnum userTypeEnums[] = UserTypeEnum.values();
        for(UserTypeEnum userTypeEnum : userTypeEnums){
            if(userTypeEnum.getValue().equals(value)){
                return userTypeEnum;
            }
        }
        return null;
    }
}

6、UserInfoMapper

package com.cn.dl.dao;

import com.cn.dl.bean.UserInfo;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;

/**
 * Created by yanshao on 2019/3/24.
 */
@Mapper
public interface UserInfoMapper {
    int insert(UserInfo userInfo);
    UserInfo getByUserId(@Param("userId") long userId);
}

7、UserInfoMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.cn.dl.dao.UserInfoMapper" >
  <resultMap id="BaseResultMap" type="com.cn.dl.bean.UserInfo" >
    <result column="user_id" property="userId" jdbcType="BIGINT" />
    <result column="name" property="name" jdbcType="VARCHAR" />
    <result column="age" property="age" jdbcType="INTEGER" />
    <result column="gender" property="gender" jdbcType="VARCHAR" />
    <result column="user_type" property="userType" jdbcType="VARCHAR" />
    <result column="address" property="address" jdbcType="VARCHAR" />
  </resultMap>

  <sql id="Base_Column_List" >
         user_id,name,age,gender,user_type,address,create_time,update_time
  </sql>

  <insert id="insert" parameterType="com.cn.dl.bean.UserInfo">
    insert into user_info (user_id,age,name,gender,user_type,address)
    values (
    #{userId,jdbcType=BIGINT},
    #{age,jdbcType=INTEGER},
    #{name,jdbcType=VARCHAR},
    #{gender,jdbcType=VARCHAR},
    #{userType,jdbcType=VARCHAR},
    #{address,jdbcType=VARCHAR}
    )
  </insert>
  <select id="getByUserId" resultMap="BaseResultMap">
    select
        <include refid="Base_Column_List" />
    from user_info
    where user_id = #{userId,jdbcType=BIGINT}
  </select>
</mapper>

8、application.properties配置


#mybatis
#开启mybatis驼峰命名,这样可以将mysql中带有下划线的映射成驼峰命名的字段
mybatis.configuration.map-underscore-to-camel-case=true
mybatis.mapper-locations=classpath*:mapper/*.xml
mybatis.type-aliases-package=com.cn.dl
mybatis.type-handlers-package=com.cn.dl.handler

datasource
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/tiger_base?useUnicode=true&characterEncoding=UTF-8&zeroDateTimeBehavior=convertToNull&autoReconnect=true&generateSimpleParameterMetadata=true
spring.datasource.username=root
spring.datasource.password=root
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
spring.datasource.max-idle=10
spring.datasource.max-wait=60000
spring.datasource.min-idle=5
spring.datasource.initial-size=5
spring.datasource.validationQuery=select 'x'

       如果没有在application.properties中申明(mybatis.mapper-locations=mapper的路径),默认和XXXMapper.class的路径是一致的

9、UserInfoMapperTest

package com.cn.dl.dao;

import com.cn.dl.bean.UserInfo;
import com.cn.dl.enums.GenderEnum;
import com.cn.dl.enums.UserTypeEnum;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

import java.util.Optional;

/**
 * Test
 * Created by yanshao on 2019/3/24.
 */
@RunWith(SpringRunner.class)
@SpringBootTest
public class UserInfoMapperTest {

    @Autowired
    private UserInfoMapper userInfoDao;

    @Test
    public void insert() throws Exception {
        UserInfo userInfo = UserInfo.builder()
                .userId(13240116)
                .name("zhoudl")
                .age(23)
                .gender(GenderEnum.MALE)
                .userType(UserTypeEnum.PROGRAMMER)
                .address("上海")
                .build();
        int num = userInfoDao.insert(userInfo);
        Assert.assertEquals("insert 失败",num,1);
    }

    @Test
    public void getByUserId() throws Exception {
        long userId = 13240117;
        UserInfo userInfo = userInfoDao.getByUserId(userId);
        Optional.ofNullable(userInfo).
                ifPresent(userInfo1 -> System.out.println("userInfo >>> " + userInfo1.toString()))
        ;
    }

}

       如果没有在UniversalEnumHandler类上使用注解@MappedTypes指定那些需要转换的枚举,需要在XXXXMapper.xml中指定

例如:

       注意:如果在代码里配置了sqlSessionFactory,需要做一下配置,否则在application.properties中配置是不生效的,比如枚举类型转换失败、 Invalid bound statement (not found)

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): com.cn.dl.dao.UserInfoMapper.insert

	at org.apache.ibatis.binding.MapperMethod$SqlCommand.<init>(MapperMethod.java:225)
	at org.apache.ibatis.binding.MapperMethod.<init>(MapperMethod.java:48)
	at org.apache.ibatis.binding.MapperProxy.cachedMapperMethod(MapperProxy.java:65)

每天进步一点点!!!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章