Pojo binding strategy for hibernate.

Hi guys,

I’ve discovered the existence of Vaadin 2 days ago and I love what I see so far, congrats guys!
I struggle with simple data binding with Hibernate for my prototype, and would love some advice/recommendation.
I’ve a long/strategic question that any Vaadin-Hibernate developer should have asked to himself, and count on your experience to answer.

The web site, JavaBlackBelt.com, is layered with

  • a presentation layer (call the service layer, and may call a dao directly for a query)
  • a Service layer (business logic, use the dao layer)
  • a Dao layer (JPA queries, and simple save operation)

I’m doing a prototype with Vaadin as the presentation layer.

The application uses Spring and the Open session in view pattern (a filter to open/close the hibernate session).
I don’t want JPA entities to remain in memory between 2 requests. It would bring old-dirty data problems and concurrency side effects.
In the current application, for each request, I re-query everything I need. I rely on the hibernate 1st land 2nd level cache mechanism to limit the load on the DB.
For example, I don’t keep a User instance in the session. Instead, I keep the user id and re-query the user from hibernate for each request.

In my Vaadin prototype, I’ve a Vaadin Button and Table.
In the ClickListener, of the button, I:
1- call the Dao layer to query auctions.

List auctionItemListDB = daoFacade.getAuctionItemDao().getAll();

2- Put these auctions in a BeanItemContainer

BeanItemContainer bid = new BeanItemContainer(auctionItemList);

3- Attach the BeanItemContainer to the table

auctionTable.setContainerDataSource(bic);

4- Try to limit the displayed columns to “id, name, openDate”.

Object[] visibleCols = {“name”, “initPrice”, “openingDate”};
auctionTable.setVisibleColumns(visibleCols);

It fails at step 3, with a LazyInitializationException on AuctionItem.bids.
The bids property is a collection of Bids (1 to many relationship).
I figured out to include the vaadin application url in the org.springframework.orm.hibernate3.support.OpenSessionInViewFilter, and it works fine.

But it brings a question: why does the Table touch the AuctionItem.bids property? I don’t want the Vaadin Table to touch the bids property, but it does.
How to prevent that?
I tried to perform step 4 before step 3 (which is not allowed by the API, but I used a workaround), with no effect: the Table still tries to access the bids property.

Vaadin probably contains a simple way to express that, and I don’t know it.

[font=Courier New]
auctionTable = Table( use auctionItemList, and limit yourself to {“name”, “initPrice”, “openingDate”}).

[/font]
I’ve read the article “Using Hibernate with Vaadin” - http://vaadin.com/wiki/-/wiki/Main/Using%20Hibernate%20with%20Vaadin?p_r_p_185834411_title=Using%20Hibernate%20with%20Vaadin
And I had a look at the HbnContainer class.
I don’t feel comfortable with it because the HbnContainer touches the Hibernate Session. I don’t want the conainter to merge any entity under the covers, for example. I leave that to the Dao layers through explicit calls.

Let’s take a little bit more complete scenario to see what should be kept in memory and when/who triggers Hibernate.

Let’s say that when I double click on a row of the Vaadin Table, I popup a modal window with an edit form. The Form/Window contains a save and cancel button.

Let’s see when my code is activated:

  1. Request 1: My table fills with data.
    I call the Dao to get a list of 200 Pojos, and I give it to the Vaadin Table.
    The Vaadin Table keeps them in memory (to refesh when I scroll for example?).

  2. Request 2: Double click on a row.
    I get the concerned Pojo from the Vaadin Table, I extract the id and I throw the instance of the Pojo away.
    I get a fresh instance of the Pojo from the DB (with the ID, explicit Dao call) and create a new Vaadin Windows/form to edit it. I fill the text fields with the Pojo properties.

  3. Request 3: User presses the save button.
    I get the concerned Pojo from the form window, I extract the id and I throw the instance of the Pojo away.
    I get a fresh instance of the Pojo from the DB (with the ID), ask the form to transfer the edit fields in the property of the fresh Pojo, and persist the fresh Pojo through an explicit Dao call.

Does this makes sense to you?
How do I do the binding of step 3 with Vaadin (put form data into fesh Pojo, not into the initial old Pojo)?

Summary -------

Question 1: How to make the Vaadin Table not touch AuctionItem.bids?

auctionTable = Table( use auctionItemList, and limit yourself to {“name”, “initPrice”, “openingDate”}).

Question 2: Does my scenario with request scoped transaction, and old pojos throw away, make sense to you with Vaadin? How do I do the binding of step 3 (put form data into fresh Pojo)?

Hi John!

I’ve been working quite much with the Vaadin/Hibernate/JPA combination and I’ll try to share my experiences with you.

The lazy loading exception occurs because you do not have an open session which the proxy object could use to load the lazy loaded reference, but this you probably already knew. I don’t know exactly why the BeanItem or BeanItemContainer tries to call on the bids field, maybe someone else can clarify that, but a solution to your problem would be to populate your own container. Since you are not using the actual Pojos anywhere, I don’t see a reason why you would have to use the BeanItemContainer, but instead you could use for example an IndexedContainer. It’s a bit more work, but it would solve your problem, since you can control directly which fields are accessed. Here’s a example method you could use to create the container:

