-->
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.  [ 3 posts ] 
Author Message
 Post subject: UserType for PostgreSQL BIT(n) with poolable connection
PostPosted: Mon Apr 14, 2008 7:52 am 
Newbie

Joined: Mon Apr 14, 2008 5:34 am
Posts: 2
Location: Poland
My problem: UserType for PostgreSQL BIT(n) works only with hibernate native connection, but fails with poolable connection (defined in Tomcat).

I worked out how to map PostgreSQL BIT(n) type (http://www.postgresql.org/docs/8.1/inte ... e-bit.html) with java.util.BitSet (using UserType interface of course). It works fine for hibernate native connection, but not for poolable connection (defined in Tomcat). More specifically I have problem with setting informations with PreparedStatement.setObject(.., .., java.sql.Types.OTHER). I must use setObject() because SQL query should look like "... B'0101010101010010101010000' ...". That's why setString() isn't good enough (among others it puts additional quotes on both sides of string).

Please! Help! I was working on this for several days but I have stuck. I have read a lot of documentation, articles, blogs etc. I think I know PreparedStatement and Connection API by heart already ;) I have no more ideas. I'm desperated and frustrated. Appreciate any help...
If you think I should not use BIT(n) type, please, read notes at the end of my message.

OK, here are results of my work for hibernate native connection:

hibernate.cfg.xml :
Code:
<property name="connection.url">jdbc:postgresql://localhost/my_db_name</property>
<property name="connection.username">user</property>
<property name="connection.password">pass</property>
<property name="dialect">org.hibernate.dialect.PostgreSQLDialect</property>
<property name="connection.driver_class">org.postgresql.Driver</property>
<property name="transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property>

annotation in my VO (value object) class:
Code:
@org.hibernate.annotations.Type(
   type = "myProject.dao.userType.PGBitStringUserType"
)
@Column(unique=true)
private BitSet code;

My DAO class is classic DAO using GenericDAO pattern. Nothing unusual. I has one additional method:
Code:
   public FooVO findByCode(BitSet bs) {
      Query q;
      if (bs==null) {
         q = getSession().createQuery("from FooVO where code = null");
      } else {
         q = getSession().createQuery("from FooVO where code = :code");
         q.setParameter("code", bs, Hibernate.custom(PGBitStringUserType.class));
      }
      return (IngredientVO)q.uniqueResult();
   }

and my UserType class:
Code:
public class PGBitStringUserType implements UserType {

   public PGBitStringUserType() {
      super();
   }
   
   public int[] sqlTypes() {
      return new int[]{java.sql.Types.OTHER};
   }

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

   public Object assemble(Serializable cached, Object owner) throws HibernateException {
      return cached;
   }

   public Serializable disassemble(Object o) throws HibernateException {
      return (Serializable) o;
   }

   public Object deepCopy(Object o) throws HibernateException {
      return o == null ? null :  ((BitSet)o).clone();
   }

   public boolean equals(Object o, Object o1) throws HibernateException {
      return (o == null) ? (o1 == null) : o.equals(o1);
   }

   public int hashCode(Object o) throws HibernateException {
      return o==null ? 0 : o.hashCode();
   }

   public boolean isMutable() {      
      return false;
   }

   public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
      String value = (String)Hibernate.STRING.nullSafeGet(rs, names[0]);
      if (value == null)
         return null;
      BitSet bs = new BitSet( 800 );
      int pos = value.indexOf("1", 0);
      while (pos != -1) {
         bs.set(pos);
         pos = value.indexOf("1", pos+1);
      }
      return bs;
   }

   public void nullSafeSet(PreparedStatement statement, Object o, int i) throws HibernateException, SQLException {
      if (o == null) {
         statement.setNull(i, java.sql.Types.OTHER);
      } else {
         BitSet bs = (BitSet) o;                  
         StringBuffer value = new StringBuffer();
         for (int k=0; k<bs.length(); k++)
            value.append(bs.get(k)==true ? '1' : '0');
         while (value.length()<800)
            value.append('0');
         statement.setObject(i, value.toString(), java.sql.Types.OTHER);
      }
   }

   public Object replace(Object original, Object target, Object owner) throws HibernateException {
      return original;
   }   
   
}

PS: I have also prepared additional class:
"public class PGBitString extends PGobject implements Serializable, Cloneable"
but have doscovered that it's not used at all (probably because I do not use ResultSet.getObject()) and everything works without it as well...


Then I wanted to move on and to change hibernate.cfg.xml settings to use poolable connection (defined in Tomcat):

Code:
<property name="hibernate.connection.datasource">java:comp/env/jdbc/myProject</property>

Of course it caused Exceptions in nullSafeSet() method caused by statement.setObject().
I tried to fix it so I changed nullSafeSet() method and added some debug code as well:
Code:
public class PGBitStringUserType implements UserType {

   [...]
   public void nullSafeSet(PreparedStatement statement, Object o, int i) throws HibernateException, SQLException {
      if (o == null) {
         statement.setNull(i, java.sql.Types.OTHER);
      } else {
         // some informations from debug process:
         // statement is of type Jdbc3PreparedStatement for hibernate connection
         //      and org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement type for poolable connection
         // statement.getConnection() is of type org.postgresql.jdbc3.Jdbc3Connection for hibernate connection
         //      and org.apache.tomcat.dbcp.dbcp.PoolableConnection type for poolable connection

         BitSet bs = (BitSet) o;                  
         PGBitSet PGbs = new PGBitSet(bs); // PGBitSet definition is beneath
         statement.setObject(i, PGbs, java.sql.Types.OTHER);
      }
   }
}


