@ManyToMany双方问题的数据不一致

时间:2010-11-16 04:06:49

标签: hibernate orm jpa transactions many-to-many

我有一个类似博客的场景,有两个Java类:Post和Tag,这是一个@ManyToMany关系,带有Post_Tag关联表,这是我的简化定义:

public class Post
{
  @ManyToMany(fetch=FetchType.LAZY) 
  @Fetch(FetchMode.SELECT)
  @JoinTable(name = "Post_Tag"
    , joinColumns        = @JoinColumn(name="Post_id")
    , inverseJoinColumns = @JoinColumn(name="Tag_id")
   )
  private Set<PostTag> tags = new HashSet<PostTag>();
}

public class Tag 
{
  @ManyToMany(mappedBy="tags" , fetch=FetchType.LAZY)
  private Set<Post> comments = new HashSet<Post>();
}

看起来没问题,但在以下测试场景中失败了:

  1. 创建标记,tag1
  2. 创建第1篇帖子,post1
  3. 创建第2篇帖子,post2
  4. 将tag1添加到post1.getTags()和post2.getTags()
  5. 更新post1,post2
  6. 列表列表= dao.getPostByTag(tag1)
  7. 断言list.size()== 2,FAILED
  8. 这是我的测试代码:

    public void testGetCommentsByTag()
    {
      Tag tag1 = tagDao.save(new Tag("tag1"));
      assertTrue(tag1.getId() > 0); 
    
      Post post1 = dao.save("...");
      Post post2 = dao.save("...");
    
      post1.getTags().add(tag1);
      post2.getTags().add(tag1);
      dao.update(post1);
      dao.update(post2);
    
      List<Post> list = dao.getPostsByTag(tag1 , 0 , 100);
    
      assertSame(2 , list.size()); // FAILED !
      assertTrue(list.contains(post1));
      assertTrue(list.contains(post2));
    }
    

    这是我的dao.getPostsByTag()的实现:

    public List<Post> getPostsByTag(Tag tag , int start, int count)
    {
      Session session = (Session) em.getDelegate();
      Criteria c = session.createCriteria(Post.class);
    
      c.createCriteria("tags")
       .add(Restrictions.eq("id", tag.getId()));
    
      c.setFirstResult(start);
      c.setMaxResults(count);
      c.setCacheable(true);
    
      return c.list();
    }
    

    返回的列表大小== 0! 我注意到生成的SQL命令并首先找到了hibernate getPostsByTag()然后插入到关联表中,这使得getPostsByTag()返回0长度列表。 :

    Hibernate: 
        insert 
        into
            Tag
        values
            (?, ?, ?, ?)
    Hibernate: 
        insert 
        into
            Post
            (...) 
        values
            (???)
    Hibernate: 
        insert 
        into
            Post
            (...) 
        values
            (???)
    Hibernate: 
        select
            ooxx
        from
            Post this_ 
        inner join
            Post_Tag tags3_ 
                on this_.id=tags3_.Post_id 
        inner join
            Tag tag1_ 
                on tags3_.Tag_id=tag1_.id 
        where
            and tag1_.id=? 
        order by
            this_.created desc limit ?
    
    Hibernate: 
        insert 
        into
            Post_Tag
            (Post_id, Tag_id) 
        values
            (?, ?)
    Hibernate: 
        insert 
        into
            Post_Tag
            (Post_id, Tag_id) 
        values
            (?, ?)
    

    如何确保插入关联表时执行getPostsByTag()after

    我知道spring-JUnit3中有'endTransaction()和startNewTransaction()'方法,但是在spring-with-junit4中似乎没有。

    但我想知道如何在one交易中通过此测试? 感谢。

    环境:Spring4(SpringJUnit4ClassRunner),hibernate-3.5.6,JPA 2.0

2 个答案:

答案 0 :(得分:1)

你可以尝试两件事:

  1. 在调用getPostsByTag()之前,在两次更新调用之后,在hibernate会话上调用session.flush()。这应该将您的更改推送到数据库。
  2. 修复对象管理。当你有一个双边关联时,hibernate希望你正确地维护关联的两面。
  3. 因此:

    Tag tag1 = tagDao.save(new Tag("tag1"));
    assertTrue(tag1.getId() > 0); 
    
    Post post1 = dao.save("...");
    Post post2 = dao.save("...");
    
    post1.getTags().add(tag1);
    tag1.getPosts().add(post1);
    post2.getTags().add(tag1);
    tag1.getPosts().add(tag2);
    dao.update(post1);
    dao.update(post2);
    

    创建一次管理关联双方的方法是个好主意。

答案 1 :(得分:1)

您可以在测试类中创建两个方法,每次调用测试方法时都会执行这两个方法。这些方法将打开事务并在其后执行回滚:

@Before public void setUp() throws Exception {
     em.getTransaction().begin();
}

@After public void tearDown() throws Exception {    
      em.getTransaction().rollback();
}

你应该检查你是否有一个与deffault不同的flushmode,因为通常在查询之前进行刷新...