Monday, November 19, 2012

JavaFX 2 with Spring

I'm going to start this one with a bold statement: I always liked Java Swing, or applets for that matter. There, I said it. If I perform some self analysis, this admiration probably started when I got introduced to Java. Swing was (practically) the first thing I ever did with Java that gave some statisfactionary result and made me able to do something with the language at the time. When I was younger we build home-brew fat clients to manage our 3.5" floppy/CD collection (written in VB and before that in basic) this probably also played a role.

Anyway, enough about my personal rareness. Fact is that Swing has helped many build great applications but as we all know Swing has it drawbacks. For starters it hasn't been evolving since well, a long time. It also requires a lot of boiler plate code if you want to create high quality code. It comes shipped with some quirky design "flaws", lacks out of the box patterns such as MVC. Styling is a bit of a limitation since you have to fall back on the limited L&F architecture, I18N is not build in by default and so on. One could say that developing Swing these days is well, basically going back into time.

Fortunately Oracle tried to change this some years ago by Launching JavaFX. I recall getting introduced to JavaFX on Devoxx (or Javapolis as it was named back then). The nifty demo's looked very promising, so I was glad to see that a Swing successor was finally on its way. This changed from the moment I saw its internals. One of its major drawbacks was that it was based on a dark new syntax (called JavaFX script). In case you have never seen JavaFX script; it looks like a bizarre breed between Java, JSON and JavaScript. Although it is compiled to Java byte-code, and you could use the Java API's from it, integration with Java was never really good.

The language itself (although pretty powerful) required you to spend a lot of time understanding the details, for ending up with, well, again source code, but this time less manageable and supported then plain Java code. As it turned out, I wasn't the only one. A lot of people felt the same (for sure there were other reasons as well) and JavaFX never was a great success.

However, a while ago Oracle changed the tide by introducing JavaFX 2.
First of all they got rid of JavaFX script (which is no longer supported) and turned it into a real native Java SE API (JavaFX 2.2.3 is part of the Java 7 SE update 6) . The JavaFX API now looks more like the familiar Swing API, which is a good thing. It gives you layout managers lookalikes, event listeners, and all those other components you were so used to, but even better. So if you want you can code JavaFX like you did Swing you can, albeit with slightly different syntax and improved architecture. It is also possible now to intermix existing Java Swing applications with JavaFX.

But there is more. They introduced an XML based markup language that allows you to describe the view. This has some advantages, first of all coding in XML works faster then Java. XML can be more easily be generated then Java and the syntax for describing a view is simply more compact. It is also more intuitive to express a view using some kind of markup, especially if you ever did some web development before. So, one can have the view described in FXML (thats how its called), the application controllers separate from the view, both in Java, and your styling in CSS (yeah, so no more L&F, CSS support is standard). You can still embed Java (or other languages) directly in the FXML; but this is probably not what you want (scriptlet anti-pattern). Another nice thing is support for binding. You can bind each component in your view to the application controller by putting an fx:id attribute on the view component and an @FXML annotation on the instance variable in the application controller. The corresponding element will then be auto injected, so you can change its data or behavior from inside your application controller. It also turns out that with some lines of code you can painlessly integrate the DI framework of your choice, isn't that sweet?

And what about the tooling?
Well, first of all there is a plug-in for Eclipse (fxclipse) which will render you FXML on the fly. You can install it via Eclipse market place:


The plug-in will render any adjustment you make immediately:



Note that you need at least JDK7u6 for this plug-in to work. If your JDK is too old you'll get an empty pane in eclipse. Also, if you create a JavaFX project I needed to put the jfxrt.jar manually on my build classpath. You'll find this file in %JAVA_HOME%/jre/lib.

