我想为JPA编写一个转换器,它可以将任何枚举存储为大写。我们遇到的一些枚举不遵循只使用大写字母的约定,所以在重构它们之前,我仍然存储未来的值。
到目前为止,我得到的是:
package student;
public enum StudentState {
Started,
Mentoring,
Repeating,
STUPID,
GENIUS;
}
我希望"Started“存储为"STARTED”,依此类推。
package student;
import jpa.EnumUppercaseConverter;
import javax.persistence.*;
import java.io.Serializable;
import java.util.Date;
@Entity
@Table(name = "STUDENTS")
public class Student implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "ID")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long mId;
@Column(name = "LAST_NAME", length = 35)
private String mLastName;
@Column(name = "FIRST_NAME", nullable = false, length = 35)
private String mFirstName;
@Column(name = "BIRTH_DATE", nullable = false)
@Temporal(TemporalType.DATE)
private Date mBirthDate;
@Column(name = "STUDENT_STATE")
@Enumerated(EnumType.STRING)
@Convert(converter = EnumUppercaseConverter.class)
private StudentState studentState;
}
转换器目前看起来如下所示:
package jpa;
import javax.persistence.AttributeConverter;
import java.util.EnumSet;
public class EnumUppercaseConverter<E extends Enum<E>> implements AttributeConverter<E, String> {
private Class<E> enumClass;
@Override
public String convertToDatabaseColumn(E e) {
return e.name().toUpperCase();
}
@Override
public E convertToEntityAttribute(String s) {
// which enum is it?
for (E en : EnumSet.allOf(enumClass)) {
if (en.name().equalsIgnoreCase(s)) {
return en;
}
}
return null;
}
}
不起作用的是,我不知道在运行时enumClass会是什么。我想不出在@Converter注解中将此信息传递给转换器的方法。
那么,有没有办法向转换器添加参数,或者稍微作弊呢?还是有别的办法?
我使用的是EclipseLink 2.4.2
谢谢!
发布于 2018-03-03 04:23:38
基于我的@scottb解决方案,针对hibernate 4.3进行了测试:(没有hibernate类,应该可以在JPA上运行良好)
接口枚举必须实现:
public interface PersistableEnum<T> {
public T getValue();
}
基本抽象转换器:
@Converter
public abstract class AbstractEnumConverter<T extends Enum<T> & PersistableEnum<E>, E> implements AttributeConverter<T, E> {
private final Class<T> clazz;
public AbstractEnumConverter(Class<T> clazz) {
this.clazz = clazz;
}
@Override
public E convertToDatabaseColumn(T attribute) {
return attribute != null ? attribute.getValue() : null;
}
@Override
public T convertToEntityAttribute(E dbData) {
T[] enums = clazz.getEnumConstants();
for (T e : enums) {
if (e.getValue().equals(dbData)) {
return e;
}
}
throw new UnsupportedOperationException();
}
}
您必须为每个枚举创建一个转换器类,我发现在枚举中创建静态类更容易:(jpa/hibernate可以为枚举提供接口,哦,好吧……)
public enum IndOrientation implements PersistableEnum<String> {
LANDSCAPE("L"), PORTRAIT("P");
private final String value;
@Override
public String getValue() {
return value;
}
private IndOrientation(String value) {
this.value= value;
}
public static class Converter extends AbstractEnumConverter<IndOrientation, String> {
public Converter() {
super(IndOrientation.class);
}
}
}
和带有注释的映射示例:
...
@Convert(converter = IndOrientation.Converter.class)
private IndOrientation indOrientation;
...
通过一些更改,您可以创建一个IntegerEnum接口并对其进行泛化。
发布于 2020-08-14 02:46:43
上面的解决方案真的很好。我在这里添加了一些小功能。
在实现编写转换器类的接口时,我还添加了以下内容以强制执行。当您忘记时,jpa开始使用默认机制,这实际上是模糊的解决方案(特别是当映射到某个数值时,我总是这样做)。
接口类如下所示:
public interface PersistedEnum<E extends Enum<E> & PersistedEnum<E>> {
int getCode();
Class<? extends PersistedEnumConverter<E>> getConverterClass();
}
与之前的帖子类似的PersistedEnumConverter。然而,在实现此接口时,您必须处理getConverterClass实现,它除了强制提供特定的转换器类之外,完全没有用。
下面是一个实现示例:
public enum Status implements PersistedEnum<Status> {
...
@javax.persistence.Converter(autoApply = true)
static class Converter extends PersistedEnumConverter<Status> {
public Converter() {
super(Status.class);
}
}
@Override
public Class<? extends PersistedEnumConverter<Status>> getConverterClass() {
return Converter.class;
}
...
}
我在数据库中所做的就是为每个枚举值创建一个配套的表,每个枚举值包含一行
create table e_status
(
id int
constraint pk_status primary key,
label varchar(100)
);
insert into e_status
values (0, 'Status1');
insert into e_status
values (1, 'Status2');
insert into e_status
values (5, 'Status3');
并从使用枚举类型的位置放置fk约束。这样,就可以保证使用正确的枚举值。我特别在这里设置了值0、1和5,以显示它的灵活性,并且仍然是可靠的。
create table using_table
(
...
status int not null
constraint using_table_status_fk references e_status,
...
);
发布于 2022-01-28 01:39:44
如果你不介意反思,这是可行的。归功于另一个人,所以回答内联。
abstract class EnumTypeConverter<EnumType,ValueType> implements AttributeConverter<EnumType, ValueType> {
private EnumType[] values
@Override
ValueType convertToDatabaseColumn(EnumType enumInstance) {
return enumInstance ? enumInstance.getProperty(getValueColumnName()) : null
}
@Override
EnumType convertToEntityAttribute(ValueType dbData) {
if(dbData == null){
return null
}
EnumType[] values = getValues()
EnumType rtn = values.find {
it.getProperty(getValueColumnName()).equals(dbData)
}
if(!rtn) {
throw new IllegalArgumentException("Unknown ${values.first().class.name} value: ${dbData}")
}
rtn
}
private EnumType[] getValues() {
if(values == null){
Class cls = getTypeParameterType(getClass(), EnumTypeConverter.class, 0)
Method m = cls.getMethod("values")
values = m.invoke(null) as EnumType[]
}
values
}
abstract String getValueColumnName()
// https://stackoverflow.com/a/59205754/3307720
private static Class<?> getTypeParameterType(Class<?> subClass, Class<?> superClass, int typeParameterIndex) {
return getTypeVariableType(subClass, superClass.getTypeParameters()[typeParameterIndex])
}
private static Class<?> getTypeVariableType(Class<?> subClass, TypeVariable<?> typeVariable) {
Map<TypeVariable<?>, Type> subMap = new HashMap<>()
Class<?> superClass
while ((superClass = subClass.getSuperclass()) != null) {
Map<TypeVariable<?>, Type> superMap = new HashMap<>()
Type superGeneric = subClass.getGenericSuperclass()
if (superGeneric instanceof ParameterizedType) {
TypeVariable<?>[] typeParams = superClass.getTypeParameters()
Type[] actualTypeArgs = ((ParameterizedType) superGeneric).getActualTypeArguments()
for (int i = 0; i < typeParams.length; i++) {
Type actualType = actualTypeArgs[i]
if (actualType instanceof TypeVariable) {
actualType = subMap.get(actualType)
}
if (typeVariable == typeParams[i]) return (Class<?>) actualType
superMap.put(typeParams[i], actualType)
}
}
subClass = superClass
subMap = superMap
}
return null
}
}
然后在实体类中:
enum Type {
ATYPE("A"), ANOTHER_TYPE("B")
final String name
private Type(String nm) {
name = nm
}
}
...
@Column
Type type
...
@Converter(autoApply = true)
static class TypeConverter extends EnumTypeConverter<Type,String> {
String getValueColumnName(){
"name"
}
}
这是用groovy编写的,因此您需要对Java进行一些调整。
https://stackoverflow.com/questions/23564506
复制相似问题