FAQ for Harder Cases

How can I create DB-Schemes where Tables contain a Primary-Key with more than one column?

For Example: {{{<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE database SYSTEM "D:\JAVA\database_3_1.dtd"> <database defaultIdMethod="native" name="survey_db">

... ... ... </database>}}}

If you try to insert a "Survey" you will get an Error. Or isn't it allowed to use multi-column-Primary-Keys?!? After debugging the Torque-Source-Code (I still have a head-ache :-) ) I found out, that the PK-Engine only looks for the first column with the PK-Attribute. If i'm right, I had to completely restructure my database-design to have only ONE column as Primary-Key.

Any Ideas or Comments?!?

--- Torque is ok with multi-column primary keys (I've only tried it in 3.1). In my case it was two CHAR fields. --Gary S.

--- Yes, you can have a multi-column primary-key, BUT it does not work, if you need one column of this PK as autoincrement. And this is my problem. The Torque engine looks up the new key from the sequence and writes it into the System.out/Log-File, but it does not put it into the PK-field, where I need the value. Would be nice, if someone had a solution for this. I found the same problem in another forum, but there was also no answer to this question.

SyncMaster

Using XMLEncoder/XMLDecoder with Torque

java.beans.{XMLEncoder,XMLDecoder} is a useful and fairly simple way of transferring Torque objects to non-Java clients. The encoder uses introspection and will correctly handle any network of objects that conform to the Java beans conventions. Unfortunately, Torque's OM classes do not quite conform.

1. If you declare a field as DATE or TIMESTAMP the OM field will be of class java.util.Date but when you retrieve the object from the database the returned field will be of type java.sql.Date or java.sql.Timestamp. Since none of these classes is a valid Java bean the encoder's introspection fails, though it does have special rules for dealing with java.util.Date.

2. When you have a foreign key Torque generates get and set methods for both the key and the object referrred to. The introspector does not realise that these are linked fields and so fetches the underlying object for each foreign key.

The solutions are

1. If you have a field declaration of the form

{{{ <field name="entrydate"

}}}

you have to over-ride the getEntrydate() method in the OM class with

        public java.util.Date getEntrydate()
        {
                java.util.Date  d       = super.getEntrydate();
                return (d instance of java.util.Date) ? d :
                        new java.util.Date(d.getTime());
        }

2. To prevent the fetching of underlying objects referred to by foreign keys create a BeanInfo class for each OM class that has foreign keys.

Suppose that you have the following table declaration, which contains a foreign key.

    <table name="MODELEVENT"
    >
        <column
            name="MODELEVENTID"
            type="INTEGER"
            required="true"
            primaryKey="true"
            autoIncrement="true"
        />
        <column
            name="MODELVERSIONID"
            type="INTEGER"
            required="true"
        />
        <column
            name="EVENT"
            type="VARCHAR"
            size="50"
            required="true"
        />
        <column
            name="COMMENTS"
            type="VARCHAR"
            size="256"
            required="false"
        />
        <column
            name="ENTRYDATE"
            type="TIMESTAMP"
            required="true"
        />
        <column
            name="PERSON"
            type="VARCHAR"
            size="50"
            required="true"
        />
       <foreign-key foreignTable="MODELVERSION">
           <reference local="MODELVERSIONID" foreign="MODELVERSIONID"/>
        </foreign-key>
    </table>

You then need to have this BeanInfo class

package com.gsk.pmf;

import java.beans.IntrospectionException;
import java.beans.PropertyDescriptor;
import java.beans.SimpleBeanInfo;

/**
 * @version $Id;$
 */
public class ModeleventBeanInfo extends SimpleBeanInfo {
        Class   meClass = Modelevent.class;
    /**
     *
     */
    public ModeleventBeanInfo() {
        super();
     }
        public PropertyDescriptor[] getPropertyDescriptors()
        {
                try {
                        PropertyDescriptor modelversionid = new PropertyDescriptor("modelversionid", meClass);
                        PropertyDescriptor modeleventid = new PropertyDescriptor("modeleventid", meClass);
                        PropertyDescriptor comments = new PropertyDescriptor("comments", meClass);
                        PropertyDescriptor entrydate = new PropertyDescriptor("entrydate", meClass);
                        PropertyDescriptor person = new PropertyDescriptor("person", meClass);
                        PropertyDescriptor event = new PropertyDescriptor("event", meClass);
                        PropertyDescriptor[] rv = {
                                        comments, entrydate, person, modeleventid,
                                        event, modelversionid
                        };
                        return rv;
                } catch (IntrospectionException e) {
                        System.err.println("IntrospectionException"+e.getMessage());
                        e.printStackTrace();
                }
                return null;
        }
}

This BeanInfo includes the modelversionid but excludes the modelversion that it refers to. The encoder will therefore include the ID field but omit the target object. It will also omit all the housekeeping fields such as new and modified.

RaphaelMankin

There is a simple solution for creating all the BeanInfo classes for all Tables without too much overhead.

First, create an abstract base class. This base class constructs the property descriptor array from the 'getFields()' method. The method 'getTheClass()' provides the class of your concrete Table class derived from BaseObject.

package com.gsk.pmf;

import java.beans.*;
import java.util.*;

/**
 * @author Hans Hülf
 */
public abstract class TorqueBeanInfo extends SimpleBeanInfo {

    abstract protected Class getTheClass();
    abstract protected Collection getFields();
        
    public PropertyDescriptor[] getPropertyDescriptors()
    {
            try {
                        PropertyDescriptor[] pdar = new PropertyDescriptor[getFields().size()];
                        Iterator it = getFields().iterator();
                        for (int i = 0; it.hasNext(); i++) {
                            String field = (String) it.next();
                            PropertyDescriptor pd = new PropertyDescriptor(field, getTheClass());
                            pdar[i]=pd;
                        }
                        
                        return pdar;
                        
            } catch (IntrospectionException e) {
                    System.err.println("IntrospectionException"+e.getMessage());
                    e.printStackTrace();
            }
            return null;
    }

}

Then you only have to subclass once for every BaseObject class you have. In this example the above XML created a class called Modelevent (extends BaseModelevent (extends BaseObject)).

package com.gsk.pmf;

import java.util.*;
/**
 * @author Hans Hülf
 */
public class ModeleventBeanInfo extends TorqueBeanInfo {
        protected Class getTheClass() {
                return Modelevent.class;
        }
        protected Collection getFields(){
                return Modelevent.getFieldNames();
        }
}

The advantage of this method is that you don't have to change the BeanInfo classes after the Table classes have changed. Only if you add new Tables you have to provide a new BeanInfo class.

HansHülf

HarderTorqueFaq (last edited 2009-09-20 23:04:22 by localhost)