按 属性 值休眠映射枚举
Hibernate map enum by property value
我正在尝试使用 Hibernate 执行以下操作:
- 当对象被保存到数据库时,它被保存为枚举名称的值 属性。
- 当从数据库中检索对象时,对象从数据库中读取字符串,并通过枚举名称的值实例化枚举 属性。
让我解释一下。
这是枚举:
public enum Position {
UNSPECIFIED(1L, "Unspecified"),
SPECIALIST(2L, "Specialist"),
NOT_SPECIALIST(3L, "Not Specialist");
private Long id;
private String name;
Position(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public static Position from(Long id) {
for(Position position: values()) {
if(position.getId().equals(id))
return position;
}
throw new IllegalArgumentException("Cannot get position for ID " + id);
}
public static Position from(String name) {
for(Position position: values()) {
if(position.getName().toUpperCase().equals(name.toUpperCase()))
return position;
}
throw new IllegalArgumentException("No such position " + name);
}
}
这是一个使用枚举的class:
@Entity(name = "worker")
public class Worker {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private Position position;
private String email;
}
这是数据在数据库中的显示方式:
worker
id first_name last_name position email
0 Adam Applegate Unspecified adam@company.com
1 Bob Barrett Specialist bob@company.com
所以基本上,当我调用 workerRepository.findById(0);
时,我返回的 worker 对象具有以下值:
id --> 0
firstName --> Adam
lastName --> Applegate
position --> Position.UNSPECIFIED
email --> adam@company.com
现在,假设我创建了一个具有以下值的新工作对象:
firstName --> Chad
lastName --> Carlton
position --> Position.NOT_SPECIFIED
email --> chad@company.com
调用 workerRepository.save(newWorker);
后,数据库应如下所示:
id first_name last_name position email
0 Adam Applegate Unspecified adam@company.com
1 Bob Barrett Specialist bob@company.com
2 Chad Carlton Not Specialist chad@company.com
请注意,位置列的值为 Position.NOT_SPECIALIST.getName()。
有什么办法可以在休眠状态下做到这一点吗?
我建议避免 AttributeConverter
并选择简单的 @Enumerated(STRING)
。
显然,这仅适用于您可以忍受大写和下划线位置值的情况。
public enum Position
{
UNSPECIFIED,
SPECIALIST,
NOT_SPECIALIST;
}
@Entity(name = "worker")
public class Worker
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
@NotNull
@Enumerated(STRING)
@Column(nullable = false)
private Position position = Position.UNSPECIFIED;
private String email;
}
worker
id first_name last_name position email
0 Adam Applegate UNSPECIFIED adam@company.com
1 Bob Barrett SPECIALIST bob@company.com
还不清楚为什么你需要 Position.getId()
而你已经有了 Position.ordinal()
:你的 Position.from(id)
等同于 Position.values()[id]
。
最后,在枚举中声明 "labels" 硬编码(通常在编译代码中)不是一个好的做法:只需使用 ResourceBundle
,你就可以为 i18n 做好准备了。
正如@chrylis 所建议的,我的问题的答案是使用 AttributeConverter
,像这样:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter(autoApply = true)
public class PositionConverter implements AttributeConverter<Position, String> {
@Override
public String convertToDatabaseColumn(Position attribute) {
return attribute.getName();
}
@Override
public VolleyballPlayerPosition convertToEntityAttribute(String dbData) {
return Position.from(dbData);
}
}
我正在尝试使用 Hibernate 执行以下操作:
- 当对象被保存到数据库时,它被保存为枚举名称的值 属性。
- 当从数据库中检索对象时,对象从数据库中读取字符串,并通过枚举名称的值实例化枚举 属性。
让我解释一下。
这是枚举:
public enum Position {
UNSPECIFIED(1L, "Unspecified"),
SPECIALIST(2L, "Specialist"),
NOT_SPECIALIST(3L, "Not Specialist");
private Long id;
private String name;
Position(Long id, String name) {
this.id = id;
this.name = name;
}
public Long getId() {
return id;
}
public String getName() {
return name;
}
public static Position from(Long id) {
for(Position position: values()) {
if(position.getId().equals(id))
return position;
}
throw new IllegalArgumentException("Cannot get position for ID " + id);
}
public static Position from(String name) {
for(Position position: values()) {
if(position.getName().toUpperCase().equals(name.toUpperCase()))
return position;
}
throw new IllegalArgumentException("No such position " + name);
}
}
这是一个使用枚举的class:
@Entity(name = "worker")
public class Worker {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
private Position position;
private String email;
}
这是数据在数据库中的显示方式:
worker
id first_name last_name position email
0 Adam Applegate Unspecified adam@company.com
1 Bob Barrett Specialist bob@company.com
所以基本上,当我调用 workerRepository.findById(0);
时,我返回的 worker 对象具有以下值:
id --> 0
firstName --> Adam
lastName --> Applegate
position --> Position.UNSPECIFIED
email --> adam@company.com
现在,假设我创建了一个具有以下值的新工作对象:
firstName --> Chad
lastName --> Carlton
position --> Position.NOT_SPECIFIED
email --> chad@company.com
调用 workerRepository.save(newWorker);
后,数据库应如下所示:
id first_name last_name position email
0 Adam Applegate Unspecified adam@company.com
1 Bob Barrett Specialist bob@company.com
2 Chad Carlton Not Specialist chad@company.com
请注意,位置列的值为 Position.NOT_SPECIALIST.getName()。
有什么办法可以在休眠状态下做到这一点吗?
我建议避免 AttributeConverter
并选择简单的 @Enumerated(STRING)
。
显然,这仅适用于您可以忍受大写和下划线位置值的情况。
public enum Position
{
UNSPECIFIED,
SPECIALIST,
NOT_SPECIALIST;
}
@Entity(name = "worker")
public class Worker
{
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstName;
private String lastName;
@NotNull
@Enumerated(STRING)
@Column(nullable = false)
private Position position = Position.UNSPECIFIED;
private String email;
}
worker
id first_name last_name position email
0 Adam Applegate UNSPECIFIED adam@company.com
1 Bob Barrett SPECIALIST bob@company.com
还不清楚为什么你需要 Position.getId()
而你已经有了 Position.ordinal()
:你的 Position.from(id)
等同于 Position.values()[id]
。
最后,在枚举中声明 "labels" 硬编码(通常在编译代码中)不是一个好的做法:只需使用 ResourceBundle
,你就可以为 i18n 做好准备了。
正如@chrylis 所建议的,我的问题的答案是使用 AttributeConverter
,像这样:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;
@Converter(autoApply = true)
public class PositionConverter implements AttributeConverter<Position, String> {
@Override
public String convertToDatabaseColumn(Position attribute) {
return attribute.getName();
}
@Override
public VolleyballPlayerPosition convertToEntityAttribute(String dbData) {
return Position.from(dbData);
}
}