-->
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.  [ 4 posts ] 
Author Message
 Post subject: Problem implementing CompositeUserType <NEW>
PostPosted: Mon Jul 10, 2006 8:05 pm 
Newbie

Joined: Mon Jul 10, 2006 7:56 pm
Posts: 4
Hello all, wonder if someone could help me on this.

I am trying to map four fields (Century, Year, Month, Day) in the DB into a Date object.

The fields in the DB are:

Code:
   <property name="expenseDate" type="richard.customType.FourPartsDate">
         <column name="EHIDCN"/>
         <column name="EHIDYY"/>
         <column name="EHIDMM"/>
         <column name="EHIDDD"/>
      </property>


I have endeavoured to implement the CompositeUserType, but seem to have problem with it, to be specific, a ClassCastException is thrown. being a relative newbie in Hibernate, could someone give me some pointer/hint as to what i have been doing wrong!?

Many thanks!

Code:
package richard.customType;

import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

import org.hibernate.Hibernate;
import org.hibernate.HibernateException;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.type.Type;
import org.hibernate.usertype.CompositeUserType;

/**
* Class Description
*
* @version 1.00 4/07/2006
* @author Richard Zhou
*/

public class FourPartsDate implements CompositeUserType {

   private static final int[] TYPES = { Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC };
   private SimpleDateFormat dateFormatter = new SimpleDateFormat("yyyyMMdd");
   
   public int[] sqlTypes() {
      return TYPES;
   }

   public Class returnedClass() {
      return Date.class;
   }

   public boolean equals(Object x, Object y) {
      if (x == y)
         return true;
      if (x == null || y == null)
         return false;
      return ((int[]) x)[0] == (((int[]) y)[0])
            && ((int[]) x)[1] == (((int[]) y)[1])
            && ((int[]) x)[2] == (((int[]) y)[2])
            && ((int[]) x)[3] == (((int[]) y)[3]);
   }

   public Object deepCopy(Object x) {
      if (x == null)
         return null;
      /*int[] result = new int[4];
      int[] input = (int[]) x;
      result[0] = input[0];
      result[1] = input[1];
      result[2] = input[2];
      result[3] = input[3];*/
      
      return new Date(((Date) x).getTime());
   }

   public boolean isMutable() {
      return true;
   }

   public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session,
         Object owner) throws HibernateException, SQLException {

      int first = ((Integer) Hibernate.INTEGER.nullSafeGet(rs, names[0])).intValue();
      int second = ((Integer) Hibernate.INTEGER.nullSafeGet(rs, names[1])).intValue();
      int third = ((Integer) Hibernate.INTEGER.nullSafeGet(rs, names[2])).intValue();
      int fourth = ((Integer) Hibernate.INTEGER.nullSafeGet(rs, names[3])).intValue();

      return getDateFromInts(first, second, third, fourth);
   }

   public void nullSafeSet(PreparedStatement st, Object value, int index,
         SessionImplementor session) throws HibernateException, SQLException {

      Date dateTime = (value == null) ? new Date() : (Date) value;
      
      int[] ints = getIntArrayFromDate(dateTime);

      Hibernate.INTEGER.nullSafeSet(st, Integer.valueOf(ints[0]), index);
      Hibernate.INTEGER.nullSafeSet(st, Integer.valueOf(ints[1]), index + 1);
      Hibernate.INTEGER.nullSafeSet(st, Integer.valueOf(ints[2]), index + 2);
      Hibernate.INTEGER.nullSafeSet(st, Integer.valueOf(ints[3]), index + 3);
   }

   public String[] getPropertyNames() {
      return new String[] { "century", "year", "month", "day" };
   }

   public Type[] getPropertyTypes() {
      return new Type[] { Hibernate.INTEGER, Hibernate.INTEGER, Hibernate.INTEGER, Hibernate.INTEGER };
   }

   public Object getPropertyValue(Object component, int property) {
      return getDatePartAsInt((Date)component, property);
   }

   public void setPropertyValue(Object component, int property, Object value) {
      Date date = ((Date) component);
      int val = ((Integer)value).intValue();
      
      setDatePartAsInt(date, property, val);
   }

   public Object assemble(Serializable cached, SessionImplementor session, Object owner) {

      return deepCopy(cached);
   }

   public Serializable disassemble(Object value, SessionImplementor session) {
      return (Serializable) deepCopy(value);
   }

   public int hashCode(Object x) throws HibernateException {
      return super.hashCode();
   }

   public Object replace(Object original, Object target, SessionImplementor session,
         Object owner) throws HibernateException {
      // TODO Auto-generated method stub
      return null;
   }
   
   private Date getDateFromInts(int a , int b, int c, int d){
      int[] result = {a, b, c, d};
      
      return getDateFromIntArray(result);
   }
   
   private Date getDateFromIntArray(int[] result){
      Date date = new Date();
      
      if (result[0] == 0 && result[1] == 0 && result[2] == 0 && result[3] == 0)
         return date;
      
      int year = result[0] * 100 + result[1];
      
      Calendar cal = Calendar.getInstance();
      cal.set(Calendar.YEAR, year);
      cal.set(Calendar.MONTH, result[2]);
      cal.set(Calendar.DAY_OF_MONTH, result[3]);
      
      return date;
   }
   
   private int[] getIntArrayFromDate(Date date){
      int[] result = new int[4];
      
      if (date == null)
         return result;
      
      Calendar cal = Calendar.getInstance();
      cal.setTime(date);
      
      result[0] = cal.get(Calendar.YEAR) / 100;
      result[1] = cal.get(Calendar.YEAR) % 100;
      result[2] = cal.get(Calendar.MONTH);
      result[3] = cal.get(Calendar.DAY_OF_MONTH);

      return result;
   }
   
   private Integer getDatePartAsInt(Date date, int index) {
      if (date == null)
         return new Integer(0);

      Calendar cal = Calendar.getInstance();
      cal.setTime(date);

      switch (index) {
      case 0:
         return new Integer(cal.get(Calendar.YEAR) / 100);
      case 1:
         return new Integer(cal.get(Calendar.YEAR) % 100);
      case 2:
         return new Integer(cal.get(Calendar.MONTH));
      case 3:
         return new Integer(cal.get(Calendar.DAY_OF_MONTH));
      default:
         return null;
      }
   }
   
   private void setDatePartAsInt(Date date, int index, int value){
      if (date == null)
         return;

      Calendar cal = Calendar.getInstance();
      cal.setTime(date);

      switch (index) {
      case 0:
         break;
      case 1:
         int year = cal.get(Calendar.YEAR);
         int cent = year - (year % 100);
         cal.set(Calendar.YEAR, cent + value);
         break;
      case 2:
         cal.set(Calendar.MONTH, value);
         break;
      case 3:
         cal.set(Calendar.DAY_OF_MONTH, value);
         break;
      default:
         break;
      }
   }
}


