Estou usando uma pequena modificação de java.util.RegularEnumSet para ter um EnumSet persistente:
@MappedSuperclass
@Access(AccessType.FIELD)
public class PersistentEnumSet<E extends Enum<E>>
extends AbstractSet<E> {
private long elements;
@Transient
private final Class<E> elementType;
@Transient
private final E[] universe;
public PersistentEnumSet(final Class<E> elementType) {
this.elementType = elementType;
try {
this.universe = (E[]) elementType.getMethod("values").invoke(null);
} catch (final ReflectiveOperationException e) {
throw new IllegalArgumentException("Not an enum type: " + elementType, e);
}
if (this.universe.length > 64) {
throw new IllegalArgumentException("More than 64 enum elements are not allowed");
}
}
// Copy everything else from java.util.RegularEnumSet
// ...
}
Esta classe agora é a base para todos os meus conjuntos de enum:
@Embeddable
public class InterestsSet extends PersistentEnumSet<InterestsEnum> {
public InterestsSet() {
super(InterestsEnum.class);
}
}
E esse conjunto posso usar na minha entidade:
@Entity
public class MyEntity {
// ...
@Embedded
@AttributeOverride(name="elements", column=@Column(name="interests"))
private InterestsSet interests = new InterestsSet();
}
Vantagens:
- Trabalhar com um enum de tipo seguro e desempenho definido em seu código (consulte Recursos
java.util.EnumSet
para obter uma descrição)
- O conjunto é apenas uma coluna numérica no banco de dados
- tudo é JPA simples (sem tipos personalizados específicos de provedor )
- declaração fácil (e curta) de novos campos do mesmo tipo, em comparação com as outras soluções
Desvantagens:
- Duplicação de código (
RegularEnumSet
e PersistentEnumSet
são quase iguais)
- Você pode envolver o resultado de
EnumSet.noneOf(enumType)
em seu PersistenEnumSet
, declarar AccessType.PROPERTY
e fornecer dois métodos de acesso que usam reflexão para ler e escrever o elements
campo
- Uma classe de conjunto adicional é necessária para cada classe de enum que deve ser armazenada em um conjunto persistente
- Se o seu provedor de persistência suporta incorporáveis sem um construtor público, você pode adicionar
@Embeddable
a PersistentEnumSet
e solte a aula extra ( ... interests = new PersistentEnumSet<>(InterestsEnum.class);
)
- Você deve usar um
@AttributeOverride
, como mostrado no meu exemplo, se tiver mais de um PersistentEnumSet
em sua entidade (caso contrário, ambos seriam armazenados na mesma coluna "elementos")
- O acesso de
values()
com reflexão no construtor não é o ideal (especialmente quando se olha para o desempenho), mas as outras duas opções também têm suas desvantagens:
- Uma implementação como
EnumSet.getUniverse()
faz uso de uma sun.misc
classe
- Fornecer a matriz de valores como parâmetro corre o risco de que os valores dados não sejam os corretos
- Apenas enums com até 64 valores são suportados (isso é realmente uma desvantagem?)
- Você poderia usar BigInteger em vez disso
- Não é fácil usar o campo de elementos em uma consulta de critérios ou JPQL
- Você pode usar operadores binários ou uma coluna de bitmask com as funções apropriadas, se o seu banco de dados suportar isso