Results 1 to 10 of 10

Thread: I Want Instance-level not Method-level Transactions

  1. #1
    Join Date
    Aug 2006
    Posts
    29

    Default I Want Instance-level not Method-level Transactions

    I'm using Spring with Hibernate with an application-ctx of:
    Code:
    <?xml version="1.0" ?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:aop="http://www.springframework.org/schema/aop"
           xmlns:tx="http://www.springframework.org/schema/tx"
           xsi:schemaLocation="
           http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd
           http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd">
    
        <bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
             <property name="driverClassName" value="org.hsqldb.jdbcDriver" />
             <property name="url" value="jdbc:hsqldb:mem:testdb" />
             <property name="username" value="sa" />
             <property name="password" value="" />
        </bean>
              
        <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean">
            <property name="dataSource" ref="dataSource" />
            <property name="hibernateProperties">
                <props>
                    <prop key="hibernate.dialect">org.hibernate.dialect.HSQLDialect</prop>
                    <prop key="hibernate.hbm2ddl.auto">update</prop>
                    <prop key="hibernate.current_session_context_class">thread</prop>
                    <prop key="hibernate.transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</prop>                 
                </props>
            </property>
            <property name="annotatedClasses">
                <list>
                    <value>com.pd.springHibernate.domain.Person</value>
                </list>
            </property>
        </bean>
    
        <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
            <property name="sessionFactory" ref="sessionFactory"/>
        </bean>
        
        <tx:annotation-driven/>  
            
        <bean id="personDao" class="com.pd.springHibernate.dao.GenericDaoHibernateImpl">
             <constructor-arg>
                 <value>com.pd.springHibernate.domain.Person</value>
             </constructor-arg>
             <property name="sessionFactory" ref="sessionFactory"/>
        </bean>
    
    </beans>
    The GenericDaoHibernateImpl implements a GenericDao interface that looks like this:
    Code:
    @Transactional
    public interface GenericDao< T, PK extends Serializable >
    {
        PK create( T newInstance );
        
        T read( PK id );
    
        void update( T transientObject );
    
        void delete( T persistentObject );
        
        void longRunningUpdate( T transientObject );
    }
    The longRunningUpdate method sleeps for a couple seconds on either side of a SessionFactory.getCurrentSession().update( Object ). So with it, I can test the transactional nature of the DAO.

    What I'd expect:
    1) I have a thread call longRunningUpdate.
    2) While step one is running (it takes several seconds to complete), I have another thread call update - a different method that does exactly the same thing as
    longRunningUpdate but has no sleep delays.
    3) I'd expect the 2nd thread should either block until the 1st thread finishes or possibly throw an exception since it can't immediately complete.

    BUT WHAT ACTUALLY HAPPENS: the 2nd thread happily updates/persists the object - even though
    longRunningUpdate should be blocking it since it started before the 2nd thread and still hasn't finished yet.

    Now, if instead of calling update the 2nd thread calls longRunningUpdate just like the 1st thread does, then it does wait until the 1st thread finishes before doing its work.

    So it seems the locking I have is only when two threads call the same method - not the same DAO.

    Is there a way (using Hibernate and no J2EE infrastructure) to get such coarser grained transactional behavior?

    Thanks!
    Sean

  2. #2
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    Hello,

    I'm sorry to say that your question consists of chain of mistakes/misunderstandings. I try to clarify them below
    (with some simplification or it would be not mail, but book ).

    First of all, locking has nothig to do with transaction - they are (almost) orthogonal concepts. Almost - because, as a rule, ending of transaction releases locks (and even this is not true for some DBs and lock types).

    But transaction start does not aquire locks. So, while longRunningUpdate is sleeping before updateobject) it helds no locks and second transaction is free to do whatever it want.

    Second, you really do not know which thread starts first - JVM provides absolutely no warranty on this matter. It is free to pick-up ready threads for execution in any moment and any order.

    Third, Hibernate, by default, use optimistic locking (or, more correctly, version-based optimistic concurrency control), which means that there is no real DB-locking till session flush (which, as far as I understand, in your case occurs on commit only, re-read parts 10.10, 11.3 and 11.4 of Hibernate reference guide for details). To put it short with optimistic control none of the threads (or processes) has to wait it works as other concurrents do not exist and only at time of persisting the change fails if the object has been modified by somebody else.

    So, your anticipation are just wrong.

    Regards,

    Oleksandr Alesinskyy

  3. #3
    Join Date
    Aug 2006
    Posts
    29

    Default

    Thank you for the reply, Oleksandr!

    I realize now that "locking" was a bad term to use. I was referring to the C and I parts of ACID. And it seems that - in some way, at least - spring/hibernate is doing what I'd like: multiple calls to the same method are "serialized". I just want this behavior to expand to all methods that are doing "write" work on a particular persisted object. I want "reads" to proceed unimpeded (knowing that they may be dirty).

    I suppose I could force my desired behavior by creating a single method through which the C, U, and D parts of CRUD would access the db (leaving the READ free to work independently). But this seems silly. I thought the idea behind Spring-managed transactions was better control over the scope. Otherwise, I could just get rid of Spring entirely and just use Hibernate's built-in abilities.


    Thanks,
    Sean


    NOTE: I recognize that one can't force thread order in the JVM. That's why I'm using such long "sleeps" in the longRunningUpdate method. It's designed to make sure other threads get kicked off in the right order. I do it like this:

    (All of these steps are performed in the main thread)
    1) Start a thread to run
    longRunningUpdate
    2) sleep for 500 ms
    3) start a thread to run
    update

  4. #4
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    Hello,

    may, you, possibly, explain what you whant to achive (not how, but what) and for which purpose? Try to explain it in simple words, rather business goals then technical details.

    Normally, there is no need to serialize operation, appropriate level of concurrency control can be achieved without it.

    Concerning transactions in Spring - Spring goals in this area (as far as I understand it) are not related to ACID as such (this is achived by underlying transaction managers) but to conversion of transaction management to a crosscuting concern alleviating business code from it and to hide differences between various transaction managers from application code.

    ACID does not assume transaction serialization (in any real-world application it is just suicidal), but to "emulate" such behavior without serialization. Different levels of such emulations are represented by different "transaction isolation levels" (and not all of them are equally usable).

    BTW, if you can afford operation serialization, you likely do not anything above "A",
    all other ACID letters have sense in conccurent environment only.

    Once more, BTW, ACID article on wikipedia is far from ideal, while it is accurate im main definitions some database-related stuff is misleading (about locks and so on).

    Have you read Hibernate reference manual chapters devoted to concurrency control?

    Regards,
    Oleksandr

    Quote Originally Posted by scrotty
    Thank you for the reply, Oleksandr!

    I realize now that "locking" was a bad term to use. I was referring to the C and I parts of ACID. And it seems that - in some way, at least - spring/hibernate is doing what I'd like: multiple calls to the same method are "serialized". I just want this behavior to expand to all methods that are doing "write" work on a particular persisted object. I want "reads" to proceed unimpeded (knowing that they may be dirty).

    I suppose I could force my desired behavior by creating a single method through which the C, U, and D parts of CRUD would access the db (leaving the READ free to work independently). But this seems silly. I thought the idea behind Spring-managed transactions was better control over the scope. Otherwise, I could just get rid of Spring entirely and just use Hibernate's built-in abilities.


    Thanks,
    Sean


    NOTE: I recognize that one can't force thread order in the JVM. That's why I'm using such long "sleeps" in the longRunningUpdate method. It's designed to make sure other threads get kicked off in the right order. I do it like this:

    (All of these steps are performed in the main thread)
    1) Start a thread to run
    longRunningUpdate
    2) sleep for 500 ms
    3) start a thread to run
    update

  5. #5
    Join Date
    Aug 2006
    Posts
    29

    Default

    may, you, possibly, explain what you whant to achive (not how, but what) and for which purpose? Try to explain it in simple words, rather business goals then technical details.
    Sure. I want to create a system where READs on persisted objects can occur at any time (even if the data is slightly stale), but WRITEs on a particular persisted object must be serialized. That serialization may be blocking or exception based (e.g. an exception is thrown if access to the object for write is denied because another process/thread currently "holds" the object).

    By way of example:
    1) Say a db table called PERSON defines rows/objects with fields: FIRSTNAME, LASTNAME, and TITLE.
    2) The PERSON table contains two rows: { FRED, FLINTSTONE, MR } and { WILMA, FLINTSTONE, MRS }
    3) A thread wants to modify/update the TITLE of WILMA to MS
    4) Another thread wants to modify/update the same row/object to change FIRSTNAME from WILMA to WILLAMINA.
    5) A third thread wants to update the FIRSTNAME of row/object FRED to FREDERICK.

    6) The first thread persists its change { WILMA, FLINTSTONE, MS } to the backend datastore, but it isn't done with other application-specific work it feels needs to be atomic with respect to the update of this row/object. In other words, it doesn't want anyone else to update this row/object in the datastore just yet - although it wants anyone to be able to read it.
    7) The second thread wants to update WILMA to WILLAMINA, but it was beaten to the row/object and is happy to wait until the first thread finishes all application-specific (not just datastore) work on the row/object
    8) The third thread wants to update FRED and has no problem because, even though the first thread is writing to the same table, it's working on a different row/object. Thus, the third thread can update without delay.
    9) The first thread completes its application specific work and is ready to allow any other process CUD (of CRUD) access to the row/object.
    10) The second thread can now update to WILLAMINA.

    The upshot is that I would like to be able to deny CUD access to a row/object longer than it takes just to do the backend store work. I thought that Spring would allow me via its transaction support. In other words, I'd like Spring to say, "All CUD actions on this DAO serialized for the duration of any write methods, READs are allowed at any time."

    I was expecting to be able to do something like this (annotation-driven notation):

    Code:
    public class DAO
    {
    @Transactional(readonly=false) public void update( object ) { // While in this method, no other (readonly=false) methods // can proceed } @Transactional(readonly=false) public void delete( object ) { // While in this method, no other (readonly=false) methods // can proceed } @Transactional(readonly=true) public object read( key ) { // This method can proceed regardless of other concurrent // (readonly=false) activity }
    }
    Have you read Hibernate reference manual chapters devoted to concurrency control?
    Yes, and found that control as I describe above is not possible via Hibernate alone. That's why I'm working with Spring. I got the impression that Spring could do what I'm asking for above. Am I wrong on this?

    Thanks!
    Sean

  6. #6
    Join Date
    Sep 2004
    Posts
    1,086

    Default

    I think it's wrong to think about database issues in terms of Spring or Hibernate. Hibernate and Spring do not control concurrency at all. They both use standard database concurrency mechanisms to achieve certain things.

    If you can't do something using pure JDBC Hibernate or Spring will not help you. If you can do it with pure JDBC, it's certainly doable with Hibernate, Spring or both.

  7. #7
    Join Date
    Aug 2006
    Posts
    29

    Default

    If you can't do something using pure JDBC, Hibernate or Spring will not help you
    Grr, what a shame. If that's the case, why bother with Spring at all - Hibernate can do it for me?

    I guess I'll have to implement this mechanism myself. It just seems like something Spring AOP could do for me.

    Thanks,
    Sean

  8. #8
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    Quote Originally Posted by scrotty
    Sure. I want to create a system where READs on persisted objects can occur at any time (even if the data is slightly stale), but WRITEs on a particular persisted object must be serialized. That serialization may be blocking or exception based (e.g. an exception is thrown if access to the object for write is denied because another process/thread currently "holds" the object). ...
    Sory, but you have answered "what" but "not what for". And transaction serialization in not business requirment, rather technical solution (almost always, wrong one).

    Ok. Assuming that you really need it there are at least 2 simple solutions that does not require nor Hibernate, nor Spring.

    1. Use database capabilities - set transaction level to "serializable". It is normally accomplished on JDBC connection level by calling setTransactionIsoltion(TRANSACTION_SERIALIZABLE) method of Connection object or by specifying appropriate (driver-specific) parameter in connection URL. This does not provide "real" serialization but ensure that transactions result are exactly the same as if such serialization has taken place. The only trouble is that HSQL (with which you are working with) does support only TRANSACTION_READ_UNCOMMITED level (which, IMHO, shall be prohibited in DB-development), so you need to switch to more serious database (e.g. Derby). The advantage of this approach is that "serialization" effect is enforced by DB-engine and other application (and even ad-hoc updates) may acces the same data without breaking your functionality.
    2. Create separate object (e.g. single-row single column table) to lock it at the beginning of every transaction thus ensuring transaction serialization. E.g. CREATE TABLE SerializeOnMe (x integer); INSERT INTO SerializeOnMe VALUES(1); COMMIT; and then, start each transaction whith UPDATE SerializeOnMe SET x=x; Each next transaction would wait till previous finishes. Defintely, you need not include it in read.only transactions. Note that this approach would work with several copies of your application running in parallel, but would not work with other application working with the same data.
    3. Synchronize bodies of CRUD transaction on the same Java object. Would work only inside single application copy.


    And last, but not least - formulate buisness requirment and business terms and then try to map it into transaction/locks/serialization semantic properly - I may bet that it will not be complete serialization of CRUD operations.

    Oleksandr Alesinskyy

  9. #9
    Join Date
    Aug 2006
    Posts
    29

    Default

    And transaction serialization in not business requirment, rather technical solution (almost always, wrong one).
    Again, thanks, Oleksandr! Yes, my need is a bit odd in that I'm purposefully trying not to have the system perform perfectly. I'm writing a simulator that needs to mimic an unpredictable and potentially long delayed system.

    Create separate object (e.g. single-row single column table) to lock it at the beginning of every transaction thus ensuring transaction serialization. E.g. CREATE TABLE SerializeOnMe (x integer); INSERT INTO SerializeOnMe VALUES(1); COMMIT; and then, start each transaction whith UPDATE SerializeOnMe SET x=x; Each next transaction would wait till previous finishes. Defintely, you need not include it in read.only transactions. Note that this approach would work with several copies of your application running in parallel, but would not work with other application working with the same data.
    Also, thank you for spending the time to describe several options for this non-optimal system. I'm intrigued by your second suggestion. I think I will work a bit at mocking it up.

    Thank you!
    Sean

  10. #10
    Join Date
    Aug 2006
    Location
    Now Germany, previously Ukraine
    Posts
    1,546

    Default

    Up to you

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •