This page is my setup and adventure to get cforms work with OJB and 1:n relationships. After all this is working, this could be used as a template to create tutorial / something to help others to do the same thing. In this tutorial / something I use persistentBroker API in OJB because at the time it was only one handling foreign key relationships correctly with postgreSQL.
Everything is working
10.5.2005: jdo stuff removed, because it is not needed. Not tested if I updated files correctly
22.5.2005: factory stuff removed, using pbfactory directly, no cocoon needed theer.
Comments?
CREATE TABLE parent ( id SERIAL, name VARCHAR(50), PRIMARY KEY (id)); CREATE TABLE childs ( id SERIAL, parent_id INTEGER NOT NULL, name VARCHAR(20), PRIMARY KEY(id), FOREIGN KEY(parent_id) REFERENCES parent (id));
So we have one-to-many (1:n) connection between parent and child.
So if we add new records, we first have to add record to table A and afterthat records to table childs.
package net.vettenranta;
import java.util.ArrayList;
import java.util.Collection;
import java.io.Serializable;
public class Parent implements Serializable {
private Integer id;
private Collection childs = new ArrayList ();
private String name;
public Integer getId () { return id; }
public void setId (Integer id) { this.id = id; }
public Collection getChilds () { return childs; }
public void setChilds (Collection childs ) { this.childs = childs; }
public void addChild (Child child) {
child.setParent (this);
this.childs.add (child);
}
public String getName () { return name; }
public void setName (String name) { this.name = name; }
}
package net.vettenranta;
import java.io.Serializable;
public class Child implements Serializable {
private Integer id;
private Parent parent;
private String name;
public Integer getId () { return id; }
public void setId (Integer id) { this.id = id; }
public Parent getParent () { return parent; }
public void setParent (Parent parent) { this.parent = parent; }
public String getName () { return name; }
public void setName (String name) { this.name = name; }
}
package net.vettenranta;
import java.util.Iterator;
import org.apache.ojb.broker.Identity;
import org.apache.ojb.broker.PersistenceBroker;
import org.apache.ojb.broker.PersistenceBrokerFactory;
import org.apache.ojb.broker.util.ObjectModificationDefaultImpl;
public class DAO {
// requires lot's of comments
public Parent retrieve(Parent bean) {
PersistenceBroker broker = PersistenceBrokerFactory.defaultPersistenceBroker();
Query query = new QueryByIdentity(bean);
Parent result = (Parent) broker.getObjectByQuery(query);
broker.close();
return result;
}
public void insert(Parent bean) {
PersistenceBroker broker = PersistenceBrokerFactory.defaultPersistenceBroker();
broker.beginTransaction();
broker.store(bean);
broker.commitTransaction();
broker.close();
}
public void remove(Parent bean) {
PersistenceBroker broker = PersistenceBrokerFactory.defaultPersistenceBroker();
broker.beginTransaction();
broker.delete(bean);
broker.commitTransaction();
broker.close();
}
public void update(Parent bean) {
PersistenceBroker broker = PersistenceBrokerFactory.defaultPersistenceBroker();
broker.beginTransaction();
broker.store(bean, ObjectModificationDefaultImpl.UPDATE);
broker.commitTransaction();
broker.close();
}
}
<descriptor-repository version="1.0" isolation-level="read-uncommitted"> <jdbc-connection-descriptor jcd-alias="test" default-connection="true" platform="PostgreSQL" subprotocol="postgresql"> <sequence-manager className="org.apache.ojb.broker.util.sequence.SequenceManagerNextValImpl"/> </jdbc-connection-descriptor> <class-descriptor class="net.vettenranta.Parent" table="PARENT"> <field-descriptor name="id" primarykey="true" nullable="false" default-fetch="true" autoincrement="true" column="ID" sequence-name="parent_id_seq" jdbc-type="INTEGER"/> <field-descriptor name="name" default-fetch="true" column="NAME" jdbc-type="VARCHAR"/> <collection-descriptor name="childs" element-class-ref="net.vettenranta.Child" auto-retrieve="true" auto-delete="true" auto-update="true"> <inverse-foreignkey field-ref="parent_id"/> </collection-descriptor> </class-descriptor> <class-descriptor class="net.vettenranta.Child" table="CHILDS"> <field-descriptor name="id" primarykey="true" nullable="false" default-fetch="true" column="ID" jdbc-type="INTEGER" autoincrement="true" sequence-name="childs_id_seq" /> <field-descriptor name="parent_id" nullable="false" default-fetch="true" column="PARENT_ID" jdbc-type="INTEGER" access="anonymous"/> <field-descriptor name="name" default-fetch="true" column="NAME" jdbc-type="VARCHAR"/> <reference-descriptor name="parent" class-ref="net.vettenranta.Parent"> <foreignkey field-ref="parent_id" /> </reference-descriptor> </class-descriptor> </descriptor-repository>
This script works. So It's pretty safe to say that everything should be ok.
function test () {
var bean = new Packages.net.vettenranta.Parent ();
var child;
var dao = new Packages.net.vettenranta.DAO ();
var id;
var iterator;
// 1.
// let's make a new bean with 1 child
bean.setName ('Dad 1');
child = new Packages.net.vettenranta.Child ();
child.setName ('Child 1');
bean.addChild (child);
// 2.
// let's save it
dao.insert (bean);
// 3.
// let's retrieve it from database
id = bean.getId();
bean = new Packages.net.vettenranta.Parent ();
bean.setId(id);
bean = dao.retrieve(bean);
iterator=bean.getChilds().iterator();
while (iterator.hasNext()) {
child = iterator.next();
child.setName ('Child XXX');
}
// 4.
// let's modify the bean
bean.setName ('Dad 2');
child = new Packages.net.vettenranta.Child ();
child.setName ('Child 2');
bean.addChild (child);
// let's save it to the database
dao.update (bean);
// and remove it from database
dao.remove (bean);
cocoon.redirectTo ("add.html");
}
bean.setId (id);
bean = dao.retrieve (bean);
form.load(bean);
form.showForm("forms/edit.html"); // EVERYTHING OK SO FAR
form.save(bean);
dao.update (bean);
<fb:context xmlns:fb="http://apache.org/cocoon/forms/1.0#binding" path="/" >
<fb:value id="id" path="id" />
<fb:value id="name" path="name" />
<fb:repeater id="childs"
parent-path="."
row-path="childs">
<fb:on-bind>
<fb:value id="name" path="name"/>
</fb:on-bind>
<fb:on-delete-row>
<fb:delete-node />
</fb:on-delete-row>
<fb:on-insert-row>
<fb:insert-bean
classname="net.vettenranta.Child"
addmethod="addChild"/>
</fb:on-insert-row>
</fb:repeater>
</fb:context>
<fd:form
xmlns:fd="http://apache.org/cocoon/forms/1.0#definition"
xmlns:i18n="http://apache.org/cocoon/i18n/2.1">
<fd:widgets>
<fd:field id="id">
<fd:datatype base="integer" />
</fd:field>
<fd:field id="name">
<fd:datatype base="string" />
<fd:label>Name</fd:label />
</fd:field>
<fd:repeater id="childs">
<fd:widgets>
<fd:field id="id">
<fd:datatype base="integer" />
</fd:field>
<fd:field id="name" required="true">
<fd:datatype base="string">
<fd:validation>
<fd:length min="3"/>
</fd:validation>
</fd:datatype>
<fd:label>Name</fd:label>
</fd:field>
<fd:booleanfield id="select">
<fd:label>Select</fd:label>
</fd:booleanfield>
</fd:widgets>
</fd:repeater>
<fd:repeater-action id="addchild" action-command="add-row" repeater="childs">
<fd:label>New child</fd:label>
</fd:repeater-action>
<fd:repeater-action id="removechild" action-command="delete-rows" repeater="childs" select="select">
<fd:label>Remove child</fd:label>
</fd:repeater-action>
</fd:widgets>
</fd:form>
CForms template file has to have (in row-repeater)
<fi:widget id="id"><fi:styling type="hidden"><fi:widget>
Joose Vettenranta <joose (at) iki fi>