سؤال

لدينا pojo يحتاج إلى قائمة من الأعداد الصحيحة. على سبيل المثال ، قمت بإنشاء ملف Message Pojo ويرغب في ربط قائمة من groupIds (تحتاج هذه المعرفات إلى الاستعلام وعرضها في واجهة المستخدم). من الناحية المثالية ، نود أن نكون قادرين على فعل شيء مثل هذا:

Message msg = em.find(Message.class, 101);
List<Integer> groupIds = msg.getGroupIds();

كنت تحت انطباع أن هذا سيتطلب فقط pojo مع JPA ، ولكن وفقا ل مناقشة هنا, ، أحتاج إلى إنشاء pojo ثانية لأن JPA يعمل من حيث الكائنات بدلاً من الأنواع البدائية.

من تلك المناقشة ، جربت رمز المثال التالي ، لكنني أحصل على الخطأ openjpa-1.2.3-SNAPSHOT-r422266:907835 fatal user error: org.apache.openjpa.util.MetaDataException: The type of field "pojo.Group.messageId" isn't supported by declared persistence strategy "ManyToOne". Please choose a different strategy.

DDL:

CREATE TABLE "APP"."MESSAGE" (
  "MESSAGE_ID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
  "AUTHOR" CHAR(20) NOT NULL
 );

ALTER TABLE "APP"."MESSAGE" ADD CONSTRAINT "MESSAGE_PK" PRIMARY KEY ("MESSAGE_ID");

CREATE TABLE "APP"."GROUP_ASSOC" (
  "GROUP_ID" INTEGER NOT NULL,
  "MESSAGE_ID" INTEGER NOT NULL
 );

ALTER TABLE "APP"."GROUP_ASSOC" ADD CONSTRAINT "GROUP_ASSOC_PK" PRIMARY KEY ("MESSAGE_ID", "GROUP_ID");

ALTER TABLE "APP"."GROUP_ASSOC" ADD CONSTRAINT "GROUP_ASSOC_FK" FOREIGN KEY ("MESSAGE_ID")
 REFERENCES "APP"."MESSAGE" ("MESSAGE_ID");

بوجوس:

@Entity
@Table(name = "MESSAGE")
public class Message {
    @Id
    @Column(name = "MESSAGE_ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)    
    private Long messageId;

    @OneToMany   
    private List<Group> groups = new ArrayList<Group>();

    @Column(name = "AUTHOR")
    private String author;

    // getters/setters ommitted
}    

@Entity
@IdClass(pojo.Group.GroupKey.class)
@Table(name = "GROUP_ASSOC")
public class Group {

 @Id
 @Column(name = "GROUP_ID")
 private Long groupId;

 @Id
 @Column(name = "MESSAGE_ID")
 @ManyToOne
 private Long messageId;

 public static class GroupKey {
  public Long groupId;
  public Long messageId;

  public boolean equals(Object obj) {
   if(obj == this) return true;
            if(!(obj instanceof Group)) return false;
   Group g = (Group) obj;
   return g.getGroupId() == groupId && g.getMessageId() == messageId; 
  }

  public int hashCode() {
            return ((groupId == null) ? 0 : groupId.hashCode())
                ^ ((messageId == null) ? 0 : messageId.hashCode());
  } 
 }

 // getters/setters ommitted 
}

رمز الاختبار:

EntityManager em = Persistence.createEntityManagerFactory("JPATest").createEntityManager();
em.getTransaction().begin();

Message msg = new Message();
msg.setAuthor("Paul");
em.persist(msg);
List<Group> groups = new ArrayList<Group>();

Group g1 = new Group();
g1.setMessageId(msg.getMessageId());
Group g2 = new Group();
g2.setMessageId(msg.getMessageId());

msg.setGroups(groups);
em.getTransaction().commit();

كل هذا يبدو سخيفًا - 3 فئات (إذا قمت بتضمين فئة هوية GroupKey Composite) لنمذجة قائمة من الأعداد الصحيحة - أليس هناك حل أكثر أناقة؟

هل كانت مفيدة؟

المحلول

أعتقد حقًا أن ما لديك هو في الواقع الكثير للكثيرين الارتباط بين كيانين (دعنا نسميهما Message و Group).

سيكون DDL لتمثيل هذا:

CREATE TABLE "APP"."MESSAGE" (
  "MESSAGE_ID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1),
  "AUTHOR" CHAR(20) NOT NULL
 );

ALTER TABLE "APP"."MESSAGE" ADD CONSTRAINT "MESSAGE_PK" PRIMARY KEY ("MESSAGE_ID");

CREATE TABLE "APP"."GROUP" (
  "GROUP_ID" INTEGER NOT NULL GENERATED ALWAYS AS IDENTITY (START WITH 1, INCREMENT BY 1)
 );

ALTER TABLE "APP"."GROUP" ADD CONSTRAINT "GROUP_PK" PRIMARY KEY ("GROUP_ID");

CREATE TABLE "APP"."MESSAGE_GROUP" (
  "GROUP_ID" INTEGER NOT NULL,
  "MESSAGE_ID" INTEGER NOT NULL
 );

ALTER TABLE "APP"."MESSAGE_GROUP" ADD CONSTRAINT "MESSAGE_GROUP_PK" PRIMARY KEY ("MESSAGE_ID", "GROUP_ID");

ALTER TABLE "APP"."MESSAGE_GROUP" ADD CONSTRAINT "MESSAGE_GROUP_FK1" FOREIGN KEY ("MESSAGE_ID")
 REFERENCES "APP"."MESSAGE" ("MESSAGE_ID");

ALTER TABLE "APP"."MESSAGE_GROUP" ADD CONSTRAINT "MESSAGE_GROUP_FK2" FOREIGN KEY ("GROUP_ID")
 REFERENCES "APP"."MESSAGE" ("GROUP_ID");

والفصول المشروحة:

@Entity
public class Message {
    @Id
    @Column(name = "MESSAGE_ID")
    @GeneratedValue(strategy = GenerationType.IDENTITY)    
    private Long messageId;

