我们有一个需要有一个整数列表的pojo。作为一个例子,我创建了一个Message
pojo,并希望关联groupIds
列表(这些ID需要在UI中查询和显示)。理想情况下,我们希望能够做到这样的事情:
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复合身份类)来建模整数列表 - 是不是有更优雅的解决方案?
答案 0 :(得分:2)
我真的认为你所拥有的实际上是两个实体之间的多对多关联(让我们称之为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,你肯定需要开始思考对象(在你的例子中,你仍然设置id,你应该设置实体)。或者也许JPA不是你需要的。
是不是有更优雅的解决方案?
我不确定“优雅”是否合适,但JPA 2.0定义ElementCollection
mapping(正如我在之前的回答中所说):
它旨在处理几个非标准关系映射。
ElementCollection
可用于定义与Embeddable
对象的一对多关系,或Basic
值(例如字符串集合)。
但这是在JPA 2.0中。在JPA 1.0中,如果您的提供商提供此类扩展,则必须使用提供商特定的等效项。似乎OpenJPA使用@PersistentCollection
。
答案 1 :(得分: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 ;
答案 2 :(得分:0)
根据您的架构,您在Group和Message之间拥有ManyToOne关系。这意味着单个Message可以属于多个组,但每个组可以只有一个消息。
实体看起来像这样。
@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包含多个列,则只需要一个IDClass)。
要获取给定消息的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.
}