wemu wemu - 5 months ago 331
Java Question

Is it possible to write a generic enum converter for JPA?

I wanted to write a Converter for JPA that stores any enum as UPPERCASE. Some enums we encounter do not follow yet the convention to use only Uppercase letters so until they are refactored I still store the future value.

What I got so far:

package student;

public enum StudentState {

Started,
Mentoring,
Repeating,
STUPID,
GENIUS;
}


I want "Started" to be stored as "STARTED" and so on.

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;

}


the converter currently looks like this:

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;
}

}


what will not work is that I do not know what enumClass will be at runtime. And I could not figure out a way to pass this information to the converter in the @Converter annotation.

So is there a way to add parameters to the converter or cheat a bit? Or is there another way?

I'm using EclipseLink 2.4.2

Thanks!

Answer

What you need to do is write a generic base class and then extend that for each enum type you want to persist. Then use the extended type in the @Converter annotation:

public abstract class GenericEnumUppercaseConverter<E extends Enum<E>> implements AttributeConverter<E, String> {
    ...
}

public FooConverter
    extends GenericEnumUppercaseConverter<Foo> 
    implements AttributeConverter<Foo, String> // See Bug HHH-8854
{
    public FooConverter() {
        super(Foo.class);
    }
}

where Foo is the enum you want to handle.

The alternative would be to define a custom annotation, patch the JPA provider to recognize this annotation. That way, you could examine the field type as you build the mapping information and feed the necessary enum type into a purely generic converter.

Related:

Comments