Bộ sưu tập bản đồ JPA của Enums


92

Có cách nào trong JPA để ánh xạ tập hợp các Enum trong lớp Entity không? Hay giải pháp duy nhất là bọc Enum bằng một lớp miền khác và sử dụng nó để ánh xạ bộ sưu tập?

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

Tôi đang sử dụng triển khai Hibernate JPA, nhưng tất nhiên sẽ thích giải pháp triển khai bất khả tri hơn.

Câu trả lời:


112

sử dụng Hibernate bạn có thể làm

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

141
Trong trường hợp bất cứ ai đọc này ngay bây giờ ... @CollectionOfElements hiện đang bị phản đối, thay vì sử dụng: @ElementCollection

2
Bạn có thể tìm thấy một mẫu trong câu trả lời của câu hỏi này: stackoverflow.com/q/3152787/363573
Stephan

1
Vì bạn đã đề cập đến Hibernate, tôi nghĩ câu trả lời này có thể là dành riêng cho nhà cung cấp nhưng tôi không nghĩ là như vậy trừ khi việc sử dụng JoinTable gây ra sự cố trong các triển khai khác. Từ những gì tôi đã thấy, tôi tin rằng CollectionTable nên được sử dụng thay thế. Đó là những gì tôi đã sử dụng trong câu trả lời của tôi và nó làm việc cho tôi (mặc dù có, tôi cũng đang sử dụng Hibernate ngay bây giờ.)
spaaarky21

Tôi biết đây là một chủ đề cũ, nhưng chúng tôi đang triển khai cùng một loại bằng cách sử dụng javax.persistence. Khi chúng tôi thêm: @ElementCollection (targetClass = Roles.class) @CollectionTable (name = "USER_ROLES", joinColumns = @ JoinColumn (name = "USER_ID")) @Column (name = "ROLE", nullable = false) @Enumerated ( EnumType.STRING) private Set <Roles> role; vào bảng Người dùng của chúng tôi, mọi thứ sẽ xuất hiện trong toàn bộ gói mô hình. Trong đối tượng Người dùng của chúng tôi, ngay cả lỗi trên trình tạo @Id ... @ GeneratedValue của Khóa chính và @OneToMany đầu tiên cũng gây ra lỗi ngốc nghếch khi xây dựng.
LinuxLars

Đối với những gì nó đáng giá - các lỗi tôi đang thấy là một lỗi - Problem.jboss.org/browse/JBIDE-16016
LinuxLars

65

Liên kết trong câu trả lời của Andy là một điểm khởi đầu tuyệt vời để ánh xạ tập hợp các đối tượng "không phải thực thể" trong JPA 2, nhưng không hoàn toàn hoàn chỉnh khi nói đến ánh xạ enums. Đây là những gì tôi nghĩ ra để thay thế.

@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
Đáng buồn là một số "admin" đã quyết định xóa câu trả lời đó mà không đưa ra lý do (về par cho khóa học ở đây). Để tham khảo, đó là datanucleus.org/products/accessplatform_3_0/jpa/orm/…
DataNucleus,

2
Tất cả những gì bạn thực sự cần ở đây là @ElementCollectionCollection<InterestsEnum> interests; Phần còn lại có thể hữu ích nhưng không cần thiết. Ví dụ @Enumerated(EnumType.STRING)đặt các chuỗi có thể đọc được của con người trong cơ sở dữ liệu của bạn.
CorayThan

2
Bạn là chính xác - trong ví dụ này, bạn có thể dựa vào các @Column's nameđược ngụ ý. Tôi chỉ muốn làm rõ những gì được ngụ ý khi @Column bị bỏ qua. Và @Enumerated luôn được khuyến khích vì thứ tự là một điều tồi tệ để mặc định. :)
spaaarky21

Tôi nghĩ rằng giá trị của nó để đề cập đến mà bạn thực sự cần person_interest bảng
lukaszrys

Tôi đã phải thêm tham số joinColumn để làm cho nó hoạt động@CollectionTable(name="person_interest", joinColumns = {@JoinColumn(name="person_id")})
Tiago

8

Tôi đã có thể thực hiện điều này theo cách đơn giản sau:

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

Yêu cầu tải eager để tránh lỗi inizializing tải lười biếng như được giải thích ở đây .


5

Tôi đang sử dụng một sửa đổi nhỏ của java.util.RegularEnumSet để có EnumSet bền vững:

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

