-->
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.  [ 19 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Trimming fixed length char fields
PostPosted: Tue Feb 24, 2004 7:14 am 
Newbie

Joined: Sun Jan 18, 2004 9:52 am
Posts: 11
Hi,

I am using hibernate 2.1 against DB2/400 and all of the char fields are using the char() data type, which pads out the string with whitespace.

The issue here is that when I trim the string on the object setter, it obviously triggers a database update because the getter now returns a different value than the one that was set. Obviously I don't want to update in this situation. At the moment the two solutions I can see are

1. Use a custom type for char columns. This is pretty undesirable, since now all my custom String objects will be littered through the code, adding complexity where it isn't really needed (Or have I misunderstood this aspect?)

2. Working on the assumption that modifying data to make it pleasing to the eye (i.e. trimming) is really a view operation and should be done in the view - i.e. trim the string in the jsp. This makes the jsp alot more messy than they need to be.

I guess what would be great would be if there was a 'trimmed_string' type as well as a 'string' type for my mapping.

I found a previous post regarding this issue, where the suggested solution was to use XDoclet and Ant to substitute type, but I am not using XDoclet at the moment, and this also seemed like a lot of work for a simple issue.

I figure there is probably a really obvious way to sort this out easily that I am missing - has any one else found an elegant solution to this problem?

Thanks,

Colin


Top
 Profile  
 
 Post subject:
PostPosted: Tue Feb 24, 2004 8:07 am 
Hibernate Team
Hibernate Team

Joined: Tue Sep 09, 2003 2:10 pm
Posts: 3246
Location: Passau, Germany
Quote:
1. Use a custom type for char columns. This is pretty undesirable, since now all my custom String objects will be littered through the code, adding complexity where it isn't really needed (Or have I misunderstood this aspect?)


You have missunderstood it. The UserTypes will only be in the mapping, your code will still contain only String objects. A UserType works just as an adapter between ResultSet and object property.


Top
 Profile  
 
 Post subject: thanks!
PostPosted: Tue Feb 24, 2004 1:31 pm 
Newbie

Joined: Sun Jan 18, 2004 9:52 am
Posts: 11
Thanks for that - quite an easy and elegant solution after all - for the benefit of others who might find it useful - here is the UserType -

Code:
/*
* Created on 24-Feb-04
*/
package dao.hibernate;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.UserType;

/**
* @author Colin Hawkett
*/
public class TrimmedString implements UserType {
   public TrimmedString() {
      super();
   }

   public int[] sqlTypes() {
      return new int[] { Types.CHAR };
   }

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

   public boolean equals(Object x, Object y) throws HibernateException {
      return (x == y) || (x != null && y != null && (x.equals(y)));
   }

   public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
      String val = rs.getString(names[0]);
        return val.trim();
   }

   public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
      st.setString(index, (String)value);
   }

   public Object deepCopy(Object value) throws HibernateException {
      if (value == null) return null;
        return new String((String)value);
   }

   public boolean isMutable() {
      return false;
   }
}


and an example mapping...

Code:
<property name="title" type="dao.hibernate.TrimmedString">
            <column name="PHTITLE" sql-type="char(80)" not-null="true"/>
        </property>


Thanks again,

Colin


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 07, 2004 5:44 am 
Newbie

Joined: Wed Jun 30, 2004 4:00 am
Posts: 4
Thanks for posting your code.
I had the same problem like you
and your TrimmedString class saved me a lot of time.

Anibal


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 17, 2004 12:03 am 
Newbie

Joined: Mon Aug 09, 2004 4:28 am
Posts: 18
Location: Australia
I just want to thank the Hibernate Team for providing the information and especially Colin for posting his code .....

Like Anibal it has saved me many hours of head scratching .......

Many Thanks,

Mike.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 17, 2004 1:01 am 
Newbie

Joined: Mon Aug 09, 2004 4:28 am
Posts: 18
Location: Australia
I found it necessary to make a small change to the code to allow for nullable columns:

public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
String val = rs.getString(names[0]);
if (null == val)
return(null);

return val.trim();
}


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 17, 2004 4:17 am 
Hibernate Team
Hibernate Team

