views:

675

answers:

3

I am using hibernate 3.2.6, and spring 2.5.6 The database Im connecting to is: DB2 for Z OS 390 V8.1

When I run my testcase (below) I save the test file object. Hibernate DOES Save the object to the database, but my test blows out after the save, where it's trying to update the object with the correct id. I think that it's not getting the correct id (it looks like it's getting a '0' for the id), or Im not even sure why its trying to update the object that is already persisted. I dont know if this is a spring, hibernate, mapping, dialect, or etc issue.

My config:

The business object:

<hibernate-mapping>

    <class name="cat.edis.tmiweb.business.File" table="FILE">

     <id name="id" type="java.lang.Long" column="gen_file_id">
      <generator class="native"></generator>
     </id>

     <property name="creationTimeStamp" type="java.util.Date" column="crte_ts" />
     <property name="name" type="java.lang.String" column="file_nm" />
     <property name="type" type="java.lang.String" column="file_typ_desc" />
     <property name="description" type="java.lang.String" column="file_desc" />
     <property name="length" type="java.lang.Long" column="file_lgth" />
     <property name="contentBlob" type="blob" column="file_cntnt" />

    </class>

</hibernate-mapping>

The pojo:

/*
 * Created on Oct 9, 2009
 *
 */
package cat.edis.tmiweb.business;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Blob;
import java.sql.SQLException;
import java.util.Date;

import org.hibernate.Hibernate;

/**
 * @author dudekta
 */
public class File {
    private Long id;
    private Date creationTimeStamp;
    private String name;
    private String type;
    private String description;
    private Long length;
    private byte[] content;


    /** Don't invoke this. Used by Hibernate only. */
    public void setContentBlob(Blob imageBlob) {
     this.content = this.toByteArray(imageBlob);
    }

    /** Don't invoke this. Used by Hibernate only. */
    public Blob getContentBlob() {
     return Hibernate.createBlob(this.content);
    }

    private byte[] toByteArray(Blob fromBlob) {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        try {
         return toByteArrayImpl(fromBlob, baos);
        } catch (SQLException e) {
         throw new RuntimeException(e);
        } catch (IOException e) {
         throw new RuntimeException(e);
        } finally {
         if (baos != null) {
          try {
           baos.close();
          } catch (IOException ex) {
          }
         }
        }
       }

    private byte[] toByteArrayImpl(Blob fromBlob, ByteArrayOutputStream baos)
    throws SQLException, IOException {
    byte[] buf = new byte[4000];
    InputStream is = fromBlob.getBinaryStream();
    try {
     for (;;) {
      int dataSize = is.read(buf);

      if (dataSize == -1)
       break;
      baos.write(buf, 0, dataSize);
     }
    } finally {
     if (is != null) {
      try {
       is.close();
      } catch (IOException ex) {
      }
     }
    }
    return baos.toByteArray();
   }

    /**
     * @return Returns the creationTimeStamp.
     */
    public Date getCreationTimeStamp() {
        return creationTimeStamp;
    }
    /**
     * @param creationTimeStamp
     *            The creationTimeStamp to set.
     */
    public void setCreationTimeStamp(Date creationTimeStamp) {
        this.creationTimeStamp = creationTimeStamp;
    }
    /**
     * @return Returns the description.
     */
    public String getDescription() {
        return description;
    }
    /**
     * @param description
     *            The description to set.
     */
    public void setDescription(String description) {
        this.description = description;
    }
    /**
     * @return Returns the id.
     */
    public Long getId() {
        return id;
    }
    /**
     * @param id
     *            The id to set.
     */
    public void setId(Long id) {
        this.id = id;
    }
    /**
     * @return Returns the length.
     */
    public Long getLength() {
        return length;
    }
    /**
     * @param length
     *            The length to set.
     */
    public void setLength(Long length) {
        this.length = length;
    }
    /**
     * @return Returns the name.
     */
    public String getName() {
        return name;
    }
    /**
     * @param name
     *            The name to set.
     */
    public void setName(String name) {
        this.name = name;
    }
    /**
     * @return Returns the type.
     */
    public String getType() {
        return type;
    }
    /**
     * @param type
     *            The type to set.
     */
    public void setType(String type) {
        this.type = type;
    }
    /**
     * @return Returns the content.
     */
    public byte[] getContent() {
        return content;
    }

    /**
     * @param content
     *            The content to set.
     */
    public void setContent(byte[] content) {
        this.content = content;
    }
}

The application context:

<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd"&gt;

    <!--  Webservices -->
    <bean id="FPSService" class="cat.edis.tmiweb.services.fps.FPSServiceImpl">
     <constructor-arg type="java.lang.String" value="http://localhost:8888/someendpoint" />
     <constructor-arg type="java.lang.String" value="bill" />
     <constructor-arg type="java.lang.String" value="will" />
    </bean>

    <!--  Database stuff that we should separate out-->

    <bean id="fileDAO" class="cat.edis.tmiweb.dao.FileDAOImpl">
     <property name="sessionFactory">
      <ref bean="TMISessionFactory" />
     </property>
    </bean>

    <bean id="TMIDataSource" class="cat.cis.template.spring.util.TUFDataSource" destroy-method="close">
     <property name="poolName" value="db2OS390GenData" />
    </bean>

    <bean id="TMISessionFactory" class="org.springframework.orm.hibernate3.LocalSessionFactoryBean">
     <property name="dataSource" ref="TMIDataSource" />

     <property name="mappingResources">
      <list>
       <!-- PUT HIBERNATE MAPPING FILES HERE -->
       <value>cat/edis/tmiweb/business/File.hbm.xml</value>
      </list>
     </property>
     <property name="hibernateProperties">
      <map>
       <entry>
        <key>
         <value>hibernate.show_sql</value>
        </key>
        <value>true</value>
       </entry>
       <entry>
        <key>
         <value>hibernate.default_schema</value>
        </key>
        <value>N4GK001$</value>
       </entry>
       <entry>
        <key>
         <value>hibernate.dialect</value>
        </key>
        <ref bean="TMIDialect" />
       </entry>
      </map>
     </property>
    </bean>

    <bean id="TMIDialect" factory-bean="TMIDialectFactory" factory-method="instance" />

    <bean id="TMIDialectFactory" class="cat.cis.template.spring.util.EnvironmentBeanStrategyFactory">
     <property name="envKey" value="tuf.environment" />
     <property name="defaultKey" value="DEV" />
     <property name="target">
      <map>
       <entry key="DEV" value="org.hibernate.dialect.DB2390Dialect" />
       <entry key="TEST" value="org.hibernate.dialect.DB2390Dialect" />
       <entry key="QA" value="org.hibernate.dialect.DB2390Dialect" />
       <entry key="PROD" value="org.hibernate.dialect.DB2390Dialect" />
      </map>
     </property>
    </bean>

    <bean id="transactionManager" class="org.springframework.orm.hibernate3.HibernateTransactionManager">
     <property name="sessionFactory">
      <ref bean="TMISessionFactory" />
     </property>
    </bean>

</beans>

FileDAO:

/*
 * Created on Oct 9, 2009
 */
package cat.edis.tmiweb.dao;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

import cat.edis.tmiweb.business.File;

/**
 * @author dudekta
 *  
 */
public class FileDAOImpl extends HibernateDaoSupport implements FileDAO {

    /**
     * 
     * @param id
     * @return
     */
    public File load(Long id) {
        return (File) getHibernateTemplate().get(File.class, id);
    }

    /**
     * 
     * @param file
     * @return
     */
    public void save(File file) {
        getHibernateTemplate().save(file);
    }

}

My Test:

public void testSaveBlob() {
        FileDAO fileDAO =(FileDAO)SpringTestInitializer.getContext().getBean("fileDAO");
        File file = new File();
        file.setName("TestMe");
        file.setType("txt");
        file.setCreationTimeStamp(new Date());
        file.setDescription("idc");

        byte[] testBytes = new byte[1024];
        byte byteValue = 1;
        for (int i = 0; i < testBytes.length; i++) {
            testBytes[i] = byteValue;
        }
        file.setLength(new Long(testBytes.length));
        file.setContent(testBytes);
        fileDAO.save(file);

        File dbFile = fileDAO.load(file.getId());
        assertNotNull(file);
    }

Output to console from Hibernate:

Initializing Spring....
Spring test context created....
Hibernate: insert into N4GK001$.FILE (gen_file_id, crte_ts, file_nm, file_typ_desc, file_desc, file_lgth, file_cntnt) values (default, ?, ?, ?, ?, ?, ?)
Hibernate: select identity_val_local() from sysibm.sysdummy1
Hibernate: update N4GK001$.FILE set crte_ts=?, file_nm=?, file_typ_desc=?, file_desc=?, file_lgth=?, file_cntnt=? where gen_file_id=?

Trace:

org.springframework.dao.DataIntegrityViolationException: could not update: [cat.edis.tmiweb.business.File#0]; nested exception is org.hibernate.exception.DataException: could not update: [cat.edis.tmiweb.business.File#0] at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:639) at org.springframework.orm.hibernate3.HibernateAccessor.convertHibernateAccessException(HibernateAccessor.java:412) at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:424) at org.springframework.orm.hibernate3.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:374) at org.springframework.orm.hibernate3.HibernateTemplate.save(HibernateTemplate.java:694) at cat.edis.tmiweb.dao.FileDAOImpl.save(FileDAOImpl.java:31) at cat.edis.tmiweb.dao.FileDAOImplTest.testSaveBlob(FileDAOImplTest.java:50) at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:85) at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:58) at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:60) at java.lang.reflect.Method.invoke(Method.java:391) at junit.framework.TestCase.runTest(TestCase.java:154) at org.jmock.core.VerifyingTestCase.runBare(VerifyingTestCase.java:39) 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.RemoteTestRunner.runTests(RemoteTestRunner.java:436) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:311) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) Caused by: org.hibernate.exception.DataException: could not update: [cat.edis.tmiweb.business.File#0] at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:77) at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43) at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2430) at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2312) at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2612) at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:96) at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279) at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263) at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168) 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.springframework.orm.hibernate3.HibernateAccessor.flushIfNecessary(HibernateAccessor.java:390) at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:420) ... 20 more Caused by: COM.ibm.db2.jdbc.DB2Exception: [IBM][CLI Driver][DB2] SQL0100W No row was found for FETCH, UPDATE or DELETE; or the result of a query is an empty table. SQLSTATE=02000

at COM.ibm.db2.jdbc.app.SQLExceptionGenerator.throw_SQLException(Unknown Source) at COM.ibm.db2.jdbc.app.SQLExceptionGenerator.throw_SQLException(Unknown Source) at COM.ibm.db2.jdbc.app.SQLExceptionGenerator.check_return_code(Unknown Source) at COM.ibm.db2.jdbc.app.DB2PreparedStatement.loadParameters(Unknown Source) at COM.ibm.db2.jdbc.app.DB2PreparedStatement.execute2(Unknown Source) at COM.ibm.db2.jdbc.app.DB2PreparedStatement.executeUpdate(Unknown Source) at cat.cis.tuf.server.connector.jdbc.v1.JDBCv1DBStatement.executeUpdate(JDBCv1DBStatement.java:206) at org.hibernate.jdbc.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:23) at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2408) ... 31 more