Up until know the plug-in doesn't help you visually (by drag& drop) but that there a separate IDE: scene builder. This builder is also integrated in Netbeans, for AFAIK there is no support for eclipse yet so you'll have to run it separately if you want to use it. The builder lets you develop FXML the visual way, using drag&drop. Nice detail; scene builder is in fact written in JavaFX. Then you also have a separate application called scenic view which does introspection on a running JavaFX application and shows how it is build up. You get a graph with the different nodes and their hierarchical structure. For each node you can see its properties and so forth:


Ok, so lets start with some code examples. The first thing I did was design my demo application in scene builder:


I did this graphically by d&d the containers/controlers on to the view. I also gave the controls that I want to bind to my view and fx:id, you can do that also via scene builder:


For the buttons in particular I also added an onAction (which is the method that should be executed on the controller once the button is clicked):


Next I added the controller manually in the source view in eclipse. There can only be one controller per FXML and it should be declared in the top level element. I made two FXML's, one that represents the main screen and one that acts as the menu bar. You probably want a division of your logic in multiple controllers, rather then stuffing to much in a single controller – single responsibility is a good design guideline here. The first FXML is “search.fxml” and represents the search criteria and result view:
<?xml version="1.0" encoding="UTF-8"?>

<?import java.lang.*?>
<?import java.util.*?>
<?import javafx.scene.control.*?>
<?import javafx.scene.control.Label?>
<?import javafx.scene.control.cell.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.paint.*?>

<StackPane id="StackPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" prefHeight="400.0" prefWidth="600.0" xmlns:fx="http://javafx.com/fxml" fx:controller="be.error.javafx.controller.SearchController">
  <children>
    <SplitPane dividerPositions="0.39195979899497485" focusTraversable="true" orientation="VERTICAL" prefHeight="200.0" prefWidth="160.0">
      <items>
        <GridPane fx:id="grid" prefHeight="91.0" prefWidth="598.0">
          <children>
     <fx:include source="/menu.fxml"/>
            <GridPane prefHeight="47.0" prefWidth="486.0" GridPane.columnIndex="1" GridPane.rowIndex="5">
              <children>
                <Button fx:id="clear" cancelButton="true" mnemonicParsing="false" onAction="#clear" text="Clear" GridPane.columnIndex="1" GridPane.rowIndex="1" />
                <Button fx:id="search" defaultButton="true" mnemonicParsing="false" onAction="#search" text="Search" GridPane.columnIndex="2" GridPane.rowIndex="1" />
              </children>
              <columnConstraints>
                <ColumnConstraints hgrow="SOMETIMES" maxWidth="338.0" minWidth="10.0" prefWidth="338.0" />
                <ColumnConstraints hgrow="SOMETIMES" maxWidth="175.0" minWidth="0.0" prefWidth="67.0" />
                <ColumnConstraints hgrow="SOMETIMES" maxWidth="175.0" minWidth="10.0" prefWidth="81.0" />
              </columnConstraints>
              <rowConstraints>
                <RowConstraints maxHeight="110.0" minHeight="10.0" prefHeight="10.0" vgrow="SOMETIMES" />
                <RowConstraints maxHeight="72.0" minHeight="10.0" prefHeight="40.0" vgrow="SOMETIMES" />
              </rowConstraints>
            </GridPane>
            <Label alignment="CENTER_RIGHT" prefHeight="21.0" prefWidth="101.0" text="Product name:" GridPane.columnIndex="0" GridPane.rowIndex="1" />
            <TextField fx:id="productName" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="1" />
            <Label alignment="CENTER_RIGHT" prefWidth="101.0" text="Min price:" GridPane.columnIndex="0" GridPane.rowIndex="2" />
            <Label alignment="CENTER_RIGHT" prefWidth="101.0" text="Max price:" GridPane.columnIndex="0" GridPane.rowIndex="3" />
            <TextField fx:id="minPrice" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="2" />
            <TextField fx:id="maxPrice" prefWidth="200.0" GridPane.columnIndex="1" GridPane.rowIndex="3" />
          </children>
          <columnConstraints>
            <ColumnConstraints hgrow="SOMETIMES" maxWidth="246.0" minWidth="10.0" prefWidth="116.0" />
            <ColumnConstraints fillWidth="false" hgrow="SOMETIMES" maxWidth="537.0" minWidth="10.0" prefWidth="482.0" />
          </columnConstraints>
          <rowConstraints>
            <RowConstraints maxHeight="64.0" minHeight="10.0" prefHeight="44.0" vgrow="SOMETIMES" />
            <RowConstraints maxHeight="68.0" minHeight="0.0" prefHeight="22.0" vgrow="SOMETIMES" />
            <RowConstraints maxHeight="68.0" minHeight="10.0" prefHeight="22.0" vgrow="SOMETIMES" />
            <RowConstraints maxHeight="68.0" minHeight="10.0" prefHeight="22.0" vgrow="SOMETIMES" />
            <RowConstraints maxHeight="167.0" minHeight="10.0" prefHeight="14.0" vgrow="SOMETIMES" />
            <RowConstraints maxHeight="167.0" minHeight="10.0" prefHeight="38.0" vgrow="SOMETIMES" />
          </rowConstraints>
        </GridPane>
        <StackPane prefHeight="196.0" prefWidth="598.0">
          <children>
            <TableView fx:id="table" prefHeight="200.0" prefWidth="200.0">
              <columns>
                <TableColumn prefWidth="120.0" resizable="true" text="OrderId">
                  <cellValueFactory>
                    <PropertyValueFactory property="orderId" />
                  </cellValueFactory>
                </TableColumn>
                <TableColumn prefWidth="120.0" text="CustomerId">
                  <cellValueFactory>
                    <PropertyValueFactory property="customerId" />
                  </cellValueFactory>
                </TableColumn>
                <TableColumn prefWidth="120.0" text="#products">
                  <cellValueFactory>
                    <PropertyValueFactory property="productsCount" />
                  </cellValueFactory>
                </TableColumn>
                <TableColumn prefWidth="120.0" text="Delivered">
                  <cellValueFactory>
                    <PropertyValueFactory property="delivered" />
                  </cellValueFactory>
                </TableColumn>
                <TableColumn prefWidth="120.0" text="Delivery days">
                  <cellValueFactory>
                    <PropertyValueFactory property="deliveryDays" />
                  </cellValueFactory>
                </TableColumn>
                <TableColumn prefWidth="150.0" text="Total order price">
                  <cellValueFactory>
                    <PropertyValueFactory property="totalOrderPrice" />
                  </cellValueFactory>
                </TableColumn>
              </columns>
            </TableView>
          </children>
        </StackPane>
      </items>
    </SplitPane>
  </children>
