Using Hibernate with Google Web Toolkit (GWT) By Robert Green on November 12, 2007 5:09 PM | Permalink | Comments (4) | TrackBacks (0) Hibernate is an excellent tool which saves many projects from substantial amounts of code. Gone are the days of writing massive datasource layers full of obnoxious JDBC code. As gravy you also get simple query objects, caching, connection pooling and countless other great features. GWT on the other hand provides a robust API using a revolutionary new paradigm for writing javascript and complex dhtml interfaces. The latest version of Hibernate supports annotations, meaning you can ditch all of the XML that was previously required. This feature of course requires Java 5 to support annotations. Unfortunately, as of GWT 1.4, only Java 1.4 is supported on the client-side. I have created a solution to bridge the Java 1.4 to Java 5.0 gap. In my latest project, I started by creating the server-side code all using Hibernate 3 annotations. I didn't use a single XML file for configuration. Using pojo (Plain Old Java Object) entity classes annotated with EJB 3.0 / Hibernate compatible annotations, I had no problems building the relational object mapping and the core classes of the service. I had decided on trying GWT for the first time to develop the client-side code. I was able to integrate it into my development environment but immediately ran into an issue when I found out that there was no Java 5 support which is needed for Hibernate 3 annotations. What I ended up doing is creating a clientside class to work as the serializable, transient entity object. Yes, this does mean that I have both a serverside entity class and a client-side entity class, but think: All of your client-side GWT Java compiles into javascript anyway. Of course you'll need a javascript-based entity to work on the client-side. Example: Mapping two tables with a one-to-many relationship. The diagram below shows the server-side to client-side responsibilities. Each side has entities, but the server side does all the transformation between the two with the business logic. One could say that the client application requests the service which uses the business logic to process server-side entities and interface with the client-side with client-side entities. With that said, on to the code examples!
In my example we are modeling Shelfs, Books and the relationship between the two. What we have is a relational database with two tables: Shelf and Book. The Book table has a field with a foreign key reference to the Shelf's primary ID, creating a One-To-Many relationship of Shelfs to Books. In the real world this means that a shelf can have many books on it but a book can only be on one shelf at a time. On the server side, you will end up with some code that may look something like this:
@Entity @Table(name="shelf") public class Shelf { private Integer id; private String name; private List<Book> Books; public Shelf(Integer id, String name) { this.id = id; this.name = name; } @Id @GeneratedValue public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; public void setName(String name) { this.name = name; } @OneToMany(mappedBy="bookId") @Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.DELETE_ORPHAN}) public List<Book> getBooks() { return Books; } public void setBooks(List<Book> Books) { this.Books = Books; } } @Entity @Table(name="book") public class Book { private Integer id; private String name; private Shelf shelf; public Shelf(Integer id, String name) { this.id = id; this.name = name; } @Id @GeneratedValue public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; public void setName(String name) { this.name = name;
} public Shelf getShelf() { return shelf; } public void setShelf(Shelf shelf) { this.shelf = shelf; } } These annotations provide the meta info Hibernate will use to create object-relational mapping. This is usable for any server-side code, however GWT can not use annotated code and also can not have hibernate running on the client-side. Business logic is required to translate the Hibernate entity into a GWT entity. Unfortunately this does mean that you must have 1 server-side class and a separate clientside class, however both are fairly thin classes and don't require a lot of work. Your client-side classes in this example would look like this: public class GWTShelf implements Serializable { private Integer id; private String name; private List Books; public Shelf(Integer id, String name) { this.id = id; this.name = name; } public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; public void setName(String name) { this.name = name; } public List getBooks() { return Books; } public void setBooks(List Books) { this.Books = Books; } } public class GWTBook implements Serializable{ private Integer id; private String name; private Shelf shelf; public Shelf(Integer id, String name) { this.id = id; this.name = name; }
public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getName() { return name; public void setName(String name) { this.name = name; } public GWTShelf getShelf() { return shelf; } public void setShelf(GWTShelf shelf) { this.shelf = shelf; } } Now you have both server and client entity classes. What you need is the business logic to process GWT objects using Hibernate objects on the back end. In this example, MyService is the proper GWT RPC service and we're going to write a simple method called getStudents which is to return a List of students. public class MyService extends RemoteService { public List getStudents() { List gwtStudents = new ArrayList(); List<Student> students = getHibSession().createCriteria(Student.class).list(); // lists all students //iterate students, create a GWTStudent from each for (Student student : students) { GWTStudent gwtStudent = new GWTStudent(); gwtStudent.setId(student.getId()); gwtStudent.setName(student.getName()); //iterate books, create list of GWTBooks, set on student (optional) ... } return gwtStudents; } } This is a very simple example that only shows 1-directional use of this. To save a gwt version of a class into a hibernate version, just pass the gwt object into the service call and convert it back to Hibernate's version. There are a few caveats, though: You must query the object by ID, then update its fields to update it. Updating lists requires special handling You must be careful to honor lazy loading to some degree else you may load a whole DB with one call GWT Objects must be serializable and Java 1.4 compliant. I've successfully employed this strategy in my latest web application. It did the trick for every case I had. Though it's not the perfect solution, it does get the job done and still gives you operating efficiencies gained with using Hibernate while allowing full use of GWT's capabilities.