Lớp này bây giờ là cơ sở cho tất cả các bộ enum của tôi:

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

Và tập hợp đó tôi có thể sử dụng trong thực thể của mình:

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

Ưu điểm:

  • Làm việc với một loại enum an toàn và hiệu quả được đặt trong mã của bạn (xem java.util.EnumSetmô tả)
  • Tập hợp chỉ là một cột số trong cơ sở dữ liệu
  • mọi thứ đều là JPA thuần túy (không có loại tùy chỉnh cụ thể của nhà cung cấp )
  • khai báo dễ dàng (và ngắn gọn) các trường mới cùng loại, so với các giải pháp khác

Hạn chế:

  • Mã trùng lặp ( RegularEnumSetPersistentEnumSetgần giống nhau)
    • Bạn có thể bao gồm kết quả EnumSet.noneOf(enumType)trong của bạn PersistenEnumSet, khai báo AccessType.PROPERTYvà cung cấp hai phương thức truy cập sử dụng phản chiếu để đọc và ghi elementstrường
  • Một lớp tập hợp bổ sung là cần thiết cho mọi lớp enum cần được lưu trữ trong một tập hợp liên tục
    • Nếu nhà cung cấp độ bền vững của bạn hỗ trợ các tệp nhúng mà không có hàm tạo công khai, bạn có thể thêm @Embeddablevào PersistentEnumSetvà loại bỏ lớp bổ sung ( ... interests = new PersistentEnumSet<>(InterestsEnum.class);)
  • Bạn phải sử dụng một @AttributeOverride, như đã cho trong ví dụ của tôi, nếu bạn có nhiều hơn một PersistentEnumSettrong thực thể của mình (nếu không, cả hai sẽ được lưu trữ vào cùng một cột "phần tử")
  • Việc truy cập values()với phản xạ trong hàm tạo không phải là tối ưu (đặc biệt là khi xem xét hiệu suất), nhưng hai tùy chọn khác cũng có nhược điểm của chúng:
    • Một triển khai như EnumSet.getUniverse()sử dụng một sun.misclớp
    • Việc cung cấp mảng giá trị dưới dạng tham số có nguy cơ các giá trị đã cho không phải là giá trị chính xác
  • Chỉ hỗ trợ enums có tối đa 64 giá trị (đó có thực sự là một nhược điểm?)
    • Bạn có thể sử dụng BigInteger để thay thế
  • Không dễ sử dụng trường phần tử trong truy vấn tiêu chí hoặc JPQL
    • Bạn có thể sử dụng toán tử nhị phân hoặc cột bitmask với các chức năng thích hợp, nếu cơ sở dữ liệu của bạn hỗ trợ

4

tl; dr Một giải pháp ngắn gọn sẽ như sau:

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

Câu trả lời dài là với chú thích này, JPA sẽ tạo một bảng chứa danh sách InterestEnum trỏ đến mã định danh lớp chính (trong trường hợp này là Person.class).

@ElementCollections chỉ định nơi JPA có thể tìm thấy thông tin về Enum

@CollectionTable tạo bảng giữ mối quan hệ từ Người đến Sở thích

@Enumerated (EnumType.STRING) yêu cầu JPA duy trì Enum dưới dạng chuỗi, có thể là EnumType.ORDINAL


Trong trường hợp này, tôi không thể thay đổi bộ sưu tập này vì nó lưu dưới dạng PersistenceSet không thể thay đổi.
Nicolazz92

Lỗi của tôi. Chúng ta có thể thay đổi bộ này bằng một bộ định vị.
Nicolazz92

0

Các tập hợp trong JPA đề cập đến các mối quan hệ một-nhiều hoặc nhiều-nhiều và chúng chỉ có thể chứa các thực thể khác. Xin lỗi, nhưng bạn cần phải bao bọc những enums đó trong một thực thể. Nếu bạn nghĩ về nó, bạn vẫn cần một số loại trường ID và khóa ngoại để lưu trữ thông tin này. Đó là trừ khi bạn làm điều gì đó điên rồ như lưu trữ một danh sách được phân tách bằng dấu phẩy trong một Chuỗi (đừng làm điều này!).


8
Điều này chỉ hợp lệ cho JPA 1.0. Trong JPA 2.0, bạn có thể sử dụng chú thích @ElementCollection như hình trên.
gỉ
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.