</StackPane>
On line 11 you can see that I configured the application controller class that should be used with the view. On line 17 you can see the import of the separate menu.fxml which is shown here:
<?xml version="1.0" encoding="UTF-8"?>

<?import javafx.scene.control.*?>
<?import javafx.scene.layout.*?>
<?import javafx.scene.control.MenuItem?>

<Pane prefHeight="465.0" prefWidth="660.0" xmlns:fx="http://javafx.com/fxml" fx:controller="be.error.javafx.controller.FileMenuController">
 <children>
  <MenuBar layoutX="0.0" layoutY="0.0">
   <menus>
    <Menu mnemonicParsing="false" text="File">
     <items>
      <MenuItem text="Exit" onAction="#exit" /> 
     </items>
    </Menu>
   </menus>
  </MenuBar>
 </children>
</Pane>
On line 7 you can see that it uses a different controller. In Eclipse, if you open the fxclipse view from the plug-in you will get the same rendered view as in scene builder. Its however convenient if you want to make small changes in the code to see them directly reflected: The code for launching the application is pretty standard:
package be.error.javafx;

import javafx.application.Application;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.stage.Stage;

public class TestApplication extends Application {

 private static final SpringFxmlLoader loader = new SpringFxmlLoader();

 @Override
 public void start(Stage primaryStage) {
  Parent root = (Parent) loader.load("/search.fxml");
  Scene scene = new Scene(root, 768, 480);
  primaryStage.setScene(scene);
  primaryStage.setTitle("JavaFX demo");
  primaryStage.show();
 }