Hibernate version:

Hibernate 3.0

Mapping documents:

Code:
<hibernate-mapping package="richard.domain">
   <class name="Expense" table="CREXP">
      <id name="expenseId" type="java.lang.String">
         <column name="EHEXP#" length="6"/>
      </id>
      <property name="employeeUserId" type="java.lang.String">
         <column name="EHUSER" length="10"/>
      </property>
      <property name="expenseCreditor" type="java.lang.String">
         <column name="EHICR" length="8"/>
      </property>
      <property name="expenseNarrative" type="java.lang.String">
         <column name="EHNARR" length="30"/>
      </property>
      <property name="expenseTotals" type="java.lang.Double">
         <column name="EHINVA" precision="11"/>
      </property>
      <property name="expenseGST" type="java.lang.Double">
         <column name="EHGST" precision="11"/>
      </property>
      
      <property name="expenseDate" type="richard.customType.FourPartsDate">
         <column name="EHIDCN"/>
         <column name="EHIDYY"/>
         <column name="EHIDMM"/>
         <column name="EHIDDD"/>
      </property>
      
      <property name="ehstat" type="java.lang.String">
         <column name="EHSTAT" length="1"/>
      </property>
      <property name="ehdta" type="java.lang.Long">
         <column name="EHDTA" precision="6" scale="0"/>
      </property>
      <property name="ehdtc" type="java.lang.Long">
         <column name="EHDTC" precision="6" scale="0"/>
      </property>
      <property name="ehusrc" type="java.lang.String">
         <column name="EHUSRC" length="10"/>
      </property>
      <property name="ehstcn" type="java.lang.Long">
         <column name="EHSTCN" precision="2" scale="0"/>
      </property>
      <property name="ehstyy" type="java.lang.Long">
         <column name="EHSTYY" precision="2" scale="0"/>
      </property>
      <property name="ehstmm" type="java.lang.Long">
         <column name="EHSTMM" precision="2" scale="0"/>
      </property>
      <property name="ehstdd" type="java.lang.Long">
         <column name="EHSTDD" precision="2" scale="0"/>
      </property>
      <property name="ehauth" type="java.lang.String">
         <column name="EHAUTH" length="10"/>
      </property>
      <property name="ehsqu" type="java.lang.Long">
         <column name="EHSQU" precision="7" scale="0"/>
      </property>
      <property name="ehgkey" type="java.lang.Long">
         <column name="EHGKEY" precision="9" scale="0"/>
      </property>
      
   </class>
</hibernate-mapping>


Code between sessionFactory.openSession() and session.close():

