开发者

@ManyToMany inconsistent data on both side problem

I have a blog-like scenario , with two Java classes : Post and Tag , which is a @ManyToMany relationship , with a Post_Tag association table , here is my simplified definitions:

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>();
}

It seems OK , but it fails in the following testing scenario :

  1. Create a Tag , tag1
  2. Create 1st Post , post1
  3. Create 2nd Post , post2
  4. add tag1 to post1.getTags() and post2.getTags()
  5. update post1 , post2
  6. List list = dao.getPostByTag(tag1)
  7. assert list.size() == 2 , FAILED

Here is my test code :

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));
}

And here is my dao.getPostsByTag()'s implementation :

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();
}

The returned list size == 0 ! I noticed the generated SQL command and found hibernate first getPostsByTag() and then insert to association table , which makes the getPostsByTag() return 0-length list. :

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 de开发者_StackOverflowsc limit ?

Hibernate: 
    insert 
    into
        Post_Tag
        (Post_id, Tag_id) 
    values
        (?, ?)
Hibernate: 
    insert 
    into
        Post_Tag
        (Post_id, Tag_id) 
    values
        (?, ?)

How do I make sure the getPostsByTag() is executed after inserting the association table ?

I know there was 'endTransaction() , and startNewTransaction()' methods in spring-JUnit3 , but seems not available in spring-with-junit4.

But I wonder how can I pass this test in one transaction ? Thanks.

environments : Spring4 (SpringJUnit4ClassRunner) , hibernate-3.5.6 , JPA 2.0


You can create two methods in the test class that will be executed each time a test method is called. These methods will open the transaction and do rollback after it:

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

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

You should check also if you have a flushmode different from the deffault because normally the flushs are made before a query...


Two things you can try:

  1. Call session.flush() on your hibernate session after the two update calls, before calling getPostsByTag(). This should push your changes to the database.
  2. Fix your object management. When you have a two-sided association, hibernate expects you to correctly maintain both sides of the association.

Hence:

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);

It's a good idea to create methods which manage both sides of the association at once.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