-->
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.  [ 17 posts ]  Go to page 1, 2  Next
Author Message
 Post subject: Sequence/identity generation on non primary key columns
PostPosted: Thu Mar 01, 2007 2:09 pm 
Newbie

Joined: Thu Mar 01, 2007 1:56 pm
Posts: 11
Hibernate version: 3.2

Name and version of the database: Oracle 9i (9.2.0.4)

Hi,

I am new to hibernate and I am trying to create hibernate mapping files for our database tables.

Table definition follows :

Table Name : Customer

Columns :

CUSTOMER_ID GUID Autogenerated Primary Key 32 Chars
CUSTOMER_IID Unique NOT NULL Number(20) Uses Oracle Sequence to autogenerate the id and is not part of the primary key
CUSTOMER_NAME
CUSTOMER_ADDRESS

It seems like I cannot use the autogenerate attribute on any other property but inside <id> element.

In my case my primary key which maps to <id> element is the CUSTOMER_ID column which is set to autogenerate as UUID

How do I make hibernate aware of the second column "CUSTOMER_IID" which is using oracle sequence to generate the id ?

Any help would be highly appreciated!!!


Last edited by manojpn on Wed Mar 14, 2007 11:54 am, edited 2 times in total.

Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 01, 2007 9:27 pm 
Newbie

Joined: Thu Mar 01, 2007 6:58 pm
Posts: 9
You could add a custom <sql-insert> to your mapping file to insert your object correctly or you could create a custom type that loads the next value from the sequence to input the value.


Top
 Profile  
 
 Post subject: Thanks
PostPosted: Fri Mar 02, 2007 9:34 am 
Newbie

Joined: Thu Mar 01, 2007 1:56 pm
Posts: 11
Thanks for the input, appreciate your help.


Top
 Profile  
 
 Post subject: Need help with Custom User Type
PostPosted: Tue Mar 06, 2007 4:06 pm 
Newbie

Joined: Thu Mar 01, 2007 1:56 pm
Posts: 11
I have created a customer user type for resolving this issue. The user type selects the next sequence number from the database and sets it into the statement. The database row gets inserted as needed with the right sequence number. But when I look at the attrbutes of the customer object on the java side after the transaction commit, the sequence attribute does not have the right value in it. Do I need to so something special to get the generated sequence synched up with the actual java object after the insert ?

Hibernate Mapping File :

<hibernate-mapping>
<class name="com.wf.model.vo.MPCustomerVO" table="MP_CUSTOMER">

<id name="UUID" type="string">
<column name="CUSTOMER_ID" length="32" />
<generator class="uuid" />
</id>

<timestamp name="lastUpdated" column="LAST_UPDATED" generated="never"/>

<property name="identity" column="CUSTOMER_IID" update="false" insert="true" unique="true" not-null="true">
<type name="com.wf.dao.hibernate.OracleSequenceGeneratorType">

<param name="sequence-name">MP_CUSTOMER_SEQ</param>
</type>
</property>

</hibernate-mapping>

OracleSequenceGeneratorType class code :

package com.wf.dao.hibernate;

import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;


public class OracleSequenceGeneratorType implements UserType, ParameterizedType, Serializable {

Long defaultValue = new Long(0);
private String sequenceName;

Logger _log = Logger.getLogger(OracleSequenceGeneratorType.class);

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


public Object deepCopy(Object value) throws HibernateException {
return new Long(((Long)value).longValue());
}

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

public boolean equals(Object x, Object y) throws HibernateException {
if (x==y) return true;
if (x==null || y==null) return false;
return x.equals(y);
}

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

public boolean isMutable() {
return false;
}

public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
Long result = new Long(rs.getLong(names[0]));

return result == null ? null : new Long(result.longValue());
}


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

Connection conn = st.getConnection();
Statement sta;
ResultSet rs;

String sql = "select " + sequenceName + ".nextval from dual";

sta = conn.createStatement();
rs = sta.executeQuery(sql);
rs.next();

long nextSequence = rs.getLong(1);

rs.close();
sta.close();

_log.error("OracleSequenceGeneratorType.nullSafeSet: binding passed in variable : [" + value + "] at index [" + index + "]");

