Enums JPA harita koleksiyonu


93

JPA'da Varlık sınıfı içindeki bir Enum koleksiyonunu eşlemenin bir yolu var mı? Veya tek çözüm, Enum'u başka bir etki alanı sınıfıyla sarmak ve koleksiyonu eşlemek için kullanmak mı?

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

Hibernate JPA uygulamasını kullanıyorum, ancak elbette uygulama agnostik çözümü tercih ederim.

Yanıtlar:


112

Hazırda bekletme kullanarak yapabilirsiniz

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

141
@ElementCollection: durumda herkes artık önerilmemektedir bu şimdi ... @CollectionOfElements yerine kullanmak okur

2
Bu sorunun yanıtında bir örnek bulabilirsiniz: stackoverflow.com/q/3152787/363573
Stephan

1
Hibernate'den bahsettiğinizden beri, bu cevabın satıcıya özgü olabileceğini düşündüm, ancak JoinTable'ı kullanmanın diğer uygulamalarda sorun yaratmadığı sürece öyle olduğunu sanmıyorum. Gördüğüm kadarıyla bunun yerine CollectionTable'ın kullanılması gerektiğine inanıyorum.
Cevabımda

Bunun eski bir iş parçacığı olduğunu biliyorum, ancak aynı türden bir şeyi javax.persistence kullanarak uyguluyoruz. Eklediğimizde: @ElementCollection (targetClass = Roles.class) @CollectionTable (name = "USER_ROLES", joinColumns = @ JoinColumn (name = "USER_ID")) @Column (name = "ROLE", nullable = false) @Enumerated ( EnumType.STRING) özel Set <Roles> rolleri; Kullanıcı tablomuza göre, her şey model paketinin tamamına yayılır. Kullanıcı nesnemizde, Birincil anahtarın @Id ... @ GeneratedValue oluşturucusundaki bir hata ve ilk @OneToMany derleme sırasında saçma hatalar atıyor.
LinuxLars


65

Andy'nin cevabındaki bağlantı, JPA 2'deki "Varlık dışı" nesnelerin koleksiyonlarını haritalamak için harika bir başlangıç ​​noktasıdır, ancak haritalama numaralandırması söz konusu olduğunda tam olarak tamamlanmamıştır. İşte onun yerine bulduğum şey.

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

4
Maalesef, bazı "yönetici" bir neden belirtmeden bu yanıtı silmeye karar verdi (buradaki kurs için eşit). Referans için datanucleus.org/products/accessplatform_3_0/jpa/orm/…
DataNucleus

2
Bundan gerçekten ihtiyacınız olan tek şey @ElementCollectionve Collection<InterestsEnum> interests; Geri kalanı potansiyel olarak yararlı ancak gereksizdir. Örneğin @Enumerated(EnumType.STRING), veritabanınıza insan tarafından okunabilir dizeler koyar.
CorayThan

2
Sen doğru - bu örnekte, güvenebileceğini @Column'ın nameima ediliyor. @Column atlandığında neyin ima edildiğini açıklığa kavuşturmak istedim. Ve @Enumerated her zaman önerilir çünkü ordinal varsayılan olarak kötü bir şeydir. :)
spaaarky21

Person_interest tablosuna gerçekten ihtiyacınız olduğunu belirtmeye değer olduğunu düşünüyorum
lukaszrys

Çalışması için joinColumn parametresini eklemem gerekiyordu@CollectionTable(name="person_interest", joinColumns = {@JoinColumn(name="person_id")})
Tiago

8

Bunu şu basit yolla başardım:

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

Burada açıklandığı gibi geç yükleme başlatma hatasını önlemek için istekli yükleme gereklidir .


5

Kalıcı bir EnumSet'e sahip olmak için java.util.RegularEnumSet'in küçük bir değişikliğini kullanıyorum:

@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
  // ...
}

Bu sınıf artık tüm sıralama kümelerimin temelini oluşturuyor:

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

Ve varlığımda kullanabileceğim bu set:

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

