Hi all,
I have a Set of (Composite)UserTypes that work well for reading and writing (insert),
calling nullSafeSet with an insert containing three parameter placeholders for my
three columns.
However, when I update the set, the statement passed to nullSafeSet is a delete
statement with only one parameter placeholder. Obviously, setting three columns
here is not supported.
Am I supposed to map the User type in another way ? (composite-element maybe!?)
Hibernate version: 3.2.5
Mapping documents:
Code:
PriceData.hbm.xml:
<hibernate-mapping>
<class name="myapp.business.component.supplymasterdata.emea.types.entitytypes.PriceData"
table="im_se_price_data" lazy="true">
<id name="id">
...
<set name="pricesSet" cascade="all" table="im_se_price_data_to_money" outer-join="true" lazy="false">
<key foreign-key="money_id"/>
<element type="myapp.common.foundation.basetypes.MoneyWithExchangeRateUserType">
<column name="currency" length="3"/>
<column name="amount" />
<column name="rate" not-null="true"/>
</element>
</set>
</class>
</hibernate-mapping>
PriceData.java:
...
private Set<MoneyWithExchangeRate> pricesSet = new HashSet<MoneyWithExchangeRate>();
protected Set<MoneyWithExchangeRate> getPricesSet() {
return pricesSet;
}
protected void setPricesSet(Set<MoneyWithExchangeRate> prices) {
this.pricesSet = prices;
}
...
MoneyWithExchangeRate.java:
...
public double getExchangeRate() {
return exchangeRate;
}
protected void setExchangeRate(double exchangeRate) {
this.exchangeRate = exchangeRate;
}
public Currency getCurrency() {
return currency;
}
protected long getAmount() {
return amount;
}
protected void setAmount(long amount) {
this.amount = amount;
}
protected void setCurrency(Currency currency) {
this.currency = currency;
}
...
MoneyWithExchangeRateUserType.java:
...
public class MoneyWithExchangeRateUserType implements CompositeUserType, Serializable {
public MoneyWithExchangeRateUserType() {
super();
}
public String[] getPropertyNames() {
return new String[] { "currency", "amount", "rate" };
}
public Type[] getPropertyTypes() {
return new Type[] { Hibernate.STRING, Hibernate.LONG, Hibernate.DOUBLE };
}
public Object getPropertyValue(Object component, int property) throws HibernateException {
MoneyWithExchangeRate money = (MoneyWithExchangeRate) component;
switch (property) {
case 0:
return money.getCurrency();
case 1:
return new Long(money.amount);// yes really access the attribute here directly
case 2:
return new Double(money.getExchangeRate());// yes really access the attribute here directly
default:
throw new ApplicationRuntimeException("That should really never happen: we got a request for property: " + property);
}
}
public void setPropertyValue(Object component, int property, Object value) throws HibernateException {
// intended to be empty
}
public Class returnedClass() {
return MoneyWithExchangeRate.class;
}
public boolean equals(Object money1, Object money2) throws HibernateException {
if (money1 == money2) return true;
if (money1 == null || money2 == null) return false;
return money1.equals(money2);
}
public int hashCode(Object arg0) throws HibernateException {
return arg0.hashCode();
}
public Object nullSafeGet(ResultSet resultSet, String[] names, SessionImplementor session, Object owner) throws HibernateException,
SQLException {
CurrencyService currencyService = (CurrencyService) ServiceRegistry.lookup(CurrencyService.class);
Currency currency = null;
if (resultSet == null) return null;
String currencyAbbreviation = resultSet.getString(names[0]);
// If currency id retrieved ahs a value of 0 or is null then the default
// currency is retrieved. This
// has been done so that there is a compatibility with M1 code
if (currencyAbbreviation == null) {
currency = currencyService.defaultCurrency();
} else {
currency = currencyService.getCurrency(currencyAbbreviation);
}
long value = resultSet.getLong(names[1]);
double rate = resultSet.getDouble(names[2]);
// We have to use the setter here,
// as the corresponding constructor
// will convert the input to the internal representation
MoneyWithExchangeRate m = new MoneyWithExchangeRate();
m.setAmount(value);
m.setCurrency(currency);
m.setExchangeRate(rate);
return m;
}
public void nullSafeSet(PreparedStatement statement, Object value, int index, SessionImplementor session) throws HibernateException,
SQLException {
if (value == null) {
statement.setNull(index, Types.VARCHAR);
statement.setNull(index + 1, Types.INTEGER);
statement.setNull(index + 2, Types.DOUBLE);
} else {
MoneyWithExchangeRate amount = (MoneyWithExchangeRate) value;
String currencyAbbreviation = amount.getCurrency().getAbbreviation();
statement.setString(index, currencyAbbreviation);
statement.setLong(index + 1, amount.amount); // yes, intended to access the attribute directly
statement.setDouble(index + 2, amount.getExchangeRate());
}
}
public Object deepCopy(Object value) throws HibernateException {
return value;
}
public boolean isMutable() {
return false;
}
public Serializable disassemble(Object value, SessionImplementor session) throws HibernateException {
return (Serializable) value;
}
public Object assemble(Serializable cached, SessionImplementor session, Object owner) throws HibernateException {
return cached;
}
public Object replace(Object value, Object arg1, SessionImplementor session, Object arg3) throws HibernateException {
return null;
}
}
Now, putting a breakpoint at the beginning of the nullSafeSet, I see that upon the
successful insert, I get a statement
Code:
insert into im_se_price_data_to_money (id, currency, amount, rate) values (668, ?, ?, ?)
But updating the set gives me a
Code:
delete from im_se_price_data_to_money where id=815 and rate=?
and now the nullSafeSet fails, obviously, with
Code:
2008-02-22 13:05:13,557 DEBUG http-8080-Processor23 org.hibernate.SQL - delete from im_se_price_data_to_money where id=? and rate=?
2008-02-22 13:05:13,566 WARN http-8080-Processor23 org.hibernate.util.JDBCExceptionReporter - SQL Error: 0, SQLState: 22023
2008-02-22 13:05:13,566 ERROR http-8080-Processor23 org.hibernate.util.JDBCExceptionReporter - Der Spaltenindex 3 ist ausserhalb des gültigen Bereichs. Anzahl Spalten: 2.
2008-02-22 13:05:13,567 ERROR http-8080-Processor23 org.hibernate.event.def.AbstractFlushingEventListener - Could not synchronize database state with session
org.hibernate.exception.DataException: could not delete collection rows: [myapp.business.component.supplymasterdata.emea.types.entitytypes.PriceData.pricesSet#815]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:77)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.persister.collection.AbstractCollectionPersister.deleteRows(AbstractCollectionPersister.java:1292)
at org.hibernate.action.CollectionUpdateAction.execute(CollectionUpdateAction.java:54)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:250)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:234)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:144)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:558)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:662)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:632)
at myapp.webapp.support.filter.transaction.TransactionSetupFilter.doFilter(TransactionSetupFilter.java:108)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:215)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
at myapp.webapp.support.filter.responseheader.ResponseHeaderFilter.doFilter(ResponseHeaderFilter.java:43)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:215)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
at myapp.webapp.support.filter.logging.ClickPathRecordingFilter.doFilter(ClickPathRecordingFilter.java:61)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:215)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:210)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:174)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:108)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:151)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:870)
at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:665)
at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:528)
at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:81)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:685)
at java.lang.Thread.run(Thread.java:637)
Caused by: org.postgresql.util.PSQLException: Der Spaltenindex 3 ist ausserhalb des gültigen Bereichs. Anzahl Spalten: 2.
at org.postgresql.core.v3.SimpleParameterList.bind(SimpleParameterList.java:52)
at org.postgresql.core.v3.SimpleParameterList.setLiteralParameter(SimpleParameterList.java:113)
at org.postgresql.jdbc2.AbstractJdbc2Statement.bindLiteral(AbstractJdbc2Statement.java:2106)
at org.postgresql.jdbc2.AbstractJdbc2Statement.setLong(AbstractJdbc2Statement.java:1165)
at org.apache.commons.dbcp.DelegatingPreparedStatement.setLong(DelegatingPreparedStatement.java:239)
at myapp.common.foundation.basetypes.MoneyWithExchangeRateUserType.nullSafeSet(MoneyWithExchangeRateUserType.java:112)
at org.hibernate.type.CompositeCustomType.nullSafeSet(CompositeCustomType.java:219)
at org.hibernate.persister.collection.AbstractCollectionPersister.writeElementToWhere(AbstractCollectionPersister.java:784)
at org.hibernate.persister.collection.AbstractCollectionPersister.deleteRows(AbstractCollectionPersister.java:1256)
... 33 more
2008-02-22 13:05:13,707 ERROR http-8080-Processor23 myapp.webapp.support.filter.transaction.TransactionSetupFilter - doFilter() - internal error while closing down exception: org.springframework.dao.InvalidDataAccessResourceUsageException: could not delete collection rows: [myapp.business.component.supplymasterdata.emea.types.entitytypes.PriceData.pricesSet#815]; nested exception is org.hibernate.exception.DataException: could not delete collection rows: [myapp.business.component.supplymasterdata.emea.types.entitytypes.PriceData.pricesSet#815]
org.springframework.dao.InvalidDataAccessResourceUsageException: could not delete collection rows: [myapp.business.component.supplymasterdata.emea.types.entitytypes.PriceData.pricesSet#815]; nested exception is org.hibernate.exception.DataException: could not delete collection rows: [myapp.business.component.supplymasterdata.emea.types.entitytypes.PriceData.pricesSet#815]
at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:616)
at org.springframework.orm.hibernate3.HibernateTransactionManager.convertHibernateAccessException(HibernateTransactionManager.java:690)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:566)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.processCommit(AbstractPlatformTransactionManager.java:662)
at org.springframework.transaction.support.AbstractPlatformTransactionManager.commit(AbstractPlatformTransactionManager.java:632)
at myapp.webapp.support.filter.transaction.TransactionSetupFilter.doFilter(TransactionSetupFilter.java:108)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:215)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
at myapp.webapp.support.filter.responseheader.ResponseHeaderFilter.doFilter(ResponseHeaderFilter.java:43)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:215)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
at myapp.webapp.support.filter.logging.ClickPathRecordingFilter.doFilter(ClickPathRecordingFilter.java:61)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:215)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:188)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:210)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:174)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:117)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:108)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:151)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:870)
at org.apache.coyote.http11.Http11BaseProtocol$Http11ConnectionHandler.processConnection(Http11BaseProtocol.java:665)
at org.apache.tomcat.util.net.PoolTcpEndpoint.processSocket(PoolTcpEndpoint.java:528)
at org.apache.tomcat.util.net.LeaderFollowerWorkerThread.runIt(LeaderFollowerWorkerThread.java:81)
at org.apache.tomcat.util.threads.ThreadPool$ControlRunnable.run(ThreadPool.java:685)
at java.lang.Thread.run(Thread.java:637)
Caused by: org.hibernate.exception.DataException: could not delete collection rows: [myapp.business.component.supplymasterdata.emea.types.entitytypes.PriceData.pricesSet#815]
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:77)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.persister.collection.AbstractCollectionPersister.deleteRows(AbstractCollectionPersister.java:1292)
at org.hibernate.action.CollectionUpdateAction.execute(CollectionUpdateAction.java:54)
at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:250)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:234)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:144)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:298)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:1000)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:338)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:106)
at org.springframework.orm.hibernate3.HibernateTransactionManager.doCommit(HibernateTransactionManager.java:558)
... 23 more
Caused by: org.postgresql.util.PSQLException: Der Spaltenindex 3 ist ausserhalb des gültigen Bereichs. Anzahl Spalten: 2.
at org.postgresql.core.v3.SimpleParameterList.bind(SimpleParameterList.java:52)
at org.postgresql.core.v3.SimpleParameterList.setLiteralParameter(SimpleParameterList.java:113)
at org.postgresql.jdbc2.AbstractJdbc2Statement.bindLiteral(AbstractJdbc2Statement.java:2106)
at org.postgresql.jdbc2.AbstractJdbc2Statement.setLong(AbstractJdbc2Statement.java:1165)
at org.apache.commons.dbcp.DelegatingPreparedStatement.setLong(DelegatingPreparedStatement.java:239)
at myapp.common.foundation.basetypes.MoneyWithExchangeRateUserType.nullSafeSet(MoneyWithExchangeRateUserType.java:112)
at org.hibernate.type.CompositeCustomType.nullSafeSet(CompositeCustomType.java:219)
at org.hibernate.persister.collection.AbstractCollectionPersister.writeElementToWhere(AbstractCollectionPersister.java:784)
at org.hibernate.persister.collection.AbstractCollectionPersister.deleteRows(AbstractCollectionPersister.java:1256)
... 33 more
Note: "Der Spaltenindex 3 ist ausserhalb des gültigen Bereichs. Anzahl Spalten: 2." means
"The column index 3 is outside the allowed range. columns: 2"
Name and version of the database you are using: PostgreSQL 8
Any idea appreciated :-)
Cheers, Tom.