 public static void main(String[] args) {
  launch(args);
 }
}
The only special thing to note is that we extend from Application. This is a bit boiler plate code which will for example make sure that creating of the UI happens on the JavaFX application thread. You might remember such stories from Swing, where every UI interaction needs to occur on the event dispatcher thread (EDT), this is the same with JavaFX. You are by default on the “right thread” when you are called back by the application (in for example action listeners alike methods). But if you start the application or perform long running tasks in separate threads you need to make sure you start UI interaction on the right thread. For swing you would use SwingUtilities.invokeLater() for JavaFX: Platform.runLater().
More special is our SpringFxmlLoader:
package be.error.javafx;

import java.io.IOException;
import java.io.InputStream;

import javafx.fxml.FXMLLoader;
import javafx.util.Callback;

import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class SpringFxmlLoader {

 private static final ApplicationContext applicationContext = new AnnotationConfigApplicationContext(SpringApplicationConfig.class);

 public Object load(String url) {
  try (InputStream fxmlStream = SpringFxmlLoader.class
    .getResourceAsStream(url)) {
   System.err.println(SpringFxmlLoader.class
    .getResourceAsStream(url));
   FXMLLoader loader = new FXMLLoader();
   loader.setControllerFactory(new Callback<Class<?>, Object>() {
    @Override
    public Object call(Class<?> clazz) {
     return applicationContext.getBean(clazz);
    }
   });
   return loader.load(fxmlStream);
  } catch (IOException ioException) {
   throw new RuntimeException(ioException);
  }
 }
}
The highlighted lines show the custom ControllerFactory. Without setting this JavaFX will simply instantiate the class you specified as controller in the FXML without anything special. In that case the class will not be Spring managed (unless you would be using CTW/LTW AOP). By specifying a custom factory we can define how the controller should be instantiated. In this case we lookup the bean from the application context. Finally we have our two controllers, the SearchController:
package be.error.javafx.controller;

import java.math.BigDecimal;
import java.net.URL;
import java.util.ResourceBundle;

import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.fxml.FXML;
import javafx.fxml.Initializable;
import javafx.scene.control.Button;
import javafx.scene.control.TableView;
import javafx.scene.control.TextField;

import org.apache.commons.lang.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;

import be.error.javafx.model.Order;
import be.error.javafx.model.OrderSearchCriteria;
import be.error.javafx.model.OrderService;

public class SearchController implements Initializable {

 @Autowired
 private OrderService orderService;
 @FXML
 private Button search;
 @FXML
 private TableView<Order> table;
 @FXML
 private TextField productName;
 @FXML
 private TextField minPrice;
 @FXML
 private TextField maxPrice;

 @Override
 public void initialize(URL location, ResourceBundle resources) {
  table.setColumnResizePolicy(TableView.CONSTRAINED_RESIZE_POLICY);
 }

 public void search() {
  OrderSearchCriteria orderSearchCriteria = new OrderSearchCriteria();
  orderSearchCriteria.setProductName(productName.getText());
  orderSearchCriteria
    .setMaxPrice(StringUtils.isEmpty(minPrice.getText()) ? null:new BigDecimal(minPrice.getText()));
  orderSearchCriteria
    .setMinPrice(StringUtils.isEmpty(minPrice.getText()) ? null: new BigDecimal(minPrice.getText()));
  ObservableList<Order> rows = FXCollections.observableArrayList();
  rows.addAll(orderService.findOrders(orderSearchCriteria));
  table.setItems(rows);
 }

