Exposing javaType (resultMap/parameterMap) attribute in TypeHandler methods for 
Superclass/Subclass handlers.
-------------------------------------------------------------------------------------------------------------

                 Key: IBATIS-332
                 URL: http://issues.apache.org/jira/browse/IBATIS-332
             Project: iBatis for Java
          Issue Type: Improvement
          Components: SQL Maps
    Affects Versions: 2.1.7
         Environment: WebLogic 8.1.5, Oracle 9i, Solaris
            Reporter: Jay Blanton


This improvement is inregards to TypeHandler and its usage with 
Superclasses/Subclasses.  Currently we have an Abstract object 
(TypeSafeEnumeration) that users will extend to create their own specific 
implementations of our TypeSafeEnumeration.  When they want to map this data 
type in iBatis, they have to create a specific handler for each implementation 
of the TypeSafeEnumeration.  What we would like to accomplish is to create a 
single handler for the superclass.  But in order to do this...methods like the 
getResult in the TypeHandler interface would need to know how to take a VARCHAR 
from the database and create the specific implementation of that superclass.  
This information is not available to getResult (but not needed in the 
setParameter), so therefore a handler must be created specific to each 
implementation.  I would like to register a superclass with a global 
typeHandler in my sqlMapConfig that can handle the conversion for each specific 
implementation of that superclass, by utilizing the javaType (for example).

So a superclass could be registered as the the global javaType with a 
superclass based TypeHandler.  Then each resultMap/parameterMap could defined 
the specific implementation in the javaType and the TypeHandler could use that 
resultMap/parameterMap javaType to instantiate the specific version of that 
superclass.

I have included below the TO that is getting mapped, and the Superclass 
(Abstract) that needs the TypeHandler, and two specific implementations of that 
TypeHandler:

TO:
public class ContactTO {
    
    private GenderType gender;
    private ContactMethodType contactMethod;
    public ContactMethodType getContactMethod() {
        return this.contactMethod;
    }
    public void setContactMethod(ContactMethodType contactMethod) {
        this.contactMethod = contactMethod;
    }
    public GenderType getGender() {
        return this.gender;
    }
    public void setGender(GenderType gender) {
        this.gender = gender;
    }
    
}

Superclass (Enum)
import java.io.Serializable;
import java.util.Hashtable;
import java.util.Iterator;

/**
* Base class for type-safe Enumeration to provide a consistent interface and
* common methods, and to make Castor-ation easier.
*/
public abstract class AbstractTypeSafeEnumeration implements Serializable {
    private static Hashtable _types = new Hashtable();

    private String _value;
    private String _displayValue;

    public static final AbstractTypeSafeEnumeration getTypeByValue(String 
value, Class clazz ) {

        Hashtable classType = (Hashtable)_types.get(clazz.getName());
        AbstractTypeSafeEnumeration out = 
(AbstractTypeSafeEnumeration)classType.get(value);
        return out;
    }

    public static final AbstractTypeSafeEnumeration 
getTypeByDisplayValue(String displayValue, Class clazz)
    {
        Hashtable classType = (Hashtable)_types.get(clazz.getName());
        Iterator values = classType.values().iterator();
        AbstractTypeSafeEnumeration value = null;
        while (values.hasNext() && value == null)
        {
            AbstractTypeSafeEnumeration tempValue = 
(AbstractTypeSafeEnumeration) values.next();
            if (value.getDisplayValue().equals(displayValue)) {
                value = tempValue;
            }
        }
        return value;
    }

    protected AbstractTypeSafeEnumeration( String value, String displayValue ) {
        this._value = value;
        this._displayValue = displayValue;
        synchronized( _types ) {
            String className = this.getClass().getName();
            Hashtable classType = ( Hashtable )_types.get( className );
            if( classType == null ) {
                classType = new Hashtable();
                _types.put( className, classType );
            }
            classType.put( value, this );
       }
    }

    public final String getValue() {
        return this._value;
    }

    public final String getDisplayValue() {
        return this._displayValue;
    }

}

Implementations of Enum (located in ContactTO)
import com.foo.to.AbstractTypeSafeEnumeration;

/**
 * This class is a type-safe Enumeration of ContactMethodTypes.
 *
 * @author Jay Blanton
 */
public class ContactMethodType extends AbstractTypeSafeEnumeration {
    public static final ContactMethodType PHONE = new ContactMethodType("P", 
"Phone");
    public static final ContactMethodType MAIL = new ContactMethodType("M", 
"Mail");
    public static final ContactMethodType UNKNOWN = new ContactMethodType("U", 
"Unknown");


