-->
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.  [ 6 posts ] 
Author Message
 Post subject: map of strings that might be empty strings ("") -&
PostPosted: Thu Jan 29, 2004 6:31 am 
Newbie

Joined: Thu Jan 29, 2004 5:33 am
Posts: 9
Hello!

In going from hibernate version 1 to 2 we have encountered the change that maps may no longer contain null values.

In our application we have several maps that contain Strings, and using Oracle (9i) as a database causes empty strings ("") to be stored as NULL values; meaning that we have a problem. Empty strings are very valid in our application.

There are of course several solutions, either on the application side or by implementing some form of userType null escaping string. The question is: who has invented this wheel before? It seems to me that having a map containing strings that may be empty is a very common case.

I will post my solution here when I have finished it, if a common reply does not get here before I finish my solution (which will probably be a user-type).

Cheers,
Erik Visser


Top
 Profile  
 
 Post subject: sample code
PostPosted: Thu Jan 29, 2004 6:46 am 
Newbie

Joined: Thu Jan 29, 2004 5:33 am
Posts: 9
For completeness:


Test class that should not generate exceptions:


package nl.chess.it.vipas.bos.base.dao.stringmap;

import java.sql.SQLException;
import java.util.HashMap;
import java.util.Map;

import junit.framework.TestCase;
import net.sf.hibernate.HibernateException;
import net.sf.hibernate.Session;
import net.sf.hibernate.SessionFactory;
import net.sf.hibernate.cfg.Configuration;
import net.sf.hibernate.tool.hbm2ddl.SchemaExport;

/**
* This is a standalone test that can be integrated in the total test suite.
* It investigates the Hibernate 2 behaviour taht
* @author Erik Visser, Chess-iT B.V.
*
*/
public class NullStringMapTest extends TestCase {

/**
* Constructor for NullStringMapTest.
* @param name
*/
public NullStringMapTest(String name) {
super(name);
}

public void testCreate() throws HibernateException, SQLException {
Configuration cfg = new Configuration();
//Datastore ds = Hibernate.createDatastore();
cfg.addClass(PhoneBook.class);
new SchemaExport(cfg).create(true, true);

SessionFactory f = cfg.buildSessionFactory();

Map startBookEntries = new HashMap();
startBookEntries.put("santa", "555-12321");

PhoneBook phoneBook = new PhoneBook();
phoneBook.setEntries(startBookEntries);

Session s;

s = f.openSession();

s.save(phoneBook);

Long id = phoneBook.getDbId();

s.flush();
s.connection().commit();
s.close();

PhoneBook liveBook = null;

s = f.openSession();

liveBook = (PhoneBook)s.load(PhoneBook.class, id);

assertEquals( "555-12321", liveBook.getEntries().get("santa"));

liveBook.getEntries().put("anonymous", "");

s.flush();
s.connection().commit();
s.close();

s = f.openSession();

liveBook = (PhoneBook)s.load(PhoneBook.class, id);
String anonymousNumber = (String)liveBook.getEntries().get("anonymous" );

System.out.println("anonymous nr = \"" + anonymousNumber + "\"");

s.flush();
s.connection().commit();
s.close();


s = f.openSession();

liveBook = (PhoneBook)s.load(PhoneBook.class, id);
liveBook.getEntries().put("anonymous", "555-00000");

s.flush();
s.connection().commit();
s.close();

}
}



The NOT-WORKING hibernate 1 implementation:



package nl.chess.it.vipas.bos.base.dao.stringmap;

import java.util.Map;

/**
* A phone book containing a single map of name, value entries both Strings.
*
* @author Erik Visser, Chess-iT B.V.
*
*/
public class PhoneBook {
private Long dbId = null;
private Map entries = null;


public Long getDbId() {
return dbId;
}

public Map getEntries() {
return entries;
}

public void setDbId(Long long1) {
dbId = long1;
}

public void setEntries(Map map) {
entries = map;
}

}


with mapping file:

<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">
<hibernate-mapping>
<class name="nl.chess.it.vipas.bos.base.dao.stringmap.PhoneBook"
table="test_phonebook">
<id name="dbId" type="long">
<generator class = "native">
</generator>
</id>

