ibatis-user-java mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "William Shields" <william_shie...@jdv.com>
Subject RE: JAVA 1.5 enum > typehandler woes
Date Mon, 05 Nov 2007 23:47:31 GMT

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:ray@mcdermott.be]
Sent: Tue 11/6/2007 8:32 AM
To: user-java@ibatis.apache.org
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 <ray@mcdermott.be> 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 <ray@mcdermott.be> 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 < n.beekman@wis.nl> 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:ray@mcdermott.be]
> > > *Sent:* maandag 5 november 2007 10:45
> > > *To:* user-java@ibatis.apache.org
> > > *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* < n.beekman@wis.nl> 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: ray@mcdermott.be]
> > > *Sent:* zondag 4 november 2007 21:57
> > > *To:* user-java@ibatis.apache.org
> > > *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.
-----------------------------------------------------------------------------------------------------------------------------

Mime
View raw message