2010-03-23 30 views
0

我們有一個pojo需要有一個整數列表。例如,我創建了一個Message pojo,並且想要關聯groupIds的列表(這些id需要在UI中查詢和顯示)。所以,理想情況下,我們希望能夠做這樣的事情:使用JPA保持整數列表?

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

我的印象是,這將只需要一個與JPA POJO,但根據discussion here,我需要創建一個第二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");

的POJO:

@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複合標識類)爲整型列表建模 - 沒有更優雅的解決方案嗎?

回答

2

我真的認爲你所擁有的實際上是兩個實體(我們稱之爲MessageGroup)之間的多對多關聯。

的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(在你的例子中,你仍然設置ID,你應該設置實體)。或者,也許JPA不是你所需要的。


是不是有更好的解決方案?

我不知道「優雅」是適當的,但JPA 2.0定義ElementCollection mapping(正如我在前面回答說):

它是爲了處理一些不規範的關係映射。 ElementCollection可用於定義與Embeddable對象或Basic值(如字符串集合)的一對多關係。

但是這在JPA 2.0中。在JPA 1.0中,如果您的提供者確實提供了這樣的擴展,您將不得不使用提供者特定的等價物。看來,OpenJPA與@PersistentCollection

+0

不幸的是,我沒有控制最終的模式(這當然是我用來學習的測試模式)。我不能添加第三個「GROUP」表..我只有'MESSAGE'和'GROUP_ASSOC'表。這仍然有可能嗎? – Lightbeard 2010-03-23 19:50:49

0

根據您的模式,您在Group和Message之間建立了ManyToOne關係。這意味着單個消息可以屬於多個組,但每個組可以具有單個消息。

實體看起來像這樣。

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

在您的應用中不需要IDClass(如果您的ID包含多列,則只需要一個)。

要獲得一個給定的消息,你可以寫一個這樣的查詢一個

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()的組id:

Message m = em.find(Message.class, 1); 
for(Group g : m.getGroups()) { 
    // create a list, process the group whatever fits. 
} 
2

這是一個老話題,但事情自OpenJPA2以來發生了變化,現在您可以直接持久化原始類型或String對象。使用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 ;