_log.error("OracleSequenceGeneratorType.nullSafeSet: Retrieved Sequence Value = [" + nextSequence + "]");

st.setLong(index, nextSequence);


}

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

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

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

public void setParameterValues(Properties parameters) {
sequenceName= (String) parameters.get("sequence-name");
_log.error("Recevied Parameter Name : 'sequence-name' = [" + sequenceName + "]");
}

}

Test Code which fails to get the sequence attribute on java side after the transaction commit :

Session session = HibernateUtil.getSessionFactory().getCurrentSession();
Assert.assertNotNull("Expected hibernate session to be not null", session);

Transaction tx = session.beginTransaction();
Assert.assertTrue("Expected hibernate transaction to be active", tx.isActive());

MPCustomerDAO dao = new MPCustomerDAO();

MPCustomerVO vo = new MPCustomerVO ("Test");
vo.setCustomerName("TestName");
vo.setCustomerAddress1("Address1");
vo.setCustomerAddress2("Address2");
dao.persist(vo);

tx.commit();

_log.error("CustomerVO.uuid = [" + vo.getUUID() + "]");
_log.error("CustomerVO.identity = [" + vo.getIdentity() + "]"); <<<<<<< Fails : Does not have the correct sequence number >>>>
_log.error("CustomerVO.address1 = [" + vo.getCustomerAddress1() + "]");
_log.error("CustomerVO.address1 = [" + vo.getCustomerAddress2() + "]");
_log.error("CustomerVO.name = [" + vo.getCustomerName() + "]");


Last edited by manojpn on Thu Mar 08, 2007 4:13 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 06, 2007 8:48 pm 
Regular
Regular

Joined: Thu Jul 29, 2004 11:55 pm
Posts: 75
It has been a while since I wrote a custom user type and I don't have my books with me, but I think you need to set the value on your object. You are just setting it as a param for the prepared statement.

From an excerpt from the MEAP of Java persistence with Hibernate:

"The isMutable() method tells Hibernate that this type is immutable.
¹ The UserType is also partially responsible for creating a "snapshot" of a value in the first place. In the case of a mutable type, it would need to return a copy of the argument to be used as the snapshot value."

I think you need to make your type mutable isMutable() -> true.

If this doesn't work (have never tried that before), you can probably solve this by calling refresh() after your commit, but a cleaner way would be to set it when you retrieve the value.


Top
 Profile  
 
 Post subject:
PostPosted: Thu Mar 08, 2007 12:25 pm 
Newbie

Joined: Thu Mar 01, 2007 1:56 pm
Posts: 11
Setting the isMutable to true did not work. Where can I set the value of the sequence into the object ? I do not get reference to the actual object in any of the functions.

Am I on the wrong track with the Custom User type ? Is it even possible to set the generated sequence back into the java object ?

Somebody please help! I need to resolve this issue to move forward with the implementation.

Thanks!!!


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 09, 2007 9:28 am 
Newbie

Joined: Thu Mar 01, 2007 1:56 pm
Posts: 11
Can somebody please help out with this issue ?


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 09, 2007 2:55 pm 
Regular
Regular

Joined: Thu Jul 29, 2004 11:55 pm
Posts: 75
Can you email my to take this offline to try a few things?

chuckc at intava dot com


Top
 Profile  
 
 Post subject: Issue Resolved
PostPosted: Wed Mar 14, 2007 11:11 am 
Newbie

Joined: Thu Mar 01, 2007 1:56 pm
Posts: 11
Thanks to ccanning, this issue has been resolved.

Attached below is the code and the mapping file to achieve this :

Oracle Sequence Generator Class

Code:
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.Properties;

import org.apache.log4j.Logger;
import org.hibernate.HibernateException;
import org.hibernate.usertype.ParameterizedType;
import org.hibernate.usertype.UserType;

import com.hibernate.model.vo.OracleSequence;


public class OracleSequenceGeneratorType implements UserType, ParameterizedType, Serializable {
    private String sequenceName;
    Logger _log = Logger.getLogger(OracleSequenceGeneratorType.class);