The exception object:

 ex= DataException  (id=341)
backtrace= Object[35]  (id=349)
cause (NestableRuntimeException)= DB2Exception  (id=325)
cause (Throwable)= DataException  (id=341)
delegate= NestableDelegate  (id=350)
detailMessage= "could not update: [cat.edis.tmiweb.business.File#0]"
sql= "update N4GK001$.FILE set crte_ts=?, file_nm=?, file_typ_desc=?, file_desc=?, file_lgth=?, file_cntnt=? where gen_file_id=?"
sqle= DB2Exception  (id=325)
stackTrace= null

**

Switched to type 4 driver: (still using org.hibernate.dialect.DB2390Dialect)

NEW error! **\

org.springframework.orm.hibernate3.HibernateOptimisticLockingFailureException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1; nested exception is org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
   at org.springframework.orm.hibernate3.SessionFactoryUtils.convertHibernateAccessException(SessionFactoryUtils.java:672)
   at org.springframework.orm.hibernate3.HibernateAccessor.convertHibernateAccessException(HibernateAccessor.java:412)
   at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:424)
   at org.springframework.orm.hibernate3.HibernateTemplate.executeWithNativeSession(HibernateTemplate.java:374)
   at org.springframework.orm.hibernate3.HibernateTemplate.save(HibernateTemplate.java:694)
   at cat.edis.tmiweb.dao.FileDAOImpl.save(FileDAOImpl.java:31)
   at cat.edis.tmiweb.dao.FileDAOImplTest.testSaveBlob(FileDAOImplTest.java:50)
   at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:85)
   at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:58)
   at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:60)
   at java.lang.reflect.Method.invoke(Method.java:391)
   at junit.framework.TestCase.runTest(TestCase.java:154)
   at org.jmock.core.VerifyingTestCase.runBare(VerifyingTestCase.java:39)
   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.RemoteTestRunner.runTests(RemoteTestRunner.java:436)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:311)
   at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192)
