Here is an example of how to set up a custom type handler with a complex property. This example is for a Type Safe Enumeration.

In the example below there is a class Report that represents a report that a company might produce. This class has a complex property "frequency" that is represented by a Type Safe Enumeration.

Here is the report table

REPORT {
        id              varchar2(5),
        name            varchar2(25),
        description     varchar2(1000),
        frequency       number(1)
}

As you see the frequency is stored as a number, but in the class Report is is a Frequency object. There are two question that I ran into. First, how do you cleanly map the number stored in the database with the instance of a Frequency? Second, how can iBatis and custom type handlers help?

To answer the first let me show you the code for the Frequency class.

/*
 * Frequency.java
 *
 * Created on December 27, 2004, 11:00 AM
 */

package reporting.viewer.domain;

/**
 *
 * @author nmaves
 */
public class Frequency {
    
    /**
     * Holds value of property value.
     */
    private int value;
    
    /** Creates a new instance of Frequency */
    private Frequency(int value, String name) {
        this.value = value;
        this.name = name;
    }
    
    /**
     * Getter for property value.
     * @return Value of property value.
     */
    public int getValue() {
        
        return this.value;
    }
    
    private static final int FC_DAILY = 1;
    private static final int FC_WEEKLY = 2;
    private static final int FC_MONTHLY = 3;
    private static final int FC_QUARTERLY = 4;
    
    private static final String FN_DAILY = "Daily";
    private static final String FN_WEEKLY = "Weekly";
    private static final String FN_MONTHLY = "Monthly";
    private static final String FN_QUARTERLY = "Quarterly";
    
    public static final Frequency DAILY = new Frequency(FC_DAILY, FN_DAILY);
    public static final Frequency WEEKLY = new Frequency(FC_WEEKLY, FN_WEEKLY);
    public static final Frequency MONTHLY = new Frequency(FC_MONTHLY, FN_MONTHLY);
    public static final Frequency QUARTERLY = new Frequency(FC_QUARTERLY, FN_QUARTERLY);

    /**
     * Holds value of property name.
     */
    private String name;

    /**
     * Holds value of property format.
     */
    private String format;
    
    public static Frequency get(int value) {
        switch (value) {
            case FC_DAILY:      return DAILY;
            case FC_WEEKLY:     return WEEKLY;
            case FC_MONTHLY:    return MONTHLY;
            case FC_QUARTERLY:  return QUARTERLY;
            default:            throw new IllegalArgumentException(value + " is not a valid Frequency");
        }
    }
    
    public String toString() {
        return this.name;
    }

    /**
     * Getter for property name.
     * @return Value of property name.
     */
    public String getName() {

        return this.name;
    }

}

As you see the  get(int value)  method allows for the mapping of the number stored in the database to an instance of this class. I am sure there is a better way of pulling these initial value from a properties file but this is only an example.

For the second part you will need a way to allow iBatis to make the conversion. This is where the custom type handler comes into play.

Below is my FrequencyTypeHandler.java

package reporting.viewer.dao.ibatis;

import java.sql.*;
import com.ibatis.sqlmap.client.extensions.*;
import reporting.viewer.domain.Frequency;

public class FrequencyTypeHandler implements TypeHandlerCallback {
    
    public Object getResult(ResultGetter getter) throws SQLException {
        int value = getter.getInt();
        if (getter.wasNull())
            return null;
        return Frequency.get(value);
    }
    
    public void setParameter(ParameterSetter setter, Object parameter) throws SQLException {
        if (parameter == null) {
            setter.setNull(Types.INTEGER);
        } else {
            Frequency frequency = (Frequency) parameter;
            setter.setInt(frequency.getValue());
        }
    }
    
    public Object valueOf(String s){
        return s;
    }
}

You are almost there! All you need left to do is tell iBatis to map instances of Frequency object to this handler.

I added the following line into my SqlMapConfig file.

 <typeHandler javaType="reporting.viewer.domain.Frequency" callback="reporting.viewer.dao.ibatis.FrequencyTypeHandler"/> 

Make sure that you add it before the transaction manager section.

That is it. No need to change any of your other sqlmap entries. Here is an example of a select and insert that use this handler.

<resultMap class="Report" id="ReportResult">
        <result column="id" property="id" />
        <result column="name" property="name" />
        <result column="description" property="description" />
        <result column="frequency" property="frequency"/>
</resultMap>

<select id="getReportById" parameterClass="string" resultMap="ReportResult">
        SELECT 
            *
        FROM 
            REPORT
        WHERE 
            id = #value#
</select>

<insert id="insertReport" parameterClass="Report">
        INSERT INTO 
            REPORT (
                id, 
                name, 
                frequency,
                description
                )
            values (
                #id#, 
                #name#, 
                #frequency#,
                #description#
            )
</insert>

Notice that there is nothing special that need to be done for the frequency columns.

Hope this helps,

Nathan Maves Nathan.Maves@sun.com

How_do_I_use_a_Custom_Type_Handler_with_complex_property_(Type_Safe_Enumeration) (last edited 2009-09-20 22:57:02 by localhost)