-->
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.  [ 4 posts ] 
Author Message
 Post subject: IllegalArgumentException when saving object
PostPosted: Sat Aug 09, 2008 11:51 am 
Newbie

Joined: Sat Aug 09, 2008 11:24 am
Posts: 5
Need help with Hibernate? Read this first:
http://www.hibernate.org/ForumMailingli ... AskForHelp

Problem Description
I am writing a very simple money management system as a mess-about project and am encountering an infurating error when trying to implement a many-to-one relationship.

The concept is very simple. I have a table of Accounts and a table of Transactions. One account has many transactions. All is fine with the one-to-many relationship from Account to Transaction but when I add the many-to-one from Transaction to Account I get the exception described below when I try to save the transaction.

I am using int for all numeric values. However, I have tried long and Long and still get the same problem.

Hibernate version: 3.2.6

Mapping documents:

- Account.hbm.xml

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

<hibernate-mapping>
   <class name="com.moneymanager.orm.Account" table="ACCOUNT">
      <id name="accountId" column="ACCOUNT_ID" >
         <generator class="native" />
      </id>
      <property name="shortName" />
      <property name="description" />
      <set name="transactions" >
         <key column="accountId" />
         <one-to-many class="com.moneymanager.orm.AccountTransaction" />
      </set>
   </class>
</hibernate-mapping>



- AccountTransaction.hbm.xml

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

<hibernate-mapping>
   <class name="com.moneymanager.orm.AccountTransaction" table="ACCOUNT_TRANSACTION">
      <id name="transactionId" column="TRANSACTION_ID" >
         <generator class="native" />
      </id>
      <many-to-one name="accountId" class="com.moneymanager.orm.Account" column="accountId"  not-null="true"/>
      <property name="transactionDate" type="timestamp" />
      <property name="description" />
      <property name="type" />
      <property name="amount" />
   </class>
</hibernate-mapping>


- hibernate.cfg.xml

Code:
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">

<hibernate-configuration>

    <session-factory>

        <!-- Database connection settings -->
        <property name="connection.driver_class">org.hsqldb.jdbcDriver</property>
        <property name="connection.url">jdbc:hsqldb:hsql://localhost</property>
        <property name="connection.username">sa</property>
        <property name="connection.password"></property>

        <!-- JDBC connection pool (use the built-in) -->
        <property name="connection.pool_size">1</property>

        <!-- SQL dialect -->
        <property name="dialect">org.hibernate.dialect.HSQLDialect</property>

        <!-- Enable Hibernate's automatic session context management -->
        <property name="current_session_context_class">thread</property>

        <!-- Disable the second-level cache  -->
        <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>

        <!-- Echo all executed SQL to stdout -->
        <property name="show_sql">true</property>

        <!-- Drop and re-create the database schema on startup -->
        <property name="hbm2ddl.auto">create</property>

        <mapping resource="com/moneymanager/orm/Account.hbm.xml"/>
        <mapping resource="com/moneymanager/orm/AccountTransaction.hbm.xml"/>
    </session-factory>

</hibernate-configuration>



Bean Code

- Account.java

Code:
package com.moneymanager.orm;

import java.util.HashSet;
import java.util.Set;

/**
* A Bank Account or Credit Card

*/
public class Account
{
   private int accountId;
   private String shortName;
   private String description;
   private Set <AccountTransaction> transactions = new HashSet<AccountTransaction>();

   
   public int getAccountId() {
      return accountId;
   }
   
   public void setAccountId(int accountId) {
      this.accountId = accountId;
   }
   
   public String getShortName() {
      return shortName;
   }
   
   public void setShortName(String shortName) {
      this.shortName = shortName;
   }
   
   public String getDescription() {
      return description;
   }
   
   public void setDescription(String description) {
      this.description = description;
   }

   public Set<AccountTransaction> getTransactions() {
      return transactions;
   }

   public void setTransactions(Set<AccountTransaction> transactions) {
      this.transactions = transactions;
   }
   
}



- AccountTransaction.java

Code:
package com.moneymanager.orm;

import java.util.Date;

public class AccountTransaction {

   private int transactionId;
   private int accountId;
   private Date transactionDate;
   private String description;
   private String type;
   private double amount;
   
   public long getTransactionId() {
      return transactionId;
   }
   
   private void setTransactionId(int transactionId) {
      this.transactionId = transactionId;
   }
   
   public int getAccountId() {
      return accountId;
   }
   
   public void setAccountId(int accountId) {
      this.accountId = accountId;
   }
   
   public Date getTransactionDate() {
      return transactionDate;
   }
   
   public void setTransactionDate(Date transactionDate) {
      this.transactionDate = transactionDate;
   }
   
   public String getDescription() {
      return description;
   }
   
   public void setDescription(String description) {
      this.description = description;
   }
   
   public String getType() {
      return type;
   }
   
   public void setType(String type) {
      this.type = type;
   }
   
   public double getAmount() {
      return amount;
   }
   
   public void setAmount(double amount) {
      this.amount = amount;
   }
   
}



Helper Class

- HibernateUtils.java

