@ManyToMany противоречивые данные по обеим сторонам проблемы - PullRequest
2 голосов
/ 16 ноября 2010

У меня есть сценарий, похожий на блог, с двумя классами 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. добавить тег1 к post1.getTags () и post2.getTags ()
  5. обновить post1, post2
  6. Список списка = dao.getPostByTag (tag1)
  7. список утверждений.size () == 2, FAILED

Вот мой тестовый код:

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 при вставке таблицы ассоциации?

Я знаю, что были методы 'endTransaction () и startNewTransaction ()'в Spring-JUnit3, но, по-видимому, недоступен в Spring-Junit4.

Но мне интересно, как я могу пройти этот тест в транзакции one?Спасибо.

среды: Spring4 (SpringJUnit4ClassRunner), hibernate-3.5.6, JPA 2.0

Ответы [ 2 ]

1 голос
/ 16 ноября 2010

Вы можете создать два метода в классе теста, которые будут выполняться при каждом вызове метода теста. Эти методы откроют транзакцию и сделают откат после нее:

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

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

Вам также следует проверить, есть ли у вас режим сброса, отличный от значения по умолчанию, поскольку обычно сброс выполняется перед запросом ...

1 голос
/ 16 ноября 2010

Две вещи, которые вы можете попробовать:

  1. Вызовите session.flush() в сеансе гибернации после двух вызовов обновления, прежде чем вызывать getPostsByTag (). Это должно подтолкнуть ваши изменения в базе данных.
  2. Исправьте ваше управление объектами. Когда у вас есть двусторонняя ассоциация, hibernate ожидает, что вы правильно поддержите обе стороны ассоциации.

Таким образом:

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

Хорошая идея - создавать методы, которые управляют обеими сторонами ассоциации одновременно.

...