-->
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.  [ 4 posts ] 
Author Message
 Post subject: Custom ID generator with separate transaction and datasource
PostPosted: Mon Jul 09, 2007 2:19 am 
Newbie

Joined: Mon Jul 09, 2007 12:53 am
Posts: 3
Hi there, due to our legacy database, we needed to create our own id generator to use a stored procedure that updates a row from a sequence number table.

We wanted this generator to have its own datasource and start its own transaction:

Code:
begin tran

execute stored procedure:
    select next id from sequence number table
    increment next id field in sequence number table

commit tran


We've run into cases before where our application takes too long to process requests that any other requests to this sequence number table is blocked because of it. We've previously solved it by creating our own id allocator class, but this time we wanted to try to use Hibernate's generator tag and specify our own class in the mapping file.

Does anyone know if the generator tag for the HBM file has attributes or any way of passing in a bean definition or parameters to set up the separate transaction and datasource ? (see payment.hbm.xml below).

I've attached the code chunks below for our ID generator.

We currently use the SingleConnectionDataSource datasource for this generator which isn't correct either as it's meant to only be used in testing :)

Hibernate version: 3.2.1
Spring version: 2.0
Name and version of the database you are using: Sybase ASE 12.5.3

Mapping documents: Payment.hbm.xml

Code:
...
<id name="productionRunId" column="production_run_id" unsaved-value="0" length="19">
    <generator class="com.application.domainpersist.StoredProcedureGenerator">
        <param name="sequenceId">payment_id</param>
    </generator>
</id>
...



Generator code: StoredProcedureGenerator

Code:

package com.application.domain.persist;

import java.io.Serializable;
import java.util.Properties;

import org.hibernate.HibernateException;
import org.hibernate.MappingException;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.SessionImplementor;
import org.hibernate.id.Configurable;
import org.hibernate.id.IdentifierGenerator;
import org.hibernate.type.Type;
import org.springframework.jdbc.datasource.SingleConnectionDataSource;


public class StoredProcedureGenerator implements IdentifierGenerator, Configurable {

    protected static final String SEQUENCE_ID_PARAM = "sequenceId";
    private Integer _sequenceId;

    public Serializable generate(SessionImplementor session, Object object) throws HibernateException {
        SequenceStoredProcedure storedProcedure = new SequenceStoredProcedure(
                _sequenceId,
                new SingleConnectionDataSource(session.connection(), true));

        return storedProcedure.getNextSequenceNumber();
    }

    public void configure(Type type, Properties params, Dialect dialect) throws MappingException {
        _sequenceId = (Integer) params.get(SEQUENCE_ID_PARAM);
        if (_sequenceId == null) {
            throw new MappingException("Missing required " + SEQUENCE_ID_PARAM + " parameter");
        }

        Class returnedClass = type.getReturnedClass();
        if (!Integer.class.equals(returnedClass)) {
            throw new MappingException("Cannot currently generate identifiers of type: " + returnedClass);
        }
    }
}




Generator code: SequenceStoredProcedure

Code:

package com.application.domain.persist;

import java.io.Serializable;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

import javax.sql.DataSource;

import org.springframework.jdbc.core.SqlOutParameter;
import org.springframework.jdbc.core.SqlParameter;
import org.springframework.jdbc.object.StoredProcedure;

public class SequenceStoredProcedure extends StoredProcedure {

    private static final String GENERATE_SEQUENCE_STORED_PROC = "st_increment_sequence_number";
    private static final String SEQUENCE_ID_PARAM = "p_sequence_id";
    private static final String SEQUENCE_NUMBER_RANGE_PARAM = "p_sequence_num_range";
   
    private static final String SEQUENCE_NUMBER_START = "p_sequence_number_start";
    private static final String SEQUENCE_NUMBER_END = "p_sequence_number_end";
   
    private Integer _sequenceId;

    public SequenceStoredProcedure(Integer sequenceId, DataSource datasource) {
        super(datasource, GENERATE_SEQUENCE_STORED_PROC);
        _sequenceId = sequenceId;
       
        declareParameter(new SqlParameter(SEQUENCE_ID_PARAM, Types.INTEGER));
        declareParameter(new SqlParameter(SEQUENCE_NUMBER_RANGE_PARAM, Types.INTEGER));
       
        declareParameter(new SqlOutParameter(SEQUENCE_NUMBER_START, Types.INTEGER));
        declareParameter(new SqlOutParameter(SEQUENCE_NUMBER_END, Types.INTEGER));
    }

