Java、Mysql、MyBatis 中枚举 enum 的使用
From: https://yulaiz.com/java-mysql-enum/
Java 和 MySql 中都有枚舉的概念,合理的使用枚舉,可以讓代碼閱讀和數(shù)據(jù)庫數(shù)據(jù)查詢更加直觀、高效。那么我們?cè)趺词褂媚?#xff0c;什么時(shí)候使用,兩者之間怎么進(jìn)行數(shù)據(jù)關(guān)聯(lián)呢?(本文使用 MyBatis 做為 Java 與 MySql 之間的關(guān)聯(lián))
文章目錄 [hide]
- 1 1. 當(dāng)年我們?cè)趺炊x狀態(tài)
- 1.1 1.1 數(shù)據(jù)庫設(shè)計(jì)
- 1.2 1.2 Java Bean 代碼
- 1.3 1.3 MyBatis 代碼
- 1.4 1.4 代碼效果
- 2 2. 現(xiàn)在我們?cè)趺炊x狀態(tài)
- 2.1 2.1 Java Bean 代碼
- 2.2 2.2 數(shù)據(jù)庫設(shè)計(jì)
- 2.3 2.3 MyBatis 代碼
- 2.4 2.4 代碼效果
- 3 3. 怎么把當(dāng)年定義的狀態(tài)改成使用枚舉
- 3.1 3.1 數(shù)據(jù)庫設(shè)計(jì)
- 3.2 3.2 Java Bean 代碼
- 3.3 3.3 MyBatis 代碼
- 3.3.1 3.3.1 內(nèi)置枚舉轉(zhuǎn)換器
- 3.3.1.1 3.3.1.1 EnumTypeHandler
- 3.3.1.2 3.3.1.2 EnumOrdinalTypeHandler
- 3.3.2 3.3.2 自定義枚舉轉(zhuǎn)換器
- 3.3.3 3.3.3 在 Mapper.xml 中配置自定義的枚舉轉(zhuǎn)換器
- 3.3.3.1 方法1:修改指定xml文件,指定的Mapper生效
- 3.3.3.2 方法2:全局指定枚舉轉(zhuǎn)換器,無需單獨(dú)修改Mapper.xml
- 3.3.3.3 方法3:不使用枚舉轉(zhuǎn)換器
- 3.3.1 3.3.1 內(nèi)置枚舉轉(zhuǎn)換器
- 4 4. 枚舉到底好不好用
- 4.1 4.1 Java 枚舉的實(shí)現(xiàn)
- 4.1.1 4.1.1 基礎(chǔ)聲明、屬性、構(gòu)造函數(shù)
- 4.1.2 4.1.2 常用方法
- 4.1.2.1 4.1.2.1 name() 方法
- 4.1.2.2 4.1.2.2 ordinal() 方法。
- 4.1.2.3 4.1.2.3 toString() 方法
- 4.1.2.4 4.1.2.4 equals(Object other) 方法
- 4.1.2.5 4.1.2.5 compareTo(E o) 方法
- 4.1.2.6 4.1.2.6 values() 方法
- 4.1.2.7 4.1.2.7 valueOf(String name) 方法
- 4.1.3 4.1.3 比較兩個(gè)枚舉值是否一致
- 4.2 4.2 MySql 中的枚舉
- 4.2.1 4.2.1 DDL 操作效率
- 4.2.1.1 4.2.1.1 新增一個(gè)枚舉狀態(tài)
- 4.2.1.2 4.2.1.2 修改一個(gè)枚舉狀態(tài)
- 4.2.1.3 4.2.1.3 刪除一個(gè)枚舉狀態(tài)
- 4.2.2 4.2.2 查詢效率
- 4.2.1 4.2.1 DDL 操作效率
- 4.1 4.1 Java 枚舉的實(shí)現(xiàn)
- 5 5. 枚舉使用總結(jié)
- 6 6. 參考鏈接
1. 當(dāng)年我們?cè)趺炊x狀態(tài)
我們定義狀態(tài)變量時(shí),通常需要約定幾個(gè)狀態(tài),比如交易訂單中,我們就有常見的創(chuàng)建、交易中、支付成功、支付失敗等等狀態(tài),當(dāng)年我們都是約定好 0,1,2,3,4,5 這樣的字段來表示上述幾個(gè)字段。
1.1 數(shù)據(jù)庫設(shè)計(jì)
例如我們的數(shù)據(jù)庫設(shè)計(jì)時(shí),直接這樣描述:
CREATE TABLE `order_test` (`id` int(20) NOT NULL AUTO_INCREMENT,`status` int(1) NOT NULL COMMENT '0-創(chuàng)建,1-支付中,2-支付成功,3-支付失敗,4-取消訂單',PRIMARY KEY (`id`) USING BTREE )SQL
0-創(chuàng)建,1-支付中,2-支付成功,3-支付失敗,4-取消訂單 這就是我們的約定了。
當(dāng)然也有惡心的設(shè)計(jì)數(shù)據(jù)庫直接使用 varchar 數(shù)據(jù)類型,這就更惡心了,搜索都不好了。
1.2 Java Bean 代碼
在 Java 中我們代碼這樣寫:
public class OrderInfo {private int id;//0-創(chuàng)建,1-支付中,2-支付成功,3-支付失敗,4-取消訂單private int status; }Java
有時(shí)候牛逼點(diǎn),我們定義點(diǎn)常量:
public interface class OrderConstants {//創(chuàng)建int ORDER_STATUS_CREATE = 0;//支付中int ORDER_STATUS_PAYING = 1;//支付成功int ORDER_STATUS_IN_PROGRESS = 2;//支付失敗int ORDER_STATUS_FAILED = 3;//取消訂單int ORDER_STATUS_REVERSED = 4; }Java
1.3 MyBatis 代碼
然后用 MyBatis 插入查詢什么的時(shí)候也簡(jiǎn)單:
@Mapper public interface OrderMapper {int addOrder(@Param("item") OrderInfo info);OrderInfo getOrderById(@Param("id") String id); }Java
<insert id="addOrder" parameterType="com.yulaiz.model.order.entity.OrderInfo">INSERT INTO order_test ( status )VALUES (#{item.status}) </insert> <select id="getOrderById" resultType="com.yulaiz.model.order.entity.OrderInfo">SELECT id, statusFROM order_testWHERE id = #{id} </select>XML
1.4 代碼效果
我們最后拿到的就是 0,1,2,3,4 這樣的數(shù)值來表示訂單狀態(tài)了,當(dāng)然現(xiàn)在的例子實(shí)在簡(jiǎn)單,實(shí)際項(xiàng)目中,那可不僅僅就這么 0-4,還有很多,并且一個(gè)訂單的狀態(tài),可不僅僅就這些,還有其他字段來描述,那么在一些復(fù)雜的查詢中,那就亂套了,一堆狀態(tài),都是數(shù)字,沒辦法一個(gè)個(gè)查吧,這可是真麻煩。
2. 現(xiàn)在我們?cè)趺炊x狀態(tài)
2.1 Java Bean 代碼
一般在我設(shè)計(jì)枚舉字段的時(shí)候,我會(huì)先設(shè)計(jì) Java 部分的枚舉字段,因?yàn)槁?#xff0c;我覺得這樣粘貼復(fù)制方便一點(diǎn):
public enum OrderStatus {CREATE("創(chuàng)建"),PAYING("支付中"),IN_PROGRESS("支付成功"),FAILED("支付失敗"),REVERSED("取消訂單");private String value;OrderStatus(String value) {this.value = value;}public String getValue() {return value;} }Java
在使用中也很簡(jiǎn)單,首先 Java Bean 設(shè)計(jì)時(shí)直接將數(shù)據(jù)類型改為 OrderStatus :
public class OrderInfo {private int id;private OrderStatus status; }Java
賦值的時(shí)候使用:
orderInfo.setStatus(OrderStatus.CREATE);Java
2.2 數(shù)據(jù)庫設(shè)計(jì)
在 Mysql 中這樣描述 enum 字段:
CREATE TABLE `order_test` (`id` int(20) NOT NULL AUTO_INCREMENT,`status` enum('CREATE','PAYING','IN_PROGRESS','FAILED','REVERSED') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'CREATE' COMMENT 'CREATE-創(chuàng)建,PAYING-支付中,IN_PROGRESS-支付成功,FAILED-支付失敗,REVERSED-取消訂單',PRIMARY KEY (`id`) USING BTREE )SQL
2.3 MyBatis 代碼
接著 MyBatis 中插入查詢完全不用修改,還是上次那個(gè)原樣就ok,是不是很簡(jiǎn)單。
2.4 代碼效果
這樣一來我們?cè)?Java 代碼中就可以直接看到這個(gè)狀態(tài)是 CREATE 或者 PAYING 或者其他狀態(tài),在sql查詢中也會(huì)顯示 CREATE 或者 PAYING 或者其他狀態(tài),清晰明了,不需要再去一個(gè)個(gè)的查字段定義,非常方便閱讀。
其中Java代碼里枚舉類有一個(gè)隱藏的屬性叫 name 就是我們定義時(shí)候使用的 CREATE("創(chuàng)建"),PAYING("支付中") 當(dāng)中的 CREATE 和 PAYING ,MyBatis在插入數(shù)據(jù)庫的時(shí)候,會(huì)自動(dòng)使用這個(gè) name 屬性作為賦值,而查詢的時(shí)候也是通過數(shù)據(jù)庫查詢的內(nèi)容和 name 屬性進(jìn)行匹配。
3. 怎么把當(dāng)年定義的狀態(tài)改成使用枚舉
好了,由于本人比較懶,發(fā)現(xiàn)枚舉的好處之后就覺得,當(dāng)年寫的到底是什么垃圾代碼,由此也就想把之前定義的都替換成枚舉。
3.1 數(shù)據(jù)庫設(shè)計(jì)
首先,咱從底層入手,先看看數(shù)據(jù)庫。
要想修改成枚舉,那么我就要先新增一個(gè)枚舉字段,然后根據(jù)原有的映射,來分別 update 對(duì)應(yīng)數(shù)據(jù),但是這樣是很糟糕的操作:
那么數(shù)據(jù)庫就先不改了,咱直接把新功能用上枚舉吧,之前的代碼不改動(dòng)了,新功能的 Java 部分直接用上枚舉,盡量讓自己寫代碼舒服點(diǎn),看著爽一些。
3.2 Java Bean 代碼
我們?nèi)耘f想使用 orderInfo.setStatus(OrderStatus.CREATE); 的方式進(jìn)行賦值,但是現(xiàn)在數(shù)據(jù)庫中的數(shù)據(jù)是 0,1,2... 跟枚舉類 OrderStatus 的 name屬性 不能對(duì)應(yīng)上了。
首先我們還是先寫 Java 的枚舉,由于這次要跟數(shù)據(jù)庫對(duì)應(yīng)上,數(shù)據(jù)結(jié)構(gòu)就有點(diǎn)區(qū)別了:
public enum OrderStatus {CREATE(0, "創(chuàng)建"),PAYING(1, "支付中"),IN_PROGRESS(2, "支付成功"),FAILED(3, "支付失敗"),REVERSED(4, "取消訂單");private int value;private String desc;OrderStatus(int value, String desc) {this.value = value;this.desc = desc;}public int getValue() {return value;}public String getDesc() {return desc;} }Java
當(dāng)然,也可以:
public enum OrderStatus {CREATE(0),PAYING(1),IN_PROGRESS(2),FAILED(3),REVERSED(4);private int value;OrderStatus(int value) {this.value = value;}public int getValue() {return value;} }Java
但我覺得還是加個(gè)中文方便,那么我就想用 CREATE(0, "創(chuàng)建") 這種方式。
3.3 MyBatis 代碼
實(shí)際上這里的關(guān)鍵就是 MyBatis 了,怎么樣讓 MyBatis 知道我們?cè)谫x值 orderInfo.setStatus(OrderStatus.CREATE); 的時(shí)候用 CREATE(0, "創(chuàng)建") 中的 0 而不是 CREATE 呢。
首先,我們先看看 MyBatis 是否能夠滿足我們的需求。MyBatis 內(nèi)置了兩個(gè)枚舉轉(zhuǎn)換器分別是org.apache.ibatis.type.EnumTypeHandler 和 org.apache.ibatis.type.EnumOrdinalTypeHandler。
3.3.1 內(nèi)置枚舉轉(zhuǎn)換器
3.3.1.1 EnumTypeHandler
這是默認(rèn)的枚舉轉(zhuǎn)換器,轉(zhuǎn)換器將枚舉實(shí)例轉(zhuǎn)換為實(shí)例名稱的字符串,即 name 屬性,也就是將 OrderStatus.CREATE 轉(zhuǎn)換為 CREATE。就是我們?cè)?2.現(xiàn)在我們?cè)趺炊x狀態(tài) 中所使用的方式。
3.3.1.2 EnumOrdinalTypeHandler
這個(gè)轉(zhuǎn)換器將枚舉實(shí)例的 ordinal 屬性作為取值,這個(gè)屬性可以通過 orderInfo.getStatus().ordinal() 來獲取。
public final int ordinal()Null
Returns the ordinal of this enumeration constant (its position in its enum declaration, where the initial constant is assigned an ordinal of zero). Most programmers will have no use for this method. It is designed for use by sophisticated enum-based data structures, such as EnumSet and EnumMap.
- Returns:
the ordinal of this enumeration constant
通過官方文檔并且通過自己的實(shí)驗(yàn)來看,這個(gè) ordinal 屬性其實(shí)是一個(gè)序號(hào),這個(gè)序號(hào)從 0 開始,只跟定義枚舉類時(shí)的順序有關(guān)。
當(dāng)我們按照下述方式定義枚舉時(shí):
public enum OrderStatus {CREATE("創(chuàng)建"),PAYING("支付中"),IN_PROGRESS("支付成功"),FAILED("支付失敗"),REVERSED("取消訂單");//省略部分代碼... }Java
其 ordinal 屬性就是跟這個(gè)先后順序有關(guān),OrderStatus.CREATE.ordinal()==0 OrderStatus.PAYING.ordinal()==1 剛好跟我們后來自定義的 value 屬性同步了。
但是我們不能相信這個(gè)屬性,所以 MyBatis 提供的兩種枚舉轉(zhuǎn)換器均不適用,我們也就只好繼續(xù)自定義了。
3.3.2 自定義枚舉轉(zhuǎn)換器
MyBatis 提供了 org.apache.ibatis.type.BaseTypeHandler 類用于我們自己擴(kuò)展類型轉(zhuǎn)換器,上面的 EnumTypeHandler 和 EnumOrdinalTypeHandler 也都實(shí)現(xiàn)了這個(gè)接口。
public class EnumOrderStatusHandler extends BaseTypeHandler<OrderStatus> {/*** 設(shè)置配置文件設(shè)置的轉(zhuǎn)換類以及枚舉類內(nèi)容,供其他方法更便捷高效的實(shí)現(xiàn)** @param type 配置文件中設(shè)置的轉(zhuǎn)換類*/public EnumOrderStatusHandler(Class<OrderStatus> 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.");}//用于定義設(shè)置參數(shù)時(shí),該如何把Java類型的參數(shù)轉(zhuǎn)換為對(duì)應(yīng)的數(shù)據(jù)庫類型@Overridepublic void setNonNullParameter(PreparedStatement ps, int i, OrderStatus parameter, JdbcType jdbcType) throws SQLException {// 根據(jù)數(shù)據(jù)庫存儲(chǔ)類型決定獲取類型,本例子中數(shù)據(jù)庫中存放int類型// ps.setStringps.setInt(i, parameter.getValue());}//用于定義通過字段名稱獲取字段數(shù)據(jù)時(shí),如何把數(shù)據(jù)庫類型轉(zhuǎn)換為對(duì)應(yīng)的Java類型@Overridepublic OrderStatus getNullableResult(ResultSet rs, String columnName) throws SQLException {// 根據(jù)數(shù)據(jù)庫存儲(chǔ)類型決定獲取類型,本例子中數(shù)據(jù)庫中存放int類型// String i = rs.getString(columnName);int i = rs.getInt(columnName);if (rs.wasNull()) {return null;} else {// 根據(jù)數(shù)據(jù)庫中的值,定位Enum子類return locateEnum(i);}}//用于定義通過字段索引獲取字段數(shù)據(jù)時(shí),如何把數(shù)據(jù)庫類型轉(zhuǎn)換為對(duì)應(yīng)的Java類型@Overridepublic OrderStatus getNullableResult(ResultSet rs, int columnIndex) throws SQLException {// 根據(jù)數(shù)據(jù)庫存儲(chǔ)類型決定獲取類型,本例子中數(shù)據(jù)庫中存放int類型// String i = rs.getString(columnIndex);int i = rs.getInt(columnIndex);if (rs.wasNull()) {return null;} else {// 根據(jù)數(shù)據(jù)庫中的值,定位Enum子類return locateEnum(i);}}//用定義調(diào)用存儲(chǔ)過程后,如何把數(shù)據(jù)庫類型轉(zhuǎn)換為對(duì)應(yīng)的Java類型@Overridepublic OrderStatus getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {// 根據(jù)數(shù)據(jù)庫存儲(chǔ)類型決定獲取類型,本例子中數(shù)據(jù)庫中存放int類型// String i = cs.getString(columnIndex);int i = cs.getInt(columnIndex);if (cs.wasNull()) {return null;} else {// 根據(jù)數(shù)據(jù)庫中的值,定位Enum子類return locateEnum(i);}}/*** 枚舉類型轉(zhuǎn)換** @param value 數(shù)據(jù)庫中存儲(chǔ)的自定義屬性* @return value對(duì)應(yīng)的枚舉類*/private OrderStatus locateEnum(int value) {for (OrderStatus status : OrderStatus.values()) {if (status.getValue() == value) {return status;}}throw new IllegalArgumentException("未知的枚舉類型:" + value);} }Java
類轉(zhuǎn)換器就這樣編寫,注意的是,各個(gè)重寫方法中,數(shù)據(jù)類型,用對(duì)應(yīng)數(shù)據(jù)庫數(shù)據(jù)的枚舉屬性的數(shù)據(jù)類型,本次就是 value 屬性對(duì)應(yīng)數(shù)據(jù)庫中的內(nèi)容,所以是 int 類型,再有就是在最后的方法中,給出枚舉匹配的方法。
3.3.3 在 Mapper.xml 中配置自定義的枚舉轉(zhuǎn)換器
方法1:修改指定xml文件,指定的Mapper生效
我們只需要在 insert update select 等語句中配置 typeHandler , 而 Java 文件無需改動(dòng)。
<insert id="addOrder" parameterType="com.yulaiz.model.order.entity.OrderInfo">INSERT INTO order_test ( status) VALUES (#{item.status, typeHandler=com.yulaiz.model.order.entity.enums.mybatis.EnumOrderStatusHandler}) </insert> <resultMap id="get" type="com.yulaiz.model.order.entity.OrderInfo"><result column="status" property="status"typeHandler="com.yulaiz.model.order.entity.enums.mybatis.EnumOrderStatusHandler"/> </resultMap> <select id="getOrderById" id="getOrderById" resultMap="get">SELECT id, statusFROM order_testWHERE id = #{id} </select>XML
方法2:全局指定枚舉轉(zhuǎn)換器,無需單獨(dú)修改Mapper.xml
直接在 Mybatis 的配置文件中配置自定義的類型轉(zhuǎn)換,這里我使用的 Spring-Boot,直接在 application.yml 文件中配置:
mybatis:type-handlers-package: com.yulaiz.model.order.entity.enums.mybatisYml
方法3:不使用枚舉轉(zhuǎn)換器
對(duì)于 insert 和 update 語句,還有一個(gè)簡(jiǎn)單的方法:
<insert id="addOrder" parameterType="com.yulaiz.model.order.entity.OrderInfo">INSERT INTO order_test ( status) VALUES (#{item.status.value}) </insert>XML
對(duì)于 Java Bean 來說,也可以手動(dòng)設(shè)置 get set 方法
public class OrderInfo {private int id;private OrderStatus status;public int getStatus() {return status.getValue();}public void setStatus(int value) {for (OrderStatus status : OrderStatus.values()) {if (status.getValue() == value) {this.status = status;break;}}} }Java
4. 枚舉到底好不好用
枚舉這樣直觀方便,在大數(shù)據(jù)量的情況下到底好不好用呢,下面我們就這么一個(gè)問題進(jìn)行分析。
4.1 Java 枚舉的實(shí)現(xiàn)
4.1.1 基礎(chǔ)聲明、屬性、構(gòu)造函數(shù)
java 的枚舉類 以關(guān)鍵字 enum 聲明,該關(guān)鍵字隱含著該類為 java.lang.Enum 的子類,Java編譯器在編譯枚舉類時(shí),會(huì)生成一個(gè)相關(guān)的類,這個(gè)類就是實(shí)際的枚舉類,繼承自 java.lang.Enum 。
public enum OrderStatus {CREATE("創(chuàng)建"), PAYING("支付中"), IN_PROGRESS("支付成功"), FAILED("支付失敗"), REVERSED("取消訂單"); }Java
該類有兩個(gè)屬性,分別是 name 和 ordinal ,在自定義的枚舉類中,我們聲明 CREATE("創(chuàng)建"), PAYING("支付中"), IN_PROGRESS("支付成功"), FAILED("支付失敗"), REVERSED("取消訂單") 的時(shí)候,前面括號(hào)外部分就是 name 屬性,比如:CREATE ,而 ordinal 屬性則是我們聲明中寫的順序,從 0 開始。這兩個(gè)屬性在父類的構(gòu)造函數(shù)中進(jìn)行賦值,我們自定義的枚舉類中不用去費(fèi)心這兩個(gè)屬性的賦值。子類中如果需要,只需要在構(gòu)造方法中定義其他自定義屬性的賦值即可。
protected Enum(String name, int ordinal) {this.name = name;this.ordinal = ordinal; }Java
而 CREATE("創(chuàng)建") 中括號(hào)內(nèi)就是我們自定義的屬性,也就是我們聲明的 value 屬性。
4.1.2 常用方法
4.1.2.1 name() 方法
返回 Enum 對(duì)象的 name 屬性。
4.1.2.2 ordinal() 方法。
返回 Enum 對(duì)象的 ordinal 屬性。
4.1.2.3 toString() 方法
返回 Enum 對(duì)象的名稱,也就是 name 屬性。
4.1.2.4 equals(Object other) 方法
比較兩個(gè)對(duì)象是否相等。詳細(xì)用法在下方 4.1.3 比較兩個(gè)枚舉值是否一致
4.1.2.5 compareTo(E o) 方法
比較兩個(gè)枚舉對(duì)象的順序,在該對(duì)象小于、等于或大于指定對(duì)象時(shí),分別返回負(fù)整數(shù)、零或正整數(shù)。詳細(xì)用法在下方 4.1.3 比較兩個(gè)枚舉值是否一致
4.1.2.6 values() 方法
這個(gè)方法我們查看JDK中源碼沒有看到,并且在API文檔中也沒有找到。但確實(shí)是有這么一個(gè)方法。
在 Oracle的文檔 中可以找到這么一段話
The compiler automatically adds some special methods when it creates an enum. For example, they have a static values method that returns an array containing all of the values of the enum in the order they are declared. This method is commonly used in combination with the for-each construct to iterate over the values of an enum type.
大致意思就是,Java 編譯器在編譯枚舉類的時(shí)候會(huì)自動(dòng)為該類加入一些靜態(tài)方法,比如說 values() ,這個(gè)方法返回一個(gè)包含這個(gè)枚舉類所有枚舉項(xiàng)的數(shù)組,這個(gè)數(shù)組是按照聲明的順序來排列(意味著 ordinal 屬性可以當(dāng)成這個(gè)數(shù)組的下標(biāo)),這個(gè)方法通常與 for-each 語句配合在遍歷枚舉值時(shí)使用。
注意,這是一個(gè) static 靜態(tài)方法,意味著使用的時(shí)候直接通過枚舉類來調(diào)用,比如 OrderStatus.values() 。
4.1.2.7 valueOf(String name) 方法
根據(jù)傳入的 name 名稱,找到相對(duì)應(yīng) name 屬性的枚舉值。
這個(gè)方法實(shí)際上也是 Java 編譯器在編譯的時(shí)候加入的方法,在父類 java.lang.Enum 中,同樣存在 valueOf 方法,不過父類的方法是有兩個(gè)參數(shù),而這個(gè)第一個(gè)參數(shù)就是用來指定是哪一個(gè)枚舉類,那我們?cè)谧宇惖臅r(shí)候使用這個(gè)方法就很明確是這個(gè)類本身,所以編譯器直接在編譯的時(shí)候加入了一個(gè)方法,只需要傳入 name 參數(shù)即可。
注意,這是一個(gè) static 靜態(tài)方法,意味著使用的時(shí)候直接通過枚舉類來調(diào)用,比如: OrderStatus.valueOf("CREATE") 。
4.1.3 比較兩個(gè)枚舉值是否一致
我們?cè)趯?shí)際代碼中經(jīng)常根據(jù)狀態(tài)來進(jìn)行流程向?qū)?#xff0c;而枚舉類大部分時(shí)間就是用來表示狀態(tài),那么我們就需要對(duì)比枚舉類是否為某個(gè)值來進(jìn)行判斷:
- 通過 switch 語句
通過 switch 語句來判斷,簡(jiǎn)單方便,好看:
OrderStatus status = OrderStatus.CREATE; switch (status) {case CREATE:System.out.println("CREATE");break;case PAYING:System.out.println("PAYING");break;default:System.out.println("default"); }Java
- 通過 equals() 方法
用 String 的時(shí)候經(jīng)常使用的方法:
OrderStatus status = OrderStatus.CREATE; boolean result = status.equals(OrderStatus.CREATE);Java
- 通過 == 運(yùn)算符
實(shí)際上效果跟 equals() 方法是一樣的,我們可以點(diǎn)進(jìn) equals() 方法的源碼查看,實(shí)際上就是使用 == 運(yùn)算符來實(shí)現(xiàn)的:
/*** Returns true if the specified object is equal to this* enum constant.** @param other the object to be compared for equality with this object.* @return true if the specified object is equal to this* enum constant.*/public final boolean equals(Object other) {return this==other;}Java
實(shí)際使用上就更簡(jiǎn)單了:
OrderStatus status = OrderStatus.CREATE; if (status == OrderStatus.CREATE) {System.out.println("=="); }Java
- 通過compareTo(E o) 方法
稍微麻煩一點(diǎn):
OrderStatus status = OrderStatus.CREATE; if (status.compareTo(OrderStatus.CREATE) == 0) {System.out.println("=="); }Java
?
4.2 MySql 中的枚舉
首先,我們要知道對(duì)于 select 語句,枚舉對(duì)于其展示出來的數(shù)據(jù)是非常友好的,可以直觀的看到具體的含義。而枚舉的優(yōu)點(diǎn)就是固定的有限的枚舉項(xiàng),那么就需要我們?cè)诙x數(shù)據(jù)庫的時(shí)候就定義好這個(gè)枚舉可能用到的所有值,相比int類型,我們來看看對(duì)于枚舉類型 enum 的枚舉項(xiàng)的刪除和修改操作的效率如何。
我在數(shù)據(jù)庫中分別創(chuàng)建了兩張表:order_test 、order_test_enum 。
CREATE TABLE `order_test` (`id` int(20) NOT NULL AUTO_INCREMENT,`status` int(1) NOT NULL COMMENT '0-創(chuàng)建,1-支付中,2-支付成功,3-支付失敗,4-取消訂單',PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;CREATE TABLE `order_test_enum` (`id` int(20) NOT NULL AUTO_INCREMENT,`status` enum('CREATE','PAYING','IN_PROGRESS','FAILED','REVERSED') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'CREATE' COMMENT 'CREATE-創(chuàng)建,PAYING-支付中,IN_PROGRESS-支付成功,FAILED-支付失敗,REVERSED-取消訂單',PRIMARY KEY (`id`) USING BTREE ) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;SQL
在兩張表中分別插入了 1000W 條數(shù)據(jù),其數(shù)據(jù)格式就是每個(gè)枚舉項(xiàng)都依次循環(huán)插入,保證每個(gè)枚舉項(xiàng)的數(shù)量盡可能平均的分布。
4.2.1 DDL 操作效率
對(duì)于狀態(tài)的修改,我們一般會(huì)有幾種操作:
- 新增一個(gè)狀態(tài)。
- 修改一個(gè)狀態(tài)。
- 刪除一個(gè)狀態(tài)。
4.2.1.1 新增一個(gè)枚舉狀態(tài)
對(duì)于 int 類型來說,這個(gè)直接無需操作了,要什么新狀態(tài),直接 insert 的時(shí)候直接插入就行了。
對(duì)于 enum 類型來說,這個(gè)就需要進(jìn)行 DDL 操作:
ALTER TABLE `test`.`order_test_enum` MODIFY COLUMN `status` enum('CREATE','PAYING','IN_PROGRESS','FAILED','REVERSED','TEST') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'CREATE' COMMENT 'CREATE-創(chuàng)建,PAYING-支付中,IN_PROGRESS-支付成功,FAILED-支付失敗,REVERSED-取消訂單,TEST-新增測(cè)試';SQL
執(zhí)行時(shí)間:0.008s 還不錯(cuò)的速度。
4.2.1.2 修改一個(gè)枚舉狀態(tài)
比方說我們定義的狀態(tài),寫錯(cuò)值了,又不想將錯(cuò)就錯(cuò),int 類型應(yīng)該不會(huì)出現(xiàn)這個(gè)問題吧,奈何英文水平實(shí)在太差,拼錯(cuò)了單詞,或者單詞用的不合理,我們這里假設(shè)將 PAYING 換成 PAYING1 :
ALTER TABLE `test`.`order_test_enum` MODIFY COLUMN `status` enum('CREATE','PAYING1','IN_PROGRESS','FAILED','REVERSED','TEST') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'CREATE' COMMENT 'CREATE-創(chuàng)建,PAYING1-支付中,IN_PROGRESS-支付成功,FAILED-支付失敗,REVERSED-取消訂單,TEST-新增測(cè)試';SQL
1265 - Data truncated for column 'status' at row 2 修改失敗,Mysql 不允許修改已經(jīng)使用的枚舉項(xiàng)。
那么我們想到達(dá)換枚舉值的情況就只能曲折一點(diǎn),先新增一個(gè),再 update 原有的值到新值,剛剛已經(jīng)建好的TEST 值,那么我們現(xiàn)在就把 PAYING 修改成 TEST :
UPDATE order_test_enum SET `status` = 'TEST' WHERE`status` = 'PAYING';SQL
執(zhí)行時(shí)間: 12.456s 這樣的一個(gè)時(shí)間說得過去,因?yàn)槲覄倓傄苍?order_test 表中做了一個(gè)差不多的操作執(zhí)行時(shí)間是 10.459s 。
UPDATE order_test SET `status` = 5 WHERE`status` = 1;SQL
4.2.1.3 刪除一個(gè)枚舉狀態(tài)
同樣 int 類型無需修改,修改 enum 類型進(jìn)行 DDL 操作,假設(shè)我們刪除 IN_PROGRESS 這項(xiàng):
ALTER TABLE `test`.`order_test_enum` MODIFY COLUMN `status` enum('CREATE','PAYING','FAILED','REVERSED','TEST') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'CREATE' COMMENT 'CREATE-創(chuàng)建,PAYING-支付中,FAILED-支付失敗,REVERSED-取消訂單,TEST-新增測(cè)試';SQL
1265 - Data truncated for column 'status' at row 2 刪除失敗,這是由于MySql不允許刪除已經(jīng)被使用的枚舉項(xiàng)。那我們刪除剛剛已經(jīng)把數(shù)據(jù)改為 TEST 的 PAYING 試試:
ALTER TABLE `test`.`order_test_enum` MODIFY COLUMN `status` enum('CREATE','IN_PROGRESS','FAILED','REVERSED','TEST') CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL DEFAULT 'CREATE' COMMENT 'CREATE-創(chuàng)建,IN_PROGRESS-支付成功,FAILED-支付失敗,REVERSED-取消訂單,TEST-新增測(cè)試';SQL
執(zhí)行時(shí)間: 34.971s 這個(gè)時(shí)間是真的不快。
4.2.2 查詢效率
可能有些人會(huì)說枚舉對(duì)于大數(shù)據(jù)查詢是非常不友好的,畢竟是字符型。
而實(shí)際上 Mysql 中的 enum 類型,還恰恰不是字符型,而是整型存儲(chǔ)。
實(shí)際上在建立 enum 字段的時(shí)候,MySql 會(huì)根據(jù)設(shè)定的幾個(gè)字段的順序來編號(hào),而這個(gè)編號(hào)實(shí)際上才是 MySql 真正存儲(chǔ)的內(nèi)容,這個(gè)編號(hào)在某些文章里也會(huì)稱呼為索引值。
在上面的例子中,建立的枚舉值分別為 'CREATE','PAYING','IN_PROGRESS','FAILED','REVERSED' ,那么他們的索引值就分別為:
| NULL | NULL |
| ” | 0 |
| ‘CREATE’ | 1 |
| ‘PAYING’ | 2 |
| ‘IN_PROGRESS’ | 3 |
| ‘FAILED’ | 4 |
| ‘REVERSED’ | 5 |
所以在搜索的時(shí)候 WHERE 條件中的 status = 'CREATE' 和 status = 1 是等效的,這里也要注意就是 enum 字段同樣有 NULL 值 和 ” 的情況,需要我們?cè)诙x數(shù)據(jù)類型的時(shí)候就需要注意。
SELECTid,`status` FROMorder_test_enum WHEREid = 1 AND `status` = 1;SQL
SELECTid,`status` FROMorder_test_enum WHEREid = 1 AND `status` = 'CREATE';SQL
上面兩個(gè)sql查詢到的內(nèi)容就是一樣的,那么同是整型,在查詢上的差距,咱就可以忽略不計(jì)了吧。
5. 枚舉使用總結(jié)
雖然 Java 中的枚舉比 C 或 C++ 中的 enum 更成熟,但它仍然是一個(gè)“小”功能,Java 沒有它也已經(jīng)(雖然有點(diǎn)笨拙)存在很多年了。而本章正好說明了一個(gè)“小”功能所能帶來的價(jià)值。有時(shí)恰恰因?yàn)樗?#xff0c;你才能夠優(yōu)雅而干凈地解決問題。正如我在本書中一再強(qiáng)調(diào)的那樣,優(yōu)雅與清晰很重要,正是它們區(qū)別了成功的解決方案與失敗的解決方案。而失敗的解決方案就是因?yàn)槠渌藷o法理解它。
直接引用了《Thinking in Java》這么一段話(《Thinking in Java》第四版 19章12節(jié)),很明顯,很清楚,至少在 Java 中使用枚舉是值得的。
而對(duì)于數(shù)據(jù)庫來說,根據(jù)實(shí)際情況來構(gòu)建,至少枚舉時(shí)一個(gè)特別方便的結(jié)構(gòu),如果有舊表的狀態(tài)描述字段沒有使用枚舉,請(qǐng)慎重是否進(jìn)行改進(jìn),需要考慮存量數(shù)據(jù)的改造,以及對(duì)接代碼的改造。
即便 MySql 中依舊使用數(shù)字來表達(dá)狀態(tài),依然不妨礙在 Java 中改造成枚舉,具體改造的范圍需要自行斟酌,防止接口對(duì)上下游系統(tǒng)的不友好。
枚舉是非常適合用來描述狀態(tài)的結(jié)構(gòu),并且不推薦使用數(shù)字來定義,請(qǐng)使用字符串來表達(dá)各個(gè)狀態(tài),否則在查看代碼,查看數(shù)據(jù)庫數(shù)據(jù)的時(shí)候,還是一個(gè)數(shù)字,那我們使用枚舉的意義何在。
總的來說,請(qǐng)盡量合理的使用枚舉,功能雖小,但咱們很優(yōu)雅。
6. 參考鏈接
mysql enum類型存在大量數(shù)據(jù)的時(shí)候方便修改數(shù)據(jù)項(xiàng)么
mysql 數(shù)據(jù)庫枚舉類型enum,方便添加新的枚舉項(xiàng)嗎?
MySQL中的enum類型有什么優(yōu)點(diǎn)?
深入理解Java枚舉類型(enum)
重新認(rèn)識(shí)java(十) —- Enum(枚舉類)
java enum(枚舉)使用詳解 + 總結(jié)
如何在MyBatis中優(yōu)雅的使用枚舉
MYSQL中 ENUM 類型
java枚舉enum類中的values()
MyBatis對(duì)于Java對(duì)象里的枚舉類型處理
MyBatis對(duì)于Java對(duì)象里的枚舉類型處理
總結(jié)
以上是生活随笔為你收集整理的Java、Mysql、MyBatis 中枚举 enum 的使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 顺序栈的实验报告c语言,顺序栈的基本操作
- 下一篇: mysql安装可视化界面_MySQL的下