Articles
Developer: Java
Using Oracle Berkeley DB Java Edition as a Persistence Manager for the Google Web Toolkitby Erick Audet and Gregory Burd When building Web applications with the Google Web Toolkit, using Berkeley DB Java Edition as a persistent data store can reduce project costs significantly.Published February 2008 The standard Java Platform, Enterprise Edition (Java EE) approach for object persistence in Web applications is to use Enterprise JavaBeans (EJB), an object-to-relational mapping (ORM) technique. Java objects are translated back and forth into SQL statements and stored within a relational database management system (RDBMS) as rows related across a number of database tables. This is the most common approach for good reason. Most customers will find that many aspects of their organization depend on SQL and RDBMS servers from Oracle to manage business critical information. In these cases EJB and Java Persistence API (JPA) are the best approaches for object persistence. They bring Java applications and existing applications together, speaking the same SQL language, to access the same relational data. Interestingly, some EJB applications store data in a relational database simply because it is a common and familiar design pattern, not because there are multiple applications accessing the same data using SQL for different reasons. If the data isn't in an existing relational database and there are no plans for it to be accessed by any other means than through the ORM layer, then the ORM layer can be overhead. There are some specific cases where data must be accessed by other SQL-based solutions running ad hoc SQL queries, but this was not true for our application. Viewed objectively, the ORM design pattern adds a great deal of unnecessary overhead when it is the only aspect of the system interacting with the RDBMS using SQL. In contrast, Oracle Berkeley DB Java Edition's Direct Persistence Layer (DPL) stores object data directly into a Btree database, without the need to translate it into some intermediate language. In this article, you will explore real-world experiences from combining Berkeley DB Java Edition and the Google Web Toolkit to create a highly effective solution for object persistence. Oracle Berkeley DB Java EditionOracle Berkeley DB Java Edition is one of three products acquired by Oracle when it purchased Sleepycat Software in February 2006. It is a pure-Java database engine designed to store data to a local file system. It provides Atomicity, Consistency, Isolation, Durability (ACID) transactions, allows for high levels of concurrency, can scale to terabytes of data, and will use a predictable portion of the Java virtual machine's memory as a cache for that stored data. Berkeley DB Java Edition is a reimplementation of the highly successful Berkeley DB product that was written in ANSI C, but it has significant differences. Primary among those differences is the DPL API provided by Berkeley DB Java Edition. This layer provides a JPA-like set of Java annotations allowing for easy storage, indexing, updating, and deleting of object graphs, without the need to translate object data to and from SQL. There is no ORM component or back-end database when using the Berkeley DB Java Edition DPL and no client/server connection. The DPL stores object graphs, relationships, and indexes in a very intuitive and powerful manner. The DPL annotations are similar in name and concept to the JPA. The DPL can store any Plain Old Java Objects (POJO) and keeps all the persistence rules and configuration within the class itself. This is the first key difference and advantage of the Berkeley DB Java Edition DPL when compared to ORM solutions; it is not necessary to maintain external configuration files. This approach greatly enhances the developer's efficiency, reduces project complexity, and eliminates overhead. Applications developed using EJB-based solutions will typically need a configuration file for each class mapped to a physical database table. Although persistence such as Hibernate also use Java annotations, in practice, you will need to at least understand and control the Hibernate.config.xml file to manage the connection strings and other settings enabling your application to connect to a relational database server via Java DataBase Connectivity (JDBC). Despite excellent integrated development environment (IDE) integration, configuration file management, and other support for EJB-style solutions, the developer will inevitably end up hand editing them at some point, a highly error-prone and time-consuming process. Berkeley DB Java Edition's use of Java annotations to encapsulate these settings within the persistent POJO classes is unique, simple, and less error prone by contrast. The DPL annotations keep the storage context in a single, obvious, and intuitive location: the source code where a developer would expect to find it. The Google Web ToolkitThe Google Web Toolkit (GWT) is a model-view-controller (MVC) framework abstracting the process of writing HTML, JavaScript, and associated Asynchronous JavaScript And XML (Ajax) messaging into code written in Java, and then processed by the GWT for deployment in Java EE application servers. It eliminates the complexity of hand coding HTML, JavaScript, and other elements that bind the JavaScript actions triggered in an HTML page (clientside code run within the user's browser) to the controlling layer executing within the Java Enterprise application server (serverside, where the database resides). When developed within a good IDE, the GWT can provide an immeasurable advantage over other frameworks and considerably lower the cost and complexity of the project. The GWT also provides a debugging environment called hosted mode. This enables developers to debug their clientside code (JavaScript and HTML) in real time simply by stepping through their Java source code in a familiar debugger-style manner. The GWT will handle, in real time, the mappings between the generated JavaScript/HTML code and the Java code, drastically reducing development time for complex Web applications. Storage aspects of the application can also be quickly debugged in hosted mode. Developers can concentrate on the application when their supporting tools eliminate configuration overhead and unnecessary complexity. Peanut Butter and Chocolate: The GWT and BBD Java Edition DPLIt is very simple to combine the GWT and Berkeley DB Java Edition DPL. The GWT controller framework is straightforward and easy to customize. The sequence diagram below presents a typical flow of calls between the GWT remote procedure call (RPC) mechanism and Berkeley DB Java Edition. A widget (or any other GWT user-interface client class in the com.google.gwt.user.client.ui package) contains user-interface (UI) controls such as Buttons or ListBoxes. These controls handle events triggered by a user viewing a page within their Web browser. Each of these controls has a definition of the screen events it can handle. Screen events such as "OnClick" can be directed to invoke code on the serverside classes using the GWT RemoteService interface. Before calling a RemoteService, the Widget must create a Data Transfer Object (DTO, a common Java EE design pattern) from its widget fields. Then an AsynCallBack (Save DTO) method is created and passed to the RemoteService. This serverside class will create a model object, a POJO representing the data delivered from the browser to the server, using the DTO, which is then handled by Berkeley DB Java Edition via calls through a BusinessService object. DataAccessors are then used by the business services object and manage the transactional storage of the various conceptual objects within the Oracle Berkeley DB Java Edition database. Demo Details and CodeThis section describes in more detail how to implement the proposed architecture by showing a few key source code examples ( download zip). To understand it, the reader must have some level of comfort with Ajax, Java annotations, and object-orientation design. Data ModelTwo conceptual model classes are used to illustrate this example: a generic class called Person and a subclass of Person called User. These objects and their relationships to one another are easily expressed with DPL annotations. Berkeley DB Java Edition will then store the data using transactions to local database files.
package ...
import com.sleepycat.persist.model.Persistent;
import com.sleepycat.persist.model.PrimaryKey;
@Persistent
public class Person {
@PrimaryKey
private String userName;
private String lastName;
private String firstName;
...
}
Class User
package ...
import static com.sleepycat.persist.model.Relationship.ONE_TO_MANY;
import java.util.ArrayList;
import com.sleepycat.persist.model.Entity;
@Entity
public class User extends Person {
private String injury;
...}
The GWT UI, Callbacks, and DTOsOur example uses a very simple concept of setting nominal information and an injury to an existing user (or patient). It shows how it can use the concept of inheritance with the Berkeley DB Java Edition DPL database. The following picture is a simple Widget used to record a user's injury report. Here is the actual code for this window:
public class Profile extends Composite {
private TextBox injury;
private TextBox userNameTextBox;
private ListBox typesOfInjuries;
public Profile() {
...
final Button
saveButton = new Button();
absolutePanel.add(saveButton, 13, 6);
saveButton.addClickListener(new ClickListener() {
public void
onClick(final Widget sender) {
saveProfile();
}
});
saveButton.setText("Save");
...
protected void
saveProfile() {
AsyncCallback callback = new
AsyncCallback() {
public void onSuccess(Object result) {
Window.confirm("Profile Saved");
}
public void onFailure(Throwable ex) {
Window.confirm(ex.getMessage());
}
};
UserDTO wUserDTO = new UserDTO();
wUserDTO.setUserName( userNameTextBox.getText() );
wUserDTO.setInjury( injury.getText() );
ProfileService.Util.getInstance().save( wUserDTO, callback);
}
As you can see in the example code, the SaveButton event "OnClick" will call the SaveProfile() method. This method uses a simple callback block to handle the return object from the serverside class method UserService.createUser(). The UserDTO, a POJO used to carry data from the serverside to the clientside, contains only basic Java datatypes, allowing it to be a serializable class.
JavaScript works with basic datatypes only. They must be serialized to be transferred to the client Web browser and the remote application server; this restriction thus becomes a requirement of all GWT DTOs. If you look closely at our entity classes, they do not have any concept of client application nor do they have to implement GWT interfaces. They are completely isolated from the client application. To achieve this separation, a Java EE DTO design pattern is used. These DTOs contain only basic datatypes and are used by both packages (Services and GWT Remote Services). DTOs must implement the GWT IsSerializable interface and a GWT configuration XML file must also be created. Another important reason for using DTOs is that GWT client classes must be transformed into JavaScript code. To achieve that, GWT uses a Java-like assembler, which does not support the complete Java Platform, Standard Edition API or the latest features such as annotations and typed arrays. Although this is a minor design limitation of the GWT, it generally doesn't affect the majority of programming tasks. Side Notes:
Remote Services, Business Services, and AccessorsAt this point the conceptual entities have been created and are properly annotated with Berkeley DB Java Edition DPL annotations. The basic services methods will manage the transaction states of those entities. The next step is to create GWT Remote Services. They will invoke the Berkeley DB Java Edition DPL code to store the data. Remote Services are the standard way to communicate with the Java EE application server from the clientside browser. This is done using a standard Ajax, essentially an HTTP POST, call generated and managed by the GWT. The serverside code for that Ajax call is found within the "server" package of the GWT application. This is code that executes within a servlet container or a Java EE application server (because we are on the server side, this code is Java, whereas the code running in the browser was JavaScript) and this is where we tie user actions at the browser into business logic and then the Berkeley DB Java Edition DPL for object persistence. The RemoteService implementation of the save() method follows:
...
public void createUser(UserDTO pUserDTO) {
//transform the presentation layer object into a conceptual object
User wUser = new User();
wUser.setUserName(pUserDTO.getUserName());
wUser.setInjury( pUserDTO.getInjury());
UserService userService = new UserService();
userService.createUser(wUser);
}
...
Model objects are business data classes that implement the data management aspects of the application within the database. The EJB programmer can roughly think of the UserService as a stateless session bean.
The class BaseService itself does not handle any logic. This class handles the transactional state of a specific task on one or more objects in the database. To add business process logic to a service, the application will need to extend that BaseService class. In the example, the class UserService will function in this role and handle all access to the User entity by providing implementations for methods such as createUser(), saveUser(), and getUser(). This implementation resides within the serverside package of the project. It calls a business service called UserService. It is the UserService that implements the business logic for a particular instance of User. It's at this point in our example that we transform the UserDTO object into a User object, the model POJO with DPL annotations that will be stored within an Oracle Berkeley DB Java Edition database. Recall that the database is simply a set of files located on the file system of a server accessible by the Web application servlet container. Tried-and-true design patterns dictate that the application should separate the DTO objects from the business objects by layering and abstraction. Business services must not know the DTO classes, they need only to deal with the conceptual model objects. These objects are persisted into the Berkeley DB Java Edition database. The business service classes are responsible for the management of the conceptual model objects in the data store. To relate this to EJB, the UserService is similar to a stateless session bean. Here is the UserService.createUser() method:
...
public User createUser( User pUser ) throws DatabaseException, Exception{
UserAccessor wUserAccessor = new UserAccessor();
//check mandatory fields
if ( pUser.getFirstName().equals("") ||
pUser.getLastName().equals("") ||
pUser.getUserName().equals("") ){
throw new Exception("Missing mandatory fields");
}
open( false, wUserAccessor);
// Start a transaction.
startTransaction();
// Put it in the store. Note that this causes our secondary key
// to be automatically updated for us.
try {
wUserAccessor.userByUserName.put( pUser );
// Commit the transaction. The data is now safely written to the
// store.
commit();
} catch (DatabaseException dbe) {
try {
System.out.println("Error creating a user");
} catch (Exception willNeverOccur) {
}
txn.abort();
throw dbe;
} catch (Exception e) {
txn.abort();
e.printStackTrace();
}
return pUser;
}
...
This method uses the inherited methods open() and startTransaction() to set up and use Berkeley DB Java Edition as a database (aka entity store), and then begins a transaction. The separation helps to keep developers from making errors when setting up the Berkeley DB Java Edition connections and transactions later in the process. The UserAccessor class is responsible for setting up the various Berkeley DB Java Edition DPL accessors, essentially indexes, for data access. This class encapsulates the indexes used later in the code to create, update, and delete objects in the Berkeley DB Java Edition database. Here is the content of the UserAccessor class:
...
public class UserAccessor extends DefaultAccessor {
public PrimaryIndex<String, User> userByUserName;
public UserAccessor() {}
public void init() throws DatabaseException{
userByUserName = entityStore.getPrimaryIndex( String.class, User.class );
}
}
...
By setting up these accessors, the business service layer is simplified and consistent. The task of creating and managing indexes is isolated in one place and will not interfere with your business logic. Here is an example that uses an index to retrieve an instance of User by name.
wUser = wUserAccessor.userByUserName.get( pUser.getUserName() );Otherwise, to retrieve a user object without the use of these accessor methods, the code would look more like this:
PrimaryIndex<String, User>
userByUserName;
userByUserName = entityStore.getPrimaryIndex( String.
class, User.
class );
User wUser = userByUserName.get( <user name> );
The advantage of the accessor method should be clear: the code is much simpler in the first case. This is simply a design pattern to consider when combining the GWT and Berkeley DB Java Edition.
Evaluating the GWT and Berkeley DB Java Edition DPL Persistence SolutionAs with any object persistence solution, there are trade-offs. Berkeley DB Java Edition is no different in this regard. Advantages:
ConclusionsThe primary benefit of combining the GWT and Berkeley DB Java Edition is simplicity. Both frameworks are designed for ease of use and eliminate common, and often taken for granted, overhead in all stages of application development. Web applications can be complex for many reasons. The myriad of technologies that together support today's Web browsing experience is dizzying. Developers must pick and choose from dozens of different combinations of design patterns and frameworks when laying the foundations of their Web applications. Thankfully, with the help of companies like Google and Oracle, developers are able to reduce the complexity of Web application development to a manageable level. There are many cases where EJB and ORM make sense for the storage (or Model) aspect of a Web application, but in our particular case we didn't require external SQL access to our data. An ORM layer and RDBMS back end was overhead that we didn't need. It was nice to discover a new way to manage data storage that was just as robust and fast as an RDBMS and yet, in our case, far more appropriate in many ways. Berkeley DB Java Edition is a true revolution in choices for a persistence layer. It is eliminating the ORM barrier and solves so many problems, such as:
Frameworks such as GWT, JavaServer Faces, Echo2 and others are recent additions in the Web 2.0 programming paradigms. They basically bring back the rapid development factor which was present before the Web era. These tools enable you to create professional and scalable Web applications. They all offer different features. But one thing they have in common is the functionality to abstract the complexity of clientside syntax. They do this by generating and maintaining the client side, in-browser. How do the GWT and Berkeley DB Java Edition work in the real world of development? As with any new and innovative ideas, people hesitate and wait for them to prove themselves in some major implementation. Berkeley DB Java Edition might be new to the Web framework persistence layer, but it has matured over the last five years and is performing significant tasks storing terabytes of data in places you might not imagine but already use, and yet unknowingly depend on today. Web developers should take a close look at replacing their ORM mindset with one that allows for storage solutions like Berkeley DB Java Edition. If Oracle Berkeley DB Java Edition worked for us, it could work for you too. Eric Audet, M.Sc., TechSolCom, has 16 of experience in IT, mainly as a software and data architect. He currently works as a software architect for the development of transactional SOA, Wireless and Web applications using WSDL, Java EE and Java ME technologies. Gregory Burd is the Product Manager for Oracle's Berkeley DB, Berkeley DB Java Edition, and Berkeley DB XML database products. He has held this position since 2003 and continues working on Berkeley DB products within Oracle's Embeddable Databases Group. Greg has a diverse background including software engineering, product management, enterprise software consulting, software alliances and sales, and has contributed to open source and free software projects. |
||||||||||||||||||||||||||||||||||||||||||||||