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)

每天進步一點點!!!

 

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