Joined: Tue Aug 26, 2003 7:19 pm
Posts: 2364
Location: Brisbane, Australia
This has proven to be useful to a number of users (and no doubt many more that have not commented) so you should put it up on the Wiki . This is how to serve the hibernate community.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 17, 2004 8:26 pm 
Newbie

Joined: Mon Aug 09, 2004 4:28 am
Posts: 18
Location: Australia
I am trying to integrate to a legacy ingres database system.

Unfortunately, this system relies on the fact that an empty string can be successfully stored in a date field, to indicate that a date has not been stored (as opposed to making it a nullable field).

I can successfully read the date field and get an empty string returned using standard JDBC code.

Unfortunately, when reading via Hibernate, an empty string becomes "1970-01-01 00:00:00.0" ...... obviously the default date.

I have looked at tyring to implement a UserType to handle this, along the lines of the previous example (as I can happily use the date as a string in the Java class).

I have tried using :

Code:
   
  public int[] sqlTypes() {
     return new int[] { Types.VARCHAR };
   }


and

Code:
   public int[] sqlTypes() {
     return new int[] { Types.DATE };
   }


but an empty string is still being returned from the database as ""1970-01-01 00:00:00.0".

The full code listing is :

Code:
public class TrimmedDateString implements UserType {
   public TrimmedDateString() {
     super();
   }

   public int[] sqlTypes() {
     return new int[] { Types.VARCHAR };
   }

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

   public boolean equals(Object x, Object y) throws HibernateException {
     return (x == y) || (x != null && y != null && (x.equals(y)));
   }

   public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
    String val = rs.getString(names[0]);
    System.out.println("nullSafeGet: val is " + val);    
    if (null == val)
      return(null);
        
     return val.trim();
   }

   public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
     st.setString(index, (String)value);
   }

   public Object deepCopy(Object value) throws HibernateException {
System.out.println("deepCopy: value is " + (String)value);         
    if (value == null) return null;
      return new String((String)value);
   }

   public boolean isMutable() {
     return false;
   }
}


I could, test if the returned string is "1970-01-01 00:00:00.0" and then replace it with an empty string, but is there any other alternative ?????

I have not yet tried storing an empty string.

Any suggestions (I am stuck with the way in which the legacy database has been implemented, as that application is still successfully running and will continue to do so for a long while).

Thanks,

Mike.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Oct 26, 2004 2:54 am 
Newbie

Joined: Tue Oct 26, 2004 2:46 am
Posts: 3
For Oracle you have to use the Oracle specific sqltype FIXED_CHAR to avoid SELECTs that return nothing because of comparing trimmed and non-trimmed values.

See
http://www.hibernate.org/Documentation/AutomaticallyTrimmingStringPropertiesGoodForDB2AndOracle for details.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Dec 16, 2004 10:16 am 
Newbie

Joined: Wed Dec 15, 2004 10:23 am
Posts: 10
I use the TrimmedType.
To not declare it in all my hbm.xml files. I use the following code :
Code:
...
   Configuration configuration = new Configuration().configure(configFileURL);
   addTrimmedTypeInMapping(configuration);           
   sessionFactory = configuration.buildSessionFactory();      
...      

protected void addTrimmedTypeInMapping(Configuration configuration) throws MappingException{
   for (Iterator mappings = configuration.getClassMappings(); mappings.hasNext();) {
      PersistentClass  mappingClass = (PersistentClass) mappings.next();
      addTrimmedTypeInMappingProperty(mappingClass.getIdentifier());                               
      for (Iterator iterator = mappingClass.getPropertyIterator();iterator.hasNext();) {
         Property property = (Property) iterator.next();
         addTrimmedTypeInMappingProperty(property.getValue());                                               
      }
  }       
}       
       
protected void addTrimmedTypeInMappingProperty(Value value) throws MappingException{
   if (value instanceof SimpleValue) {           
      if (Hibernate.STRING.equals(value.getType())) {
         ((SimpleValue)value).setType(new CustomType(TrimmedString.class));                       
      }else if(value.getType().isComponentType()){
         Component component = (Component)value;
         for (Iterator iterator = component.getPropertyIterator();iterator.hasNext();) {
            Property property = (Property) iterator.next();                   
            addTrimmedTypeInMappingProperty(property.getValue());
         }
      }
   }                     
}