List result = session.createQuery("from Expense").list();

Full stack trace of any exception that occurs:

Code:
Save failed - exception setting property value with CGLIB (set hibernate.cglib.use_reflection_optimizer=false for more info) setter of richard.domain.Expense.setExpenseDate
org.hibernate.PropertyAccessException: exception setting property value with CGLIB (set hibernate.cglib.use_reflection_optimizer=false for more info) setter of richard.domain.Expense.setExpenseDate
   at org.hibernate.tuple.PojoTuplizer.setPropertyValuesWithOptimizer(PojoTuplizer.java:203)
   at org.hibernate.tuple.PojoTuplizer.setPropertyValues(PojoTuplizer.java:173)
   at org.hibernate.persister.entity.BasicEntityPersister.setPropertyValues(BasicEntityPersister.java:2919)
   at org.hibernate.engine.TwoPhaseLoad.initializeEntity(TwoPhaseLoad.java:113)
   at org.hibernate.loader.Loader.initializeEntitiesAndCollections(Loader.java:530)
   at org.hibernate.loader.Loader.doQuery(Loader.java:436)
   at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:218)
   at org.hibernate.loader.Loader.doList(Loader.java:1593)
   at org.hibernate.loader.Loader.list(Loader.java:1577)
   at org.hibernate.loader.hql.QueryLoader.list(QueryLoader.java:395)
   at org.hibernate.hql.ast.QueryTranslatorImpl.list(QueryTranslatorImpl.java:271)
   at org.hibernate.impl.SessionImpl.list(SessionImpl.java:844)
   at org.hibernate.impl.QueryImpl.list(QueryImpl.java:74)
   at richard.HibernateTest.loadClientList(HibernateTest.java:69)
   at richard.HibernateTest.main(HibernateTest.java:44)
Caused by: net.sf.cglib.beans.BulkBeanException: java.util.Date
   at richard.domain.Expense$$BulkBeanByCGLIB$$f33f877a.setPropertyValues(<generated>)
   at org.hibernate.tuple.PojoTuplizer.setPropertyValuesWithOptimizer(PojoTuplizer.java:200)
   ... 14 more
Caused by: java.lang.ClassCastException: java.util.Date
   ... 16 more

Name and version of the database you are using:

DB2 on AS400, V5R2m0

The generated SQL (show_sql=true):

Debug level Hibernate log excerpt:


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 11, 2006 1:44 am 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
What type have you declared the expenseDate member, getter and setter to be? They have to be java.util.Date.

If you've already got that, then turn off reflection optimization (see your excpetion) and try again. The new exception will be more helpful.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject: That worked! :)
PostPosted: Tue Jul 11, 2006 5:47 pm 
Newbie

Joined: Mon Jul 10, 2006 7:56 pm
Posts: 4
Thank you very much tenwit.
That was exactly the problem (not declaring the expenseDate member as Type java.util.Date), i followed your suggestion and made the according changes alongs with few minor alterations and everything works perfectly!

Open source library works because of guys like you, willing to lend a hand to newbies time after time, salute :)

I guess the problem i had was not fully understanding the implementation of the CompositeUserType, overlooking the returnedClass() which hints (dictates) what Type is required for the member variable.

I have enclosed the modified version of the source code, in case anyone come across with similar problem.

Code:
public class FourPartsDate implements CompositeUserType {

   private static final int[] TYPES = { Types.NUMERIC, Types.NUMERIC, Types.NUMERIC, Types.NUMERIC };
   
   public int[] sqlTypes() {
      return TYPES;
   }

   public Class returnedClass() {
      return Date.class;
   }

   public boolean equals(Object x, Object y) {
      if (x == y)
         return true;
      if (x == null || y == null)
         return false;
      
      Date d1 = (Date) x;
      Date d2 = (Date) y;
      
      return compareDates(d1, d2) == 0;
   }

   public Object deepCopy(Object x) {
      if (x == null)
         return null;
      
      return new Date(((Date) x).getTime());
   }

   public boolean isMutable() {
      return true;
   }

