-->
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.  [ 12 posts ] 
Author Message
 Post subject: floundering with detached objects
PostPosted: Tue Jul 20, 2004 2:50 pm 
Beginner
Beginner

Joined: Tue Jun 29, 2004 3:44 pm
Posts: 43
Are there any docs/tutorials/tips for using Hibernate (2.1.4) to save detached objects in a stand-alone application?

I'm just learning Hibernate and I'm using it in a project where we really just want to call some save() method when we change something. We want to have our classes depend on Hibernate classes as little as possible, and would like them to know nothing of sessions or transactions. It's a stand-alone application that will run for hours or days at a time - no JSEE, no web at all.

We have the classes set up with version properties and are successfully saving them to the DB (MySQL 4), but are now having problems saving changes. We're creating a new session and transaction, calling saveOrUpdate() and seeing no change in the DB.

I don't think I know enough about what's going on to formulate good questions, and I haven't extracted from the dozen or so classes enough to post code. So I'm not asking for a solution to any particular problem - just pointers to information that might help me figure out what's going on and how to use Hibernate like this.

I've been through the included documentation and gleaned snippets of info, and I'm reading "Hibernate, a Developer's Notebook" which doesn't seem to address this sort of app at all.

Many, many thanks for any and all help,
Scott


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 20, 2004 3:35 pm 
Hibernate Team
Hibernate Team

Joined: Thu Dec 18, 2003 9:55 am
Posts: 1977
Location: France
web or standalone ... no really differences about your problem.
if you have concurrency, you must take care of how you are using hibernate session.
So the question is: how many users do you have for your app?

The rule: one hibernate session per "unit of work", in web, generally it means one hibernate per httpRequest.

For you, you have to use an hibernate per "business process unit"... show java code, we'll try to help you more

_________________
Anthony,
Get value thanks to your skills: http://www.redhat.com/certification


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 20, 2004 4:10 pm 
Beginner
Beginner

Joined: Tue Jun 29, 2004 3:44 pm
Posts: 43
Thank you so much for your help. Right now, there is only one user - JUnit tests. It is considered incredibly unlikely that more than one user will ever access the app at once.

We've created a Saver class - a singleton - that we're using as a persistence proxy. It has a saveOrUpdate() method that we'd like to use to persist new objects and to update pre-existing (loaded) objects:
Code:
public void saveOrUpdate(Object o) throws HibernateException {
   Session session = HibernateUtil.currentSession();   
   Transaction tx = session.beginTransaction();
   session.saveOrUpdate(o);
   session.flush();
   tx.commit();
   HibernateUtil.closeSession();
}
public Object get(Class c, String id) throws HibernateException {
   Session session = HibernateUtil.currentSession();
   Transaction tx = session.beginTransaction();
   Object o = session.get(c, id);
   tx.commit();
   HibernateUtil.closeSession();
   return o;
}


That's our attempt at isolating our classes from Hibernate.

