我有一个Users和Tags表,还有一个user_tag_xref表,它包含多对多关系。现在netbeans为我生成实体类(使用eclipselink)下面是实体映射关系
用户类上的
@ManyToMany(mappedBy = "usersList")
private List<Tags> tagsList;
标签类上的
@JoinTable(name = "USERS_TAG_XREF", joinColumns = {
@JoinColumn(name = "TAG_ID", referencedColumnName = "TAG_ID")}, inverseJoinColumns = {
@JoinColumn(name = "USER_ID", referencedColumnName = "USER_ID")})
@ManyToMany
private List<Users> usersList;
现在我是业务逻辑RESTfull服务,json客户端使用此方法
@POST
@Path("/registration2/tag")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public Response registration2_3(List<Tags>tagList,@Context HttpServletRequest req){
Profile p =(Profile) registerMap.get(req.getSession().getId());
Users u = em.find(Users.class,p.getUserId());
for(Tags t : tagList){
t.getUsersList().add(u);
u.getTagsList().add(t);
em.merge(t);
em.merge(u);
}
logger.log(Level.INFO, "the taglist created for user");
return Response.ok(u,MediaType.APPLICATION_JSON).build();
}
问题是每次合并新用户以创建多对多关系,如果现有userid = 6201的标签为2,3,4,并且新用户尝试使用标记id 2, 3,4, 删除现有用户,新用户合并到标签。我已经阅读了几篇关于覆盖哈希和等于我的实体类中的方法的文章,这些方法在eclipselink中默认被覆盖,我也无法将我的集合类型更改为Set或Collection,因为List&lt;&gt;类型适用于json数组。我现在很难过,已经24小时了,可能是默认的映射策略错了吗?我需要cascasde吗?
答案 0 :(得分:3)
使用merge时,你必须格外小心,因为它的语义(如here所述)与更新有点不同。 由于您的关系是双向的,而用户是反面的,因此所有关系持久性都将由处理 标签方。假设您的标记列表包含分离的标记,意味着所有标记都设置了ID,那么您需要迭代tagList
Tag managedTag = em.merge(t);
这需要注意,如果t是新实例(非托管),那么将返回它的持久表示 必须在之后使用或者如果实例设置了id,那么ORM将使用来自数据库的数据创建一个托管实例(如果存在,则从第一/第二级缓存创建)。返回的实例是托管的
for(Tags t : tagList){
Tag managedTag = em.merge(t);
managedTag.getUsersList().add(u);
u.getTagsList().add(t);
User managedUser = em.merge(u);
}
此外,您可以在标签端设置合并级联选项以保存第二次合并调用并让ORM自动管理关系。
答案 1 :(得分:1)
以下是合并与分离的实体和关系的行为。
@Entity
public class Tag {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
@ManyToMany(cascade=CascadeType.ALL)
private List<User> users = new ArrayList();
.......................
}
@Entity
public class User {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
@ManyToMany(mappedBy="users",cascade=CascadeType.ALL)
private List<Tag> tags = new ArrayList();
........................
}
TestProgram
EntityManagerFactory emf = Persistence.createEntityManagerFactory("Test");
EntityManager em = emf.createEntityManager();
em.getTransaction().begin();
User user = new User();
user.setName("User");
User managedUser = em.merge(user);
int userId = managedUser.getId();
//create a tag
Tag tag = new Tag();
tag.setName("Tag");
tag.addUser(managedUser);
Tag managedTag = em.merge(tag);
//save the id locally
int savedId = managedTag.getId();
//managed tag was sent to UI where its name will be changed to "Changed Tag"
em.getTransaction().commit();
em.close();
//create another transaction
EntityManager em1 = emf.createEntityManager();
em1.getTransaction().begin();
//simulate a tag sent form UI
Tag tagFromUI = new Tag();
tagFromUI.setId(savedId);
tagFromUI.setName("Changed Tag");
// I want to associate a new user to this tag
// so create a new user
User newUser = new User();
newUser.setName("newUser");
tagFromUI.addUser(newUser);
Tag managedTagFromUI = em1.merge(tagFromUI);
em1.getTransaction().commit();
em1.close();
emf.close();
这是SQL生成和相应的解释
//First transaction begins
insert into User (name) values ('User');
insert into Tag (name) values ('Tag');
insert into Tag_User (tags_id, users_id) values (1, 1);
//First transaction ends
//Second transaction begins
// Since a detached tag is merged, hibernate queries for tag id 1 to load it in persistent context
//This tag is associated with a user
select tag0_.id as id1_3_1_, tag0_.name as name2_3_1_, users1_.tags_id as tags_id1_3_3_, user2_.id as users_id2_4_3_, user2_.id as id1_5_0_, user2_.name as name2_5_0_ from Tag tag0_ left outer join Tag_User users1_ on tag0_.id=users1_.tags_id left outer join User user2_ on users1_.users_id=user2_.id where tag0_.id=1;
//copies the state of detached tag from UI to the managed tag and sends update
update Tag set name='Changed Tag' where id=1;
//since merge is cascaded, hibernate looks for the user list of supplied tag and sees an transient User
// the transient instance is merged (created new in database as it is not yet persisted)
insert into User (name) values ('newUser');
// merge is called on this new managed instance and the resulted instance is set in the managed Tag instance automatically
//but for this the old relation has to be broken
delete from Tag_User where tags_id=1;
// and the new relation has to be added in database
insert into Tag_User (tags_id, users_id) values (1, 2);
//second transaction ends