Friday, September 28, 2007

Log4JCategoryLog

Apache common logging 1.0 used Log4JCategoryLog. In version 1.1, replace it with Log4JLogger. The commons-logging.properties file will look like this,
org.apache.commons.logging.Log=org.apache.commons.logging.impl.Log4JLogger

web.xml version 2.4

In web.xml version 2.4, taglib shall be defined inside a jsp-config section.

Monday, September 17, 2007

升级FC6->FC7

折腾了好几个晚上,想把自己的一台老爷机从FC6升级到FC7,未果。
根本的原因是,FC7不认PATA的硬盘。就算用RESCUE碟都不行。
记住,自此一定不能升级内核。

Tuesday, September 11, 2007

Hibernate and Struts - Identifier Altered

In our application, we used Hibernate 3.1.2, Spring 1.2.6 and Struts 1.2.4. One of the page flows was to gather input from a form and insert a record into database then go back to the list page.

We saw messages like that,
org.hibernate.HibernateException: identifier of an instance of ... was altered ...
...
org.hibernate.event.def.DefaultFlushEventListener.onFlush...
...
In the beginning of investigation, we found the Hibernate related code did not flush after insert. We added the flush, but it didn't solve the problem.

Then we read Spring document and found HibernateTemplate recommended over Session. So we replaced Session with Template. But it didn't solve the problem.

Because because it had no problem going back to list after update, we tried to use saveOrUpdate and replace save. But it was the same.

Finally we noticed the Hibernate flush was scheduled after the page was forwarded, no matter what the setting of transaction.flush_before_completion was true or false. We also noticed the struts always recovered the form after forward. We tried to reset the data bean in the form right after insert. And this time, it solved the problem.

Thursday, September 06, 2007

Hibernate and MS SQL - Nullability Problem

The nullability problem happens when Hibernate 3 deletes an instance with any null value property defined not nullable.
org.springframework.orm.hibernate3.HibernateSystemException: not-null property references a null or transient value: com.ag.applications.forms.entity.qstnaire.QstnaireBean.qstnaireNam; nested exception is org.hibernate.PropertyValueException: not-null property references a null or transient value: com.ag.applications.forms.entity.qstnaire.QstnaireBean.qstnaireNam
org.hibernate.PropertyValueException: not-null property references a null or transient value: com.ag.applications.forms.entity.qstnaire.QstnaireBean.qstnaireNam
To get rid of the error, edit the hbm.xml file and change the not-null="true" to "false".

However, it is necessary to define some properties not nullable. In this case, modify the application and let the property has non-null value or get the instance from database by primary key, then delete it.

This problem was reported to Hibernate on August, 2007. No known release fixes it.

Wednesday, September 05, 2007

Hibernate and MS SQL - Delete Operation

When does delete operation, application prompts message,
Data Access Error: Write operations are not allowed in read-only mode (FlushMode.NEVER) - turn your Session into FlushMode.AUTO or remove 'readOnly' marker from transaction definition.
This is because Spring 1.2 is incompatible with Hibernate 3. I heard Spring 2.0 solved this problem, but I found a workaround solution with Spring 1.2.

Here is the solution, in applicationContext.xml, set the property "checkWriteOperations" of "org.springframework.orm.hibernate3.HibernateTemplate" to false.

Tuesday, September 04, 2007

Hibernate and MS SQL - Unsupported prepareStatement

Insert operation generated by Hibernate didn't work with MS SQL. I was using Hibernate 3.1.2, WAS 6.1, JVM 1.5. And I was testing two JDBC drivers.

For IBM ConnectJDBC for MS SQL,
java.sql.SQLException: [IBM][SQLServer JDBC Driver]Unsupported method: Connection.prepareStatement
at com.ibm.websphere.jdbc.base.BaseExceptions.createException(Unknown Source)
at com.ibm.websphere.jdbc.base.BaseExceptions.getException(Unknown Source)
at com.ibm.websphere.jdbc.base.BaseConnection.prepareStatement(Unknown Source)
at com.ibm.websphere.jdbcx.base.BasePooledConnection.prepareStatement(Unknown Source)
at com.ibm.websphere.jdbcx.base.BaseConnectionWrapper.prepareStatement(Unknown Source)
at com.ibm.ws.rsadapter.jdbc.WSJdbcConnection.pmiPrepareStatement(WSJdbcConnection.java:3980)
at com.ibm.ws.rsadapter.jdbc.WSJdbcConnection.prepareStatement(WSJdbcConnection.java:3854)
For DirectData ConnectJDBC for MS SQL,
java.sql.SQLException: [DataDirect][SQLServer JDBC Driver]Unsupported method: Connection.prepareStatement(String, String[])
at com.ddtek.jdbc.base.BaseExceptions.createException(Unknown Source)
at com.ddtek.jdbc.base.BaseExceptions.getException(Unknown Source)
at com.ddtek.jdbc.base.BaseConnection.prepareStatement(Unknown Source)
at com.ddtek.jdbcx.base.BasePooledConnection.prepareStatement(Unknown Source)
at com.ddtek.jdbcx.base.BaseConnectionWrapper.prepareStatement(Unknown Source)
at com.ibm.ws.rsadapter.jdbc.WSJdbcConnection.pmiPrepareStatement(WSJdbcConnection.java:3980)
at com.ibm.ws.rsadapter.jdbc.WSJdbcConnection.prepareStatement(WSJdbcConnection.java:3854)
There is a saying that the Connection.prepareStatement(String sql, String[] columnNames) is not supported. Workaround solution is to set the "hibernate.jdbc.use_get_generated_keys" to false in the hibernate settings, either in the properties or in the cfg.xml.

Here is the definition of the property.
Enable use of JDBC3 PreparedStatement.getGeneratedKeys() to retrieve natively generated keys after insert. Requires JDBC3+ driver and JRE1.4+, set to false if your driver has problems with the Hibernate identifier generators. By default, tries to determine the driver capabilites using connection metadata.

eg. true|false

Here is the quote from mdiamonte in DataDirect forum.
Against SQL Server 2000, the driver can not implement this method correctly in the general case because SQL Server 2000 does not allow you to return the value of an arbitrary column from an insert, update or delete statement. At least not with out making an extra round trip to the server, which negates the purpose of this method.

I have seen implementations of this method by other drivers where the value returned will be the value of the identity column regardless of which column was actually asked for. I believe that implementation is bad. I feel it is much worse for a driver to return incorrect information than it is to not be able to return the information. Assuming the id column in the statement is an identity column, then Hibernate may have just gotten lucky that it worked in the testing that they did.