HibernateUtil.java is from the docs, with only changes to load mapping files from a directory:
Code:
/* HibernateUtil.java

package com.company.partyKeeper.persistence;

import net.sf.hibernate.*;
import net.sf.hibernate.cfg.*;

public class HibernateUtil {

   private static final SessionFactory sessionFactory;
   static {
      try {
         // MappingLoader reads files from mapping folder to add to a cfg.
         MappingLoader ml = new MappingLoader();
         
         // Create and load the Configuration
         Configuration cfg = ml.fillConfiguration(new Configuration());
         
         // Create the SessionFactory
         sessionFactory = cfg.buildSessionFactory();

      } catch (Exception ex) {
         throw new RuntimeException("*** Configuration problem: " + ex.getMessage(),
               ex);
      }
   }
   public static final ThreadLocal session = new ThreadLocal();

   public static Session currentSession() throws HibernateException {
      Session s = (Session) session.get();
      // Open a new Session, if this Thread has none yet
      if (s == null) {
         s = sessionFactory.openSession();
         session.set(s);
      }
      return s;
   }

   public static void closeSession() throws HibernateException {
      Session s = (Session) session.get();
      session.set(null);
      if (s != null) s.close();
   }
}


We are successfully saving objects with this code. We just hit a problem, though, where we call Saver.saveOrUpdate() after making some changes and the database is not showing the changes.

If what I've posted so far doesn't have any glaring problems, I can start extracting some control code to post. Right now that's a TestCase stting up an object graph of a dozen or so classes then calling a control class to act on three of those objects/classes. So I don't yet have a simple test case to show our problem.

Thanks again,
Scott


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 20, 2004 4:22 pm 
Hibernate Team
Hibernate Team

Joined: Thu Dec 18, 2003 9:55 am
Posts: 1977
Location: France
are you sure of unsaved-value in your mapping files...
can you see something on logs?

_________________
Anthony,
Get value thanks to your skills: http://www.redhat.com/certification


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 20, 2004 4:36 pm 
Beginner
Beginner

Joined: Tue Jun 29, 2004 3:44 pm
Posts: 43
Here's a test that doesn't work:
Code:
      SubscriptionAccount account = AccountRepository.getInstance()
            .getSubscriptionAccount(accountNumber);
      State state = account.getState();
      
      System.out.println("Before: " + state);
      
      state.sentIssue(); // the change
      
      Session session;
      try {
         session = HibernateUtil.currentSession();
         Transaction tx = session.beginTransaction();
         session.saveOrUpdate(account);
         session.flush();
         tx.commit();
         HibernateUtil.closeSession();
      } catch (HibernateException e) {
         System.out.println("error");
         e.printStackTrace();
      }

      System.out.println("After: " + state); // shows object was changed, but no change in DB
   }


That's a test I set up that pulls code pieces from various classes in the 'real' app code.

The class hierarchy is like this: a SubscriptionAccount has a list of Subscriptions. Each Subscription has a State. State includes an issuesRemaining property - an int. In the code above, state.sentIssue() simply decrements the issuesRemaining property. That change is not persisted to the DB when the SubscriptionAccount is given to saveOrUpdate().

Mapping files are here:

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

<hibernate-mapping>
   
   <class name="com.louisianaweekly.partyKeeper.modules.subscription.domain.SubscriptionAccount"
      table="sub_account">
      <id name="accountID" type="string" column="id" length="50">
         <generator class="assigned"/>
      </id>      
      <!-- allow updating with assigned id -->
      <!-- <timestamp name="lastSave" column="timestamp" unsaved-value="null"/> -->
      <version name="version" unsaved-value="negative"/>
      
      <property name="sponsorID" column="sponsor_id"  length="50"/>
      <property name="recipientID" column="recipient_id" length="50"/>
      <property name="currentSubPosition" column="current_sub" type="integer"/>
      <many-to-one
         name="publication" column="pub_id"
         class="com.louisianaweekly.partyKeeper.modules.subscription.promotions.Publication"
         />
         
      <!-- subscriptions -->
      <list name="subscriptions" cascade="all-delete-orphan">
         <key column="owner_id"/>
         <index column="position"/>
         <one-to-many class="com.louisianaweekly.partyKeeper.modules.subscription.domain.Subscription"/>            
      </list>
      
      
      <!-- delivery addresses: temp coll -->
      <component name="deliveryAddresses" class="com.spongocoel.libraries.collections.TemporalCollection">
         <map name="contents" table="delivery_addresses">            
            <key column="account_id"/>
            <index column="position" type="calendar"/>
            <many-to-many column="address_id" class="com.louisianaweekly.partyKeeper.contactable.ContactPoint"/>
         </map>
      </component>
      
         
      <!-- billing addresses: temp coll -->
      <component name="billingAddresses" class="com.spongocoel.libraries.collections.TemporalCollection">
         <map name="contents" table="billing_addresses">            
            <key column="account_id"/>
            <index column="position" type="calendar"/>
            <many-to-many column="address_id" class="com.louisianaweekly.partyKeeper.contactable.ContactPoint"/>
         </map>
      </component>
      
         
   </class>
</hibernate-mapping>


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

<hibernate-mapping>
   
   <class name="com.louisianaweekly.partyKeeper.modules.subscription.domain.Subscription"
      table="subscription">
      <id name="id" type="string" column="id" length="50">
         <generator class="uuid.hex"/>
      </id>      
      <version name="version" unsaved-value="negative"/>
      
      <property name="creationDate" column="creation_date"/>
      <many-to-one
         name="promotion" column="promotion_id"
         class="com.louisianaweekly.partyKeeper.modules.subscription.promotions.Promotion"/>
         
      <component name="balance" class="com.spongocoel.libraries.currency.Money">
         <property name="value" column="balance"/>
      </component>
         
      <!-- state -->
      <many-to-one name="state"
         class="com.louisianaweekly.partyKeeper.modules.subscription.states.AbstractState"
         column="state" cascade="all"/>
         
      <!-- transactions -->
      <list name="transactions" cascade="all">
         <key column="subscription_id_fk"/>
         <index column="position"/>
         <one-to-many class="com.louisianaweekly.partyKeeper.modules.subscription.domain.TransactionRecord"/>
      </list>
      
   </class>
</hibernate-mapping>


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

<hibernate-mapping>
   
   <class name="com.louisianaweekly.partyKeeper.modules.subscription.states.AbstractState" table="states">
      <id name="id" type="string" column="id" length="50">
         <generator class="uuid.hex"/>
      </id>
      <discriminator column="discriminator_type"/>
      <version name="version" unsaved-value="negative"/>
      
         <property name="type"
            type="com.louisianaweekly.partyKeeper.persistence.userTypes.StateTypeUserType"/>
         <property name="issuesRemaining" column="iss_remain"/>
         <property name="billsRemaining" column="bill_remain"/>
         <many-to-one name="promotion"
            class="com.louisianaweekly.partyKeeper.modules.subscription.promotions.Promotion"/>
         <property name="nextType" column="next_type"
             type="com.louisianaweekly.partyKeeper.persistence.userTypes.StateTypeUserType"/>
         
         
         <subclass
            name="com.louisianaweekly.partyKeeper.modules.subscription.states.State_Active"
            discriminator-value="active">
         </subclass>
         <subclass
            name="com.louisianaweekly.partyKeeper.modules.subscription.states.State_Canceled"
            discriminator-value="canceled">
         </subclass>
         <subclass
            name="com.louisianaweekly.partyKeeper.modules.subscription.states.State_CanceledDone"
            discriminator-value="canceled_done">
         </subclass>
         <subclass
            name="com.louisianaweekly.partyKeeper.modules.subscription.states.State_EndGrace"
            discriminator-value="end_grace">
         </subclass>
         <subclass
            name="com.louisianaweekly.partyKeeper.modules.subscription.states.State_Expired"
            discriminator-value="expired">
         </subclass>
         <subclass
            name="com.louisianaweekly.partyKeeper.modules.subscription.states.State_Forgiven"
            discriminator-value="forgiven">
         </subclass>
         <subclass
            name="com.louisianaweekly.partyKeeper.modules.subscription.states.State_PastDue"
            discriminator-value="past_due">
         </subclass>
         <subclass
            name="com.louisianaweekly.partyKeeper.modules.subscription.states.State_Retracted"
            discriminator-value="retracted">
         </subclass>
         <subclass
            name="com.louisianaweekly.partyKeeper.modules.subscription.states.State_StartGrace"
            discriminator-value="start_grace">
         </subclass>
            
   </class>
</hibernate-mapping>


Top
 Profile  
 
 Post subject:
PostPosted: Tue Jul 20, 2004 4:38 pm 
Beginner
Beginner

Joined: Tue Jun 29, 2004 3:44 pm
Posts: 43
There is no line in the log that isn't just a normal INFO line.

Thanks again,
Scott


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 21, 2004 10:44 am 
Beginner
Beginner

Joined: Tue Jun 29, 2004 3:44 pm
Posts: 43
The test below passes, but the database still shows no change. In the test, a new session is created (I think?) and Hibernate returns a SubscriptionAccount with the correct data. But this change is NOT persisted to the database.

The change makes its way to one of Hibernate's caches? How does Hibernate decide whether to update an object? My equals() and hashcode() methods seem to work, so it's not asking them. I have no idea what's going on at this point.

Code:
   public void testStateChangePersistence() {
              // SubscriptionAccount subAcct created and successfully persisted in an earlier test
      State ostate = subAcct.getState();
      System.out.println("subAcct: " + ostate);
      String accountNumber = subAcct.getAccountID();

      SubscriptionAccount account = AccountRepository.getInstance()
            .getSubscriptionAccount(accountNumber);
      State state = account.getState();

      System.out.println("Before: " + state);
      assertEquals(8, state.getIssuesRemaining());
      state.sentIssue();   // the change
      assertEquals(7, state.getIssuesRemaining());

      assertFalse(subAcct.equals(account)); // equals() method works
      assertFalse(subAcct.hashCode() == account.hashCode()); // hashcode() works

      Session session;
      try {
         session = HibernateUtil.currentSession();
         Transaction tx = session.beginTransaction();
         session.saveOrUpdate(account);  // save the change
         session.flush();
         tx.commit();
         HibernateUtil.closeSession();
      } catch (HibernateException e) {
         System.out.println("error");
         e.printStackTrace();
      }

      System.out.println("After: " + state);
      assertEquals(7, state.getIssuesRemaining());
      
      account = null;
      Session session2;
      SubscriptionAccount accountX = null;
      try {
         session2 = HibernateUtil.currentSession();
         Transaction tx2 = session2.beginTransaction();
         accountX = (SubscriptionAccount) session2.get(SubscriptionAccount.class, accountNumber);
         tx2.commit();
         session2.flush();
         HibernateUtil.closeSession();
      } catch (HibernateException e1) {
         e1.printStackTrace();
      }

      State stateX = accountX.getState();
      assertEquals(7, stateX.getIssuesRemaining());
      System.out.println("X: " + stateX);
   }


I really appreciate any help anyone can provide.

Thanks,
Scott


Top
 Profile  
 
 Post subject: Solved
PostPosted: Wed Jul 21, 2004 12:16 pm 
Beginner
Beginner

Joined: Tue Jun 29, 2004 3:44 pm
Posts: 43
We figured out what was going on. We were getting confused about a pass-by-reference sort of thing.

1. We were saving an Account.
2. Then we loaded the Account by its ID, changed it and saved the changes.
The Account object in 1 is not the same object as in 2, and it doesn't show the changes made to 2. Which is very obvious now, and as it should be. We were testing object 1 originally.

We complicated and prolonged this discovery by doubting our ability to use Hibernate, and by screwing up the little test that we extracted and posted. The test inherits from another test to do many lines of setup, but the super-test was destroying all the changes to the DB. So, the changes were made, but we erased them before we looked at the DB.

Too freakin' long to figure that one out.

Thank you, Anthony, for providing some reassurance that our mapping and use of Hibernate wasn't totally out of line. Many, many thanks for that.

Scott


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 21, 2004 12:17 pm 
Beginner
Beginner

Joined: Tue Jun 29, 2004 3:44 pm
Posts: 43
PS: we feel like idiots (especially me).


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 21, 2004 1:38 pm 
Hibernate Team
Hibernate Team

Joined: Thu Dec 18, 2003 9:55 am
Posts: 1977
Location: France
we all have been beginners...

now it's time for you to try helping others when you have the time ;)

_________________
Anthony,
Get value thanks to your skills: http://www.redhat.com/certification


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 21, 2004 1:42 pm 
Beginner
Beginner

Joined: Tue Jun 29, 2004 3:44 pm
Posts: 43
I think I've helped someone recently. I'll keep trying :)

Hibernate is wonderful, and I'm slowly figuring out how to think like it does.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Jul 21, 2004 1:44 pm 
Hibernate Team
Hibernate Team

Joined: Thu Dec 18, 2003 9:55 am
Posts: 1977
Location: France
pass 30 minutes a day reading new posts and trying to help and you'll learn very fast

see you soon on another post

_________________
Anthony,
Get value thanks to your skills: http://www.redhat.com/certification


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