All I wanted was to store images in the database through Hibernate. It all look so easy. Add a byte array property, add it as a binary type to the Hibernate mapping and off you go. So far so good. I'm developing with MySQL where everything worked the way it's supposed to. Image data goes into the database and comes out when required, no problem.

Enter Oracle. The first fetching of data from the database worked like a charm, but the second brought up a nice ORA error. ORA-17027: Stream has already been closed. Good stuff. Digging through the result on Google it seems that I'm not the only one having this problem. The Hibernate gang is quick enough to point out that the error lies with the Oracle JDBC driver. The problem is that a the default type for byte arrays with the OracleDialect is long raw. When a query is fired with a stream column (e.g. long raw) in the field list it has to be read last, or else the stream will be closed once the next column is read.

One suggestion was to put the corresponding column to the end of the mapping file, but that didn't work at all for me. Another one was to use a different JDBC driver. All that didn't work for me, but there had to be a way.

According to Hanson Char the solution is to use a blob to store the byte array. Since you can't just set the type of the property to blob and be done with it, he offers a simple solution: add blob handling to your model. I tried it and it works. The thing I didn't like about it was to have all that stuff from the JDBC packages in my model, so I tried to put it into a UserType. It does work to a certain extent, but since you don't have direct access to the session in it I couldn't implement everything Hibernate's own BlobType does. Respecting the hibernate.jdbc.usestreamsfor_binary is probably the most important thing.

So I just derived a combination of Hanson's Code and Hibernate's BlobType wrapped it into a ByteBlobType. It uses most of the BlobType implementation and only wraps the conversion between byte array and blob.

Just before I was done I had a look what kinds of subclasses of UserType lurks around in my project. It didn't really surprise me, but of course Spring offers a type to do just that, but Spring-style. Doh! It's called BlobByteArrayType. Though it can be used as a normal Hibernate type, it needs an additional property set in your session factory provided by Spring's LocalSessionFactoryBean, a LobHandler. That's a no-brainer though and can be done quite easily:

 <bean id="sessionFactory"
   <property name="lobHandler" ref="defaultLobHandler"/>

 <bean id="defaultLobHandler" 

That should work for most databases, of course not with all versions of Oracle. For the latter you're better off using the OracleLobHandler. The default handler works with Oracle 10g to a certain extent which I can confirm, but apparently not all of it. If you're not using Spring, then the above ByteBlobType might be an option for you.

Tags: java, oracle
Tags: links, rails, ruby

Due to some developers not so keen about running the tests I got back to my trusty friend Continuous Integration for a project I'm currently working on. Being a big fan of CruiseControl I looked for similar solutions for the Rails and Ruby market. There are several tools you can use, and they have several ups and downs.

  • The Slim Solution: Continuous Builder
    A small application disguised as a Rails plugin which you can hook into your Subversion post-commit. It's part of the Rails repository and, though it hasn't been cared for in a while, still fulfils its purpose. The commit hook runs checks out the source and runs the tests. Should one or (gasp) several of them fail, it will bug you with a nice email pointing out the abounding problems. It's the simplest solution, but it needs a separate environment in your Rails application. Ryan Daigle suggests to use a separate build environment to keep the build away from the rest of your application. This sort of repelled me from Continuous Builder since it also needs a database to work this way.

  • The Hell-Hound: Cerberus
    It's simple and easy to use, it comes as a RubyGem and is up and running in no time. Once installed, projects are added through the cerberus command.
    cerberus add http://some.server/with/my/svn/trunk

    By default, it uses the last part of your Subversion URL to create a name for your project, so you might want to add the option APPLICATION_NAME=<my_project> to specify a nicer name than just trunk.

    When you're done you can easily build your project with cerberus build <my_project>. Cerberus will check for changes in your repository and only do its magic when there are changes. It supports notification through email (duh), Jabber, RSS, CampFire and IRC. It can be run as a commit hook or as a cron job.

  • The Classic: CruiseControl.rb
    Of course I didn't discover it until Cerberus was up and running. CruiseControl.rb is the natural evolution that eventually had to come after CruiseControl, the bigger brother of the Java world. Both are products of ThoughtWorks.

    CruiseControl.rb is, of course, a Rails application. It doesn't need a database though. It's pretty easy to use. You just unpack it, add a project and start the required processes:

    cruise add my_project http://some.server/with/my/svn/trunk
    cruise start

    That's about it. After that you can point your browser to the dashboard application and enjoy the magic of freshly made builds.

    Downsides include support for a wider variety of version control systems. For now, only Subversion is on the list.

All of the above are pretty neat and depending on your continuous integration needs, can be recommended. If you need a fancy web application, there's no way around CruiseControl.rb. Its clear advantage is also that it polls the repository without needing cron or a commit hook. Otherwise I'd recommend Cerberus over Continuous Builder because it's a little bit more flexible and offers more notification options. What I realised is that even in a Rails project where the tests are mainly the proof that code hasn't been broken with a change, it's necessary to check this automatically. No more "But it works on my machine" and that warm, fuzzy feeling of not getting email after you checked in.

Tags: rails, ruby