   public Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session,
         Object owner) throws HibernateException, SQLException {

      int first = ((Integer) Hibernate.INTEGER.nullSafeGet(rs, names[0])).intValue();
      int second = ((Integer) Hibernate.INTEGER.nullSafeGet(rs, names[1])).intValue();
      int third = ((Integer) Hibernate.INTEGER.nullSafeGet(rs, names[2])).intValue();
      int fourth = ((Integer) Hibernate.INTEGER.nullSafeGet(rs, names[3])).intValue();
      
      return getDateFromInts(first, second, third, fourth);
   }

   public void nullSafeSet(PreparedStatement st, Object value, int index,
         SessionImplementor session) throws HibernateException, SQLException {

      Date dateTime = (value == null) ? new Date() : (Date) value;
      
      int[] ints = getIntArrayFromDate(dateTime);

      Hibernate.INTEGER.nullSafeSet(st, Integer.valueOf(ints[0]), index);
      Hibernate.INTEGER.nullSafeSet(st, Integer.valueOf(ints[1]), index + 1);
      Hibernate.INTEGER.nullSafeSet(st, Integer.valueOf(ints[2]), index + 2);
      Hibernate.INTEGER.nullSafeSet(st, Integer.valueOf(ints[3]), index + 3);
   }

   public String[] getPropertyNames() {
      return new String[] { "century", "year", "month", "day" };
   }

   public Type[] getPropertyTypes() {
      return new Type[] { Hibernate.INTEGER, Hibernate.INTEGER, Hibernate.INTEGER, Hibernate.INTEGER };
   }

   public Object getPropertyValue(Object component, int property) {
      return getDatePartAsInt((Date)component, property);
   }

   public void setPropertyValue(Object component, int property, Object value) {
      Date date = ((Date) component);
      int val = ((Integer)value).intValue();
      
      setDatePartAsInt(date, property, val);
   }

   public Object assemble(Serializable cached, SessionImplementor session, Object owner) {

      return deepCopy(cached);
   }

   public Serializable disassemble(Object value, SessionImplementor session) {
      return (Serializable) deepCopy(value);
   }

   public int hashCode(Object x) throws HibernateException {
      return super.hashCode();
   }

   public Object replace(Object original, Object target, SessionImplementor session,
         Object owner) throws HibernateException {
      // TODO Auto-generated method stub
      return null;
   }
   
   private Date getDateFromInts(int a , int b, int c, int d){
      int[] result = {a, b, c, d};
      
      return getDateFromIntArray(result);
   }
   
   private Date getDateFromIntArray(int[] result){
      Date date = new Date();
      
      if (result[0] == 0 && result[1] == 0 && result[2] == 0 && result[3] == 0)
         return date;
      
      int year = result[0] * 100 + result[1];
      
      Calendar cal = Calendar.getInstance();
      cal.set(Calendar.YEAR, year);
      cal.set(Calendar.MONTH, result[2]);
      cal.set(Calendar.DAY_OF_MONTH, result[3]);
      
      return cal.getTime();
   }
   
   private int[] getIntArrayFromDate(Date date){
      int[] result = new int[4];
      
      if (date == null)
         return result;
      
      Calendar cal = Calendar.getInstance();
      cal.setTime(date);
      
      result[0] = cal.get(Calendar.YEAR) / 100;
      result[1] = cal.get(Calendar.YEAR) % 100;
      result[2] = cal.get(Calendar.MONTH);
      result[3] = cal.get(Calendar.DAY_OF_MONTH);

      return result;
   }
   
   private Integer getDatePartAsInt(Date date, int index) {
      if (date == null)
         return new Integer(0);

      Calendar cal = Calendar.getInstance();
      cal.setTime(date);

      switch (index) {
      case 0:
         return new Integer(cal.get(Calendar.YEAR) / 100);
      case 1:
         return new Integer(cal.get(Calendar.YEAR) % 100);
      case 2:
         return new Integer(cal.get(Calendar.MONTH));
      case 3:
         return new Integer(cal.get(Calendar.DAY_OF_MONTH));
      default:
         return null;
      }
   }
   
   private void setDatePartAsInt(Date date, int index, int value){
      if (date == null)
         return;

      Calendar cal = Calendar.getInstance();
      cal.setTime(date);

      switch (index) {
      case 0:
         break;
      case 1:
         int year = cal.get(Calendar.YEAR);
         int cent = year - (year % 100);
         cal.set(Calendar.YEAR, cent + value);
         break;
      case 2:
         cal.set(Calendar.MONTH, value);
         break;
      case 3:
         cal.set(Calendar.DAY_OF_MONTH, value);
         break;
      default:
         break;
      }
   }
   
   private int compareDates(Date date1, Date date2) {
      if (date1 == null && date2 != null)
         return 1;

      if (date1 != null && date2 == null)
         return -1;

      if (date1 == null && date2 == null)
         return 0;

      SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
      Calendar cal = new GregorianCalendar();

      cal.setTime(date1);
      String date1Str = formatter.format(cal.getTime());

      cal.setTime(date2);
      String date2Str = formatter.format(cal.getTime());

      return date1Str.compareTo(date2Str);
   }
}


Thank you very much again for your help, i am sure i will need to borrow on your experience+expertise again in the near future as i continue on endeavoring with hibernate.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 11, 2006 6:28 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
Glad I could help. Don't forget to rate the helpful post (click "Yes").

_________________
Code tags are your friend. Know them and use them.


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

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.