Pergunta

Existe uma maneira em JPA para mapear uma coleção de enumerações dentro da classe Entity? Ou a única solução é envolver Enum com outra classe de domínio e usá-lo para mapear a coleção?

@Entity
public class Person {
    public enum InterestsEnum {Books, Sport, etc...  }
    //@???
    Collection<InterestsEnum> interests;
}

Eu estou usando implementação Hibernate JPA, mas é claro que preferiria implementação da solução agnóstica.

Foi útil?

Solução

usando Hibernate você pode fazer

@CollectionOfElements(targetElement = InterestsEnum.class)
@JoinTable(name = "tblInterests", joinColumns = @JoinColumn(name = "personID"))
@Column(name = "interest", nullable = false)
@Enumerated(EnumType.STRING)
Collection<InterestsEnum> interests;

Outras dicas

O link na resposta de Andy é um excelente ponto de partida para as coleções de mapeamento de "não-entidade" objetos em JPA 2, mas não é bem completo quando se trata de enums mapeamento. Aqui está o que eu vim acima com em seu lugar.

@Entity
public class Person {
    @ElementCollection(targetClass=InterestsEnum.class)
    @Enumerated(EnumType.STRING) // Possibly optional (I'm not sure) but defaults to ORDINAL.
    @CollectionTable(name="person_interest")
    @Column(name="interest") // Column name in person_interest
    Collection<InterestsEnum> interests;
}

Eu era capaz de fazer isso desta forma simples:

@ElementCollection(fetch = FetchType.EAGER)
Collection<InterestsEnum> interests;

Carga antecipada é necessária a fim de evitar o carregamento lento inizializing erro como explicado aqui .

Eu estou usando uma ligeira modificação do java.util.RegularEnumSet 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 sets enum:

@Embeddable
public class InterestsSet extends PersistentEnumSet<InterestsEnum> {
  public InterestsSet() {
    super(InterestsEnum.class);
  }
}

E esse conjunto que posso usar na minha entidade:

@Entity
public class MyEntity {
  // ...
  @Embedded
  @AttributeOverride(name="elements", column=@Column(name="interests"))
  private InterestsSet interests = new InterestsSet();
}

Vantagens:

  • Trabalho com um tipo de seguro e conjunto enum performance em seu código (ver java.util.EnumSet para uma descrição)
  • O conjunto é apenas uma coluna numérica no banco de dados
  • tudo é JPA simples (sem fornecedor específico tipos personalizados )
  • fácil (e curta) declaração 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 o mesmo)
    • Você poderia envolver o resultado de EnumSet.noneOf(enumType) em sua PersistenEnumSet, AccessType.PROPERTY declarar e fornecer dois métodos de acesso que a reflexão usar para ler e escrever o campo elements
  • Uma classe de conjunto adicional é necessário para todas as classes de enumeração que deve ser armazenado em um conjunto persistente
    • Se o provedor de persistência suporta incorporáveis ??sem um construtor público, você pode adicionar @Embeddable para PersistentEnumSet e soltar o aula extra (... interests = new PersistentEnumSet<>(InterestsEnum.class);)
  • Você deve usar um @AttributeOverride, como dado no meu exemplo, se você tem mais de um PersistentEnumSet na sua entidade (caso contrário, ambos seriam armazenados com os mesmos "elementos" da coluna)
  • 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 têm suas desvantagens também:
    • Uma implementação como EnumSet.getUniverse() faz uso de uma classe sun.misc
    • Fornecer a matriz valores como parâmetro tem o risco de que os valores apresentados não são os corretos
  • Somente enums com até 64 valores são suportados (isso é realmente uma desvantagem?)
    • Você pode usar BigInteger vez
  • Não é fácil usar o campo de elementos em uma consulta critérios ou JPQL
    • Você pode usar operadores binários ou uma coluna máscara de bits com as funções apropriadas, se o seu banco de dados suporta que

Coleções em JPA referem-se a um-para-muitos ou muitos-para-muitos relacionamentos e eles só podem conter outras entidades. Desculpe, mas você precisa quebrar esses enums em uma entidade. Se você pensar sobre isso, você precisa de algum tipo de campo ID e chave estrangeira para armazenar essas informações de qualquer maneira. Isso é menos que você faça algo louco como armazenar uma lista separada por vírgula em uma String (não faça isso!).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top