 public void clear() {
  table.setItems(null);
  productName.setText("");
  minPrice.setText("");
  maxPrice.setText("");
 }
}
The highlighted lines in respective order:
  • Auto injection by Spring, this is our Spring managed service which we will use to lookup data from
  • Auto injection by JavaFX, our controls that we need to manipulate or read from in our controller
  • Special init method to initialize our table so columns will auto resize when the view is enlarged
  • action listener style callback which is invoked when the search button is pressed
  • action listener style callback which is invoked when the clear button is pressed
Finally the FileMenuController which does nothing special besides closing our app:
package be.error.javafx.controller;

import javafx.application.Platform;
import javafx.event.ActionEvent;

public class FileMenuController {

 public void exit(ActionEvent actionEvent) {
  Platform.exit();
 }
}
And finally the (not so exciting) result:

After searching:


Making view wider, also stretches the columns:


The file menu allowing us the exit:


After playing a bit with JavaFX2 I was pretty impressed. There are also more and more controls coming (I believe there is already a browser control and such). So I think we are on the right track here.

Wednesday, September 12, 2012

Hibernate's Map behavior

Hibernate has a map construct which makes mapping key/value pairs rather elegant. Let's take a "car" as our example domain object. In our imagination we could store the options of a car as key/value pairs. The options could be things like: engine type, color and so forth.

The hibernate annotations that we would need to map this car entity are pretty straightforward:
@Entity
public class Car {

 @Id
 @GeneratedValue(strategy = GenerationType.AUTO)
 private Long id;

 @ElementCollection
 @CollectionTable(name = "options_map", joinColumns = @JoinColumn(name = "car_id"))
 private Map<String, String> options = new HashMap<String, String>();

 public Long getId() {
  return id;
 }

 public Map<String, String> getOptions() {
  return options;
 }
}
We created a "car" entity (and table) having a one-to-many relationship to our map storing the options as key/value pairs in map backed by a options_map table. Now, what will happen with entries having null as value?

You can argue that storing an entry with a null value does not make much sense in most scenarios. This is probably true as the only way you would do something like this is to keep an exhaustive list of possible key values. Even so, it would be a not so good solution since you would be duplicating your entire key set for each and every car. A better solution would be to map the key values to enumeration instead. Or, if the set of keys are also needed outside of your application, store them in a separate table for example.

Leaving this discussion behind, the thing I want to point out here is that whichever reason you have to store entries with null as their values, think again: hibernate ignores such entries completely.

Lets have a look at how hibernate handles this. In the first example we will create and store a car with three options; engine type, color and cupholder.
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { InfrastructureContextConfiguration.class, TestDataContextConfiguration.class })
@Transactional
public class CarTest {

 @PersistenceContext
 private EntityManager entityManager;

 @Autowired
 private DataSource dataSource;

 @Test
 public void testWithNonNullValues() throws Exception {
  // Save 3 options with non-null value
  Car merc = new Car();
  merc.getOptions().put("AIRCO", "DUAL-AUTO");
  merc.getOptions().put("GEARBOX", "AUTO");
  merc.getOptions().put("CUPHOLDER", "YES");

  entityManager.persist(merc);
  entityManager.flush();
  entityManager.clear();

  merc = entityManager.find(Car.class, merc.getId());
  Assert.assertEquals(3, merc.getOptions().size());
 }
}
Nothing special happens when we run this test, the test passes: all ok.

Next, we choose to drop an option (the cupholder), but we want to keep all keys existent for each car. So we will again have 3 keys, but the cupholder will have a null value:
 public void testWithNullValue() throws Exception {
  // Save 3 properties, 2 with non-null value, 1 with null value
  Car bmw = new Car();
  bmw.getOptions().put("AIRCO", "DUAL-AUTO");
  bmw.getOptions().put("GEARBOX", "AUTO");
  bmw.getOptions().put("CUPHOLDER", null);

  entityManager.persist(bmw);
  entityManager.flush();
  entityManager.clear();

  bmw = entityManager.find(Car.class, bmw.getId());
  // FAIL: java.lang.AssertionError: expected:<2> but was:<3>
  Assert.assertEquals(3, bmw.getOptions().size());
 }
