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.xmlCode:
<?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.xmlCode:
<?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.javaCode:
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.javaCode:
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.javaCode:
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