    public Object assemble(Serializable cached, Object owner) throws HibernateException {
        _log.error("OracleSequenceGeneratorType.assemble: Object cached [" + cached.getClass().getName() + "]" );
        _log.error("OracleSequenceGeneratorType.assemble: Object owner [" + owner.getClass().getName() + "]" );

        return cached;
    }

    public Object deepCopy(Object value) throws HibernateException {
        _log.error("OracleSequenceGeneratorType.deepCopy: Object [" + value.getClass().getName() + "]" );

        return value;
    }

    public Serializable disassemble(Object value) throws HibernateException {

        _log.error("OracleSequenceGeneratorType.disassemble: Object Type [" + value.getClass().getName() + "]" );

        return (Serializable) value;
    }

    public boolean equals(Object x, Object y) throws HibernateException {
        _log.error("OracleSequenceGeneratorType.equals: Object [" + x.getClass().getName() + "]" );
        if (x==y) return true;
        if (x==null || y==null) return false;
        return x.equals(y);
    }

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

    public boolean isMutable() {
        return false;
    }

    public Object nullSafeGet(ResultSet rs, String[] names, Object owner) throws HibernateException, SQLException {
        _log.error("OracleSequenceGeneratorType.nullSafeGet" );

        OracleSequence result = new OracleSequence();
        result.setSequence(new Long(rs.getLong(names[0])));

        return result;
    }


    public void nullSafeSet(PreparedStatement st, Object value, int index) throws HibernateException, SQLException {
        _log.error("OracleSequenceGeneratorType.nullSafeSet" );
        OracleSequence sequence;
        Long nextValue;

        _log.error("OracleSequenceGeneratorType.nullSafeSet: binding passed in variable : [" + value + "] at index [" + index + "]");

        //cast the value to the oracle sequence (should not be null since constructed in owning object
        sequence = (OracleSequence)value;
        //check to see if we have already set the sequence (could be update, etc... also use this to generate new sequence
        if (sequence.getSequence() == null) {
            //get the next sequence value
            nextValue = getNextValue(st);
            //set it on the internal/owning sequence object
            sequence.setSequence(nextValue);
        }

        //set the value on the prepared statement
        st.setLong(index, sequence.getSequence().longValue());
        _log.error("OracleSequenceGeneratorType.nullSafeSet: Retrieved Sequence Value = [" + sequence.getSequence() + "]");
    }

    private Long getNextValue(PreparedStatement st) throws SQLException {
        Connection conn = st.getConnection();
        Statement sta;
        ResultSet rs;
        Long nextValue;

        String sql = "select " + sequenceName + ".nextval from dual";

        sta = conn.createStatement();
        rs = sta.executeQuery(sql);
        rs.next();

        nextValue = new Long(rs.getLong(1));

        rs.close();
        sta.close();

        return nextValue;

    }

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

        _log.error("OracleSequenceGeneratorType.replace: Object Type Original [" + original.getClass().getName() + "]" );
        _log.error("OracleSequenceGeneratorType.replace: Object Type target [" + target.getClass().getName() + "]" );
        _log.error("OracleSequenceGeneratorType.replace: Object Type owner [" + owner.getClass().getName() + "]" );

        return original;
    }

    public Class returnedClass() {
        _log.error("OracleSequenceGeneratorType.returnedClass" );
        return OracleSequence.class;
    }

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

    public void setParameterValues(Properties parameters) {
        sequenceName= (String) parameters.get("sequence-name");
        _log.error("Recevied Parameter Name : 'sequence-name' = [" + sequenceName + "]");
    }
}



Oracle Sequence Class :



Code:

public class OracleSequence {
    Long sequence;

   public Long getSequence() {
      return sequence;
   }

   public void setSequence(Long sequence) {
      this.sequence = sequence;
   }

}