Code:
package com.moneymanager.orm.hibernate.util;

import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;

public class HibernateUtils {

   private static final SessionFactory sessionFactory;

    static {
        try {
            // Create the SessionFactory from hibernate.cfg.xml
            sessionFactory = new Configuration().configure().buildSessionFactory();
        } catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            System.err.println("Initial SessionFactory creation failed." + ex);
            throw new ExceptionInInitializerError(ex);
        }
    }

    public static SessionFactory getSessionFactory() {
        return sessionFactory;
    }

}


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

This is the unit test that I wrote (it a bit hacky and verbose I'm afraid but I have copied it verbatim anyway as it may contain the error)

Code:
public void testAccount_storeAndRetrieveTransaction() {
      try {
         // Account data
         String shortName = "Business Account";
         String description = "The main business account";

         // Transaction data
         double amount = 45.50;
         String transDescription = "O'Reilly Subscription";
         Date transDate = Calendar.getInstance().getTime();
         String type = "DD";

         // Clear all accounts
         Session session = HibernateUtils.getSessionFactory()
               .getCurrentSession();
         session.beginTransaction();
         session.createQuery("delete from Account").executeUpdate();

         // Create and store the account
         Account theAccount = new Account();
         theAccount.setShortName(shortName);
         theAccount.setDescription(description);
         session.save(theAccount);

         // Create and store the transaction
         List result = session.createQuery("from Account").list();
         assertEquals(1, result.size());
         theAccount = (Account) result.get(0);
         System.out.println("Account ID before save: " + theAccount.getAccountId());
         session.getTransaction().commit();
         
         session = HibernateUtils.getSessionFactory().getCurrentSession();
         session.beginTransaction();
         AccountTransaction transaction = new AccountTransaction();
         transaction.setAccountId(theAccount.getAccountId());
         transaction.setAmount(amount);
         transaction.setDescription(transDescription);
         transaction.setTransactionDate(transDate);
         transaction.setType(type);

         theAccount.getTransactions().add(transaction);
         System.out.println("Account ID after save: " + theAccount.getAccountId());
         
         session.save(transaction);
         session.save(theAccount);
         
         session.getTransaction().commit();

         // Now get the transaction back
         session = HibernateUtils.getSessionFactory().getCurrentSession();
         session.beginTransaction();
         result = session.createQuery("from Account").list();

         assertEquals(1, result.size());
         Account retrievedAccount = (Account) result.get(0);
         Set<AccountTransaction> transResults = retrievedAccount
               .getTransactions();

         assertEquals(1, transResults.size());

         AccountTransaction retrievedTrans = (AccountTransaction) transResults
               .iterator().next();
         assertEquals(amount, retrievedTrans.getAmount());
         assertEquals(transDescription, retrievedTrans.getDescription());
         assertEquals(transDate, retrievedTrans.getTransactionDate());
         assertEquals(type, retrievedTrans.getType());

         session.getTransaction().commit();

         // Now query the transaction table directly
         session = HibernateUtils.getSessionFactory().getCurrentSession();
         session.beginTransaction();
         result = session.createQuery("from AccountTransaction").list();
         assertEquals(1, result.size());

         retrievedTrans = (AccountTransaction) result.iterator().next();
         assertEquals(amount, retrievedTrans.getAmount());
         assertEquals(transDescription, retrievedTrans.getDescription());
         assertEquals(transDate, retrievedTrans.getTransactionDate());
         assertEquals(type, retrievedTrans.getType());

         System.out.println("Transaction ID : "
               + retrievedTrans.getTransactionId());

      } catch (Exception e) {
         e.printStackTrace();
      }

   }


The exception is thrown on the following line (about half way down):

Code:
session.save(transaction);


I have tried saving the theAccount object first but it makes no difference.

Full stack trace of any exception that occurs:

Code:
Aug 9, 2008 4:20:20 PM org.hibernate.property.BasicPropertyAccessor$BasicGetter get
SEVERE: IllegalArgumentException in class: com.moneymanager.orm.Account, getter method of property: accountId
org.hibernate.PropertyAccessException: IllegalArgumentException occurred calling getter of com.moneymanager.orm.Account.accountId
   at org.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:171)
   at org.hibernate.tuple.AbstractEntityTuplizer.getIdentifier(AbstractEntityTuplizer.java:176)
   at org.hibernate.persister.entity.AbstractEntityPersister.getIdentifier(AbstractEntityPersister.java:3256)
   at org.hibernate.persister.entity.AbstractEntityPersister.isTransient(AbstractEntityPersister.java:2982)
   at org.hibernate.engine.ForeignKeys.isTransient(ForeignKeys.java:181)
   at org.hibernate.engine.ForeignKeys$Nullifier.isNullifiable(ForeignKeys.java:137)
   at org.hibernate.engine.ForeignKeys$Nullifier.nullifyTransientReferences(ForeignKeys.java:69)
   at org.hibernate.engine.ForeignKeys$Nullifier.nullifyTransientReferences(ForeignKeys.java:47)
   at org.hibernate.event.def.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:263)
   at org.hibernate.event.def.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:167)
   at org.hibernate.event.def.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:101)
   at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.saveWithGeneratedOrRequestedId(DefaultSaveOrUpdateEventListener.java:186)
   at org.hibernate.event.def.DefaultSaveEventListener.saveWithGeneratedOrRequestedId(DefaultSaveEventListener.java:33)
   at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.entityIsTransient(DefaultSaveOrUpdateEventListener.java:175)
   at org.hibernate.event.def.DefaultSaveEventListener.performSaveOrUpdate(DefaultSaveEventListener.java:27)
   at org.hibernate.event.def.DefaultSaveOrUpdateEventListener.onSaveOrUpdate(DefaultSaveOrUpdateEventListener.java:70)
   at org.hibernate.impl.SessionImpl.fireSave(SessionImpl.java:524)
   at org.hibernate.impl.SessionImpl.save(SessionImpl.java:514)
   at org.hibernate.impl.SessionImpl.save(SessionImpl.java:510)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:585)
   at org.hibernate.context.ThreadLocalSessionContext$TransactionProtectionWrapper.invoke(ThreadLocalSessionContext.java:271)
   at $Proxy0.save(Unknown Source)
   at com.moneymanager.AccountTest.testAccount_storeAndRetrieveTransaction(AccountTest.java:107)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:585)
   at junit.framework.TestCase.runTest(TestCase.java:154)
   at junit.framework.TestCase.runBare(TestCase.java:127)
   at junit.framework.TestResult$1.protect(TestResult.java:106)
   at junit.framework.TestResult.runProtected(TestResult.java:124)
   at junit.framework.TestResult.run(TestResult.java:109)
   at junit.framework.TestCase.run(TestCase.java:118)
   at junit.framework.TestSuite.runTest(TestSuite.java:208)
   at junit.framework.TestSuite.run(TestSuite.java:203)
   at org.eclipse.jdt.internal.junit.runner.junit3.JUnit3TestReference.run(JUnit3TestReference.java:130)
   at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:460)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:196)