<map name="entries" cascade="save-update"
table="test_phonebook_entries">
<key column="phonebook_dbid" />
<index column="person" type="string"/>
<element column="phoneNumber" type="string" not-null="false" />
</map>
</class>
</hibernate-mapping>




---------

Using Hibernate 2.1.2,
Oracle 9i 9.0.1
Java 1.4.1_02


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 29, 2004 8:58 am 
Newbie

Joined: Thu Jan 29, 2004 5:33 am
Posts: 9
Creating user types is easy :-)

I used the following userType:

---
Code:
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

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

/**
* This class allows strings to be stored in an escaped form, so that they will never be
* automatically converted to NULL values by the database, should they be empty.
* Note that this class will not allow you to use NULL value strings when they are not allowed by
* Hibernate (such as in Maps).
*
*
* @author Erik Visser, Chess-iT B.V.
*
*/
public class HibernateEscapedString implements UserType {
   
   private static final char QUOTING_CHAR = '\"';

   private static final int[] TYPES = new int[] { Types.VARCHAR };

   /**
    * @see net.sf.hibernate.UserType#sqlTypes()
    */
   public int[] sqlTypes() {
      return TYPES;
   }

   /**
    * @see net.sf.hibernate.UserType#returnedClass()
    */
   public Class returnedClass() {
      return String.class;
   }

   /**
    * @see net.sf.hibernate.UserType#equals(java.lang.Object, java.lang.Object)
    */
   public boolean equals(Object x, Object y) throws HibernateException {
      if ( x == y ) {
         return true;
      }
      if ( x == null ) {
         return false;
      }
      return x.equals(y);
   }

   /**
    * @see net.sf.hibernate.UserType#nullSafeGet(java.sql.ResultSet, java.lang.String[], java.lang.Object)
    */
   public Object nullSafeGet(ResultSet rs, String[] names, Object owner)
      throws HibernateException, SQLException {
         
      String dbValue = (String) Hibernate.STRING.nullSafeGet(rs, names[0]);
      
      if ( dbValue != null ) {
         return unescape(dbValue);
      }
      else {
         return null;
      }

   }

   /**
    * @see net.sf.hibernate.UserType#nullSafeSet(java.sql.PreparedStatement, java.lang.Object, int)
    */
   public void nullSafeSet(PreparedStatement st, Object value, int index)
      throws HibernateException, SQLException {
         
      if ( value != null ) {
         String v = escape((String) value);
         Hibernate.STRING.nullSafeSet(st, v, index);
      }
      else {
         Hibernate.STRING.nullSafeSet(st, value, index);
      }

   }


   /**
    * @see net.sf.hibernate.UserType#deepCopy(java.lang.Object)
    */
   public Object deepCopy(Object value) throws HibernateException {
      if ( value == null ) {
         return null;
      }
      else {
         return new String((String)value);
      }
   }

   /**
    * @see net.sf.hibernate.UserType#isMutable()
    */
   public boolean isMutable() {
      return false;
   }

   /**
    * Escape a string by quoting the string.
    */
   private String escape(String string) {
      return QUOTING_CHAR + string + QUOTING_CHAR;
   }


   /**
    * Unescape by removing the quotes
    */
   private Object unescape(String string) throws HibernateException {
      if ( !(string.charAt(0) == QUOTING_CHAR ) ||
          !(string.charAt(string.length() -1 ) == QUOTING_CHAR )) {
            throw new HibernateException("Persistent storage of " +
         HibernateEscapedString.class.getName() +
         " corrupted, database contained string:" + string);
      }
      
      return string.substring(1, string.length() - 1);
   }

}


---


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 29, 2004 7:29 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
Post it into the wiki area if you think this code is useful to the community.

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Thu Jan 29, 2004 7:30 pm 
Hibernate Team
Hibernate Team

Joined: Sun Sep 14, 2003 3:54 am
Posts: 7256
Location: Paris, France
I mean, please and if you have time :)

_________________
Emmanuel


Top
 Profile  
 
 Post subject:
PostPosted: Fri Jan 30, 2004 4:47 am 
Newbie

Joined: Thu Jan 29, 2004 5:33 am
Posts: 9
Errr, well thx for the confidence... I doubt it is the best solution, but then again the wiki area will allow others to comment more easily.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 6 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.