-->
These old forums are deprecated now and set to read-only. We are waiting for you on our new forums!
More modern, Discourse-based and with GitHub/Google/Twitter authentication built-in.

All times are UTC - 5 hours [ DST ]



Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 
Author Message
 Post subject: JPA @Convert to boolean broken
PostPosted: Wed May 13, 2015 8:19 am 
Newbie

Joined: Wed Jul 10, 2013 1:43 pm
Posts: 3
Setup is DB2 9.5, Java 1.7, Hibernate 4.1.4(tried 4.3.8 as well), Struts 1.3 application. There are multiple columns in the database set to VARCHAR(1) that are being used to store "Y", "N", or null values. Null is not desirable but is due to old data being loaded, the application will always send "Y" or "N" back to the ORM layer. The idea was to convert these to primitive booleans where null would be considered false as primitive booleans are simple to work with in Java.

First thought was to use Hibernate's @Type(type="yes_no") annotation but that returns a Boolean class because the column may be null and the primitive boolean cannot be null. Too bad there wasn't another attribute where null would default to false.

Second thought was to use JPA 2.1 @Converter and @Convert annotations using the AttributeConverter class. There is also the autoApply=true attribute that would apply this converter to all booleans in the project. This seemed like the best solution. So we decided to test it out on one column. This resulted in the following class:

Code:
import javax.persistence.AttributeConverter;
import javax.persistence.Converter;

@Converter
public class BooleanToStringConverter implements AttributeConverter<Boolean, String> {   
   @Override
    public String convertToDatabaseColumn(Boolean value) {       
        return (value != null && value) ? "Y" : "N";           
    }   

    @Override
    public Boolean convertToEntityAttribute(String value) {
        return (value == null ? false : "Y".equals(value));
    }
}


and the following annotation in a pojo:

Code:
@Convert(converter=BooleanToStringConverter.class)
private Boolean active;


On Hibernate 4.1.4, this results in the following exception:

Code:
org.hibernate.exception.GenericJDBCException: [jcc][1093][10419][3.64.96] Invalid data conversion: Parameter instance Y is invalid for the requested conversion to short. ERRORCODE=-4220, SQLSTATE=null
   at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:54)
   at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:125)
   at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:110)
   at org.hibernate.engine.jdbc.internal.proxy.AbstractResultSetProxyHandler.continueInvocation(AbstractResultSetProxyHandler.java:108)
   at org.hibernate.engine.jdbc.internal.proxy.AbstractProxyHandler.invoke(AbstractProxyHandler.java:81)
   at com.sun.proxy.$Proxy57.getShort(Unknown Source)
   at org.hibernate.type.descriptor.sql.SmallIntTypeDescriptor$2.doExtract(SmallIntTypeDescriptor.java:66)
   at org.hibernate.type.descriptor.sql.BasicExtractor.extract(BasicExtractor.java:65)
   at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:269)
   at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:265)
   at org.hibernate.type.AbstractStandardBasicType.nullSafeGet(AbstractStandardBasicType.java:238)
   at org.hibernate.type.AbstractStandardBasicType.hydrate(AbstractStandardBasicType.java:357)
   at org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:2704)
   at org.hibernate.loader.Loader.loadFromResultSet(Loader.java:1541)
   at org.hibernate.loader.Loader.instanceNotYetLoaded(Loader.java:1473)
   at org.hibernate.loader.Loader.getRow(Loader.java:1373)
   at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:640)
   at org.hibernate.loader.Loader.doQuery(Loader.java:850)
   at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:289)
   at org.hibernate.loader.Loader.doList(Loader.java:2438)
   at org.hibernate.loader.Loader.doList(Loader.java:2424)
   at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2254)
   at org.hibernate.loader.Loader.list(Loader.java:2249)
   at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:470)
   at org.hibernate.hql.internal.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:355)
   at org.hibernate.engine.query.spi.HQLQueryPlan.performList(HQLQueryPlan.java:195)
   at org.hibernate.internal.SessionImpl.list(SessionImpl.java:1248)
   at org.hibernate.internal.QueryImpl.list(QueryImpl.java:101)


Debugging some of this code and it looks like Hibernate is trying to convert "Y" to TINYINT, or thinks it is a TINYINT, and then trying to convert it to a Java short. Looking at the object being fetched it says that the column is org.hibernate.type.Boolean. It never reaches the code in the BooleanToStringConverter class.

Switching to Hibernate 4.3.8 and it works if that database column is not null but when it is null it gives this exception:

Code:
org.hibernate.PropertyAccessException: Null value was assigned to a property of primitive type setter of ************************************
   at org.hibernate.property.DirectPropertyAccessor$DirectSetter.set(DirectPropertyAccessor.java:126)
   at org.hibernate.tuple.entity.AbstractEntityTuplizer.setPropertyValues(AbstractEntityTuplizer.java:713)
   at org.hibernate.tuple.entity.PojoEntityTuplizer.setPropertyValues(PojoEntityTuplizer.java:362)
   at org.hibernate.persister.entity.AbstractEntityPersister.setPropertyValues(AbstractEntityPersister.java:4718)
   at org.hibernate.engine.internal.TwoPhaseLoad.doInitializeEntity(TwoPhaseLoad.java:188)
   at org.hibernate.engine.internal.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:144)
   at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.performTwoPhaseLoad(AbstractRowReader.java:244)
   at org.hibernate.loader.plan.exec.process.internal.AbstractRowReader.finishUp(AbstractRowReader.java:215)
   at org.hibernate.loader.plan.exec.process.internal.ResultSetProcessorImpl.extractResults(ResultSetProcessorImpl.java:140)
   at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:138)
   at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:102)
   at org.hibernate.loader.collection.plan.AbstractLoadPlanBasedCollectionInitializer.initialize(AbstractLoadPlanBasedCollectionInitializer.java:100)
   at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:693)
   at org.hibernate.event.internal.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:92)
   at org.hibernate.internal.SessionImpl.initializeCollection(SessionImpl.java:1897)
   at org.hibernate.collection.internal.AbstractPersistentCollection$4.doWork(AbstractPersistentCollection.java:558)
   at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:260)
   at org.hibernate.collection.internal.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:554)
   at org.hibernate.collection.internal.AbstractPersistentCollection.read(AbstractPersistentCollection.java:142)
   at org.hibernate.collection.internal.AbstractPersistentCollection$1.doWork(AbstractPersistentCollection.java:171)
   at org.hibernate.collection.internal.AbstractPersistentCollection$1.doWork(AbstractPersistentCollection.java:156)
   at org.hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded(AbstractPersistentCollection.java:260)
   at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:155)
   at org.hibernate.collection.internal.PersistentSet.size(PersistentSet.java:160)


While debugging this one, I can see it does reach the BooleanToStringConverter class and successfully assigns a false value to the object's property. A little later while stepping through the code, Hibernate then tries to assign null to the property which causes the exception. It's like Hibernate doesn't know that the JPA converter has already converted and set the value.

I did run across a few posts about AttributeConverter being broken with Hibernate Envers but not much else. Is this a bug in Hibernate?


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 

All times are UTC - 5 hours [ DST ]


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum

Search for:
© Copyright 2014, Red Hat Inc. All rights reserved. JBoss and Hibernate are registered trademarks and servicemarks of Red Hat, Inc.