Sunday, January 15, 2006

What is the time when the clock reads 0000-00-00 00:00:00?

Understanding Zero time sounds like something you might find in a zen koan. Apparently, JDBC doesn't get it. For the past couple of days, I didn't either. But now, I've reached Hibernate nirvana; at least for a moment.

I've been working on getting a very basic resultset back when using Hibernate. The table I'm using has a couple of datetime fields, and by default, they are filled with zero time, such as 0000-00-00 00:00:00 in the format YY-MM-DD HH:mm:SS. I had lots of trouble pulling out the results, as a result. I'd get the following error:

Value '0000-00-00' can not be represented as java.sql.Timestamp

This only happens when I have existing data in my database with a "null" date. When the dates are good, the error doesn't appear.

Assuming that I don't have control over the entry of a null date in the database, I tried resolving the issue in a couple of ways. First I tried to resolve it by adjusting the hibernate mapping. I changed the "type" attribute of the property to be a timestamp, but that didn't work.

After some more research, I realized that a custom UserType might be the answer. I came to that decision after reading this post about the using dates in hibernate. I also read a bit about more date and time issues in the hibernate forums. After going through these discussions, and coming to the understanding that my problems related to the odd date in my database, Things pointed to the custom UserType. I didn't know much about them, but I do know that there's a method specified in the interface named nullSafeGet and that sounds a LOT like what I needed. My strategy was to implement the UserType interface and create a custom class to use as a type in my mapping. Then, when a "0000-00-00 00:00:00" value is retrieved from a DATETIME typed column in the database, I'd simply return null instead of the beginning-of-time value.

I tried this out a moment ago, using a UserType class for Timestamp-like fields, which I found again, on the Hibernate forums. However, by the time the code execution could reach the nullSafeGet method of the class I was using, the error had already been thrown. So, apparently the answer to resolving zero dates does NOT lie in creating a custom UserType for Hibernate. So what's the answer?

The answer is so simple, that I nearly tipped over in my chair when I read it. Interestingly enough, I found the beginning of the trail to the answer in a RadRails forums post. This post points out that all I need to do is add an argument to the JDBC URL. All of the arguments for the JDBC URL for MySQL using the Connector/J driver are here. Specifically, the following argument handles the odd dates


So, a full JDBC URL for connecting to a database that might have zero dates would look like this:


There you have it. The solution turned out not to be Hibernate related at all, but rather, JDBC related.

Sunday, January 01, 2006

I WAS missing something!

I'm glad, and a bit embarrassed that I was so hasty to point a finger at the Struts folks. It turns out that they have a simple solution for resubmitting a form post. And it's exactly what I was hoping for; redirection.

In the action-mapping, and action only needs a forward with a redirect="true" attribute. Such as:

<action name="login" ...>
<forward name="loginSuccess" redirect="true" path="/path/"/>

My faith is restored in this framework, and I promise to do my homework a bit more before I decide to point fingers and spew forth my expert opinions :-)

Struts re-submits form posts? Am I missing something?

I just finished my first bona fide Struts application, and I'm a bit perplexed. I find an inherent problem with the design, and I'm trying to figure out if I missed something.

The problem is that the framework doesn't protect against form re-submission. The Struts framework seems to use a forward instead of a redirect after a form is successfully submitted and validated. If you do this and hit the refresh button on your browser, you'll see the problem. The form re-submits. This is a basic web development problem, and the ramifications are severe. If a form is resubmitted, a user can add as many records to a database as they hit the refresh button, make numerous purchases of the identical set of products, add the exact product to a shopping cart numerous times, etc. I'm surprised that this problem exists in such a widely used framework!

This problem is an obvious one, and I'm SURE that someone else has figured out an elegant solution to it. So I'm not about to give up on the framework altogether. However, I'm beginning to be skeptical of it. And I'm not the only one.

In my opinion, the solution involves a different workflow. Struts uses a Post -> Forward workflow. For successful form submissions, a Post -> Redirect -> Get (PRG) workflow is much better. Struts does provide support for tokens to see if a form is submitted more than once, but in my opinion, tokens aren't the way to solve the problem. Not only does it leave the server open to tampering, it also poses usability problems for the user.

Now, I don't claim to be any sort of expert in Struts matters. I just know that forwarding and allowing a form to be resubmitted is dead wrong, even if tokens can help check to see of a form is being resubmitted. In my simple understanding of Struts and ActionForwards, I do have an idea for a solution. Seeing an ActionMapping in the struts-config.xml, where there's a form, there's a <forward>. When the form is successfully validated without any errors, the ActionServlet should send the HttpResponse.redirect() instead of the HttpResponse.forward(), which I am assuming is what happens.

So, first, I think I'll look a little deeper into what other people have done to solve this problem,. If I can't find anything that seems to work, I'm going to subclass the ActionServlet as PRGActionServlet, and make it redirect to an ActionForward instead of forward. And MAYBE I'll add support for a different XML element called <redirect>. I don't know, but I'll report back with what I decide to do.