-->
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.  [ 5 posts ] 
Author Message
 Post subject: How to ensure a singleton database row?
PostPosted: Mon Nov 29, 2004 11:34 pm 
Beginner
Beginner

Joined: Wed Nov 19, 2003 6:46 pm
Posts: 41
Location: Auckland, New Zealand
Hello DB/Hibernate experts

I'm not sure how to do this in Hibernate, or relational databases in general.

What I need is one of my persistent classes to have only a single instance in the database - a persistent singleton, in other words. I think I need to use pessimistic locking somehow...

I have the following mapping document:

Code:
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
   "-//Hibernate/Hibernate Mapping DTD//EN"
   "http://hibernate.sourceforge.net/hibernate-mapping-2.0.dtd">

<hibernate-mapping package="com.gfg.pcm.riskalerts.domain">
    <class name="AuthsNextNumber" >
      <id name="id" type="int" unsaved-value="0">
         <generator class="assigned" />
      </id>
        <property name="nextId" type="java.lang.Long" not-null="true" />
    </class>
</hibernate-mapping>



I'm trying to use the DB to store the next number in a batch. So I want a table with a single record, the ID can just be 1. I want to check if it exists, and if not, create it. But obviously I don't want some concurrent process to create an instance if it also finds that it doesn't exist.

I'm thinking along the lines of:

Code:
AuthsNextNumber nextNumber = (AuthsNextNumber)
    session.get(AuthsNextNumber.class, new Integer(1), LockMode.UPGRADE);

if (nextNumber == null)
{
    nextNumber = new AuthsNextNumber(1);
    session.save(nextNumber);
}
else
{
    // Update nextNumber, etc.
    session.update(nextNumber);
}


but, I suspect this won't work. How should I do this?

Any help much appreciated...

Craig


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 30, 2004 3:31 am 
Beginner
Beginner

Joined: Tue Oct 26, 2004 12:45 pm
Posts: 43
Location: Israel
Hi.
i have a suggestion, hope it fits:

This is a method that checks if an instance is already in the DB, else, it returns the same instance you passed it.
Code:
public static Object prepareForSaveOrUpdate(Session session, Object obj) throws HibernateException
   {
      List objects;
      Criteria criteria = session.createCriteria(obj.getClass());

      criteria.add(Example.create(obj));

      objects = criteria.list();
      if (!objects.isEmpty())
         return objects.get(0);
      else
         return obj;
   }



so you should use it like that:

Code:
AuthsNextNumber nextNumber = new AuthsNextNumber(1);
nextNumber = (AuthsNextNumber) prepareForSaveOrUpdate(nextNumber);

session.saveOrUpdate(nextNumber);


it ensures that there is only one row with id = 1.

Hope it helps somehow,
Jus.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Nov 30, 2004 4:49 am 
Beginner
Beginner

Joined: Wed Nov 19, 2003 6:46 pm
Posts: 41
Location: Auckland, New Zealand
Hi Jus

Thanks for your reply, but won't that just leave a timing hole in that two concurrent processes could each discover that there was no existing row and thus both attempt to create the row. Then one will fail with an exception.

Is this really the only way to do it, or is there some more elegant way?

Craig


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 06, 2004 8:09 pm 
Newbie

Joined: Thu Nov 04, 2004 6:00 pm
Posts: 8
Location: Palo Alto, CA
If you declare that ID column as the PK for the table, you can then simply insert a new row and flush() the session. If you get an exception, there's a duplicate row. In that case, select and update it.


Top
 Profile  
 
 Post subject:
PostPosted: Mon Dec 06, 2004 8:23 pm 
Beginner
Beginner

Joined: Wed Nov 19, 2003 6:46 pm
Posts: 41
Location: Auckland, New Zealand
Actually, I've experimented with this a bit, and learned a lot.

It seems that I really don't have a problem. From my experiments, in MySQL, at least, if I do a SELECT FOR UPDATE (in Hibernate, a load, with a LockMode.Upgrade) and don't find it, I can INSERT it without worry as MySQL will hold a lock on the index that prevents any other process from doing the insert.

As an aside, however, if there is a concurrent process that also does a SELECT FOR UPDATE then attempting to INSERT the row will fail with a deadlock exception - which could be retried, hopefully successfully second-time around.

So, I guess the general rule is that if I want to do an insert and not have to worry about duplicate key exceptions then I could do a SELECT first and be guaranteed of being able to INSERT if no row was found.


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