It's correct ???

Jer.


Top
 Profile  
 
 Post subject: Replace the default 'string' type with 'trimmed_string'?
PostPosted: Wed May 25, 2005 5:45 pm 
Newbie

Joined: Tue Apr 19, 2005 4:32 pm
Posts: 14
The above will no longer work in v3 since the method SimpleValue.setType no longer exists.

Is it possible to replace the default 'string' type with an implementation of 'trimmed_string', without having to use 'trimmed_string' everywhere in .hbm.xml files, something like what the code above is attempting to do?

The best solution of course would have been to extend the behavior of the default StringType class, which I believe is already under consideration and is on JIRA.

Rishabh


Top
 Profile  
 
 Post subject: Re: thanks!
PostPosted: Mon Aug 15, 2005 4:29 pm 
Beginner
Beginner

Joined: Thu Apr 29, 2004 4:03 pm
Posts: 40
hawkettc wrote:
Thanks for that - quite an easy and elegant solution after all - for the benefit of others who might find it useful - here is the UserType -

Code:
/*
* Created on 24-Feb-04
*/
package dao.hibernate;

import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import net.sf.hibernate.HibernateException;
import net.sf.hibernate.UserType;

/**
* @author Colin Hawkett
*/
public class TrimmedString implements UserType {
   public TrimmedString() {
      super();
   }

   public int[] sqlTypes() {
      return new int[] { Types.CHAR };
   }

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

   public boolean equals(Object x, Object y) throws HibernateException {
      return (x == y) || (x != null && y != null && (x.equals(y)));
   }

   public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
      String val = rs.getString(names[0]);
        return val.trim();
   }

   public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
      st.setString(index, (String)value);
   }

   public Object deepCopy(Object value) throws HibernateException {
      if (value == null) return null;
        return new String((String)value);
   }

   public boolean isMutable() {
      return false;
   }
}


and an example mapping...

Code:
<property name="title" type="dao.hibernate.TrimmedString">
            <column name="PHTITLE" sql-type="char(80)" not-null="true"/>
        </property>


Thanks again,

Colin


thanx, this was a really usefull posting, I had the same problem on informix.

Is there any way to configure this behaviour runtime? Our application may run in different envirements, therefore ideal will be not to hold trimming information in the mapping files, but let it configure through application (whether properties file, or applicationContext.xml)


Top
 Profile  
 
 Post subject:
PostPosted: Fri Feb 24, 2006 1:05 pm 
Newbie

Joined: Thu Jan 05, 2006 11:52 am
Posts: 11
Warning: this code should probably only trim trailing spaces. Leading spaces should still be preserved as I suspect most fixed-char db types preserve and distinguish leading spaces.

(In my case I use NHibernate so am using C#'s TrimEnd(null) instead of Trim() for SQL Server's CHAR.)

Cheers,
-Matthew Hobbs


Top
 Profile  
 
 Post subject:
PostPosted: Mon Sep 04, 2006 9:26 am 
Newbie

Joined: Fri Jul 15, 2005 4:21 am
Posts: 14
michael wrote:
Quote:
1. Use a custom type for char columns. This is pretty undesirable, since now all my custom String objects will be littered through the code, adding complexity where it isn't really needed (Or have I misunderstood this aspect?)


You have missunderstood it. The UserTypes will only be in the mapping, your code will still contain only String objects. A UserType works just as an adapter between ResultSet and object property.

Does this mean that the getters and setters in the corresponding Java beans should return a String object and receive a String object even though the mapping data type is a custom User Type?

Kristoffer


Top
 Profile  
 
 Post subject:
PostPosted: Fri Sep 08, 2006 9:53 am 
Regular
Regular

Joined: Fri May 12, 2006 4:05 am
Posts: 106
Yes, it does. The UserType will only be used by hibernate to handle the database-operations, but as you can see in the code it uses the String-class quite often and that is what will be returned to your application. (Just take a look at nullSafeGet() or nullSafeSet() and see what kind of Object is passed in or out).

Greets

piet


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 19 posts ]  Go to page 1, 2  Next

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.