2010-03-23 56 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 ;