public static IndexedContainer createContainer(Collection<AuctionItem> collection) {
		// Create an empty container
		IndexedContainer container = new IndexedContainer();
		// Set the properties you want to use
		container.addContainerProperty("name", String.class, null);
		container.addContainerProperty("initPrice", Float.class, null);
		container.addContainerProperty("openingDate", Date.class, null);
		
		for(AuctionItem ai : collection) {
			Item item = container.addItem(ai.getId());
			item.getItemProperty("name").setValue(ai.getName());
			item.getItemProperty("initPrice").setValue(ai.getInitPrice());
			item.getItemProperty("openingDate").setValue(ai.getOpeningDate());
			// populate the values of other fields
		}
		
		return container;
	}

Pay attention to the this code
Item item = container.addItem(ai.getId());

The addItem() method takes as input an object. I’ve decided to use just the id of your AuctionItem for two reasons. Firstly, I assume the id is unique and secondly if you anyway are not going to use anything else from the Pojo than the id, there’s no need to use the entire Pojo as the item id even though it is possible.

Your way to face the problem does make sense. Personally, I’ve solved the same problem a bit differently. I’ve used a session-per-request pattern, where I basically just throw away the sessions (but keep the Pojos) and open new ones for each request. When I need to store the changes made to a pojo, I just reattach it to the opened session. This pattern has seemed to work rather well and additionally I get the benefit that I do not need to re-query each object from the database when I want to make changes.

Now to address your problem, if you are using the form component, then I do not thing you are able to change the item data source on the fly without it having effects on the values you’ve entered in the UI fields. I’m afraid that you’ll have to manually copy the data from the old Pojo to the newly fetched one. Maybe someone else can come up with a better solution?

  • Kim

Thank you Kim for your answers, it helps a lot.

I’ll probably indeed create a generic container doing what you show, that I could call in one line:

Container c = new BeanCopyContainer(auctionList, {"name", "initPrice", "openingDate"});

to put things into an IndexedContainer. It would also reduce the amount of memory kept in the session. The size of the session is probably a major concern/attention point for Vaadin programmers, isn’t it?

About my question 2, how would you manage the following a scenario? :

The user has 2 browser tabs open.
In tab 1, he is editing his user profile in a form. He only views 2 fields (name and e-mail).
But the User class has 20 fields. For example, the sum of his contribution points.

In tab 2, he is actively contributing. He leaves tab 1 for 10 minutes, in edit mode, while he contributes in tab 2. The his amounts of contribution points (stored in the user DB table) grows.

Back in tab 1, he changes his e-mail address in the form and press save.
I don’t want his save to override the recent contribution points value in the DB, with his old contribution point value from that old User Pojo bound to the form.

Isn’t that scenario common for Vaadin programmers?

Of course, we won’t hold a DB lock for 10 minutes.
A more reasonable option would be to use optimistick locking from Hibernate, and prevent the user edit save.
My option would be (when the save button clicks) to reload the user pojo from the DB, change the 2 fields concerned by the form; and save that user.

Well, I wouldn’t say that is a major concern, remember that the states are stored on the server so for example using the Pojo as the item id will not significantly effect the client side (browser) performance, as the Pojo is in the server’s memory. Only what can be viewed in the browser is actually sent over to the client as JSON. Of course the performance depends on how many concurrent users you have and on what kind of a server are running the application, but that’s the server admin’s problem and memory is cheap, right? :wink: On a serious note, maybe someone else can give more facts on the scalability.

I’ve used optimistic locking to solve this problem. If the application has two users modifying the same data simultaneously, the first user will be able to save the changes without problem, but when the second user tries to save his changes, he will receive a notification saying that the data was modified by someone else during the time he was editing the data and that he should check which data is “correct” before possibly overriding just made changes.

This problem isn’t directly related to Vaadin, but to all applications using centralized data storage. There are different ways to solve this problem, mine is just one of them :slight_smile:

  • Kim

I’ve taken your suggestion to use the IndexedContainer.

After having reviewing the source code of BeanItemContainer, BeanItem, MethodProperty, IndexedContainer and its inner classes; I’ve made a clone of BeanItemContainer and BeanItem.

I’m not sure to have understood everything I did, but they work.
The new version, takes a Collection of pojos, copies the values and keeps the ids (Hibernate primary keys); and throw away the pojos.

It’s used like this:

Table auctionTable = ...
List<AuctionItem> auctionItemListDB = auctionItemDao().getAll();
auctionTable.setContainerDataSource(new BeanCopyItemContainer<AuctionItem>(auctionItemListDB, {"name", "initPrice", "openingDate"}));

Vaadin BeanItem(Container) and my classes probably need a common ancestor: there is much copy/paste; and I’ve been annoyed by the package visibility of the Filter class. I’ll give you the source code in a few weeks if you are interested. Now it’s too fresh and there are probably bugs to be fixed and features to be added.

Hi John,

In session-per-request pattern, in your “save my profile” button’s action handler you just get an actual User object from a DAO (eg from a session.get(userr id) hbn call) and just set name and e-mail fields. Then, on tx commit, only those two fields will be updated to the database by a hibernate and you don’t have conflicts with your contribution points, isn’t it ?

For sure, you may also handle StaleObject Exception from hiberntate in your servlet filter or even force transaction commit in your handler to ensure to catch stale exception and silently retry (less nicer thoughts)

Yes, Dmitri. It’s exactly what I’m doing. In some kind of “generic” form, so I don’t have to specify this behavior for all my application forms.