ฉันใช้ java.util.RegularEnumSet แก้ไขเล็กน้อยเพื่อให้มี EnumSet ต่อเนื่อง:
@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");
}
}
}
ตอนนี้คลาสนี้เป็นฐานสำหรับชุด enum ทั้งหมดของฉัน:
@Embeddable
public class InterestsSet extends PersistentEnumSet<InterestsEnum> {
public InterestsSet() {
super(InterestsEnum.class);
}
}
และชุดนั้นฉันสามารถใช้ในเอนทิตีของฉัน:
@Entity
public class MyEntity {
@Embedded
@AttributeOverride(name="elements", column=@Column(name="interests"))
private InterestsSet interests = new InterestsSet();
}
ข้อดี:
- การทำงานกับ enum ประเภทปลอดภัยและประสิทธิภาพที่กำหนดไว้ในรหัสของคุณ (ดู
java.util.EnumSet
คำอธิบาย)
- ชุดนี้เป็นเพียงคอลัมน์ตัวเลขหนึ่งคอลัมน์ในฐานข้อมูล
- ทุกอย่างเป็น JPA ธรรมดา (ไม่มีประเภทกำหนดเองเฉพาะของผู้ให้บริการ)
- การประกาศเขตข้อมูลใหม่ประเภทเดียวกันที่ง่าย (และสั้น) เปรียบเทียบกับโซลูชันอื่น ๆ
ข้อเสีย:
- การทำสำเนารหัส (
RegularEnumSet
และPersistentEnumSet
เกือบจะเหมือนกัน)
- คุณสามารถสรุปผลลัพธ์ของ
EnumSet.noneOf(enumType)
คุณPersistenEnumSet
ประกาศAccessType.PROPERTY
และระบุวิธีการเข้าถึงสองวิธีซึ่งใช้การสะท้อนเพื่ออ่านและเขียนelements
ฟิลด์
- จำเป็นต้องมีคลาสเซ็ตเพิ่มเติมสำหรับคลาส enum ทุกคลาสที่ควรเก็บไว้ในเซ็ตถาวร
- หากผู้ให้บริการการคงอยู่ของคุณสนับสนุน embeddables โดยไม่ต้องสร้างสาธารณะคุณสามารถเพิ่ม
@Embeddable
การPersistentEnumSet
และวางชั้นพิเศษ ( ... interests = new PersistentEnumSet<>(InterestsEnum.class);
)
- คุณต้องใช้
@AttributeOverride
ตามที่ให้ไว้ในตัวอย่างของฉันหากคุณมีมากกว่าหนึ่งรายการPersistentEnumSet
ในเอนทิตีของคุณ (มิฉะนั้นทั้งสองจะถูกเก็บไว้ในคอลัมน์ "องค์ประกอบ" เดียวกัน)
- การเข้าถึง
values()
ด้วยการสะท้อนในตัวสร้างนั้นไม่เหมาะสมที่สุด (โดยเฉพาะเมื่อดูที่ประสิทธิภาพ) แต่อีกสองตัวเลือกก็มีข้อเสียเช่นกัน:
- การใช้งานเช่น
EnumSet.getUniverse()
ใช้ประโยชน์จากsun.misc
คลาส
- การระบุอาร์เรย์ค่าเป็นพารามิเตอร์มีความเสี่ยงที่ค่าที่ระบุจะไม่ถูกต้อง
- รองรับเฉพาะ enums ที่มีค่าสูงสุด 64 ค่าเท่านั้น (นั่นเป็นข้อเสียเปรียบจริงหรือ?)
- คุณสามารถใช้ BigInteger แทนได้
- ไม่ใช่เรื่องง่ายที่จะใช้ช่ององค์ประกอบในแบบสอบถามเกณฑ์หรือ JPQL
- คุณสามารถใช้ตัวดำเนินการไบนารีหรือคอลัมน์บิตมาสก์กับฟังก์ชันที่เหมาะสมหากฐานข้อมูลของคุณรองรับ