Mapping File for Customer table :

Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<!-- Generated Feb 22, 2007 5:06:53 PM by Hibernate Tools 3.2.0.beta8 -->
<hibernate-mapping>
   <class name="com.model.vo.MPCustomerVO" table="MP_CUSTOMER">

      <id name="UUID" type="string">
         <column name="CUSTOMER_ID" length="32" />
         <generator class="uuid" />
      </id>

      <timestamp name="lastUpdated" column="LAST_UPDATED" generated="never"/>

      <property name="identity" column="CUSTOMER_IID" update="false" insert="true" unique="true" not-null="true">
      
         <type name="com.dao.hibernate.OracleSequenceGeneratorType">
            <param name="sequence-name">MP_CUSTOMER_SEQ</param>
         </type>
      </property>

      <property name="customerName" type="string">
         <column name="CUSTOMER_NAME" length="200" />
      </property>

      <property name="customerAddress1" type="string">
         <column name="CUSTOMER_ADDRESS1" length="200" />
      </property>

      <property name="customerAddress2" type="string">
         <column name="CUSTOMER_ADDRESS2" length="200" />
      </property>

      <!-- set name="orderList" inverse="true" cascade="all">
   </class>
</hibernate-mapping>



Top
 Profile  
 
 Post subject: SQL Server Implemantation
PostPosted: Wed Mar 14, 2007 11:35 am 
Newbie

Joined: Thu Mar 01, 2007 1:56 pm
Posts: 11
This solution does not work for MS SQL Server where the CUSTOMER_ID column is an identity column.

A good solution for this would be to have the <generator> tag enabled for non primary key columns in a hibernate mapping file.

I have submitted a feature request on Hibernate JIRA for the same.

If you need this feature implemented, please vote for this issue

http://opensource.atlassian.com/projects/hibernate/browse/HHH-2492


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 23, 2007 12:06 pm 
Newbie

Joined: Fri Mar 23, 2007 12:00 pm
Posts: 6
I would truly appreciate if you could share at what point in code does the OracleSequenceGeneratorType gets called ? I am not much converse with hibernate hence would also like to know which method of this class gets called.

Thanks
Vins


Top
 Profile  
 
 Post subject:
PostPosted: Fri Mar 23, 2007 2:23 pm 
Newbie

Joined: Fri Mar 23, 2007 12:00 pm
Posts: 6
In Simple words can you share the code to use the OracleSequence as well please? Or do point me to some resource which would help me understand how the User Defined Type are used in hibernate.


Top
 Profile  
 
 Post subject:
PostPosted: Sun Mar 25, 2007 9:16 pm 
Newbie

Joined: Thu Mar 01, 2007 1:56 pm
Posts: 11
Please look at the mapping file posted above for the customer table. You can see that the type name= used the Oracle sequence generator class. This is called internally by hibernate when it is about to persist the data into the database. If you look at the method names inside the OracleSequenceGeneator they are self explanatory. Most important ones are the nullSafeGet and nullSafeSet. They get data from the result set on a read and set value into the result set on a persist. Read hibernate documentation on User Types for more info.


<property name="identity" column="CUSTOMER_IID" update="false" insert="true" unique="true" not-null="true">

<type name="com.dao.hibernate.OracleSequenceGeneratorType">
<param name="sequence-name">MP_CUSTOMER_SEQ</param>
</type>
</property> [/u]


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 27, 2007 10:36 am 
Newbie

Joined: Fri Mar 23, 2007 12:00 pm
Posts: 6
Code:
Cheers!!
I was able to use the class.

Now the question.

*Consider a Table
rowId      businessId Name       Version
---------- ---------- ---------- ----------
1          1          Int        0

*Requirement
1.
-rowid is the primary key (uses sequence1)
-businessId is the Unique Business Key (but not unique to the table and uses sequence2)
>> im using OracleSequence for this id
-version has either 0 or 1; 0 is current version 1 is the olderversion
2.
on update
- create a new record with same businessId new rowId and version 0
- update the prev record with version  1.

*Result
rowId      businessId Name       Version
---------- ---------- ---------- ----------
1          1          Int        1
2          1          IntModify  0

*Question
OracleSequence class uses a new sequence number for every insert.
Can I command this class to generate sequence only for some insert and not all?


Top
 Profile  
 
 Post subject:
PostPosted: Tue Mar 27, 2007 10:39 am 
Newbie

Joined: Fri Mar 23, 2007 12:00 pm
Posts: 6
Code:
*Result with OracleSequence Currently
rowId      businessId Name       Version
---------- ---------- ---------- ----------
1          1          Int        1
2          2          IntModify  0


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