|
|
@@ -0,0 +1,433 @@ |
|
|
|
[[using-hibernate-with-vaadin]] |
|
|
|
Using Hibernate with Vaadin |
|
|
|
--------------------------- |
|
|
|
|
|
|
|
Using Hibernate in Toolkit application, Basic |
|
|
|
http://en.wikipedia.org/wiki/Create,_read,_update_and_delete[CRUD] |
|
|
|
actions for persistent POJO |
|
|
|
|
|
|
|
image:img/screenshot.png[Example CRUD application] |
|
|
|
|
|
|
|
Check out related source code with subversion (svn co |
|
|
|
http://dev.vaadin.com/svn/incubator/hbncontainer/) or view it with trac |
|
|
|
http://dev.vaadin.com/browser/incubator/hbncontainer/. Download the |
|
|
|
latest version as a Vaadin add-on from the Vaadin Directory (https://vaadin.com/directory/component/hbncontainer) |
|
|
|
|
|
|
|
_The project in incubator currently has a prototype of using |
|
|
|
associations. The article is outdated on that part_. |
|
|
|
|
|
|
|
Hibernate is the de facto standard when it comes to Java and Object |
|
|
|
Relational Mapping. Since version 3 onwards one can actually drop the de |
|
|
|
facto part as Hibernate 3 implements Java Persistency API with some |
|
|
|
optional packages. Hibernate is backed by a strong support from both |
|
|
|
commercial players and open source community. It is an important part of |
|
|
|
popular JBoss Application Server. |
|
|
|
|
|
|
|
As an open source project with an industry proven maturity, Hibernate |
|
|
|
makes a perfect combo with IT Mill Toolkit. Hibernate is in a key role |
|
|
|
in many projects built or supported by IT Mill. The way Hibernate is |
|
|
|
used varies a lot due different kinds of architectures and requirements. |
|
|
|
Largest questions are usually how to work with Hibernate session, |
|
|
|
transactions and how to tie entity beans into toolkit components. |
|
|
|
|
|
|
|
In this article and example application I'll show you how to implement |
|
|
|
session-per-request pattern for Hibernate session handling and present |
|
|
|
some patterns to do |
|
|
|
http://en.wikipedia.org/wiki/Create,_read,_update_and_delete[CRUD] |
|
|
|
actions of a simple entity bean. As I'm a sport fanatic, instead of |
|
|
|
storing cats and other mammals to DB we'll build a simple *WorkoutLog* |
|
|
|
application to store the details of our jogging sessions. Download the |
|
|
|
source package to see full source code. |
|
|
|
|
|
|
|
Note that this is not trying to be a yet another Hibernate tutorial. |
|
|
|
Although we'll stay in rather basic tricks, I expect the reader to have |
|
|
|
some experience on ORM and IT Mill Toolkit. The purpose of this tutorial |
|
|
|
is to show an example how to do simple Hibernate session handling in |
|
|
|
Toolkit application and explain some patterns how to entity objects can |
|
|
|
be tied into GUI. |
|
|
|
|
|
|
|
[[preparing-the-project]] |
|
|
|
Preparing the project |
|
|
|
~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
|
|
|
|
If you want want to learn by doing, it is time to put your hands on |
|
|
|
dirt. Create a new web application project in your favorite IDE, throw |
|
|
|
in latest `toolkit.jar` and all needed Hibernate related libraries. |
|
|
|
Prepare your database and configure Hibernate. Combo I chose when |
|
|
|
writing this article was Eclipse, WTP and MySQL 5, but any option should |
|
|
|
be fine. |
|
|
|
|
|
|
|
If you want to get started really easily, check out the Eclipse project |
|
|
|
from svn repository. This is done simply with subclipse plugin or via |
|
|
|
command line svn co http://dev.vaadin.com/svn/incubator/hbncontainer/. |
|
|
|
The project containtains embedded database( http://hsqldb.org/[HSQLDB] |
|
|
|
), all needed required libraries and the source code for the example |
|
|
|
project itself. That is an easy way to start experimenting with Toolkit |
|
|
|
and Hibernate. You will also need a servlet container, Tomcat is a good |
|
|
|
option. |
|
|
|
|
|
|
|
As I hate all xml configuration I created DB mappings with annotations. |
|
|
|
Below is the one and only entity class we'll be using in this example. |
|
|
|
Create it in and possibly test your Hibernate configuration with a |
|
|
|
simple test application. |
|
|
|
|
|
|
|
[source,java] |
|
|
|
.... |
|
|
|
@Entity |
|
|
|
public class Workout { |
|
|
|
@Id |
|
|
|
@GeneratedValue(strategy=GenerationType.AUTO) |
|
|
|
private Long id; |
|
|
|
private Date date = new Date(); |
|
|
|
private String title = " -- new workout -- "; |
|
|
|
private float kilometers; |
|
|
|
|
|
|
|
public Workout() {} |
|
|
|
|
|
|
|
public Long getId() { |
|
|
|
return id; |
|
|
|
} |
|
|
|
|
|
|
|
private void setId(Long id) { |
|
|
|
this.id = id; |
|
|
|
} |
|
|
|
|
|
|
|
public Date getDate() { |
|
|
|
return date; |
|
|
|
} |
|
|
|
|
|
|
|
public void setDate(Date date) { |
|
|
|
this.date = date; |
|
|
|
} |
|
|
|
|
|
|
|
public String getTitle() { |
|
|
|
return title; |
|
|
|
} |
|
|
|
|
|
|
|
public void setTitle(String title) { |
|
|
|
this.title = title; |
|
|
|
} |
|
|
|
|
|
|
|
public float getKilometers() { |
|
|
|
return kilometers; |
|
|
|
} |
|
|
|
|
|
|
|
public void setKilometers(float kilometers) { |
|
|
|
this.kilometers = kilometers; |
|
|
|
} |
|
|
|
} |
|
|
|
.... |
|
|
|
|
|
|
|
Also create a new Tookit application, configure it in web.xml. |
|
|
|
|
|
|
|
[[using-session-per-request-pattern]] |
|
|
|
Using session-per-request pattern |
|
|
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ |
|
|
|
|
|
|
|
Proper session handling in Hibernate backed applications is often the |
|
|
|
most difficult problem. Use cases vary from by architecture and load. |
|
|
|
Hibernate is known to be quite strict on session and transaction |
|
|
|
handling, so to save yourself from a headache, I'd suggest you to make |
|
|
|
it right. There is a lot's of good documentation about different session |
|
|
|
handling patterns in hibernate.org. |
|
|
|
|
|
|
|
Using session-per-request pattern is often a safe bet for Toolkit |
|
|
|
application. It is maybe the most common pattern among all Servlet based |
|
|
|
applications. When doing data manipulation we'll use the same session |
|
|
|
during the whole request and in the end of the request make sure that |
|
|
|
session and transaction is properly finalized. When implemented |
|
|
|
properly, session-per-request pattern guarantees that number of |
|
|
|
Hibernate sessions is in control, sessions are properly closed and |
|
|
|
sessions are flushed regularly. A good combo of characteristics for a |
|
|
|
multi-user web application. |
|
|
|
|
|
|
|
By Toolkits nature, session-per-request pattern is actually kind of |
|
|
|
wrong. Toolkit is a general purpose GUI framework and programmer does |
|
|
|
not need to think about requests and responses at all. Actually Toolkit |
|
|
|
applications and components don't know nothing about requests. It its |
|
|
|
the web terminal that does all the web magic. Another option is to use |
|
|
|
session-per-application or even session-per-transaction like one would |
|
|
|
do with SWING or other destop application. Always evaluate your |
|
|
|
requirements, use cases and available computing resources to have the |
|
|
|
optimal session handling pattern. |
|
|
|
|
|
|
|
To ensure that we are using only one Hibernate session per http request |
|
|
|
is the easy part. We can use Hibernates `getCurrentSession()` to retrieve |
|
|
|
thread local session instance. As we always want to actually use the |
|
|
|
session I build a helper method that will also begin a database |
|
|
|
transaction. In our !WorkoutLog we will always be using this method to |
|
|
|
get session reference. |
|
|
|
|
|
|
|
[source,java] |
|
|
|
.... |
|
|
|
/** |
|
|
|
* Used to get current Hibernate session. Also ensures an open Hibernate |
|
|
|
* transaction. |
|
|
|
*/ |
|
|
|
public Session getSession() { |
|
|
|
Session currentSession = HibernateUtil.getSessionFactory() |
|
|
|
.getCurrentSession(); |
|
|
|
if(!currentSession.getTransaction().isActive()) { |
|
|
|
currentSession.beginTransaction(); |
|
|
|
} |
|
|
|
return currentSession; |
|
|
|
} |
|
|
|
.... |
|
|
|
|
|
|
|
Closing is bit more tricky. One way around would be to use a servlet |
|
|
|
filter. You can find examples of this from hibernate.org. But we'll keep |
|
|
|
toolkits terminal independence in mind and don't pollute our program |
|
|
|
with servlet specific code. To properly implement session-per-request |
|
|
|
pattern we'll need to familiarize ourselves to a feature in Toolkits |
|
|
|
terminal. Ideally toolkit programmer don't need to care about terminal |
|
|
|
at all, but now we need to hook some logic into the end of (http) |
|
|
|
request that don't exist for the application. For the pattern it is |
|
|
|
essential that session finalization is done always and and after all |
|
|
|
hibernate related stuff is done. With event based programming model |
|
|
|
there is no way we can detect the last database action in the actual |
|
|
|
program code. |
|
|
|
|
|
|
|
The feature we need is `TransactionListeners`. `TransactionListeners` are |
|
|
|
attached to `ApplicationContext` which corresponds to http session in our |
|
|
|
current web terminal. `TransactionListeners` are notified right before |
|
|
|
and right after the clients state is synchronized with server. The |
|
|
|
transaction end is what we need here. I'll attach the transaction |
|
|
|
listener in the applications `init()` like this: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
.... |
|
|
|
getContext().addTransactionListener(new TransactionListener() { |
|
|
|
public void transactionEnd(Application application, |
|
|
|
Object transactionData) { |
|
|
|
// Transaction listener gets fired for all contexts |
|
|
|
// (HttpSessions) toolkit applications, checking to be this one. |
|
|
|
if (application == WorkoutLog.this) { |
|
|
|
closeSession(); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
public void transactionStart(Application application, Object transactionData) { |
|
|
|
} |
|
|
|
}); |
|
|
|
.... |
|
|
|
|
|
|
|
In `closeSession()` the usual Hibernate sessions finalization is done. |
|
|
|
|
|
|
|
[source,java] |
|
|
|
.... |
|
|
|
private void closeSession() { |
|
|
|
Session sess = HibernateUtil.getSessionFactory().getCurrentSession(); |
|
|
|
if(sess.getTransaction().isActive()) { |
|
|
|
sess.getTransaction().commit(); |
|
|
|
} |
|
|
|
sess.flush(); |
|
|
|
sess.close(); |
|
|
|
} |
|
|
|
.... |
|
|
|
|
|
|
|
The sequence diagram below shows how Session handling works with this |
|
|
|
pattern during one (http) request. It is an imaginary server visit that |
|
|
|
fires to event listeners. The first one does some listing and the latter |
|
|
|
re-attaches detached pojo. Note that the second database/Hibernate |
|
|
|
action uses the same Session object as the first one. Note that function |
|
|
|
names are not real ones, but trying to describe the process better. |
|
|
|
|
|
|
|
image:img/sd_s_per_r.gif[Session handling sequence diagram] |
|
|
|
|
|
|
|
Due Toolkit applications do have state, pattern can be defined more |
|
|
|
strictly as a session-per-request-with-detached-objects pattern. As the |
|
|
|
session closes quite often, our entity objects are most likely detached |
|
|
|
by the time we are updating them. So when we have our changes to entity |
|
|
|
object done, it is time to re-attach it to current session to persist |
|
|
|
changes into database. An example of that is below: |
|
|
|
|
|
|
|
[source,java] |
|
|
|
.... |
|
|
|
run.setDate((Date) date.getValue()); |
|
|
|
run.setKilometers(Float.parseFloat(kilomiters.getValue().toString())); |
|
|
|
run.setTitle((String) title.getValue()); |
|
|
|
getSession().merge(run); |
|
|
|
.... |
|
|
|
|
|
|
|
[[attaching-pojos-ui]] |
|
|
|
Attaching POJO's UI |
|
|
|
~~~~~~~~~~~~~~~~~~~ |
|
|
|
|
|
|
|
In this chapter I'll discuss briefly some options to implement basic |
|
|
|
CRUD (Create, Read, Update, Delete) actions for our DB backed Workout |
|
|
|
objects. |
|
|
|
|
|
|
|
[[listing-objects]] |
|
|
|
Listing Objects |
|
|
|
^^^^^^^^^^^^^^^ |
|
|
|
|
|
|
|
If you are learning by doing, I'd suggest that you manually insert some |
|
|
|
rows to your db at this point. Listing an empty database will be quite |
|
|
|
boring. |
|
|
|
|
|
|
|
The most natural way to list our simple Workout object is to put them |
|
|
|
into Table component. To do this there is an easy way and an the right |
|
|
|
way. We'll start with the easy one, but I suggest to use the latter in |
|
|
|
real applications. The code below (the "easy" way) is not in the |
|
|
|
*WorkoutLog* app at all, but you can try it if you want. |
|
|
|
|
|
|
|
[source,java] |
|
|
|
.... |
|
|
|
// prepare tables container |
|
|
|
table.addContainerProperty("date", Date.class, null); |
|
|
|
table.addContainerProperty("kilometers", Float.class, null); |
|
|
|
table.addContainerProperty("title", String.class, null); |
|
|
|
|
|
|
|
// list all Workouts |
|
|
|
List workouts = getSession().createCriteria(Workout.class).list(); |
|
|
|
for (Iterator iterator = workouts.iterator(); iterator.hasNext();) { |
|
|
|
Workout wo = (Workout) iterator.next(); |
|
|
|
// add item to table and set properties from POJO |
|
|
|
Item woItem = table.addItem(wo.getId()); |
|
|
|
woItem.getItemProperty("date").setValue(wo.getDate()); |
|
|
|
woItem.getItemProperty("kilometers").setValue(wo.getKilometers()); |
|
|
|
woItem.getItemProperty("title").setValue(wo.getTitle()); |
|
|
|
} |
|
|
|
.... |
|
|
|
|
|
|
|
In the above example we are using Table's default container, |
|
|
|
`IndexedContainer`. It is a good general purpose container, but using it |
|
|
|
always is not a good option. You have to load the data into it by |
|
|
|
yourself and configure properties etc. It also stores everything in |
|
|
|
memory. In our example it may start to be a problem if you |
|
|
|
do three workouts everyday, live 100 years old and memory chips don't |
|
|
|
get cheaper in the future. But in real application we might really have |
|
|
|
millions of records in DB. I really wouldn't suggest to load that table |
|
|
|
into memory anymore. |
|
|
|
|
|
|
|
As you may guess the way is to build our own container for Workouts. |
|
|
|
Building good containers is one of the most difficult tasks in Toolkit |
|
|
|
programming. There are number of different sub interfaces one might want |
|
|
|
to implement and a whole bunch of methods code. Luckily one can't safely |
|
|
|
throw `UnsupportedOperationExeception` for many of those. It is a boring |
|
|
|
tasks, but it often pays it back later. When you have your container |
|
|
|
ready, it hides lots of DB access from program logic and can be used for |
|
|
|
many components (Selects, Trees, Tables etc). With your own customized |
|
|
|
container you can also tune it to work as you want (memory-consumption |
|
|
|
versus speed etc). |
|
|
|
|
|
|
|
As building a full-featured is not in the scope of this article, it is |
|
|
|
time to throw in a nice helper class called `HbnContainer`. It takes a |
|
|
|
Hibernate entity class and a strategy to get Hibernate session in its |
|
|
|
constructor. It is indexed, ordered, sortable, had a limited supports |
|
|
|
adding/removing items and even ought to be fairly well scalable (by |
|
|
|
number of rows in DB). It is not part of Toolkit as we don't consider it |
|
|
|
ready for framework yet, but we hope to have something similar in the |
|
|
|
core Toolkit in later releases. But feel free to use it in you own |
|
|
|
projects. |
|
|
|
|
|
|
|
With `HbnContainer` loading table with Workouts simplifies quite a bit. |
|
|
|
We need to implement `HbnContainer`.`SessionManager` interface, but it is |
|
|
|
rather easy task as we already have getSession named function in our |
|
|
|
*WorkoutLog*. Create and add table to your application, load its content |
|
|
|
with following code snippet and you should have a Workout listing on |
|
|
|
your screen. |
|
|
|
|
|
|
|
[source,java] |
|
|
|
.... |
|
|
|
table.setContainerDataSource(new HbnContainer(Workout.class, this)); |
|
|
|
.... |
|
|
|
|
|
|
|
[[creating-workouts]] |
|
|
|
Creating workouts |
|
|
|
^^^^^^^^^^^^^^^^^ |
|
|
|
|
|
|
|
Now that we have listing we might want to add some rows via our web |
|
|
|
interface. To create a new Workout instance and store it in to DB we |
|
|
|
have to do the usual Hibernate stuff: instantiate POJO and attach it to |
|
|
|
session. But as I hinted earlier, having a good container will help us |
|
|
|
to do it even simpler. `HbnContainer` supports adding items with the most |
|
|
|
simplest method `addItem()`. |
|
|
|
|
|
|
|
If you look into the implementation, it does all the usual Hibernates |
|
|
|
stuff and returns items generated identifier. In addition this it also |
|
|
|
notifies appropriate listeners that the content of table has changed. So |
|
|
|
by using containers `addItem()` method instead of doing DB persist |
|
|
|
ourselves we don't need to worry about UI updates. Table listens to its |
|
|
|
container changes and changes gets sent to web browsers. |
|
|
|
|
|
|
|
[[updates-and-deletes]] |
|
|
|
Updates and deletes |
|
|
|
^^^^^^^^^^^^^^^^^^^ |
|
|
|
|
|
|
|
Building an editor for our Workout object is a straight forwarded coding |
|
|
|
task. You may organize your code just like you want. `WorkoutEditor` |
|
|
|
class is a simple example implementation that shows and editor in |
|
|
|
floating window. It has fields for workouts properties and it can be |
|
|
|
loaded with Workout instance or with an identifier. In `WorkoutLog` I |
|
|
|
attached a `ValueChangeListener` into table to open editor when user |
|
|
|
clicks a row in table. Save and delete buttons in `WorkoutEditor` |
|
|
|
delegates work back to methods in main application. Delete uses |
|
|
|
containers method and behind the scenes a normal Hibernate object |
|
|
|
deletion. When saving we just reattach detached object using `merge()`. |
|
|
|
|
|
|
|
To avoid "monkey-coding" I'll show one can to use toolkits advanced |
|
|
|
features to automatically create editable fields for items. The |
|
|
|
`WorkoutEditor` class could have created its fields automatically by |
|
|
|
using appropriate Item and a Form component. Also Table supports |
|
|
|
automatic field generation, so why not edit workouts directly in our |
|
|
|
main object listing? |
|
|
|
|
|
|
|
All we need to do is to use `setEditable()` method. In `WorkoutLog` there |
|
|
|
is a button that toggles this feature. Clicking it make table editable, |
|
|
|
clicking it again shows data only. Can't imagine any simpler way to do |
|
|
|
the 'U' part of CRUD. |
|
|
|
|
|
|
|
Both Form and Table components use `FieldFactory` interface to |
|
|
|
automatically create fields for Items properties. There is a simple |
|
|
|
default factory that you almost certainly want to modify for your needs. |
|
|
|
As an example I extended it to set proper resolution for date field and |
|
|
|
also did some other fine tuning. |
|
|
|
|
|
|
|
If you investigate the code a bit you might wonder how the database is |
|
|
|
updated now as we don't seem to call `merge()` or any other method to |
|
|
|
re-attached POJO. When field is updated it knows only about its |
|
|
|
underlaying Property. In this case it is `EntityItemProperty` built by |
|
|
|
`HbnContainer`. Field calls its `setValue()` method and that is where the |
|
|
|
underlaying POJO is re-attached into Hibernate session. |
|
|
|
|
|
|
|
[[adding-custom-columns-to-hbncontainer]] |
|
|
|
Adding custom columns to !HbnContainer |
|
|
|
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ |
|
|
|
|
|
|
|
This last bonus chapter is bit out of scope of the article. But as |
|
|
|
updating is so easy in Table we could ditch our `WorkoutEditor`. But then |
|
|
|
arises a question how to implement deletion. An option is to use Tables |
|
|
|
selection feature and "Delete selected" button. Another one is to use |
|
|
|
context menu option. This is also done in `WorkoutLog`. Both are good |
|
|
|
options, but someday someone will be asking how to add delete button on |
|
|
|
each row. So lets discuss that right away. |
|
|
|
|
|
|
|
Ideologically this is adding a new property to our items. We definitely |
|
|
|
don't want to pollute our entity object by adding `public Button |
|
|
|
getDelete()` to our Workout object. The right place to implement this is |
|
|
|
in custom Container and Item. I implemented an example of this by |
|
|
|
extending `HbnContainer` to `WorkoutListingWithSteroids`. It adds a column |
|
|
|
"actions" (or container property if we are talking "Toolkit") which is a |
|
|
|
layout containing two buttons. |
|
|
|
|
|
|
|
Another possibly little bit easier method is to use recently introduced |
|
|
|
feature in Table component called `ColumnGenerator`. *WorkoutLog* (in svn) |
|
|
|
has an example of this method too. |
|
|
|
|
|
|
|
Check out the example code if you want this kind of behavior. |
|
|
|
|
|
|
|
[[summary]] |
|
|
|
Summary |
|
|
|
~~~~~~~ |
|
|
|
|
|
|
|
Popular open source ORM tool Hibernate is a perfect companion for IT |
|
|
|
Mill Toolkit. Finding the right way to handle session in your |
|
|
|
application is a often the most critical task. Session-per-request |
|
|
|
pattern is a safe choice for Toolkit application, but not the only |
|
|
|
option. DB backed entity objects are used in a usual manner. To use more |
|
|
|
advanced features of toolkit, you'll want to use a custom built |
|
|
|
container-item-property set. ORM is never easy, but it is not a rocket |
|
|
|
science if you use tested industry proven patterns. And if your |
|
|
|
application is going to be a big or old, I can guarantee that you will |
|
|
|
have a nice ROI for hours you spend on it (ORM). |