    @ManyToMany
    @JoinTable(
        name = "MESSAGE_GROUP", 
        joinColumns = @JoinColumn(name = "MESSAGE_ID"), 
        inverseJoinColumns = @JoinColumn(name = "GROUP_ID")
    ) 
    private List<Group> groups = new ArrayList<Group>();

    private String author;

    //...
}    

@Entity
public class Group {    
    @Id
    @GeneratedValue
    @Column(name = "GROUP_ID")
    private Long groupId;

    @ManyToMany(mappedBy = "groups")
    private List<Message> messages = new ArrayList<Message>();

    //...
}

لست متأكدًا من أنك بحاجة إلى ارتباط ثنائي الاتجاه. ولكن عليك بالتأكيد أن تبدأ في التفكير في الكائن إذا كنت ترغب في استخدام JPA (في مثال ، أنت لا تزال تحدد معرفات ، يجب عليك تعيين الكيانات). أو ربما JPA ليس ما تحتاجه.


أليس هناك حل أكثر أناقة؟

لست متأكدًا من أن "الأنيقة" مناسبة ولكن JPA 2.0 يحدد ElementCollection رسم الخرائط (كما قلت في إجابتي السابقة):

من المفترض أن يتعامل مع العديد من تعيينات العلاقة غير القياسية. و ElementCollection يمكن استخدامها لتحديد علاقة واحدة إلى حد Embeddable كائن ، أو أ Basic القيمة (مثل مجموعة من الأوتار).

ولكن هذا في JPA 2.0. في JPA 1.0 ، سيتعين عليك استخدام ما يعادله مزودًا محددًا ، إذا كان مزودك يقدم مثل هذا الامتداد. يبدو أن OpenJPA يفعل مع @PersistentCollection.

نصائح أخرى

هذا موضوع قديم ولكن الأمور قد تغيرت منذ OpenJPA2 ، والآن يمكنك أن تستمر مباشرة الأنواع البدائية أو كائن السلسلة. استخدم شرح ElementCollection لاستخدام ربط بسيط واحد إلى طابع ، لا حاجة إلى كائن أو جداول الارتباط المتوسطة. هكذا ربما يقوم معظمنا بإنشاء مخططات SQL.

@Entity @Table(name="user") @Access(AccessType.FIELD)
public class User {
    @Id @GeneratedValue(strategy=GenerationType.IDENTITY)
    private long id;    // primary key (autogen surrogate)
    private String name;

    // ElementCollection provides simple OneToMany linking.
    // joinColumn.name=foreign key in child table. Column.name=value in child table
    @ElementCollection(fetch=FetchType.LAZY)
    @CollectionTable(name="user_role", joinColumns={@JoinColumn(name="user_id")})
    @Column(name="role")
    private List<String> roles;

    public long getId() { return id; }
    public void setId(long id) { this.id = id; }

    public String getName() { return name; }
    public void setName(String name) { this.name=name; }

    public List<String> getRoles() { return roles; }
    public void setRoles(List<String> roles) { this.roles=roles; }

}
- - -
CREATE TABLE user (
  id bigint NOT NULL auto_increment,
  name varchar(64) NOT NULL default '',
  PRIMARY KEY (id),
  UNIQUE KEY USERNAME (name)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;

CREATE TABLE user_role (
  user_id bigint NOT NULL,
  role varchar(64) NOT NULL default '',
  PRIMARY KEY (user_id, role)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 ;

بناءً على مخططك ، لديك علاقة متعددة بين المجموعة والرسالة. مما يعني أن رسالة واحدة يمكن أن تنتمي إلى مجموعات متعددة ، ولكن يمكن أن يكون لكل مجموعة رسالة واحدة.

سوف تبدو الكيانات شيئًا كهذا.

@Entity
@Table(name = "GROUP_ASSOC")
public class Group {
    @Id
    @Column(name="GROUP_ID")
    private int id;

    @ManyToOne
    @Column(name="MESSAGE_ID")
    @ForeignKey
    private Message message;

    // . . . 
}

@Entity
public class Message {
    @Id
    @GeneratedValue(strategy=GenerationType.IDENTITY)
    @Column(name = "MESSAGE_ID")
    private int id;

    @Column(length=20)
    private String author;

    @OneToMany(mappedBy="message")  
    private Collection<Group> groups;
}

ليست هناك حاجة لعلاج كاسحة في تطبيقك (تحتاج فقط إلى واحد إذا كان معرفك يحتوي على أعمدة متعددة).

للحصول على GroupIDs لرسالة معينة ، يمكنك كتابة استعلام مثل هذا

    Query q =  em.createQuery("Select g.id from Group g where g.message.id = :messageId");
    q.setParameter("messageId", 1);

    List results = q.getResultList();

أو مجرد تكرار عبر message.getGroups ():

Message m = em.find(Message.class, 1);
for(Group g : m.getGroups()) {
    // create a list, process the group whatever fits.
}
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top