views:

35

answers:

1

I'm developing an double-entry accounting system and I need to insert posting records without any gaps in the sequential id. Since all database access will happen through a web application, I'm taking care of id generation using a syncronized static variable:

import org.hibernate.*;

public class JournalService {    
    private static Object journalLock = new Object();    
    private Session session;

    public JournalService(Session session) {
        this.session = session;
    }

    public void insertJournalEntry(JournalEntry journalEntry) {
        Set<PostEntry> entries = journalEntry.getEntries();

        double total = 0;

        for (PostEntry entry : entries) {
            total += entry.getAmount();
        }

        if(total != 0)
            throw new InvalidParameterException("Journal entry is invalid. The sum of all entries must be zero.");

        Criteria criteria = session.createCriteria(PostEntry.class);
        criteria.setMaxResults(1);
        criteria.addOrder(Order.desc("id"));
        //here I add some other criteria
        synchronized(journalLock) {            
            PostEntry lastEntry = criteria.uniqueResult();
            long currentId = lastEntry.getId();

            for (PostEntry entry : entries) {
                entry.setId(currentId++)
                session.save(entry);
            }            
            session.save(journalEntry);
        }
    }
}

In a few words, this service has a lifetime tied to the current httprequest and is managed by a spring container, besides I make sure the entire transaction is commited at the end of request. The syncronized block will take care of any concurrency so there's no way any gaps will happen in the id's sequence. The big problem here is the added overhead of database access for selecting the last inserted record that matches a certain criteria. I don't want that to happen so I'm guessing the solution here is using a cache that will hold the last inserted record the first time its selected, so no database access is needed for generating the id. My question : Is there any out-of-box hibernate way of solving this? I'm new to hibernate and heard about query cache's but I'm not sure on how to use it. I don't want the cache to hold every value I store using this service, I just need it to store the last inserted entry that matches a certain criteria. For example, if I perfomed five inserts, in which two matches a equal criteria 'X' and the other three matches a criteria 'Y', the cache will only have two objects stored(the last inserted object that matches each criteria). I could easily implement this myself, but I would rather use a solution that integrates with hibernate API.

+1  A: 

Caching is better suited to storing frequently accessed but rarely modified objects. what it is not what you are trying to do. You could use serializable Transaction which prevents lost updates and inconsistent reads which is what you are trying To avoid. But you should keep in mind its overhead.

// Hibernate Transaction isolation level settings
// Serializable isolation level
hibernate.connection.isolation=8

Here goes what SessionFactory API says

The behaviour of a SessionFactory is controlled by properties supplied at configuration time. These properties are defined on Environment.

As workaround, you can set up Two distinct SessionFactory where one of Them is serializable as shown above.

Because you are retrieving The newest inserted PostEntry Entity, I Think pessimist locking does not apply properly because your database just lock The retrieved PostEntry.

Arthur Ronald F D Garcia
Actually, my major concern is avoid the extra database query to get the last inserted object which matches a certain criteria, I don't care much about loosing inserts because they will always happen at the end of request. Could you give a more concrete example of what you said? I didn't understood exactly what you described.
Thiado de Arruda
Ok I got it, thanks.
Thiado de Arruda