    /**
     * Creates a new ContactMethodType object.
     *
     * @param intValue param
     * @param displayString param
     */
    private ContactMethodType(String value, String displayString) {
        super(value, displayString);
    }
}

import com.foo.to.AbstractTypeSafeEnumeration

public class GenderType extends AbstractTypeSafeEnumeration {
    public static final GenderType FEMALE = new GenderType("F", "Female");
    public static final GenderType MALE = new GenderType("M", "Male");
    public static final GenderType UNKNOWN = new GenderType("U", "Unknown");

    /**
     * Creates a new GenderType object.
     *
     * @param intValue param
     * @param displayString param
     */
    private GenderType(String value, String displayString) {
        super(value, displayString);
    }
}

This is what we would like to see:

Global Type Handler:
<typeHandler javaType="com.foo.to.AbstractTypeSafeEnumeration" 
callback="com.foo.dao.datahandler.AbstractTypeSafeEnumerationTypeHandler"/>

Specific Result Map for ContactTO:
<resultMap id="loadContact" class="com.foo.to.ContactTO">
    <result property="gender" column="GENDER" jdbcType="VARCHAR" 
javaType="com.foo.to.GenderType"/>
    <result property="contactMethod" column="METHOD" jdbcType="VARCHAR" 
javaType="com.foo.to.ContactMethodType"/>    
</resultMap>


Sample of a pseudo NewTypeHandler that has access to javaType:

public class AbstractTypeSafeEnumerationTypeHandler implements NewTypeHandler {

    /**
     * This method overrides the default setParameter method in TypeHandler.
     * 
     * @param ps param
     * @param i param
     * @param parameter param
     * @param jdbcType param
     *
     * @throws SQLException can be thrown
     */
    public void setParameter(PreparedStatement ps, int i, Object parameter, 
String jdbcType)
      throws SQLException {    
        if(parameter == null) {
            JdbcType type = (JdbcType)JdbcType.getTypeByDisplayValue(jdbcType, 
JdbcType.class);
            int sqlType = Integer.parseInt(type.getValue());
            ps.setNull(i, sqlType);
        }
        else {
            AbstractTypeSafeEnumeration enum = 
(AbstractTypeSafeEnumeration)parameter;
            ps.setString(i, enum.getValue());
        }
    }

    private AbstractTypeSafeEnumeration getEnum(String value, String javaType) {
        Class clazz = null;
        try {
            clazz = Class.forName(javaType);
        }
        catch (ClassNotFoundException e) {
            throw new RuntimeException("Invalid javaType - This class does not 
exist: -> " + javaType, e); 
        }
        return AbstractTypeSafeEnumeration.getTypeByValue(value, clazz);
    }
    
    /**
     * This method overrides the default getResult method in TypeHandler.
     * 
     * @param rs param
     * @param columnName param
     *
     * @return returned
     *
     * @throws SQLException can be thrown
     */
    public Object getResult(ResultSet rs, String columnName, String javaType)
      throws SQLException {
        String result = rs.getString(columnName);
        return this.getEnum(result, javaType);
    }

    /**
     * This method overrides the default getResult method in TypeHandler.
     * 
     * @param rs param
     * @param columnIndex param
     *
     * @return returned
     *
     * @throws SQLException can be thrown
     */
    public Object getResult(ResultSet rs, int columnIndex, String javaType)
      throws SQLException {
        String result = rs.getString(columnIndex);
        return this.getEnum(result, javaType);
    }


    /**
     * This method overrides the default getResult method in TypeHandler.
     * 
     * @param cs param
     * @param columnIndex param
     *
     * @return returned
     *
     * @throws SQLException can be thrown
     */
    public Object getResult(CallableStatement cs, int columnIndex, String 
javaType)
      throws SQLException {
            String result = cs.getString(columnIndex);
            return this.getEnum(result, javaType);
    }


    /**
     * This method overrides the default equals method in TypeHandler.
     * 
     * @param object param
     * @param dbVal param
     *
     * @return returned
     */
    public boolean equals(Object object, String dbVal) {
        return 
StringHelper.equals(((AbstractTypeSafeEnumeration)object).getValue(), dbVal);
    }


    /**
     * This method overrides the default valueOf method in TypeHandler.
     * 
     * @param dbVal param
     *
     * @return returned
     */
    public Object valueOf(String dbVal) {
        return dbVal;
    }
}
 



-- 
This message is automatically generated by JIRA.
-
If you think it was sent incorrectly contact one of the administrators: 
http://issues.apache.org/jira/secure/Administrators.jspa
-
For more information on JIRA, see: http://www.atlassian.com/software/jira


Reply via email to