Caused by: org.hibernate.StaleStateException: Batch update returned unexpected row count from update [0]; actual row count: 0; expected: 1
   at org.hibernate.jdbc.Expectations$BasicExpectation.checkBatched(Expectations.java:61)
   at org.hibernate.jdbc.Expectations$BasicExpectation.verifyOutcome(Expectations.java:46)
   at org.hibernate.jdbc.NonBatchingBatcher.addToBatch(NonBatchingBatcher.java:24)
   at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2408)
   at org.hibernate.persister.entity.AbstractEntityPersister.updateOrInsert(AbstractEntityPersister.java:2312)
   at org.hibernate.persister.entity.AbstractEntityPersister.update(AbstractEntityPersister.java:2612)
   at org.hibernate.action.EntityUpdateAction.execute(EntityUpdateAction.java:96)
   at org.hibernate.engine.ActionQueue.execute(ActionQueue.java:279)
   at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:263)
   at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:168)
   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.springframework.orm.hibernate3.HibernateAccessor.flushIfNecessary(HibernateAccessor.java:390)
   at org.springframework.orm.hibernate3.HibernateTemplate.doExecute(HibernateTemplate.java:420)
   ... 20 more
A: 

Remove/comment out

 
        File dbFile = fileDAO.load(file.getId());
        assertNotNull(file);

