hibernate 使用枚举字段的最佳实践

枚举类虽然很简单,但是却往往是系统中业务逻辑最集中最复杂的地方。本文将会分享我们项目中基于hibernate的枚举类使用规范,包含数据库中枚举列数据类型、注释、枚举列与枚举类的映射等。

 

一、枚举类定义规范

 

 1 package org.jframe.data.enums;
 2 
 3 /**
 4  * Created by leo on 2017-05-31.
 5  */
 6 public enum Gender {
 7     unknown(0),
 8     male(11),
 9     female(12);
10 
11     public final static String Doc = "0: unknown; 11: male; 12: female";
12 
13     private final int value;
14     private Gender(int value){
15         this.value = value;
16     }
17 
18     public int getValue(){
19         return this.value;
20     }
21 
22 }

 

请注意,枚举类一定要包含一个常量字符串用于说明每一个枚举值的作用。为什么一定要放在枚举类里面?那是因为每当枚举值有改变的的时候,能够不用想都知道修改这个说明文档。这个说明文档在哪些地方用到呢?1: 数据库定义(见下文);2:swagger API自动文档。

 

二、数据库定义

@Column(name = "gender", columnDefinition = "int not null COMMENT '" + Gender.Doc + "'")
@Convert(converter = GenderConverter.class)
private Gender gender;

 

上一步定义的枚举类说明,在这里的columnDefinition就用到了。这样,如果使用code first模式生成数据库,那么数据库中该列就会包含该枚举类的说明文档了。这也是我们强烈建议使用code first模式的重要原因。

 

最大的好处:使用强类型的枚举类而不是string或int类型。

 

关于数据库定义,有几个为什么需要解释一下。

 

1. 为什么使用int类型而非@Enumerated(EnumType.STRING)?答:java代码中的枚举类的成员随时可能会改变,比如之前拼写错误需要重构,或者就是任何理由的重构,虽然代码改了,但是数据库中已经存在的记录还是使用原来的枚举值,这就非常危险了。所以,使用EnumType.STRING是极其危险的,因为你改了代码而没有任何地方通知你去改老数据,这对于新手程序员来讲就很容易写出危险的代码了,这就不是一个好的架构师该做的架构了。

 

2. 为什么一定要是使用AttributeConverter?答:如果不使用这个converter,hibernate默认会根据java代码中枚举成员出现的顺序的index作为存到数据库中的值。这就极其危险了。因为我们很有可能重构代码调整枚举成员顺序,或者插入新的成员,但是数据库中已经存在的记录还是原来的顺序值。所以,不使用attributeconverter是极其危险的,因为你改了代码而没有任何地方通知你去改老数据,这对于新手程序员来讲就很容易写出危险的代码了,这就不是一个好的架构师该做的架构了。

 

三、AttributeConverter

 1 package org.jframe.data.converters;
 2 
 3 import org.jframe.data.enums.Gender;
 4 import org.jframe.infrastructure.core.JList;
 5 
 6 import javax.persistence.AttributeConverter;
 7 
 8 /**
 9  * Created by leo on 2017-06-28.
10  */
11 public class GenderConverter implements AttributeConverter<Gender, Integer> {
12     @Override
13     public Integer convertToDatabaseColumn(Gender gender) {
14         return gender.getValue();
15     }
16 
17     @Override
18     public Gender convertToEntityAttribute(Integer integer) {
19         return JList.from(Gender.values()).firstOrNull(x -> x.getValue() == integer);
20     }
21 }

 

这就很简单了,无需赘述。

 

四、心得

相比于.NET体系下面的entity framework,可以看到hibernate的架构师给程序员挖了两个坑。使用entity framework,即便是初级程序员也很难因为改了枚举类代码导致数据库的数据错误,但是使用hibernate就不一样了,即便是很多中高级程序员,也很容易因为改了枚举类代码而导致数据库中的数据错误。

 

所以,我们项目中就建立了上面所描述的规范,如果没有这个规范,程序员就很容易因为改了代码而忘记改数据库导致严重数据错误。

 

本文中的代码来自jframe 框架

基于spring mvc搭建的多层级多模块java web应用程序框架。包含:基础设施层、数据库定义规范、数据库访问规范、日志记录规范、多层级异常捕获、标准ajax规范、母版页规范、视图呈现规范、JavaScript框架规范等。实际上该框架定义的规范极其详细,比如数据库定义层:枚举类使用规范、datetime/bool/string字段规范、1对1、1对多、多对1、多对多外键关系映射规范、父类定义规范、字段注释规范、懒加载规范等等。。。

 

jframe github地址: https://github.com/leotsai/jframe,技术交流QQ群:651499479,欢迎java大神加群,也欢迎新手进群学习。

 

如果你们项目中有更好的枚举类使用经验,请一定要分享出来哦!

 

THE END

posted @ 2017-07-28 14:02  Leo C.W  Views(3612)  Comments(0Edit  收藏  举报