如何使用 JPA 2.1 或更低版本 (Postgres) 在 Java 中序列化和反序列化多参数枚举
How to serialize and deserialize multi argument enum in Java using JPA 2.1 or lower (Postgres)
public enum CameraType {
CAMERA(false, false, "External lens ", ""),
CameraType{
boolean collector,
boolean hidden,
String description
) {
this.collector = collector;
this.granular = hidden;
this.description = description;
} // end ctor
public void setHide(boolean hidden) {
this.hide = hidden;
}
} // end enum
我的 CameraType 实例很少。
我有一个 setter 用于 “隐藏” 属性 在特定条件下设置为 true 或 false。
现在我用 SecurityEntity 中的几个其他字段序列化 CameraType。
```
@Entity
@Table
public class Security {
Few more fields...
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private CameraType cameraType
And other fields...
}
```
当我反序列化时,“隐藏”字段的值始终为假。如果我理解正确,在反序列化期间调用 ctor 并分配默认值。
有没有办法在每个 CameraType 实例反序列化后保留“隐藏”字段的值(true 或 false)。
我正在使用 Postgres DB 10。
enter code here
请帮助。我没有线索。
根据定义,枚举是固定集合的不可变元素。因此,要表示一个枚举值,您只需要它的名称。这正是 JPA 在 serialization/deserialization.
期间所做的
您试图违反规则。虽然 Java 允许您几乎将枚举视为普通对象,但 JPA 会根据它们应该是什么来对待它们。这就是您的代码无法正常工作的原因。
您可以:
- 将
CameraType
变成 class 并序列化它,或者
- 将
CameraType
分成两部分,例如enum CameraType
(不可变)和class CameraConfig
(包含所有可变字段)
前一个答案是正确的:枚举必须是不可变的,将部分分为不可变和可变数据是一个不错的选择。
这里补充一点:使用枚举值存储数据库通常不是一个好的选择,因为当另一个开发人员决定重构枚举名称而你正在从数据库中读取旧条目时,你会遇到一个崩溃的应用程序。 ..
所以我建议使用 javax.persistence.AttributeConverter
到 deserialize/serialize 一个特定的枚举并重命名保存方式。
这是一个非常简单的示例,其中包含一个名为 MyDefinition
:
的枚举
enum MyDefinition{
ONE("def_one"),
TWO"def_two"),
THREE("def_three"),
;
private String id;
private MyDefinition(String id){
this.id=id;
}
public String getId(){
return id;
}
public static MyDefinition fromId(String id) {
for (MyDefinition definition : MyDefinition.values()) {
if (definition.id.equals(id)) {
return definition;
}
}
return null;
}
}
这里是转换器:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter(autoApply = true)
public class MyDefinitionAttributeConverter implements AttributeConverter<MyDefinition, String> {
@Override
public String convertToDatabaseColumn(MyDefinition attribute) {
if (attribute == null){
return null;}
}
return attribute.getId();
}
@Override
public MyDefinition convertToEntityAttribute(String dbData) {
return MyDefinition.fromId(dbData);
}
所以我们可以使用数据库的ID。重命名枚举名称将不再导致应用程序在读取旧数据时崩溃。
public enum CameraType {
CAMERA(false, false, "External lens ", ""),
CameraType{
boolean collector,
boolean hidden,
String description
) {
this.collector = collector;
this.granular = hidden;
this.description = description;
} // end ctor
public void setHide(boolean hidden) {
this.hide = hidden;
}
} // end enum
我的 CameraType 实例很少。
我有一个 setter 用于 “隐藏” 属性 在特定条件下设置为 true 或 false。
现在我用 SecurityEntity 中的几个其他字段序列化 CameraType。
```
@Entity
@Table
public class Security {
Few more fields...
@Enumerated(EnumType.STRING)
@Column(nullable = false)
private CameraType cameraType
And other fields...
}
```
当我反序列化时,“隐藏”字段的值始终为假。如果我理解正确,在反序列化期间调用 ctor 并分配默认值。
有没有办法在每个 CameraType 实例反序列化后保留“隐藏”字段的值(true 或 false)。
我正在使用 Postgres DB 10。
enter code here
请帮助。我没有线索。
根据定义,枚举是固定集合的不可变元素。因此,要表示一个枚举值,您只需要它的名称。这正是 JPA 在 serialization/deserialization.
期间所做的您试图违反规则。虽然 Java 允许您几乎将枚举视为普通对象,但 JPA 会根据它们应该是什么来对待它们。这就是您的代码无法正常工作的原因。
您可以:
- 将
CameraType
变成 class 并序列化它,或者 - 将
CameraType
分成两部分,例如enum CameraType
(不可变)和class CameraConfig
(包含所有可变字段)
前一个答案是正确的:枚举必须是不可变的,将部分分为不可变和可变数据是一个不错的选择。
这里补充一点:使用枚举值存储数据库通常不是一个好的选择,因为当另一个开发人员决定重构枚举名称而你正在从数据库中读取旧条目时,你会遇到一个崩溃的应用程序。 ..
所以我建议使用 javax.persistence.AttributeConverter
到 deserialize/serialize 一个特定的枚举并重命名保存方式。
这是一个非常简单的示例,其中包含一个名为 MyDefinition
:
enum MyDefinition{
ONE("def_one"),
TWO"def_two"),
THREE("def_three"),
;
private String id;
private MyDefinition(String id){
this.id=id;
}
public String getId(){
return id;
}
public static MyDefinition fromId(String id) {
for (MyDefinition definition : MyDefinition.values()) {
if (definition.id.equals(id)) {
return definition;
}
}
return null;
}
}
这里是转换器:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter(autoApply = true)
public class MyDefinitionAttributeConverter implements AttributeConverter<MyDefinition, String> {
@Override
public String convertToDatabaseColumn(MyDefinition attribute) {
if (attribute == null){
return null;}
}
return attribute.getId();
}
@Override
public MyDefinition convertToEntityAttribute(String dbData) {
return MyDefinition.fromId(dbData);
}
所以我们可以使用数据库的ID。重命名枚举名称将不再导致应用程序在读取旧数据时崩溃。