I am trying to work with many-to-many association, and I try to adapt Item, Category and CategorizedItem that you can find in CaveatEmptor to my tables.
I have two tables: INTERNADOS and FAMILIARES, with a many-to-many association. That association (table INTERNADOS_FAMILIARES) has an attribute: acargo, that is a boolean.
Familiares is a java.util.Set<InternadoFamiliar> field in Internado. I added a method in Internado:
addFamiliar(Familiar f, boolean acargo)
that creates a new InternadoFamiliar that associates Internado instance with Familiar parameter and set relationship attribute.
Well, what I do is creating a Familiar, then I create an Internado, and I try to add an "InternadoFamiliar" instance with my method.
I suppose that "automatic dirty checking" would let me just to save Internado and Familiar, and that it would detect InternadoFamiliar and save it too. But when I to save I get a TransientObjectException that tells me that I had to save before, but because it tries to update an INTERNADO_FAMILIAR that doesn't exist.
I solved this by creating InternadoFamiliar after saving Internado and Familiar, but why automatic dirty checking doesn't detect this?
If you know of a tutorial for many-to-many associations with attributes in the relationship, please tell me, because I found it quite difficult.
Also, I looked for "foreign-key" parameter in <key> tag in the XML but I couldn't find anything, how do you use it?
foreign-key is in CaveatEmptor like-this:
<set name="categorizedItems" cascade="all-delete-orphan" inverse="true" lazy="true" access="field">
-
<key foreign-key="FK2_CATEGORIZED_ITEM_ID">
<column name="ITEM_ID" not-null="true" length="16"/>
</key>
<one-to-many class="CategorizedItem"/>
</set>
Hibernate version:
3.0.5
Mapping documents:
My tables have a lot of attributes, I cut them to get shorter my post (well, at least I tried to :) ), so may be the syntax is incorrect. Just know that Internado and Familiar have their Key (a String noAfiliado for Internado, and an Integer idFamiliar for Familiar) and an attribute apellidos (surname in English).
INTERNADO
public class Internado extends {
private Set<InternadoFamiliar> familiares = new HashSet<InternadoFamiliar>();
public String getNoAfiliado() {
return noAfiliado;
}
public void setNoAfiliado(String noAfiliado) {
this.noAfiliado = noAfiliado;
}
public String getApellidos() {
return apellidos;
}
public void setApellidos(String apellidos) {
this.apellidos = apellidos;
}
public Internado() {
}
private String noAfiliado;
private String apellidos;
public Set<InternadoFamiliar> getFamiliares() {
return familiares;
}
public void addFamiliar(Familiar fam, boolean aCargo) {
InternadoFamiliar intFam = new InternadoFamiliar();
intFam.setAcargo(aCargo);
intFam.setFamiliar(fam);
intFam.setInternado(this);
this.getFamiliares().add(intFam);
}
}
INTERNADO.HBM.XML
?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>
<class name="com.neaprog.geriatrico.datos.Internado"
table="INTERNADOS">
<id name="noAfiliado" column="NoAfiliado">
</id>
<property name="apellidos" type="string" column="apellidos" not-null="true"/>
<set name="familiares" access="field">
<key >
<column name="noAfiliado" not-null="true"/>
</key>
<one-to-many class="com.neaprog.geriatrico.datos.InternadoFamiliar"/>
</set>
</class>
</hibernate-mapping>
--------------------------------------
FAMILIAR
public class Familiar {
public Familiar() {
}
public int getIdFamiliar() {
return idFamiliar;
}
public void setIdFamiliar(int idFamiliar) {
this.idFamiliar = idFamiliar;
}
public String getApellidos() {
return apellidos;
}
public void setApellidos(String apellidos) {
this.apellidos = apellidos;
}
}
FAMILIARES.HBM.XML
<?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>
<class name="com.neaprog.geriatrico.datos.Familiar"
table="FAMILIARES">
<id name="idFamiliar" column="idFamiliar">
<generator class="increment"/>
</id>
<property name="apellidos" type="string" column="apellidos"/>
</class>
</hibernate-mapping>
------------------------------------
INTERNADOFAMILIAR
public class InternadoFamiliar {
public InternadoFamiliar() {
}
private Id id = new Id();
private Internado internado;
private Familiar familiar;
private boolean acargo;
public Id getId() {
return id;
}
public Internado getInternado() {
return internado;
}
public void setInternado(Internado internado) {
this.internado = internado;
this.getId().noAfiliado = internado.getNoAfiliado();
}
public Familiar getFamiliar() {
return familiar;
}
public void setFamiliar(Familiar familiar) {
this.familiar = familiar;
this.getId().idFamiliar = familiar.getIdFamiliar();
}
public boolean isAcargo() {
return acargo;
}
public void setAcargo(boolean acargo) {
this.acargo = acargo;
}
public static class Id implements Serializable {
private String noAfiliado;
private Integer idFamiliar;
public Id() {}
public Id(String noAfiliado, Integer idFamiliar) {
this.noAfiliado = noAfiliado;
this.idFamiliar = idFamiliar;
}
public boolean equals(Object o) {
if (o instanceof Id) {
Id that = (Id)o;
return this.noAfiliado.equals(that.noAfiliado) &&
this.idFamiliar.equals(that.idFamiliar);
} else {
return false;
}
}
public int hashCode() {
return idFamiliar.hashCode() + noAfiliado.hashCode();
}
}
}
INTERNADOFAMILIAR.HBM.XML
<?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>
<class name="com.neaprog.geriatrico.datos.InternadoFamiliar"
table="INTERNADOS_FAMILIARES">
<composite-id name="id" class="com.neaprog.geriatrico.datos.InternadoFamiliar$Id" access="field" unsaved-value="any">
<key-property name="idFamiliar" access="field" column="idfamiliar"/>
<key-property name="noAfiliado" access="field" column="noAfiliado"/>
</composite-id>
<property name="acargo" column="acargo" type="boolean" not-null="true" access="field"/>
<many-to-one name="internado" insert="false" update="false" not-null="true" access="field" column="noAfiliado"/>
<many-to-one name="familiar" insert="false" update="false" not-null="true" access="field" column="ITEM_ID"/>
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():
Transaction tx = sesionHb.beginTransaction();
Internado nuevoAbuelo = getInternadoDeVentana(); //put values into nuevoAbuelo
Familiar famACargo = jPanelFamiliar.getFamiliarDeVentana(); //put values into famACargo
nuevoAbuelo.addFamiliar(famACargo, true);
Set<InternadoFamiliar> familiares = nuevoAbuelo.getFamiliares();
sesionHb.save(famACargo);
sesionHb.save(nuevoAbuelo);
tx.commit();
Full stack trace of any exception that occurs:
74860 [AWT-EventQueue-0] ERROR def.AbstractFlushingEventListener - Could not synchronize database state with session org.hibernate.TransientObjectException: object references an unsaved transient instance - save the transient instance before flushing: com.neaprog.geriatrico.datos.InternadoFamiliar
at org.hibernate.engine.ForeignKeys.getEntityIdentifierIfNotUnsaved(ForeignKeys.java:216)
at org.hibernate.type.EntityType.getIdentifier(EntityType.java:99)
at org.hibernate.type.ManyToOneType.nullSafeSet(ManyToOneType.java:63)
at org.hibernate.persister.collection.AbstractCollectionPersister.writeElement(AbstractCollectionPersister.java:652)
at org.hibernate.persister.collection.AbstractCollectionPersister.recreate(AbstractCollectionPersister.java:914)
at org.hibernate.action.CollectionRecreateAction.execute(CollectionRecreateAction.java:23)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:239)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:223)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:140)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:274)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:730)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:324)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:86)
at com.neaprog.geriatrico.ui.JIntFrameIngreso.guardarInternado(JIntFrameIngreso.java:230)
at com.neaprog.geriatrico.ui.JIntFrameIngreso.ingresarInternado(JIntFrameIngreso.java:130)
at com.neaprog.geriatrico.ui.JIntFrameIngreso.jButtonGuardarActionPerformed(JIntFrameIngreso.java:1097)
at com.neaprog.geriatrico.ui.JIntFrameIngreso.access$700(JIntFrameIngreso.java:37)
at com.neaprog.geriatrico.ui.JIntFrameIngreso$8.actionPerformed(JIntFrameIngreso.java:990)
at javax.swing.AbstractButton.fireActionPerformed(AbstractButton.java:1849)
at javax.swing.AbstractButton$Handler.actionPerformed(AbstractButton.java:2169)
at javax.swing.DefaultButtonModel.fireActionPerformed(DefaultButtonModel.java:420)
at javax.swing.DefaultButtonModel.setPressed(DefaultButtonModel.java:258)
at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(BasicButtonListener.java:234)
at java.awt.Component.processMouseEvent(Component.java:5488)
at javax.swing.JComponent.processMouseEvent(JComponent.java:3093)
at java.awt.Component.processEvent(Component.java:5253)
at java.awt.Container.processEvent(Container.java:1966)
at java.awt.Component.dispatchEventImpl(Component.java:3955)
at java.awt.Container.dispatchEventImpl(Container.java:2024)
at java.awt.Component.dispatchEvent(Component.java:3803)
at java.awt.LightweightDispatcher.retargetMouseEvent(Container.java:4212)
at java.awt.LightweightDispatcher.processMouseEvent(Container.java:3892)
at java.awt.LightweightDispatcher.dispatchEvent(Container.java:3822)
at java.awt.Container.dispatchEventImpl(Container.java:2010)
at java.awt.Window.dispatchEventImpl(Window.java:1774)
at java.awt.Component.dispatchEvent(Component.java:3803)
at java.awt.EventQueue.dispatchEvent(EventQueue.java:463)
at java.awt.EventDispatchThread.pumpOneEventForHierarchy(EventDispatchThread.java:242)
at java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:163)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:157)
at java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:149)
at java.awt.EventDispatchThread.run(EventDispatchThread.java:110)
Name and version of the database you are using:
SQLServer 2000. JDBC: jTDS 1.1
The generated SQL (show_sql=true):
Hibernate: insert into FAMILIARES (apellidos, idFamiliar) values (?, ?)
Hibernate: insert into INTERNADOS (apellidos, NoAfiliado) values (?, ?)
Hibernate: update INTERNADOS set apellidos=? where NoAfiliado=?
Hibernate: update INTERNADOS_FAMILIARES set noAfiliado=? where idfamiliar=? and noAfiliado=?
|