The test will fail (java.lang.AssertionError: expected:<2> but was:<3>). When we load our entity in an empty session it will only contain 2 options instead of 3. The cupholder option was removed (or simply not stored in database).

This can be a problem; during the lifetime of your transaction the map will continue to have a key 'CUPHOLDER' with a null value. Only after reading data from database again (which will for example happen in a new transaction) the car loaded from database will only have two properties left, since only the properties with a non-null value were stored.
Btw; in our test case we simulate a 'new transaction' by simply flushing and clearing the session; this forces hibernate to load the data freshly again from database as would be the behaviour in a new transaction.

Finally, hibernate also ignores existing records with a null value already in the database (which could be inserted by other users). In the example below we save two options (with non-null value) and then we sneakily add another option with a null value using a plain JDBC insert (we also perform a count directly afterwards to make 100% sure there are effectively three options in the database).
 public void testWithNullValueInsertedByOtherUser() throws Exception {
  // Save 2 options with non-null value
  Car audi = new Car();
  audi.getOptions().put("AIRCO", "DUAL-AUTO");
  audi.getOptions().put("GEARBOX", "AUTO");

  entityManager.persist(audi);
  entityManager.flush();
  entityManager.clear();

  JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
  // Add another option with null value bypassing hibernate (simulating other user)
  jdbcTemplate.update("insert into options_map (car_id, options_key, options) values (?, 'CUPHOLDER', null)",
    audi.getId());
  // Verify to make sure we now have 3 options in database
  Assert.assertEquals(3,
    jdbcTemplate.queryForInt("select count(*) from options_map where car_id = ?", audi.getId()));

  audi = entityManager.find(Car.class, audi.getId());
  // FAIL: java.lang.AssertionError: expected:<2> but was:<3>
  Assert.assertEquals(3, audi.getOptions().size());

 }
The test will fail again: even though our car has 3 options in the database, hibernate will only retrieve the options with a non-null value. So instead of our expected 3 options we only get 2 options in our options map.

Conclusion: during the transaction the domain model will fully reflect the data we stored in it.
If we test for the key 'CUPHOLDER' to be present it would return true. However, in subsequent transactions, which might read the car and its options we saved before, we will only discover two options since an option with null value is not saved by hibernate and completely ignored. This would render our operation non-idempotent if we really depended on the cupholder key to be present.

To take this a bit further, if you are using Hibernate in combination with an Oracle databases you also need to be careful with empty string values. As you are probably aware Oracle transforms an empty string (a String of length equal to 0) to a null value. So if you would have a column of type varchar2 for example, inserting '' as value will be transformed to null.
So from the Java point of view, when using Oracle, you will also "lose" properties which do not only have a null value but also have a blank string value.

It is wise to think your null/empty string strategy well through, certainly when using Hibernate in combination with the map construct. The safest solution is probably to not depend on any entry with a blank or null value at all when using this construct.

Tuesday, July 17, 2012

Pro Spring MVC: with Web Flow

Today not a technical entry, but a small announcement.
The book I wrote together with Marten Deinum is finally out!
I got my copy yesterday and it looks rather good, if I say so myself:-)


My personal contributions are the chapters on :
  • Testing Spring MVC applications
  • Spring Web Flow
  • Building applications with Spring Web Flow
  • Advanced Spring Web Flow
  • Spring Security

I started writing on it around half December, the final touch (at least by us, authors) was made somewhere half June. I never expected that writing would be so difficult, it was quite a challenge. What I originally estimated as four weeks of work (two weeks full-time during vacation time and two weeks only evenings, after normal working hours) turned out to be almost five months! Writing good quality software is difficult, but writing content for a book is an even greater challenge.

At the end it was quite an interesting experience to go through the entire development cycle of a book. It was also a good learning path for the technology itself. Even though I have been working a long time with these technologies, it still took a lot of investigation to be able to write adequately about it.

