-->
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.  [ 6 posts ] 
Author Message
 Post subject: Cascade "save-update" causes unnecessary update.
PostPosted: Tue Aug 15, 2006 11:59 am 
Newbie

Joined: Tue Aug 15, 2006 10:02 am
Posts: 9
Hibernate version:3.1.3
Spring version:2.0.RC2
Oracle version:10g
JDK 5

I am new to Hibernate and trying to do the right things. Here is my scenario. I have a customer and order objects and they are being persisted to database tables with same names.
1- An external system generates order that is insert a new order in the table by assigning the id (this is unfilled order) and email to customer a link that customer can follow to complete the order form. The email address of the customer is captured in the ONLINE_ORDER table.
2- Customer will click on the link and fill the order form and submit it. After submission application will validates and if every thing is valid, then it will do insert for customer information on the ORDER_CUSTOMER table if customer information is not already in the table if it finds the customer information is there then it should just get the id (This id needs to be persisted on ONLINE_ORDER table).

Code:
Customer {
id: long
name: String
email: String
phone: String
……
}

Order {
id: long
customer: Customer
orderText: String
submissionDate: Date
orderCode: String

. ….
}


Mapping documents:
Code:
<hibernate-mapping package="com.myCompany.enterprise.order.domain" default-lazy="false">

<class name="Order" table="ONLINE_ORDER" >
        <id name="id" column=" ONLINE_ORDER _ID" type="long" />
<property name=" orderText" column="ORDER_TEXT" />
<property name="submissionDate" column="SUBMISSION_DATE" type="date" />
<property name=" orderCode " column="ORDER_TYPE_CD" update="false" />
<many-to-one name="customer" column=" CUSTOMER _ID" class=" Customer " cascade="save-update"/>
</class>

<class name="Customer" table="ORDER_CUSTOMER">
        <id name="id" column="CUSTOMER_ID" unsaved-value="0">
            <generator class="sequence">
                <param name="sequence">CUSTOMER_ID_SEQ</param>
            </generator>
        </id>
<property name="name" />
<property name="email" column="EMAIL_ADDR" />
<property name="phone" column="CONTACT_PHONE_NO" />
</class>
</hibernate-mapping>



I call my OrderDao.save( ) method to save the order and customer (if needed).
My problem:
1- I have a method isCustomerExist( ) that I call to check if customer exist in the database just before I call OrderDao.save method. If customer exist then it just sets passed customer id and return true otherwise it just return false.
Is there a way with hibernate to do this kind of thing.
2- OrderDao.save ( ) method save the order. I am using cascade “save-update” and if customer does not exist it correctly does the insert but if customer does exist then it does update that is un-necessary. How can I eliminate that. I tried cascade = “merge” and “persist” but both gives TransientObjectException when adding new customer.

Help will be greatly appreciated.


Top
 Profile  
 
 Post subject:
PostPosted: Tue Aug 15, 2006 10:34 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
1: Use a query like this:
Code:
return session.createSQLQuery("select count(*) as custExists from CustomerTable where CustomerID = :custId")
  .addScalar("custExists", Hibernate.INTEGER)
  .setLong(custId)
  .uniqueResult() == 1;
But don't do this. Read answer 2.

2: To avoid the update, use the object that Hiberante loaded. That way, Hibernate knows whether or not the related objects are dirty, and won't save them if they're not. So where you said "If customer exist then it just sets passed customer id and return true", what you should be returning is either the entity that hibernate loaded, or the passed-in object. Something like this:
Code:
Customer cust = findOrCreateCust(id);
...
public Customer findOrCreateCust(long id)
{
  Customer cust = session.get(Customer.class, id);
  if (cust == null)
  {
    return new CustomerImpl(id);
  }
}
That way you're never trying to save the detached version of a persistent entity, which is what's causing your problem.

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 16, 2006 4:26 pm 
Newbie

Joined: Tue Aug 15, 2006 10:02 am
Posts: 9
tenwit wrote:
Code:
Customer cust = findOrCreateCust(id);
...
public Customer findOrCreateCust(long id)
{
  Customer cust = session.get(Customer.class, id);
  if (cust == null)
  {
    return new CustomerImpl(id);
  }
}


