jpa:当合并多个到多个先前的记录被删除时

时间:2013-10-10 07:23:31

标签: rest jpa ejb many-to-many eclipselink

我有一个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吗?

2 个答案:

答案 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