Bonjour à tous,
Je travaille avec des objets détachés, et j'ai un problème avec la fonction "merge".
Celui-ci est reproduit avec l'exemple suivant : la classe A a deux attributs, b1 et b2 de type B :
Code:
public class A
{
private Long id;
private Integer version;
private B b1;
private B b2;
public B getB1() { return b1; }
public void setB1(B b1) { this.b1 = b1; }
public B getB2() { return b2;}
public void setB2(B b2) { this.b2 = b2; }
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Integer getVersion() { return version; }
public void setVersion(Integer version) { this.version = version; }
}
Code:
public class B {
private Long id;
private Integer version;
private String description;
public String getDescription() { return description; }
public void setDescription(String description) { this.description = description; }
public Long getId() { return id; }
public void setId(Long id) { this.id = id; }
public Integer getVersion() { return version; }
public void setVersion(Integer version) { this.version = version; }
public boolean equals(Object obj) { ... }
public int hashCode() { ... }
}
Les fichiers de mapping sont tout aussi simple :
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="fr.test.domain">
<class name="A" table="A">
<id name="id" column="A_ID">
<generator class="increment" />
</id>
<version name="version" column="VERSION">
</version>
<many-to-one name="b1" cascade="all" class="fr.test.domain.B" />
<many-to-one name="b2" cascade="all" class="fr.test.domain.B" />
</class>
</hibernate-mapping>
Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="fr.test.domain">
<class name="B" table="B">
<id name="id" column="B_ID">
<generator class="increment" />
</id>
<version name="version" column="VERSION">
</version>
<property name="description" column="DESCRIPTION"/>
</class>
</hibernate-mapping>
Quand l'utilisateur remplace b1 ar une entité détachée, si cette dernière pointe sur le même enregistrement que b2 (ce qui est un
cas d'utilisation normal dans notre application), la sauvegarde de A devient alors impossible :
- avec session.saveOrUpdate(a), je reçois une exception NonUniqueObjectException car les deux objets détachés b1 et b2 pointent sur la même entité
- avec session.merge(a), je n'ai pas d'exception mais les modifications de b1 sont perdues !
Code:
public class Test extends TestCase
{
private A a;
protected void setUp() throws Exception
{
//init
a = init();
//load B1
B newB1 = loadB(a.getB2().getId());
newB1.setDescription("b1");
a.setB1(newB1);
}
public void testSaveOrUpdate()
{
assertNotNull(a);
// throws NonUniqueObjectException
//
saveA(a);
}
public void testMerge()
{
assertNotNull(a);
// Works but...
a = mergeA(a);
// .... b1 modification is lost
assertEquals("b1", a.getB1().getDescription());
}
//-------------------------------------------------------------------------
// Internal methods
//-------------------------------------------------------------------------
protected A init() {
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
A a = new A();
B b = new B();
b.setDescription("b");
a.setB1(b);
a.setB2(b);
session.save(a);
tx.commit();
session.close();
return a;
}
protected void saveA(A a)
{
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
session.saveOrUpdate(a);
tx.commit();
session.close();
}
protected A mergeA(A a)
{
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
A result = (A) session.merge(a);
tx.commit();
session.close();
return result;
}
protected B loadB(Long id)
{
Session session = HibernateUtil.getSessionFactory().openSession();
Transaction tx = session.beginTransaction();
B b = (B)session.get(B.class, id);
tx.commit();
session.close();
return b;
}
}
J'avoue que j'aurais attendu qu'Hibernate fusionne correctement les deux objets, ou au minimum qu'il envoie une exception signifiant un conflit entre les deux entités à fusionner.
Est-ce que quelqu'un sait s'il existe un moyen "natif" de solutionner ce problème ?
Bruno