With hibernate 3, using annotation and the Spring HibernateDaoSupport
I'm trying to set up a many to many mapping and I stumble upon some update problems.
When I add items a a collection, updating then entity did not update the mapping in database.
(Its working when I setup the mapping to be unidirectional.)
Can someone tells me what wrong with my setting ?
Here is some classes reproducing my problem: it is an association between articles and categories.
(My database tables are present and well created)
The entities:
Article
Code:
package testhib;
import java.util.*; import javax.persistence.*;
@Entity @Table(name = "A_ARTICLE")
public class Article implements java.io.Serializable {
private Integer id;
private String name;
private Set<Category> categories = new HashSet<Category>(0);
public Article() {}
public Article(Integer id, String name) {this.id = id;this.name = name;}
@Id @Column(name = "ID", unique = true, nullable = false)
public Integer getId() {return id;}
public void setId(Integer id) {this.id = id;}
@Column(name = "NAME")
public String getName() {return name;}
public void setName(String name) {this.name = name;}
@ManyToMany(fetch = FetchType.EAGER, mappedBy = "articles", cascade = CascadeType.ALL)
@JoinTable(name = "A_ARTICLE_A_CATEGORY", joinColumns = @JoinColumn(name ="A_ARTICLE_ID"), inverseJoinColumns = @JoinColumn(name="A_CATEGORY_ID"))
public Set<Category> getCategories() {return categories;}
public void setCategories(Set<Category> categories) {this.categories = categories;}
}
Category
Code:
package testhib;
import java.util.*; import javax.persistence.*;
@Entity @Table(name = "A_CATEGORY")
public class Category implements java.io.Serializable {
private Integer id;
private String name;
private Set<Article> articles = new HashSet<Article>(0);
public Category() {}
public Category(Integer id, String name) {this.id = id;this.name = name;}
@Id @Column(name = "ID", unique = true, nullable = false)
public Integer getId() {return this.id;}
public void setId(Integer id) {this.id = id;}
@ManyToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
@JoinTable(name = "A_ARTICLE_A_CATEGORY",
joinColumns = @JoinColumn(name = "A_CATEGORY_ID"),
inverseJoinColumns = @JoinColumn(name = "A_ARTICLE_ID")
)
public Set<Article> getArticles() {return articles;}
public void setArticles(Set<Article> articles) {this.articles = articles;}
@Column(name = "NAME")
public String getName() {return name;}
public void setName(String name) {this.name = name;}
}
The test case (I'm using Spring hibernate dao support)
Code:
package testhib;
import junit.framework.TestCase;
import org.hibernate.SessionFactory;
import org.hibernate.classic.Session;
import org.springframework.orm.hibernate3.support.HibernateDaoSupport;
public class ArticleCategoryTest extends TestCase {
static Session SESSION;
static HibernateDaoSupport DAO;
static SessionFactory SESSION_FACTORY;
@Override
protected void setUp() throws Exception {
if(SESSION == null) {
SESSION_FACTORY = getSessionFactory();
}
openSession();
}
private void openSession() {
SESSION = SESSION_FACTORY.openSession();
DAO = new HibernateDaoSupport() {};
DAO.setSessionFactory(SESSION_FACTORY);
}
@Override
protected void tearDown() throws Exception {
SESSION.close();
}
public static SessionFactory getSessionFactory() {
return FishTestPlatformContext.getInstance().getSessionFactory();
}
/* assertions:
*
* categories can be added/removed to articles
* when a category us fetched from db, articles are not fetched: a special request will handle article retrieving
* when an article is fetched from db, it comes with all its categories
* when a category is deleted, the articles stay but not the mapping
* when an article is deleted, the categories stay but not the mapping
*
*
* */
public void testArticleCategoryAssociation() {
//Clear previous data
SESSION.createQuery("delete from Article").executeUpdate();
SESSION.createQuery("delete from Category").executeUpdate();
SESSION.beginTransaction();
//Create some categories
int categoryId = 0;
Category java = new Category(categoryId++, "java");
Category hibernate = new Category(categoryId++, "hibernate");
Category spring = new Category(categoryId++, "spring");
Category bestPractices = new Category(categoryId++, "best practices");
DAO.getHibernateTemplate().save(java);
DAO.getHibernateTemplate().save(hibernate);
DAO.getHibernateTemplate().save(spring);
//Create some articles
int articleId = 0;
//article 1: update strategy:
//step1: create the article
//step2: save
//step3: associate the categories
//step4: save
Article javaBestPracticeArticle = new Article(articleId++, "Java Best Practices");
DAO.getHibernateTemplate().save(javaBestPracticeArticle);
javaBestPracticeArticle = (Article) DAO.getHibernateTemplate().get(Article.class, javaBestPracticeArticle.getId());
javaBestPracticeArticle.getCategories().add(java);
javaBestPracticeArticle.getCategories().add(bestPractices);
DAO.getHibernateTemplate().saveOrUpdate(javaBestPracticeArticle);
//article 2 & 3: update strategy:
//step1: create the article
//step2: associate the categories
//step3: save
Article hibernateMappingArticle = new Article(articleId++, "Hibernate, damn many to many mappings");
hibernateMappingArticle.getCategories().add(java);
hibernateMappingArticle.getCategories().add(hibernate);
DAO.getHibernateTemplate().save(hibernateMappingArticle);
Article springDaoSupportArticle = new Article(articleId++, "Generic hibernate dao using spring and hibernate");
springDaoSupportArticle.getCategories().add(java);
springDaoSupportArticle.getCategories().add(hibernate);
springDaoSupportArticle.getCategories().add(spring);
DAO.getHibernateTemplate().save(springDaoSupportArticle);
SESSION.getTransaction().commit();
javaBestPracticeArticle = (Article) DAO.getHibernateTemplate().get(Article.class, javaBestPracticeArticle.getId());
assertTrue("article " + javaBestPracticeArticle.getName() + " categories must not be empty", !javaBestPracticeArticle.getCategories().isEmpty());
hibernateMappingArticle = (Article) DAO.getHibernateTemplate().get(Article.class, hibernateMappingArticle.getId());
assertTrue("article " + hibernateMappingArticle.getName() + " categories must not be empty", !hibernateMappingArticle.getCategories().isEmpty());
springDaoSupportArticle = (Article) DAO.getHibernateTemplate().get(Article.class, springDaoSupportArticle.getId());
assertTrue("article " + springDaoSupportArticle.getName() + " categories must not be empty", !springDaoSupportArticle.getCategories().isEmpty());
}
}