Anyway, if you are doing front-end work with Spring MVC, Web Flow2 or are intrigued by these technologies, go take a look! The book is available on Amazon or directly from Apress.

Happy reading!

Monday, May 21, 2012

Migrating from Hibernate 3 to 4 with Spring integration

This week it was time to upgrade our code base to the latest Hibernate 4.x.
We postponed our migration (still being on Hibernate 3.3) since the newer maintenance releases of the 3.x branch required some API changes which were apparently still in flux. An example is the UserType API which was still showing flaws and was going to be finalized in Hibernate 4.

The migration went quite smooth. Adapting the UserType's to the new interface was pretty straightforward. There were some hick ups here and there but nothing painful.

The thing to watch out for is the Spring integration. If you have been using Spring with Hibernate before, you will be using the LocalSessionFactoryBean (or AnnotationSessionFactoryBean) for creating the SessionFactory. For hibernate 4 there is a separate one in its own package: org.springframework.orm.hibernate4 instead of org.springframework.orm.hibernate3. The LocalSessionFactoryBean from the hibernate 4 package will do for both mapping files as well as annotated entities, so you only need one for both flavors.

When the upgrade was done, all our tests were running and the applications were also running fine on Tomcat using the local Hibernate transaction manager. However when running on Glassfish using JTA transactions (and Spring's JtaTransactionManager) we got the "No Session found for current thread" when calling sessionFactory.getCurrentSession();

So it seemed I missed something in relation with the JTA configuration. As you normally do with the Spring-Hibernate integration, you let Spring drive the transactions. You specify a transaction manager and Spring makes sure all resources are registered with the transaction manager and eventually call commit or rollback. Spring will integrate with Hibernate, so it makes sure the session is flushed before a transaction commit.

When using hibernate 3 and the hibernate 3 Spring integration, the session is bound to a thread local. This technique allow you to use the sessionFactory.getCurrentSession() to obtain a open session anywhere inside the active transaction. This is both the case for the local HibernateTransactionManager as for the JtaTransactionManager.

However, as of the hibernate 4 integration, the hibernate session will be bound to the currently running JTA transaction instead.

From a user point of view nothing changes as sessionFactory.getCurrentSession() will still do its job. But when running JTA this means that Hibernate must be able to lookup the transaction manager to able to register the session with the currently running transaction. This is new if you are coming from Hibernate 3 with Spring, in fact, you did not have to configure anything in regards to transactions in your Hibernate SessionFactory (or LocalSessionFactoryBean) configuration.
As it turned out, with the Hibernate 4 Spring integration the transaction manager lookup configuration is effectively done by hibernate and not by Spring's LocalSessionFactoryBean.
The solution was pretty simple; adding this to the Hibernate (LocalSessionFactoryBean) configuration solved our problems:

org.hibernate.service.jta.platform.internal.SunOneJtaPlatform

"SunOneJtaPlatform" should then be replaced by a subclass that reflects your container.
See the API docs for the available subclasses.
What this class does is actually telling Hibernate how it can lookup the transaction manager for your environment. If you don't configure this there will be nothing for Hibernate to bind the session to hence throwing the exception.

There is also a property:
hibernate.current_session_context_class
Which should point to org.springframework.orm.hibernate4.SpringSessionContext, but this automatically done by the LocalSessionFactoryBean, so there is no need to specify it in the configuration.

As this solved my "No Session found for current thread" problem, there was still another one. Changes made to the database inside a transaction were not visible after a successful transaction commit. After some research I found out that no one was calling session.flush(). While with the hibernate 3 integration there was a SpringSessionSynchronization registered which would call session.flush() prior transaction commit (in the beforeCommmit method).

In the hibernate 4 integration there is a SpringFlushSynchronization registered, which, as its name says, will perform a flush also. However, this is only implemented in the actual “flush” method of the TransactionSynchronization, and this method gets never called.

I raised an issue for this on Spring bugtracker, including two sample application which illustrates the problem clearly.
The first uses Hibernate 3 and the other is the exact same application but this time using hibernte 4. The second will show that no information is actually persisted to database (both apps are tested under the latest Glassfish 3.1.2)

Until now the best workaround seems to be creating a flushing Aspect that wraps around @Transactional annotations. Using the order attribute you can order the transactional annotation to be applied before your flushing Aspect. This way your Aspect is still running inside the transaction, and is able to flush the session. It can obtain the session the normal way by injecting the SessionFactory (one way or the other) and then calling sessionFactory.getCurrentSession().flush().



 


or, if using the annotation configuration:
@EnableTransactionManagement(order=1)

Update:
There was some feedback on the issue. As it turns out it does not seem to be a bug in the Spring Hibernate integration, but a missing Hibernate configuration element. Apparently the "hibernate.transaction.factory_class" needs to be set to JTA, the default is JDBC which depends on the Hibernate Transaction API for explicit transaction management. By setting this to JTA the necessary synchronizations are registered by hibernate which will perform the flush. See the Spring https://jira.springsource.org/browse/SPR-9404

Update 2:
As it turns out, after correcting the configuration as proposed on the preceding issue, there was still a problem. I'm not going to repeat everything, you can find detailed information in the second bug entry I submitted here: https://jira.springsource.org/browse/SPR-9480
It basically comes down to the fact that in a JTA scenario with the JtaTransactionFactory configured, hibernate does not detect that it is in a transaction and will therefore not execute intermediate flushes. With the JtaTransactionFactory configured, you are expected to control the transaction via the Hibernate API rather then via an external (Spring in our case) mechanism. One of the side effects is that you might be reading stale data in some cases.

Example:

//[START TX1]
Query query = session.createQuery("from Person p where p.firstName = :firstName and p.lastName = :lastName");
Person johnDoe = (Person)query.setString("firstName","john").setString("lastName","doe").uniqueResult();
johnDoe.setFirstName("Jim");
Person jimDoe = (Person)query.setString("firstName","jim").setString("lastName","doe").uniqueResult();
//[END TX1]
What happens is that when performing the second query at line 5, hibernate should detect that it should flush the previous update which was made to the attached entity on line 4 (updating the name from 'john' to 'jim').

However, because hibernate is not aware it is running inside an active transaction, the intermediate flushing doesn't work. It will only flush once before the transaction commits. This results in stale data, as the 2nd query would not find "jim" and return null instead. The solution (see the reply from Juergen Hoeller in the issue) is configuring hibernate.transaction.factory_class to org.hibernate.transaction.CMTTransactionFactory instead. At first I was a bit sceptical, as CMT makes be thing about EJB containers. However, if you read the Java doc on CMTTransaction it does make sense:


/**
 * Implements a transaction strategy for Container Managed Transaction (CMT) scenarios.  All work is done in
 * the context of the container managed transaction.
 *
 * The term 'CMT' is potentially misleading; the pertinent point simply being that the transactions are being
 * managed by something other than the Hibernate transaction mechanism.
 *
 * Additionally, this strategy does *not* attempt to access or use the {@link javax.transaction.UserTransaction} since
 * in the actual case CMT access to the {@link javax.transaction.UserTransaction} is explicitly disallowed.  Instead
 * we use the JTA {@link javax.transaction.Transaction} object obtained from the {@link TransactionManager}

After that everything seems to work fine. So to conclude, if you want hibernate to manage the JTA transaction via the UserTransaction, you should use JtaTransactionFactory. In that case you must use the Hibernate API to control the transaction. If there is someone else managing the transaction (Spring, EJB container ...) you should use CMTTransactionFactory instead. Hibernate will then revert to registering synchronisations, by checking for active javax.transaction.Transaction using the javax.transaction.TransactionManager. If there is any other issue popping up I'll update this entry accordingly.