Avantajlar:

  • Kodunuzda bir tür güvenli ve performanslı enum kümesiyle çalışma ( java.util.EnumSetaçıklama için bakın )
  • Küme, veritabanında yalnızca bir sayısal sütundur
  • her şey düz JPA'dır (sağlayıcıya özgü özel türler yoktur )
  • diğer çözümlere kıyasla aynı türdeki yeni alanların kolay (ve kısa) beyanı

Dezavantajlar:

  • Kod çoğaltma ( RegularEnumSetve PersistentEnumSetneredeyse aynıdır)
    • Sen sonucunu sarabilirdiniz EnumSet.noneOf(enumType)senin içinde PersistenEnumSet, bir açıklamadan AccessType.PROPERTYve okumak için yansıma kullanan iki erişim yöntemlerini sağlamak ve yazma elementsalanı
  • Kalıcı bir kümede depolanması gereken her enum sınıfı için ek bir küme sınıfı gereklidir.
    • Senin kalıcılık sağlayıcısı kamu yapıcı olmayan embeddables destekliyorsa, ekleyebilir @Embeddableiçin PersistentEnumSet(ve ekstra sınıfı bırakmak ... interests = new PersistentEnumSet<>(InterestsEnum.class);)
  • @AttributeOverrideVarlığınızda birden fazla varsa, örneğimde verildiği gibi bir kullanmalısınız PersistentEnumSet(aksi takdirde her ikisi de aynı sütun "öğeler" içinde saklanır)
  • values()Yapıcıda yansımalı erişim optimal değildir (özellikle performansa bakıldığında), ancak diğer iki seçeneğin de dezavantajları vardır:
    • Bir sınıftan EnumSet.getUniverse()yararlanan gibi bir uygulamasun.misc
    • Değerler dizisinin parametre olarak sağlanması, verilen değerlerin doğru değerler olmaması riskini taşır.
  • Yalnızca 64 değere kadar numaralandırmalar desteklenir (bu gerçekten bir dezavantaj mı?)
    • Bunun yerine BigInteger kullanabilirsiniz
  • Öğeler alanını bir ölçüt sorgusunda veya JPQL'de kullanmak kolay değildir
    • Veritabanınız destekliyorsa, uygun işlevlerle ikili operatörler veya bir bit maskesi sütunu kullanabilirsiniz.

4

tl; dr Kısa bir çözüm şu olabilir:

@ElementCollection(targetClass = InterestsEnum.class)
@CollectionTable
@Enumerated(EnumType.STRING)
Collection<InterestsEnum> interests;

Uzun cevap, bu ek açıklamalarla JPA'nın ana sınıf tanımlayıcısına (bu durumda Person.class) işaret eden InterestsEnum listesini tutacak bir tablo oluşturacağıdır.

@ElementCollections, JPA'nın Enum hakkında bilgileri nerede bulabileceğini belirtir

@CollectionTable, Kişiden İlgi Alanına İlişkin Tabloyu Oluşturun

@Enumerated (EnumType.STRING) JPA'ya Enum'u String olarak sürdürmesini söyler, EnumType.ORDINAL olabilir


Bu durumda bu koleksiyonu değiştiremem çünkü sabit olan bir PersistenceSet olarak kaydeder.
Nicolazz92

Benim hatam. Bu seti bir pasör ile değiştirebiliriz.
Nicolazz92

0

JPA'daki koleksiyonlar, bire çok veya çoktan çoğa ilişkilere atıfta bulunur ve yalnızca diğer varlıkları içerebilir. Maalesef bu numaralandırmaları bir varlığa sarmalısınız. Düşünürseniz, bu bilgileri saklamak için bir çeşit kimlik alanına ve yabancı anahtara ihtiyacınız olacaktır. Bir String'de virgülle ayrılmış bir liste saklamak gibi çılgınca bir şey yapmadığınız sürece (bunu yapmayın!).


8
Bu yalnızca JPA 1.0 için geçerlidir. JPA 2.0'da, yukarıda gösterildiği gibi @ElementCollection açıklamasını kullanabilirsiniz.
rustyx
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.