spring jpa之實體屬性類型轉換器AttributeConverter,自定義Converter,通用Converter

在JPA注解中,有個@Convert注解,其中需要傳入一個Class作為convert參數,該class需要實現AttributeConverter<X,Y>接口。下面來看看AttributeConverter接口的作用。

AttributeConverter<X,Y>

實體屬性類型轉換器。主要使用場景:

  • 持久化enum
  • 加解密數據
  • 持久化日期

簡單化操作,用持久化enum枚舉來進行一個操作。AttributeConverter<X,Y>該接口中需要實現兩個方法:

  • y convertToDatabaseColumn(x) 作用:將實體屬性x轉化為y存儲到數據庫中,即插入和更新操作時執行;
  • x convertToEntityAttribute(y) 作用:將數據庫中的字段y轉化為實體屬性x,即查詢操作時執行。

場景:用戶登錄時,記錄用戶的動作:登錄,登出,注冊,重置密碼。

  1. 創建一個枚舉類UserAction

/**
 * 用戶操作枚舉.
 * @author Wang.ch
 *
 */
public enum UserAction {
    REG(0, "注冊"), RESET(1, "重置密碼"), LOGIN(2, "用戶登錄"), LOGOUT(3, "用戶登出");
    private int value;
    private String desc;

    UserAction(int value, String desc) {
        this.value = value;
        this.desc = desc;
    }

    public int getValue() {
        return value;
    }

    public void setValue(int value) {
        this.value = value;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }

    public static class Convert implements AttributeConverter<UserAction, Integer> {
        @Override
        public Integer convertToDatabaseColumn(UserAction attribute) {
            return attribute == null ? null : attribute.getValue();
        }

        @Override
        public UserAction convertToEntityAttribute(Integer dbData) {
            for (UserAction type : UserAction.values()) { //將數字轉換為描述
                if (dbData.equals(type.getValue())) {
                    return type;
                }
            }
            throw new RuntimeException("Unknown database value: " + dbData);
        }
    }
}

其中,在已經把轉換類Convert寫入到了枚舉當中。

  1. 在實體內中添加注解
    @Convert(converter = UserAction.Convert.class)
    private UserAction action;

從轉換類中的方法可以看出,在寫入數據庫時,jpa會調用convert的convertToDatabaseColumn方法,把UserAction枚舉的value寫入到庫中,在反向查詢時,通過數據庫的值和遍歷的枚舉的value進行比較,然后返回UserAction實體。

數據的加密和日期的轉換也就類似的操作了。

但是這樣的每個枚舉可能都要去寫這樣的轉換類,可能會存在重復的操作。可以嘗試寫個通用的枚舉轉換類。

  • 所有的枚舉都實現一個接口:BaseEnum.java
  • 寫一個抽象類去實現AttributeConverter<X,Y>:BaseEnumConverter<X,Y>
  • 所有的枚舉中定義一個public靜態類繼承抽象類
  1. BaseEnum.java
/**
 * 枚舉基類.
 * @author Wang.ch
 *
 * @param <T> 數據庫存儲的java類型
 */
public interface BaseEnum<Y> {
    /**
     * 存取到數據庫中的值.
     * @return
     */
    public Y getValue();
}

  1. BaseEnumConverter.java

public abstract class BaseEnumConverter<X extends BaseEnum<Y>, Y> implements AttributeConverter<BaseEnum<Y>, Y> {
    private Class<X> xclazz;
    private Method valuesMethod;

    @SuppressWarnings("unchecked")
    public BaseEnumConverter() {
        this.xclazz = (Class<X>) (((ParameterizedType) this.getClass().getGenericSuperclass())
                .getActualTypeArguments())[0];
        try {
            valuesMethod = xclazz.getMethod("values");
        } catch (Exception e) {
            throw new RuntimeException("can't get values method from " + xclazz);
        }
    }

    @Override
    public Y convertToDatabaseColumn(BaseEnum<Y> attribute) {
        return attribute == null ? null : attribute.getValue();
    }

    @SuppressWarnings("unchecked")
    @Override
    public X convertToEntityAttribute(Y dbData) {
        try {
            X[] values = (X[]) valuesMethod.invoke(null);
            for (X x : values) {
                if (x.getValue().equals(dbData)) {
                    return x;
                }
            }
        } catch (Exception e) {
            throw new RuntimeException("can't convertToEntityAttribute" + e.getMessage());
        }
        throw new RuntimeException("unknown dbData " + dbData);
    }
}
  1. 修改后的枚舉:UserAction.java
/**
 * 用戶操作枚舉.
 * @author Wang.ch
 *
 */
public enum UserAction implements BaseEnum<Integer> {
    REG(0, "注冊"), RESET(1, "重置密碼"), LOGIN(2, "用戶登錄"), LOGOUT(3, "用戶登出");
    private Integer value;
    private String desc;

    UserAction(Integer value, String desc) {
        this.value = value;
        this.desc = desc;
    }

    public Integer getValue() {
        return value;
    }

    public void setValue(Integer value) {
        this.value = value;
    }

    public String getDesc() {
        return desc;
    }

    public void setDesc(String desc) {
        this.desc = desc;
    }
    
    public static class Convert2 extends BaseEnumConverter<UserAction, Integer>{
        
    }
}

需要注意的是,在上面的代碼中,BaseEnumConverter使用了BaseEnum作為X的限定類型,但是實際上并未能真正限定傳入的數據類型是一個枚舉類型,顯然BaseEnumConverter只適用于枚舉類型,因為在類初始化時,使用了values這個Method反射,只有枚舉類型才有。在其他的值類型轉換時,可以參照枚舉自定義其他的X限定類型。


小技巧,如果枚舉沒有這么復雜,則完全可以用@Enumerated(EnumType.ORDINAL)@Enumerated(EnumType.STRING)讓數據庫自動進行轉換。

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容

  • Spring Cloud為開發人員提供了快速構建分布式系統中一些常見模式的工具(例如配置管理,服務發現,斷路器,智...
    卡卡羅2017閱讀 134,837評論 18 139
  • 一. Java基礎部分.................................................
    wy_sure閱讀 3,832評論 0 11
  • 轉自:http://blog.csdn.net/jackfrued/article/details/4492194...
    王帥199207閱讀 8,588評論 3 93
  • 姑娘今天發燒了,看到周圍的人有感冒的,姑娘已經提前吃2天感冒藥來預防,但還是沒防住,前天還給她灸了大椎穴。唉,寶貝...
    如影相隨夢想家閱讀 250評論 0 0
  • 一歲的寶寶在想啥? 為什么會有這個問題,因為很多時候,我們在養育孩子的過程中,我們常常會抱怨,為什么別人家的小孩都...
    湯姆嗑螺絲閱讀 2,098評論 2 2