Don't rely on toString().  Many people (myself included) override that to give 
some meaningful text.  Spring up til 2.0.4 (and Webflow 1.0.1) or so made the 
same assumption when injecting enums and its irritating as hell (in fact I 
raised a bug against Spring that resulted in them using name() instead).  For 
my enums in ibatis I tend to create two properties (eg code and description).  
For example:

public enum TimeUnits {
  MONTHS("M", "Months), ... ;
  
  private final String code;
  private final String description;

  private TimeUnits(String code, String description) {
    this.code = code;
    this.description = description;
  }
}

So:

- M is the database constant;
- Months is the user visible form.

Spring still uses name() so you'll have:

<property name="units" value="MONTHS"/>

which I guess might be a bit incosnistent/confusing but the only alternative is 
to use M as the name (TimeUnits.M is likely to be confusing) or to use custom 
property editors, neither option I like.
-----Original Message-----
From: Raymond McDermott [mailto:[EMAIL PROTECTED]
Sent: Tue 11/6/2007 8:32 AM
To: [email protected]
Subject: Re: JAVA 1.5 enum > typehandler woes
 
One further (perhaps obvious) follow-up point was that after I had specified
the typehandlers properly I no longer needed to map them explicitly on the
SELECT statement.

Finally I have one small update to the generic enum handler code (use
Enum.toString() rather than Enum.name() ):

import java.sql.SQLException;

import com.ibatis.sqlmap.client.extensions.ParameterSetter;
import com.ibatis.sqlmap.client.extensions.ResultGetter;
import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback;

public abstract class EnumTypeHandler<E extends Enum> implements
TypeHandlerCallback
{
    private Class<E> enumClass_;

    public EnumTypeHandler(Class<E> enumClass)
    {
        enumClass_ = enumClass;
    }

    @SuppressWarnings("unchecked")
    public void setParameter(ParameterSetter setter, Object parameter)
            throws SQLException
    {
        // use toString rather than name -- as suggested by the 1.5 official
documentation
        setter.setString(((E) parameter).toString());
    }

    public Object getResult(ResultGetter getter) throws SQLException
    {
        return valueOf(getter.getString());
    }

    @SuppressWarnings("unchecked")
    public Object valueOf(String s)
    {
        return Enum.valueOf(enumClass_, s);
    }
}

On 06/11/2007, Raymond McDermott <[EMAIL PROTECTED]> wrote:
>
> I followed your advice on the typehandler properties on the select
> statement and it started to work.  Great!
>
> I also have to rescind one of my earlier statements.  I did some more
> debugging on the INSERT statement and although the property values are all
> being set nicely, there was no typehandler :(
>
> So I looked again at the manual and at the typeHandler definitions.  The
> reason that it does not associate the typehandler with the correct types is
> that I have incorrectly specced the type handler thus:
>
> <typeHandler javaType=" domain.Frequency"
>                     jdbcType="VARCHAR"
>                     callback="typehandlers.FrequencyTypeHandler"/>
>
> Once I removed the jdbcType it all started to work nicely :)
>
> <typeHandler javaType="domain.Frequency"
>                     callback="typehandlers.FrequencyTypeHandler"/>
>
> So all is well that ends well.
>
> Thanks for the pointers and the support.
>
> Best regards
>
> Ray
>
>
> On 05/11/2007, Raymond McDermott <[EMAIL PROTECTED]> wrote:
> >
> > I have not tried it on the select.  Good idea.  I will do that and post
> > back any new information.
> >
> > I don't know how I would be more explicit for the INSERT.  Do I have to
> > decompose the definition of the parameter class?  Given that I know from the
> > debugger that the typehandler is definitely called on the INSERT, what
> > benefit would that bring?
> >
> >
> > On 05/11/2007, Niels Beekman < [EMAIL PROTECTED]> wrote:
> > >
> > >  Have you tried to define the typehandler explicitly on the resultmap
> > > or insert statement? Do both select and insert blow up?
> > >
> > >
> > >
> > > Niels
> > >  ------------------------------
> > >
> > > *From:* Raymond McDermott [mailto:[EMAIL PROTECTED]
> > > *Sent:* maandag 5 november 2007 10:45
> > > *To:* [email protected]
> > > *Subject:* Re: JAVA 1.5 enum > typehandler woes
> > >
> > >
> > >
> > > Thanks for the comments Niels.  I read the FAQ entry completely before
> > > posting and used it to drive the coded solution.
> > >
> > > In terms of the stacktrace, it is the complete stacktrace from
> > > Eclipse.  I think it gives enough to show that there is genuinely a 
> > > problem
> > > with the typehandler.
> > >
> > > I have debugged it all the way line by line, including adding the
> > > iBATIS source code to the project.  The data is good and it should return 
> > > a
> > > valid value - this is shown in the debugger - but somehow between the
> > > typehandler returning a value from the ENUM and coming back into iBATIS, 
> > > the
> > > value goes null.
> > >
> > > That is why I wondered if there is something that I needed to do with
> > > the enum itself?
> > >
> > > Thanks for the continued support.
> > >
> > > Ray
> > >
> > >  On 05/11/2007, *Niels Beekman* < [EMAIL PROTECTED]> wrote:
> > >
> > > Hi,
> > >
> > >
> > >
> > > Are you sure this is the complete stacktrace? Did you try debugging?
> > >
> > > This error can be caused by numerous things, for example the property
> > > frequency can be null in your bean, ensure that you are handling the
> > > null-case in your typehandler, for a comprehensive explanation, see:
> > >
> > >
> > > http://opensource.atlassian.com/confluence/oss/display/IBATIS/How+do+I+use+a+Custom+Type+Handler+with+complex+property+or+Type+Safe+Enumeration
> > >
> > >
> > >
> > > Hth,
> > >
> > >
> > >
> > > Niels
> > >  ------------------------------
> > >
> > > *From:* Raymond McDermott [mailto: [EMAIL PROTECTED]
> > > *Sent:* zondag 4 november 2007 21:57
> > > *To:* [email protected]
> > > *Subject:* JAVA 1.5 enum > typehandler woes
> > >
> > >
> > >
> > > I am having some, ahem, fun trying to get enums persisting as varchars
> > > using iBatis 2.3 for Java
> > >
> > > Can I just check something... is there are a 'proper' way for iBATIS
> > > to persist enums or do we really have to write a typehandler?  Is this is
> > > something that the developer group will patch in 2.x or is there
> > > something more fundamental in the architecture that makes it a 3.0feature?
> > >
> > > Also, I wonder if i have to do something extra in the definition of an
> > > ENUM to support access by the typehandler framework?
> > >
> > > Anyway, for the moment it seems that we are stuck with the, IMHO
> > > overly complex, task of scripting support for our enum types.  I am 
> > > getting
> > > null pointers despite debugging the code and seeing that the typehandler 
> > > is
> > > being properly registered and invoked.
> > >
> > > I have a simple enum class 'frequency':
> > >
> > > ---> ENUM START
> > >
> > > /**
> > >  * The frequency over which measurements are made
> > >  */
> > >
> > > public enum Frequency {
> > >     DAILY,
> > >     WEEKLY,
> > >     MONTHLY,
> > >     YEARLY
> > > }
> > >
> > >
> > > ---> ENUM END
> > >
> > > I used the generic enum typehandler from the FAQ:
> > >
> > > ---> GENERIC TYPEHANDLER START
> > >
> > > import java.sql.SQLException;
> > >
> > > import com.ibatis.sqlmap.client.extensions.ParameterSetter;
> > > import com.ibatis.sqlmap.client.extensions.ResultGetter;
> > > import com.ibatis.sqlmap.client.extensions.TypeHandlerCallback ;
> > >
> > > public abstract class EnumTypeHandler<E extends Enum> implements
> > > TypeHandlerCallback
> > > {
> > >     private Class<E> enumClass_;
> > >
> > >     public EnumTypeHandler(Class<E> enumClass)
> > >     {
> > >         enumClass_ = enumClass;
> > >     }
> > >
> > >     @SuppressWarnings("unchecked")
> > >     public void setParameter(ParameterSetter setter, Object parameter)
> > >             throws SQLException
> > >     {
> > >         setter.setString(((E) parameter).name());
> > >     }
> > >
> > >     public Object getResult(ResultGetter getter) throws SQLException
> > >     {
> > >         return valueOf(getter.getString());
> > >     }
> > >
> > >     @SuppressWarnings("unchecked")
> > >     public Object valueOf(String s)
> > >     {
> > >         return Enum.valueOf(enumClass_, s);
> > >     }
> > > }
> > >
> > > ---> GENERIC TYPEHANDLER END
> > >
> > > Then I have the simple implementation as proposed in the FAQ:
> > >
> > > ---> SPECIFIC TYPEHANDLER START
> > >
> > > public class FrequencyTypeHandler extends EnumTypeHandler<Frequency> {
> > >
> > >     public FrequencyTypeHandler() {
> > >     super(Frequency.class);
> > >     }
> > >
> > > }
> > >
> > > ---> SPECIFIC TYPEHANDLER END
> > >
> > > ---> IBATIS CONFIGURATION FILE START
> > >
> > > <?xml version="1.0" encoding="UTF-8" ?>
> > >
> > > <!DOCTYPE sqlMapConfig
> > >     PUBLIC "-//ibatis.apache.org//DTD SQL Map Config 2.0//EN"
> > >     "http://ibatis.apache.org/dtd/sql-map-config-2.dtd";>
> > >
> > > <sqlMapConfig>
> > >
> > >     <typeHandler     javaType="
> > > com.opengrail.circles365.domain.Frequency"
> > >                     jdbcType="VARCHAR"
> > >                     callback="
> > > com.opengrail.circles365.config.ibatis.typehandlers.FrequencyTypeHandler"/>
> > >
> > >
> > >     <typeHandler     javaType="
> > > com.opengrail.circles365.domain.MeasurementType"
> > >                     jdbcType="VARCHAR"
> > >                     callback="
> > > com.opengrail.circles365.config.ibatis.typehandlers.MeasurementTypeTypeHandler"/>
> > >
> > >
> > >     <!-- List the SQL Map XML files. They can be loaded from the
> > >          classpath, as they are here (com.domain.data...) -->
> > >
> > >     <sqlMap resource="com/opengrail/circles365/config/iBatis- User.xml
> > > "/>
> > >     <sqlMap resource="com/opengrail/circles365/config/iBatis-
> > > MeasurementItem.xml"/>
> > >
> > > </sqlMapConfig>
> > >
> > > ---> IBATIS CONFIGURATION FILE END
> > >
> > > ---> IBATIS SQLMAP FILE START
> > >
> > > <?xml version="1.0" encoding="UTF-8" ?>
> > >
> > > <!DOCTYPE sqlMap
> > >     PUBLIC "-//ibatis.apache.org//DTD SQL Map 2.0//EN"
> > >     " http://ibatis.apache.org/dtd/sql-map-2.dtd";>
> > >
> > > <sqlMap namespace="MeasurementItem">
> > >
> > >     <resultMap id="result" class="
> > > com.opengrail.circles365.domain.impl.MeasurementItemImpl ">
> > >         <result property="frequency" column="FREQUENCY"/>
> > >         <result property="measurementType" column="MEASUREMENTTYPE"/>
> > >         <result property="name" column="ITEMNAME"/>
> > >         <result property="derived" column="ISDERIVED"/>
> > >     </resultMap>
> > >
> > >     <select id="findMeasurementItemsByUserId" resultMap="result">
> > >         select     FREQUENCY, MEASURETYPE, ITEMNAME, ISDERIVED
> > >         from     C365MEASUREMENTITEM MI, C365USER U
> > >         where    U.USERID = #value#
> > >         and        MI.USERID = U.ID
> > >     </select>
> > >
> > >     <insert id="createMeasurementItem" parameterClass="
> > > com.opengrail.circles365.domain.MeasurementItem">
> > >         insert
> > >         into C365MEASUREMENTITEM (frequency, measuretype, itemname,
> > > isderived, userid)
> > >             select     #frequency#, #measurementType#, #name#,
> > > #derived#, u.id
> > >             from     c365user u
> > >             where     u.userid = #userId#
> > >     </insert>
> > >
> > > </sqlMap>
> > >
> > > ---> IBATIS SQLMAP FILE END
> > >
> > > Unit test trace ON INSERT:
> > >
> > > org.springframework.jdbc.UncategorizedSQLException: SqlMapClient
> > > operation; uncategorized SQLException for SQL []; SQL state [null]; error
> > > code [0];
> > > --- The error occurred in com/opengrail/circles365/config/iBatis-
> > > MeasurementItem.xml.
> > > --- The error occurred while applying a parameter map.
> > > --- Check the createMeasurementItem-InlineParameterMap.
> > > --- Check the parameter mapping for the 'frequency' property.
> > > --- Cause: java.lang.NullPointerException; nested exception is
> > > com.ibatis.common.jdbc.exception.NestedSQLException:
> > > --- The error occurred in com/opengrail/circles365/config/iBatis-
> > > MeasurementItem.xml.
> > > --- The error occurred while applying a parameter map.
> > > *--- Check the createMeasurementItem-InlineParameterMap.
> > > --- Check the parameter mapping for the 'frequency' property.
> > > *--- Cause: java.lang.NullPointerException
> > > Caused by: com.ibatis.common.jdbc.exception.NestedSQLException:
> > > --- The error occurred in com/opengrail/circles365/config/iBatis-
> > > MeasurementItem.xml .
> > > --- The error occurred while applying a parameter map.
> > > --- Check the createMeasurementItem-InlineParameterMap.
> > > --- Check the parameter mapping for the 'frequency' property.
> > > --- Cause: java.lang.NullPointerException
> > >     at
> > > com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.executeUpdate
> > > (GeneralStatement.java:94)
> > >     at com.ibatis.sqlmap.engine.impl.SqlMapExecutorDelegate.update(
> > > SqlMapExecutorDelegate.java:505)
> > >     at com.ibatis.sqlmap.engine.impl.SqlMapSessionImpl.update(
> > > SqlMapSessionImpl.java:90)
> > >     at
> > > org.springframework.orm.ibatis.SqlMapClientTemplate$10.doInSqlMapClient
> > > (SqlMapClientTemplate.java:383)
> > >     at org.springframework.orm.ibatis.SqlMapClientTemplate.execute (
> > > SqlMapClientTemplate.java:193)
> > >     at org.springframework.orm.ibatis.SqlMapClientTemplate.update(
> > > SqlMapClientTemplate.java:381)
> > >     at
> > > com.opengrail.circles365.data.impl.MeasurementItemDaoImpl.createMeasurementItem(
> > > MeasurementItemDaoImpl.java:13)
> > >     at
> > > com.opengrail.circles365.service.impl.MeasurementItemManagementImpl.createMeasurementItem
> > > (MeasurementItemManagementImpl.java:25)
> > >     at
> > > com.opengrail.circles365.tests.ServiceImplementationTests.testGoodMeasurementItem(
> > > ServiceImplementationTests.java:56)
> > >     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> > >     at sun.reflect.NativeMethodAccessorImpl.invoke(
> > > NativeMethodAccessorImpl.java:39)
> > >     at sun.reflect.DelegatingMethodAccessorImpl.invoke (
> > > DelegatingMethodAccessorImpl.java:25)
> > >     at java.lang.reflect.Method.invoke(Method.java:585)
> > >     at junit.framework.TestCase.runTest(TestCase.java:154)
> > >     at junit.framework.TestCase.runBare(TestCase.java:127)
> > >     at org.springframework.test.ConditionalTestCase.runBare(
> > > ConditionalTestCase.java:69)
> > >     at junit.framework.TestResult$1.protect(TestResult.java:106)
> > >     at junit.framework.TestResult.runProtected(TestResult.java :124)
> > >     at junit.framework.TestResult.run(TestResult.java:109)
> > >     at junit.framework.TestCase.run(TestCase.java:118)
> > >     at junit.framework.TestSuite.runTest(TestSuite.java:208)
> > >     at junit.framework.TestSuite.run (TestSuite.java:203)
> > >     at
> > > org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(
> > > JUnit3TestReference.java:130)
> > >     at org.eclipse.jdt.internal.junit.runner.TestExecution.run(
> > > TestExecution.java:38)
> > >     at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests
> > > (RemoteTestRunner.java:460)
> > >     at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests
> > > (RemoteTestRunner.java:673)
> > >     at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run (
> > > RemoteTestRunner.java:386)
> > >     at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(
> > > RemoteTestRunner.java:196)
> > > Caused by: java.lang.NullPointerException
> > >     at
> > > com.ibatis.sqlmap.engine.mapping.parameter.BasicParameterMap.setParameter(
> > > BasicParameterMap.java:165)
> > >     at
> > > com.ibatis.sqlmap.engine.mapping.parameter.BasicParameterMap.setParameters
> > > (BasicParameterMap.java:125)
> > >     at com.ibatis.sqlmap.engine.execution.SqlExecutor.executeUpdate(
> > > SqlExecutor.java :79)
> > >     at
> > > com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.sqlExecuteUpdate
> > > (GeneralStatement.java:200)
> > >     at
> > > com.ibatis.sqlmap.engine.mapping.statement.GeneralStatement.executeUpdate
> > > (GeneralStatement.java :78)
> > >     ... 27 more
> > >
> > > Do any of you folks have any ideas?
> > >
> > > Thanks in advance for your support.
> > >
> > > Ray
> > >
> > >
> > >
> > >
> > > --
> > > Ray McDermott
> > > GSM 047/32.53.854
> > >
> >
> >
> >
> > --
> > Ray McDermott
> > GSM 047/32.53.854
> >
>
>
>
> --
> Ray McDermott
> GSM 047/32.53.854
>



-- 
Ray McDermott
GSM 047/32.53.854


------------------------------------------------------------------------------------------------------------------------------
This e-mail and any files transmitted with it are confidential and are only for 
the use of the person to whom they are addressed. If you are not the intended 
recipient, you are hereby notified that any use, dissemination, forwarding, 
printing, copying or dealing in any way whatsoever with this e-mail is strictly 
prohibited. If you have received this e-mail in error, please reply to us 
immediately and delete the document.
It is the recipient's duty to virus-scan and otherwise test the enclosed 
information before using the information or loading attached files onto any 
computer system. JDV Limited does not warrant that the information contained in 
this e-mail is free from viruses, defects, errors, interception or interference.
JDV Limited, and each of its related companies each reserve the right to 
monitor all e-mail communications through its networks.
Any views expressed in this message are those of the individual sender, except 
where that sender specifically states them to be the views of JDV Limited.
Your private information is only used and disclosed for the intention which you 
have provided it for. This information is not disclosed or used unless your 
consent has been provided or in the case that JDV Limited is permitted to do so 
under the Privacy Act of 1988.
-----------------------------------------------------------------------------------------------------------------------------

Reply via email to