    public Serializable getNextSequenceNumber() {
        ArrayList<Integer> sequenceNumbers = (ArrayList<Integer>) getNextRangeOfSequenceNumbers(1);
        return new Integer(sequenceNumbers.get(1));
    }

    public Serializable getNextRangeOfSequenceNumbers(Integer range) {
        Map<String, Integer> inParams = new HashMap<String, Integer>();
        inParams.put(SEQUENCE_ID_PARAM, _sequenceId);
        inParams.put(SEQUENCE_NUMBER_RANGE_PARAM, range);
       
        Map resultSet = execute(inParams);
        Integer sequenceNumberStart = (Integer) resultSet.get(SEQUENCE_NUMBER_START);
        Integer sequenceNumberEnd = (Integer) resultSet.get(SEQUENCE_NUMBER_END);
        ArrayList<Integer> numbers = new ArrayList<Integer>();
        numbers.add(new Integer(sequenceNumberStart));
        numbers.add(new Integer(sequenceNumberEnd));
        return numbers;
    }
}



[/code]


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 09, 2007 5:43 am 
Senior
Senior

Joined: Thu May 17, 2007 2:31 am
Posts: 194
Location: Sri Lanka
Hi

Why don't you use
Code:
SingleConnectionDataSource(String driverClassName, String url, String username, String password, boolean suppressClose)


instead of
Code:
new SingleConnectionDataSource(session.connection(), true)


Amila

(Don't forget to rate if helps)


Top
 Profile  
 
 Post subject:
PostPosted: Mon Jul 09, 2007 9:31 pm 
Newbie

Joined: Mon Jul 09, 2007 12:53 am
Posts: 3
Thanks for replying Amila

I thought that SingleConnectionDataSource isn't meant to be used in production code given that it never closes connections and isn't thread-safe ?


Top
 Profile  
 
 Post subject: Solution..
PostPosted: Fri Jul 20, 2007 3:32 am 
Newbie

Joined: Mon Jul 09, 2007 12:53 am
Posts: 3
I'm posting our solution just in case someone needed this information afterwards..

We eventually went with using the same SessionFactory and DataSource that we had defined originally.

Then set up an interceptor that had the PROPAGATION_REQUIRES_NEW attribute.

Then we used the ProxyFactoryBean to add this interceptor to our interface class.

The class itself then used SessionFactoryUtils.getDataSource(_sessionFactory) to get the datasource and pass this onto our stored procedure.

Code snippets pasted below:
Code:
persistence.xml:
    <bean name="sequenceIdGenerator" class="com.processor.SequenceGeneratorImpl" autowire="no">
        <constructor-arg type="org.hibernate.SessionFactory" ref="sessionFactory" />
    </bean>
   
    <bean id="sequenceHibernateInterceptor" class="org.springframework.transaction.interceptor.TransactionInterceptor">
        <property name="transactionManager" ref="transactionManager"/>
        <property name="transactionAttributes">
            <props>
                <prop key="*">PROPAGATION_REQUIRES_NEW,ISOLATION_REPEATABLE_READ,timeout_0300,-Exception</prop>
            </props>
        </property>
    </bean>


    <bean id="sequenceProcessor" class="org.springframework.aop.framework.ProxyFactoryBean" >
       <property name="interceptorNames">
            <list>
               <value>sequenceHibernateInterceptor</value>
            </list>
        </property>
        <property name="proxyInterfaces">
            <value>com.processor.SequenceGenerator</value>
        </property>
        <property name="target">
            <ref bean="sequenceIdGenerator" />
        </property>
    </bean>



Code:
Java class
public class SequenceGeneratorImpl implements SequenceGenerator {


    private SessionFactory _sessionFactory;

    public SequenceGeneratorImpl(SessionFactory sessionFactory) {
        _sessionFactory = sessionFactory;
    }
    private Integer getIntegerId(Integer sequenceId) {
        DataSource datasource = SessionFactoryUtils.getDataSource(_sessionFactory);
        SequenceStoredProcedure storedProcedure = new SequenceStoredProcedure(sequenceId, datasource);

        Integer id = (Integer) storedProcedure.getNextSequenceNumber();
        log(id, sequenceId);
        return id;
    }
}



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