The above solution that you gave does make lot of sense but I still have not got it to work. The code snippet that you wrote as I have quoted above assumes the id is known at the time when this piece of code will be executed that is not true. Actually when this piece will be executed all the fields of the customer will be filled except the id (id will be 0). I used the criteria query to get the customer from database. Here is the code that I am using

Code:

    private Recommender findOrCreateCustomer(Customer customer) {

        Criteria criteria = this.getSession().createCriteria(Customer.class);

        Object obj = criteria.add(Restrictions.eq("name", customer.getName())).add(
            Restrictions.eq("institution", customer.getInstitution())).add(
            Restrictions.eq("position", customer.getPosition())).add(Restrictions.eq("email", recommender.getEmail())).add(
            Restrictions.eq("phone", customer.getPhone())).uniqueResult();
       
        if (obj !=null) {
             return customer=(Customer)obj ;
        } else {
            final Customer customer2 = customer;
          return customer2 ;
        }
    }


This method is called in the saveOrder method as follows
Code:
    public void saveOrder(Order order) {
        order.setCustomer (this.findOrCreateCustomer(order.getCustomer()));
        getHibernateTemplate().saveOrUpdate(recommendation);

    }


The above method runs fine when we do not have the customer information already in database but when we find customer then it does update on the customer table that I am trying to avoid. I am assuming the call to setCustomer method is causing this. If I remove that then It tries to do the insert and get data integrity exception.
I think I am not properly attaching it.

Thanks for help.


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 16, 2006 4:55 pm 
Newbie

Joined: Tue Aug 15, 2006 10:02 am
Posts: 9
Sorry please follow the following code snippet

Code:
private Customer findOrCreateCustomer(Customer customer) {

        Criteria criteria = this.getSession().createCriteria(Customer.class);

        Object obj = criteria.add(Restrictions.eq("name", customer.getName())).add(
            Restrictions.eq("institution", customer.getInstitution())).add(
            Restrictions.eq("position", customer.getPosition())).add(Restrictions.eq("email", customer.getEmail())).add(
            Restrictions.eq("phone", customer.getPhone())).uniqueResult();
       
        if (obj !=null) {
             return customer =(Customer)obj ;
        } else {
            final Customer customer2 = customer;
          return customer2;
        }
    }




   public void saveOrder(Order order) {

        this.findOrCreateCustomer(order.getcustomer());
        getHibernateTemplate().saveOrUpdate(order);

    }


Top
 Profile  
 
 Post subject:
PostPosted: Wed Aug 16, 2006 5:36 pm 
Expert
Expert

Joined: Thu Dec 23, 2004 9:08 pm
Posts: 2008
Odd. I'd have thought that that would work. Try this: just before calling findOrCreate, call session.clear(). Immediately after findOrCreate finds a Customer, call session.isDirty(). If that's returning true, then that would explain what you're seeing. The most likely explanation is that you have a set method that changes the incoming value, something like this:
Code:
public void setDescription(String desc)
{
  this.desc = "Loaded from DB: " + desc;
}
By the way, your query, while correct, is unnecessarily verbose. You can use query by example to do the same thing. Have a look at the ref docs, section 15.6. This is the tidier code:
Code:
Object obj = session.createCriteria(Customer.class)
    .add( Example.create(customer) )
    .uniqueResult();

_________________
Code tags are your friend. Know them and use them.


Top
 Profile  
 
 Post subject: Problem Resolved
PostPosted: Thu Aug 17, 2006 4:42 pm 
Newbie

Joined: Tue Aug 15, 2006 10:02 am
Posts: 9
Tenwit

Thanks a lot for your help. The problem is resolved. The problem was with the transaction management. I was not doing the transaction management in the right way that is why the session was getting lost and I was not having the handle to the current Session object when I called getCurrentSession().
Once I wired the transaction management stuff in the spring configuration file, the problem was resolved.

Once again thanks a lot for your help.

Faikeeyes.


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