Code:
public class PGBitSet implements SQLData {
   public BitSet bs;
   private String sql_type;
   
   public PGBitSet(BitSet bs) {
      this.bs = bs;
   }

   public String getSQLTypeName() throws SQLException {
      return sql_type;
   }

   public void readSQL(SQLInput stream, String typeName) throws SQLException {      
      sql_type = typeName;
      String value = stream.readString();
      if (value == null) {
         this.bs = null;
      } else {
         BitSet bs = new BitSet( 800 );
         int pos = value.indexOf("1", 0);
         while (pos != -1) {
            bs.set(pos);
            pos = value.indexOf("1", pos+1);
         }
         this.bs = bs;
      }
   }

   public void writeSQL(SQLOutput stream) throws SQLException {
      StringBuffer value = new StringBuffer();
      for (int k=0; k<bs.length(); k++)
         value.append(bs.get(k)==true ? '1' : '0');
      while (value.length()<800)
         value.append('0');
      stream.writeString("B'" + value.toString() + "'");
   }
}

First thing: PGBitSet seems to be not used... but why??? Documentation for PreparedStatement.setObject() says: "The given Java object will be converted to the given targetSqlType before being sent to the database. If the object has a custom mapping (is of a class implementing the interface SQLData), the JDBC driver should call the method SQLData.writeSQL to write it to the SQL data stream.".

Second thing: I got Exceptions when I try to add or find something by the code field (every operation for which SQL query contains condition for code field).
Code:
ERROR puszka.web.action.ExceptionHandler- ERROR: could not execute query
[...]
Caused by: org.postgresql.util.PSQLException: Cannot cast an instance of puszka.dao.userType.PGBitSet to type Types.OTHER
   at org.postgresql.jdbc2.AbstractJdbc2Statement.setObject(AbstractJdbc2Statement.java:1677)
   at org.postgresql.jdbc3.AbstractJdbc3Statement.setObject(AbstractJdbc3Statement.java:1435)
   at org.postgresql.jdbc2.AbstractJdbc2Statement.setObject(AbstractJdbc2Statement.java:1686)
   at org.apache.tomcat.dbcp.dbcp.DelegatingPreparedStatement.setObject(DelegatingPreparedStatement.java:162)
   at puszka.dao.userType.PGBitStringUserType.nullSafeSet(PGBitStringUserType.java:111)
   at org.hibernate.type.CustomType.nullSafeSet(CustomType.java:156)
   at org.hibernate.param.NamedParameterSpecification.bind(NamedParameterSpecification.java:38)
   at org.hibernate.loader.hql.QueryLoader.bindParameterValues(QueryLoader.java:491)
   at org.hibernate.loader.Loader.prepareQueryStatement(Loader.java:1563)
   at org.hibernate.loader.Loader.doQuery(Loader.java:673)
   at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
   at org.hibernate.loader.Loader.doList(Loader.java:2213)
   ... 35 more

PGBitStringUserType.java:111 is:
statement.setObject(i, PGbs, java.sql.Types.OTHER);

By the way: searching by id works fine - it's because query doesn't contain code field (and nullSafeSet() method isn't called).

I can ommit problems for find method - changing findByCode() method in my DAO class is good enough:
Code:
   public FooVO findByCode(BitSet bs) {
      Query q;
      if (bs==null) {
         q = getSession().createQuery("from FooVO where code = null");
      } else {
         StringBuffer value = new StringBuffer();
         for (int k=0; k<bs.length(); k++)
            value.append(bs.get(k)==true ? '1' : '0');
         while (value.length()<800)
            value.append('0');
         return findSingleResultByCriteria(Expression.sql("code=B'" + value.toString() + "'"));
      }
      return (IngredientVO)q.uniqueResult();
   }

And as far as code field may be generated on database side it's not even necessary to map it in VO class at all. I don't like this solutions but it suits my needs. Nevertheless I would prefer to do it right. Can you help me?

And now one more explanation: why Bit(n)?

Well, my goal is to use PostgreSQL BIT(n) to represent some object which is composed of elements. I have already about 500 elements and I estimate that max.number of elements will not be greater than 800. I also need bit operations to compare my objects, to find objects with specified elements etc. That's why Bit(n) seems to fulfill my needs.

Each object is composed of 2 to 30 elements, so I also have considered to use arrays. But I have found that mapping arrays isn't easy as well :-/

Any other suggestions? Or maybe someone have managed to map arrays? I would appreciate ANY HELP. Thanks in advance.


Top
 Profile  
 
 Post subject: Please help!
PostPosted: Fri May 16, 2008 11:49 am 
Newbie

Joined: Fri May 16, 2008 11:28 am
Posts: 1
Dear Sir / Madam,

I just met the same problem as you. Do you have any solution now? Could you please let me know how you handle the situation now?

Your help will be greatly appreciated.

Thanks & Best Regards,
Xu Fang


Top
 Profile  
 
 Post subject: I've got Custom UserTypes to work with postgres
PostPosted: Wed Jul 16, 2008 4:55 pm 
Newbie

Joined: Wed Jul 16, 2008 12:31 pm
Posts: 1
I traced the postgresql code to find why SQLData wasnt getting translated and I discovered it would only fall back onto a PGobject if it didn't have a matching type., or else generate the error:

Quote:
ERROR puszka.web.action.ExceptionHandler- ERROR: could not execute query
[...]
Caused by: org.postgresql.util.PSQLException: Cannot cast an instance of puszka.dao.userType.PGBitSet to type Types.OTHER


Change your PGBitSet to extend PGobject and call setType("bit("+n+")") in the constructor. I was using macaddr, so I don't know if the parameterised type needs anymore tweaking or not.


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