TOC

  1. Introduction
  2. Database
  3. Java classes
  4. repository.xml
  5. test-script
  6. Forms flowscript (not complete)
  7. CForms binding
  8. CForms forms
  9. CForms template (not complete)
  10. software
  11. Author

Introduction

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.

Status

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.

TODO

Comments?

Database

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.

Java classes

parent

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

child

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

DAO

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

}

repository.xml

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

Test-script (in cocoon flowscript)

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

CForms flowscript

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

CForms binding

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

CForms forms

<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

CForms template file has to have (in row-repeater)

<fi:widget id="id"><fi:styling type="hidden"><fi:widget>

Software

Author

My homepage

Joose Vettenranta <joose (at) iki fi>