Caused by: java.lang.IllegalArgumentException: object is not an instance of declaring class
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
   at java.lang.reflect.Method.invoke(Method.java:585)
   at org.hibernate.property.BasicPropertyAccessor$BasicGetter.get(BasicPropertyAccessor.java:145)
   ... 43 more


Name and version of the database you are using:

HBSQL 1.8.0.1


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 09, 2008 1:21 pm 
Expert
Expert

Joined: Wed Mar 03, 2004 6:35 am
Posts: 1240
Location: Lund, Sweden
Thanks for posting all that code and XML. I wish everybody would do that!

The problem you are having is that Hibernate is receiving an int from the AccountTransaction.getAccountId() method, not an Account object as expected from the mapping file. Since you have...

Code:
<many-to-one
  name="accountId"
  class="com.moneymanager.orm.Account" ...>


...you also need (I suggest you change the name of the mapping to <many-to-one name="account" ...):

Code:
public void setAccount(Account account) {
   this.account = account;
}
public Account getAccount() {
      return account;
}


In your Account.hbm.xml you should also add inverse="true" to the <set> mapping the AccountTransactions.

I also noted that you are calling session.save(theAccount) twice in your test code. The seconds call is not needed. In fact, I think Hibernate will complain about this if your code gets to that line.


Top
 Profile  
 
 Post subject: IllegalArgumentException when saving object
PostPosted: Sat Aug 09, 2008 2:23 pm 
Newbie

Joined: Sat Aug 09, 2008 11:24 am
Posts: 5
Brilliant. Thank you. That has completely solved the problem. You were right about the two save() calls as well. It actually resulted in two Account objects being created, which caused the unit test to fail as it asserts that only one is present.

Does this mean that <many-to-one> can only be performed on non-primitive types? Unfortunately, I am still "thinking in terms of DB design" rather than "thinking in terms of objects".


Top
 Profile  
 
 Post subject:
PostPosted: Sat Aug 09, 2008 5:41 pm 
Expert
Expert

Joined: Wed Mar 03, 2004 6:35 am
Posts: 1240
Location: Lund, Sweden
You still need to think in terms of database design but you also need to think in terms of objects. Otherwise, you'll find that you have to fight with Hibernate every step you take. In our project, we started using Hibernate with some 2.x version. At first, we only saw it as a tool that would save us from writing SQL. We already had some prototype code and tried to just replace parts with JDBC/SQL with Hibernate instead. This didn't work very well but we kept on going and the code became very complex and "ugly".

After about a year the first edition of "Hibernate in action" came out. This was about the same time as Hibernate 3 made it to beta. We bough the book and realized that we very wrong about Hibernate and that our own code was more or less crap. So, we decided to throw more or less all code and re-start from the beginning. Of course, we still had the database design and the business rules, so it was not completely from the beginning. In any case, it took us just a couple of months to reach the state that had took a year in the first place, and now everything worked a lot more smoothly.

The money we spent on "Hibernate in action" was saved many times in development time and I really recommend reading it. Particularly if you think you are thinking too much in terms of DB design as we did.


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