-->
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.  [ 1 post ] 
Author Message
 Post subject: Hibernate fails to track changes to component fields?
PostPosted: Mon Mar 05, 2007 8:09 am 
Newbie

Joined: Mon Apr 25, 2005 9:18 am
Posts: 3
I have simple Money object that is mapped as component (@Embeddable) inside simple Account object.

The problem I'm experiencing is that Hibernate fails to properly track changes made to Money fields, i.e. something like:
account.getAmount().setAmount(new BigDecimal(3));

[account.getAmount() returns Money component which in turn has setAmount() method]


What is even worse -- it doesn't work consistently. In the example code below it appears to "notice" the first update but then ignores all subsequent updates (within the same session). Perhaps it is actually some kind of bug with data flushing (notice the update being executed even though session is not being finished).

I searched documentation for information whether Hibernate is 'supposed' to track changes to objects mapped as components or whether it treats them as 'atomic' (or 'primitive') objects and tracks only reference change -- but came up empty.


More explanations below the code sample.


My specific code uses Hibernate annotations and EJB3-style access, but I doubt the problem is specific to those, so posting in general forum.


Hibernate version:
Hibernate-Version: 3.2.1.ga

Mapping documents:
using annotations

Configuration:
Code:
<persistence xmlns="http://java.sun.com/xml/ns/persistence"
   xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
   xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"
   version="1.0">
   <persistence-unit name="manager1" transaction-type="RESOURCE_LOCAL">
      <properties>
             <property name="hibernate.dialect" value="org.hibernate.dialect.PostgreSQLDialect"/>
         <property name="hibernate.connection.driver_class" value="org.postgresql.Driver"/>
         <property name="hibernate.connection.username" value="..."/>
         <property name="hibernate.connection.password" value="..."/>
         <property name="hibernate.connection.url" value="jdbc:postgresql:database"/>
         <property name="hibernate.max_fetch_depth" value="3"/>

         <!-- Naming strategy to allow multiple instances of embeddable object -->
         <property name="hibernate.ejb.naming_strategy" value="org.hibernate.cfg.DefaultComponentSafeNamingStrategy" />

         <!-- DB creation -->
         <property name="hibernate.hbm2ddl.auto" value="validate"/>

         <!-- Debug section -->
         <property name="hibernate.show_sql" value="true"/>
         <property name="hibernate.format_sql" value="true"/>
         <property name="hibernate.use_sql_comments" value="true"/>

      </properties>
   </persistence-unit>
</persistence>


Code between sessionFactory.openSession() and session.close():

Classes that can be used to reproduce the problem:

Money.java
Code:
package hibbug;

import java.math.BigDecimal;

import javax.persistence.Embeddable;

@Embeddable
public class Money
{
    private BigDecimal amount;
    private String currency;

    public String getCurrency()
    {
        return currency;
    }

    public void setCurrency(String newCurrency)
    {
        currency = newCurrency;
    }

    public BigDecimal getAmount()
    {
        return amount;
    }

    public void setAmount(BigDecimal newAmount)
    {
        amount = newAmount;
    }
}




Account.java
Code:
package hibbug;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Account
{
    private String name;
    private Money amount;
    private Long id;

    @Id @GeneratedValue
    public Long getId()
    {
        return id;
    }

    public void setId(Long newId)
    {
        id = newId;
    }

    public Money getAmount()
    {
        return amount;
    }

    public void setAmount(Money newAmount)
    {
        amount = newAmount;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String newName)
    {
        name = newName;
    }
}




HibbugMain.java
Code:
package hibbug;

import java.math.BigDecimal;
import java.util.HashMap;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;
import javax.persistence.EntityTransaction;
import javax.persistence.Persistence;

public class HibbugMain
{
    public static void main(String[] args)
    {
        EntityManagerFactory emf;
       
        Map<String, String> configOverrides = new HashMap<String, String>();
        configOverrides.put("hibernate.hbm2ddl.auto", "create-drop");
        emf = Persistence.createEntityManagerFactory("manager1", configOverrides);
       
        EntityManager em = emf.createEntityManager();
       
        // Create object for manipulation.
        EntityTransaction tx = em.getTransaction();
        tx.begin();
       
        Account acc = new Account();
        acc.setName("Testing account");
        Money money = new Money();
        money.setCurrency("USD");
        money.setAmount( new BigDecimal(0) );
        acc.setAmount(money);
       
        em.persist(acc);
        tx.commit();
        em.close();
       
       
        em = emf.createEntityManager();
        tx = em.getTransaction();
        tx.begin();
       
        // Increase value in loop.
        for( int i = 1; i <= 4; i++ )
        {
            acc = (Account)
                em.createQuery( "from Account" ).getSingleResult();

            Money mn = acc.getAmount();
            System.out.println("Current value: " + mn.getAmount());
            mn.setAmount( new BigDecimal(i) );
            System.out.println("Set value to: " + i);
           
//            Money mn = new Money();
//            System.out.println("Current value: " + mn.getAmount());
//            mn.setAmount( new BigDecimal(i) );
//            acc.setAmount(mn);
//            System.out.println("Set value to: " + i);
        }
       
       
        tx.commit();
        em.close();
    }
}



Name and version of the database you are using:
PostgreSQL 8.2

The generated SQL (show_sql=true):

Code:
log4j:WARN No appenders could be found for logger (org.hibernate.ejb.Version).
log4j:WARN Please initialize the log4j system properly.
Hibernate:
    select
        nextval ('hibernate_sequence')
Hibernate:
    /* insert hibbug.Account
        */ insert
        into
            Account
            (name, amount_amount, amount_currency, id)
        values
            (?, ?, ?, ?)
Hibernate:
    /*
from
    Account */ select
        account0_.id as id0_,
        account0_.name as name0_,
        account0_.amount_amount as amount3_0_,
        account0_.amount_currency as amount4_0_
    from
        Account account0_
Current value: 0.00
Set value to: 1
Hibernate:
    /* update
        hibbug.Account */ update
            Account
        set
            name=?,
            amount_amount=?,
            amount_currency=?
        where
            id=?
Hibernate:
    /*
from
    Account */ select
        account0_.id as id0_,
        account0_.name as name0_,
        account0_.amount_amount as amount3_0_,
        account0_.amount_currency as amount4_0_
    from
        Account account0_
Current value: 1
Set value to: 2
Hibernate:
    /*
from
    Account */ select
        account0_.id as id0_,
        account0_.name as name0_,
        account0_.amount_amount as amount3_0_,
        account0_.amount_currency as amount4_0_
    from
        Account account0_
Current value: 2
Set value to: 3
Hibernate:
    /*
from
    Account */ select
        account0_.id as id0_,
        account0_.name as name0_,
        account0_.amount_amount as amount3_0_,
        account0_.amount_currency as amount4_0_
    from
        Account account0_
Current value: 3
Set value to: 4



As you can see from the log, Hibernate executes only single UPDATE statement after the first change. All the subsequent changes are ignored -- this is also confirmed by examining resulting value in the database.

I think Hibernate is executing update before executing query for the second time -- probably to ensure query consistency. But I have no idea why it does it only once and ignores subsequent changes.

Note that the code that is commented out performs correctly -- you get 4 updates if you use it and the resulting value is 4 (as expected).

_________________
If you aim for the sky, you are liable to end up on
the tree-top, and that tree-top may be good enough.


Top
 Profile  
 
Display posts from previous:  Sort by  
Forum locked This topic is locked, you cannot edit posts or make further replies.  [ 1 post ] 

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.