iBatis for Java [READ ONLY]
  1. iBatis for Java [READ ONLY]
  2. IBATIS-332

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

    Details

    • Type: New Feature New Feature
    • Status: Open
    • Priority: Major Major
    • Resolution: Unresolved
    • Affects Version/s: 2.1.7
    • Fix Version/s: None
    • Component/s: Core
    • Labels:
      None
    • Environment:
      WebLogic 8.1.5, Oracle 9i, Solaris

      Description

      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
        Unknown macro: { 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; }

        }

        Activity

        Hide
        Jay Blanton added a comment -

        If possible...I would actually like to see both the jdbcType and the javaType accessible on setParameter method and the getResults methods. As I stated in this post:

        http://www.nabble.com/jdbcType-javaType-accessibility-in-TypeHandler-tf2130612.html

        When it comes to one handler that handles one Java Type, but multiple jdbcTypes...we need accessibility to the jdbcType to know what PreparedStatement setXXX method to call and what ResultSet/CallableStatement getXXX to call.

        Also, in the issue I previously stated we could call the ResultSet/CallableStatement getXXX and cast the result to the specific implementation...in order to define a Superclass Type Handler that can be defined globally to handle all specific instances of that Superclass.

        Show
        Jay Blanton added a comment - If possible...I would actually like to see both the jdbcType and the javaType accessible on setParameter method and the getResults methods. As I stated in this post: http://www.nabble.com/jdbcType-javaType-accessibility-in-TypeHandler-tf2130612.html When it comes to one handler that handles one Java Type, but multiple jdbcTypes...we need accessibility to the jdbcType to know what PreparedStatement setXXX method to call and what ResultSet/CallableStatement getXXX to call. Also, in the issue I previously stated we could call the ResultSet/CallableStatement getXXX and cast the result to the specific implementation...in order to define a Superclass Type Handler that can be defined globally to handle all specific instances of that Superclass.
        Hide
        John Didion added a comment -

        +1

        This is really important for mapping Java 5 enum types.

        Show
        John Didion added a comment - +1 This is really important for mapping Java 5 enum types.
        Hide
        Gabor Willner added a comment -

        ditto. I would like to have that feature too...

        Show
        Gabor Willner added a comment - ditto. I would like to have that feature too...

          People

          • Assignee:
            Unassigned
            Reporter:
            Jay Blanton
          • Votes:
            2 Vote for this issue
            Watchers:
            2 Start watching this issue

            Dates

            • Created:
              Updated:

              Development