My problem is this: I've created a custom UserType implementation which maps to CHAR columns and right trims whitespace when data is read from the result set and right pads whitespace when data is written to the prepared statement.
Everything works fine in junit, and even in my application - well, kind of. I've got a CHAR column named "cart", like so:
Code:
<property
name="cart"
type="dars.apis.common.dao.hibernate.whitespace.WhiteSpaceSensitiveUserType1"
column="cart"
length="1"
>
<meta attribute="use-in-tostring">true</meta>
<meta attribute="property-type">java.lang.String</meta>
</property>
I can run an HQL query like the following:
Code:
"from WhatifCourse whatifCourse where whatifCourse.comp_id.stuMastNo=? and whatifCourse.cart=?",
specifying "T" for the value of "cart" in the query, and get back the rows that match the criteria. However, once I get the rows back, all of the objects' "cart" values are null! This isn't happening with over columns that are mapped using my UserType implementation, and it doesn't happen in junit tests. Also strange is that the correct rows are returned from the database (where cart="T"), but once they're returned, cart is null:(.
I've been able to work around this for the time being by punting on using my UserType for this column and instead just using java.lang.String.
Any help would be much appreciated...
Thanks,
Jon
Hibernate version: 2.1.6 (and we can't upgrade to 3.0)
Mapping documents:Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 2.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd" >
<hibernate-mapping>
<!--
Created by the Middlegen Hibernate plugin
http://boss.bekk.no/boss/middlegen/
http://hibernate.sourceforge.net/
-->
<class
name="dars.apis.student.WhatifCourse"
table="whatif_courses"
>
<meta attribute="generated-class" inherit="false">dars.apis.student.BaseWhatifCourse</meta>
<composite-id name="comp_id" class="dars.apis.student.WhatifCoursePK">
<meta attribute="generated-class" inherit="false">dars.apis.student.BaseWhatifCoursePK</meta>
<!-- replacing this with a non-directional mapping of just the column stu_mast_no -->
<!-- bi-directional many-to-one association to StuMaster -->
<!-- commented out for now because we're having trouble deleting WhatifCourse records based on stuMastNo
<key-many-to-one
name="stuMaster"
class="dars.apis.student.StuMaster"
>
<column name="stu_mast_no" />
</key-many-to-one>
-->
<key-property
name="stuMastNo"
column="stu_mast_no"
type="java.lang.Integer"
/>
<key-property
name="sourceId"
column="source_id"
type="dars.apis.common.dao.hibernate.whitespace.WhiteSpaceSensitiveUserType8"
length="8"
>
<meta attribute="property-type">java.lang.String</meta>
</key-property>
<key-property
name="sourceCd"
column="source_cd"
type="dars.apis.common.dao.hibernate.whitespace.WhiteSpaceSensitiveUserType3"
length="3"
>
<meta attribute="property-type">java.lang.String</meta>
</key-property>
<key-property
name="cdpmask"
column="cdpmask"
type="dars.apis.common.dao.hibernate.whitespace.WhiteSpaceSensitiveUserType15"
length="15"
>
<meta attribute="property-type">java.lang.String</meta>
</key-property>
<key-property
name="crsYt"
column="crs_yt"
type="dars.apis.common.dao.hibernate.whitespace.WhiteSpaceSensitiveUserType6"
length="6"
>
<meta attribute="property-type">java.lang.String</meta>
</key-property>
<key-property
name="crsNum"
column="crs_num"
type="dars.apis.common.dao.hibernate.whitespace.WhiteSpaceSensitiveUserType15"
length="15"
>
<meta attribute="property-type">java.lang.String</meta>
</key-property>
<key-property
name="crsSeq"
column="crs_seq"
type="dars.apis.common.dao.hibernate.whitespace.WhiteSpaceSensitiveUserType3"
length="3"
>
<meta attribute="property-type">java.lang.String</meta>
</key-property>
</composite-id>
<property
name="rcredit"
type="java.math.BigDecimal"
column="rcredit"
length="7"
/>
<property
name="grade"
type="dars.apis.common.dao.hibernate.whitespace.WhiteSpaceSensitiveUserType4"
column="grade"
length="4"
>
<meta attribute="property-type">java.lang.String</meta>
</property>
<property
name="ctitle"
type="java.lang.String"
column="ctitle"
length="29"
/>
<property
name="rtext"
type="dars.apis.common.dao.hibernate.whitespace.WhiteSpaceSensitiveUserType2"
column="rtext"
length="2"
>
<meta attribute="property-type">java.lang.String</meta>
</property>
<property
name="cart"
type="dars.apis.common.dao.hibernate.whitespace.WhiteSpaceSensitiveUserType1"
column="cart"
length="1"
>
<meta attribute="use-in-tostring">true</meta>
<meta attribute="property-type">java.lang.String</meta>
</property>
<property
name="memo"
type="java.lang.String"
column="memo"
length="255"
/>
<property
name="lastModUser"
type="java.lang.String"
column="last_mod_user"
length="30"
/>
<property
name="lastModDate"
type="java.sql.Timestamp"
column="last_mod_date"
length="23"
/>
<!-- associations -->
</class>
</hibernate-mapping>
Code between sessionFactory.openSession() and session.close():Code:
public List findCartCoursesByStuMastNo(Integer stuMastNo) {
return getHibernateTemplate()
.find(
"from WhatifCourse whatifCourse where whatifCourse.comp_id.stuMastNo=? and whatifCourse.cart=?",
new Object[] { stuMastNo, CART_COURSE_VALUE });
}
Full stack trace of any exception that occurs:
no exceptions...
Name and version of the database you are using:
MSSQL Server 2000
The generated SQL (show_sql=true):
[code]
Hibernate: select whatifcour0_.stu_mast_no as stu_mast1_, whatifcour0_.source_id as source_id, whatifcour0_.source_cd as
source_cd, whatifcour0_.cdpmask as cdpmask, whatifcour0_.crs_yt as crs_yt, whatifcour0_.crs_num as crs_num, whatifcour0
_.crs_seq as crs_seq, whatifcour0_.rcredit as rcredit, whatifcour0_.grade as grade, whatifcour0_.ctitle as ctitle, whati
fcour0_.rtext as rtext, whatifcour0_.cart as cart, whatifcour0_.memo as memo, whatifcour0_.last_mod_user as last_mo14_,
whatifcour0_.last_mod_date as last_mo15_ from whatif_courses whatifcour0_ where (whatifcour0_.stu_mast_no=? )and(whatifc
our0_.cart=? )
Debug level Hibernate log excerpt:
[code]
10 Mar 2005 13:45:15 : [DEBUG] org.springframework.orm.hibernate.SessionFactoryUtils - Opening Hibernate session
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - opened session
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - find: from WhatifCourse whatifCourse where whatifCourse.comp_id.stuMastNo=? and whatifCourse.cart=?
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.engine.QueryParameters - parameters: [399, T]
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.engine.QueryParameters - named parameters: {}
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - flushing session
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - Flushing entities and processing referenced collections
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - Processing unreferenced collections
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - Scheduling collection removes/(re)creates/updates
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - Flushed: 0 insertions, 0 updates, 0 deletions to 0 objects
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - Dont need to execute flush
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.hql.QueryTranslator - HQL: from dars.apis.student.WhatifCourse whatifCourse where whatifCourse.comp_id.stuMastNo=? and whatifCourse.cart=?
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.hql.QueryTranslator - SQL: select whatifcour0_.stu_mast_no as stu_mast1_, whatifcour0_.source_id as source_id, whatifcour0_.source_cd as source_cd, whatifcour0_.cdpmask as cdpmask, whatifcour0_.crs_yt as crs_yt, whatifcour0_.crs_num as crs_num, whatifcour0_.crs_seq as crs_seq, whatifcour0_.rcredit as rcredit, whatifcour0_.grade as grade, whatifcour0_.ctitle as ctitle, whatifcour0_.rtext as rtext, whatifcour0_.cart as cart, whatifcour0_.memo as memo, whatifcour0_.last_mod_user as last_mo14_, whatifcour0_.last_mod_date as last_mo15_ from whatif_courses whatifcour0_ where (whatifcour0_.stu_mast_no=? )and(whatifcour0_.cart=? )
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.BatcherImpl - about to open: 0 open PreparedStatements, 0 open ResultSets
10 Mar 2005 13:45:15 : [DEBUG] org.springframework.jdbc.datasource.DriverManagerDataSource - Creating new JDBC connection to [jdbc:jtds:sqlserver://192.168.1.54:1433/darwiniatest]
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.SQL - select whatifcour0_.stu_mast_no as stu_mast1_, whatifcour0_.source_id as source_id, whatifcour0_.source_cd as source_cd, whatifcour0_.cdpmask as cdpmask, whatifcour0_.crs_yt as crs_yt, whatifcour0_.crs_num as crs_num, whatifcour0_.crs_seq as crs_seq, whatifcour0_.rcredit as rcredit, whatifcour0_.grade as grade, whatifcour0_.ctitle as ctitle, whatifcour0_.rtext as rtext, whatifcour0_.cart as cart, whatifcour0_.memo as memo, whatifcour0_.last_mod_user as last_mo14_, whatifcour0_.last_mod_date as last_mo15_ from whatif_courses whatifcour0_ where (whatifcour0_.stu_mast_no=? )and(whatifcour0_.cart=? )
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.BatcherImpl - preparing statement
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.type.IntegerType - binding '399' to parameter: 1
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.type.StringType - binding 'T' to parameter: 2
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.loader.Loader - processing result set
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.type.IntegerType - returning '399' as column: stu_mast1_
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.loader.Loader - result row: dars.apis.student.WhatifCoursePK@1c41e49[stuMastNo=399,sourceId=MIAMI,sourceCd=TST,cdpmask=,crsYt=200401,crsNum=test123,crsSeq=001]
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.loader.Loader - Initializing object from ResultSet: dars.apis.student.WhatifCoursePK@1c41e49[stuMastNo=399,sourceId=MIAMI,sourceCd=TST,cdpmask=,crsYt=200401,crsNum=test123,crsSeq=001]
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.loader.Loader - Hydrating entity: dars.apis.student.WhatifCourse#dars.apis.student.WhatifCoursePK@1c41e49[stuMastNo=399,sourceId=MIAMI,sourceCd=TST,cdpmask=,crsYt=200401,crsNum=test123,crsSeq=001]
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.type.BigDecimalType - returning '3.00000' as column: rcredit
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.type.StringType - returning '' as column: ctitle
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.type.StringType - returning null as column: memo
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.type.StringType - returning null as column: last_mo14_
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.type.TimestampType - returning null as column: last_mo15_
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.loader.Loader - done processing result set (1 rows)
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.BatcherImpl - done closing: 0 open PreparedStatements, 0 open ResultSets
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.BatcherImpl - closing statement
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.loader.Loader - total objects hydrated: 1
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - resolving associations for [dars.apis.student.WhatifCourse#dars.apis.student.WhatifCoursePK@1c41e49[stuMastNo=399,sourceId=MIAMI,sourceCd=TST,cdpmask=,crsYt=200401,crsNum=test123,crsSeq=001]]
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - done materializing entity [dars.apis.student.WhatifCourse#dars.apis.student.WhatifCoursePK@1c41e49[stuMastNo=399,sourceId=MIAMI,sourceCd=TST,cdpmask=,crsYt=200401,crsNum=test123,crsSeq=001]]
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - initializing non-lazy collections
10 Mar 2005 13:45:15 : [DEBUG] org.springframework.orm.hibernate.HibernateTemplate - Eagerly flushing Hibernate session
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - flushing session
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - Flushing entities and processing referenced collections
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - Processing unreferenced collections
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - Scheduling collection removes/(re)creates/updates
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - Flushed: 0 insertions, 0 updates, 0 deletions to 1 objects
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.Printer - listing entities:
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.Printer - dars.apis.student.WhatifCourse{rtext=null, lastModDate=null, memo=null, comp_id=WhatifCoursePK{sourceId=MIAMI, stuMastNo=399, sourceCd=TST, crsNum=test123, cdpmask=, crsSeq=001, crsYt=200401}, lastModUser=null, grade=C, cart=null, ctitle=, rcredit=3.00000}
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - executing flush
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - post flush
10 Mar 2005 13:45:15 : [DEBUG] org.springframework.orm.hibernate.SessionFactoryUtils - Closing Hibernate session
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - closing session
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - disconnecting session
10 Mar 2005 13:45:15 : [DEBUG] net.sf.hibernate.impl.SessionImpl - transaction completion
[/code]
Custom user type
[code]
package dars.apis.common.dao.hibernate.whitespace;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;
import net.sf.hibernate.UserType;
import org.apache.commons.lang.StringUtils;
/**
* @author chaseja
*/
public abstract class AbstractWhiteSpaceSensitiveUserType implements UserType,
Serializable {
// --------------------------------------------------
// member variables
// --------------------------------------------------
private static final int[] SQL_TYPES = { Types.CHAR };
// --------------------------------------------------
// constructors
// --------------------------------------------------
// --------------------------------------------------
// abstract methods
// --------------------------------------------------
protected abstract int getColumnLength();
// --------------------------------------------------
// private methods
// --------------------------------------------------
/**
* Convenience method so we don't have to type out the Spring import every
* time we want to do a right trim.
*/
private static final String rTrim(String s) {
return org.springframework.util.StringUtils.trimTrailingWhitespace(s);
}
/**
* Convenience method so we don't have to type out the whole StringUtils
* method every time we want to do a right pad.
*/
private static final String rPad(String s, int size) {
return StringUtils.rightPad(s, size);
}
// --------------------------------------------------
// public methods
// --------------------------------------------------
// --------------------------------------------------
// methods from UserType interface
// --------------------------------------------------
public int[] sqlTypes() {
return SQL_TYPES;
}
public Class returnedClass() {
return String.class;
}
public boolean equals(Object x, Object y) {
if (x == y) {
return true;
}
if (x == null || y == null) {
return false;
}
return rPad((String) x, getColumnLength()).equals(
rPad((String) y, getColumnLength()));
}
public Object deepCopy(Object arg) {
return arg;
}
public boolean isMutable() {
return false;
}
/**
* Retrieves the property value from the JDBC ResultSet, right trimming
* whitespace from the given string.
*/
public Object nullSafeGet(ResultSet resultSet, String[] names, Object owner)
throws SQLException {
if (resultSet.wasNull()) {
return null;
}
String untrimmedValue = resultSet.getString(names[0]);
return untrimmedValue == null ? null : rTrim(untrimmedValue);
}
/**
* Writes the property value to the JDBC PreparedStatement, right padding
* <code>value</code> with whitespace to the length specified by
* <code>getColumnLength()</code>.
*/
public void nullSafeSet(PreparedStatement statement, Object value, int index)
throws SQLException {
if (value == null) {
statement.setNull(index, Types.CHAR);
} else {
String trimmedValue = (String) value;
statement.setString(index, rPad(trimmedValue, getColumnLength()));
}
}
// --------------------------------------------------
// methods from xxx superclass
// --------------------------------------------------
// --------------------------------------------------
// inner classes
// --------------------------------------------------
// --------------------------------------------------
// getter/setter methods
// --------------------------------------------------
}
[/code]