And run your test again from scratch

non sequitor
In your hbm you should refer to clob by java.sql.clob
non sequitor
yeah, the test never actually makes it to the load statement, but alas, I tried it, and removing that did not solve the problem. The error occurs during the save, Hibernate calls "select identity_val_local() from sysibm.sysdummy1" and this returns 0 when it should be returning the id of the object.
good then nice to know that solved it, if you feel it was worth it then up vote(tho i don't think u have enuf ref points to do so) or pick this as the right answer, cheers.
non sequitor
This is not solved. I see what it's doing, but how to fix it is the question.
You need to do your save and load in 2 separate transactions which, if that doesn't solve it then it's an underlying issue with the dialect-engine interaction. But try saving in 1 test (transaction) and then load in another test(transaction), don't run it as one test make the test runner perform 2 separate tests
non sequitor
A: 

From the logs, it looks like hibernate inserts an object, then tries to update it, but it cannot find the object it just inserted. I am not sure, but it might be a problem with the id generator configured. Enable logging for level org.hibernate.type and check what values are bound to the prepared statement - it would give you a better idea on how to debug this.

binil
A: 

It is not solved. Still haven't nailed down why the id returned is '0' , so when Hibernate runs the update sql, there isn't a row with the id of '0'. But even better than why, a solution to make it work correctly would be better.

Tim
Actually the query is returning null, and Hibernate returns 0 as the generatedID. Still not sure how to fix this.