Gears ORM (Iteration 1)
Posted June 9th, 2007 by robert.hanson
Demo
Browse Source
Requires: gwt-google-apis
This is the first iteration of what is likely to be many attempts at a Gears ORM solution for GWT. If you haven't heard, Google Gears is a browser plug-in which provides JavaScript services for off-line applications. The Gears feature that this project centers on is the client-side relational database. The plan is to provide a client-side ORM solution in the same sprit as Hibernate or iBatis.
Gears ORM iteration 1 (GO1) focuses on providing access to a single database table. The data access object (DAO) should be
capable of creating the database table, writing to it, deleting records from it, searching it, and deleting the table once it
is no longer needed. GO1 does not allow for relationships across tables, nor does it allow for crafting your own
where clauses the way you would with SQL or the Hibernate Query Language.
With these goals in mind, the idea is to be as simple as possible to implement, which brings us to Step 1.
Step 1 - Create a data bean
Due to the half-bakedness of this implementation you are limited to beans that have only String and Integer (not int) datatypes. For each field in the bean you must have both a getter and setter. Below is a sample bean with four fields, the getters and setters have been omitted for berevity.
Standard stuff so far, except that we need to implement the
GearsDataObject interface. This interface is used
as a marker interface to identify this class as a Gears storable class. This is nessary for now, but may go away in later
iterations. The next step is to use annotations to mark the data class and its fields.
Step 2 - Add annotations
If you think about the bean as the representation of a single record in the database table, then logically you would map the data class to a single table. Taking that to the next step, each field in the class is represented in the database as a single column. So the question is, how do we use annotations to map the class to a table, and map each field to a column.
We will start with the class. The @gears.table annotation is used to map the class to a table. The annotation
requires that you provide a table name to map the class to. This is done by providing a name attribute
to the annotation. Here we map the class to the table MyDataTable.
/** * @gears.table ( name = MyDataTable ) */ public class MyData implements GearsDataObject { // ... }
As with all of the GO1 annotations, the attributes are specified inside of parenthesis following the annotation name. The annotation attributes may have any amount of space before, inside, and after them, so format it the way you want.
That covers mapping the class to a table, next we need to map the fields to columns. In GO1 there are two annotations that
handle this. The first is @gears.id, which is used to denote that the field is not only a column, but it is also the
primary key of the database record. In GO1 you must use one and only one @gears.id annotation. In
future iterations it will allow multiple column keys, but for now we are limited to one.
/** * @gears.id (length=100) */ private String email;
The @gears.id column accepts a length attribute to specify the size of the column in the database.
This is required for all column annotations. Also note that the code generator that builds the DAO will not inform
you if you forgot to specify the size, instead you will probably get a Gears error when you try to create a table with a
column of size 0. In short, GO1 is not very developer friendly.
The second volumn annotation is the @gears.column annotation, used to mark all fields other than the primary key
field. Both the @gears.id and @gears.column annotations accept two attributes. The first,
length, as we have seen is used to specify the size of the column. The second attribute is name,
which can be used to specify the name of the database column. By default the column name will be the same as the field in
your Java class, but you may use the name attribute to override this.
With all of the annotations added, the completed class will look like this.
/** * @gears.table ( name = MyDataTable ) */ public class MyData implements GearsDataObject { /** * @gears.id (length=100) */ private String email; /** * @gears.column (name=firstName, length=25) */ private String fname; /** * @gears.column (name=lastName, length=25) */ private String lname; /** * @gears.column (length=3) */ private Integer age; /** * @gears.column (length=255) */ private String saying; /* getters and setters are -REQUIRED-, but have been ommitted here for berevity. */ }
Here we provided override names for the first and last name, but stuck with the default for the rest. Once your data bean is created, you would normally just start writing code, but we need to go to the module config first.
Step 3 - Associating the code generator with the GearsDataObject interface
The way GO1 works is that when you compile your GWT project it will inspect the data class, and your annotations, and build a DAO for you. In order to make the DAO generator execute we need to tell GWT that it should be run the GWT code trys to create an instance of a GearsDataObject object. You can do this by adding the following code to your module definition.
<generate-with class="com.gwtsandbox.gears.orm.rebind.GearsDataStoreGenerator"> <when-type-assignable class="com.gwtsandbox.gears.orm.client.GearsDataObject"/> </generate-with>
You may need to alter the packages specified here, but if you are using the source from the subversion repository you can simple cut and paste this into your configuration file. What it says is that when GWT.create() is called on a class that is assignable to a GearsDataObject (the "marker" interface), that the GearsDataStoreGenerator should be executed. The generator in turn will create the DAO.
Time to get back to the code, and start hitting the Gears database.
Step 4 - Creating and using the data store
Using the DAO is as simple as it was to create and annotate the data object... partially due to the limited functionality. To create the DAO you simply use GWT.create(), passing the data class as an argument.
public void onModuleLoad () { GearsDataStore ds = (GearsDataStore) GWT.create(MyData.class); try { // ... } catch (DatabaseException e) { e.printStackTrace(); } }
Thats about it. Create the DAO, then start using it. All of the DAO methods may throw a DatabaseException, which is part of the Gears API. Here are some of the DAO methods in action.
// delete old table, then create new table ds.deleteTable(); ds.createTable(); // save data MyData d = new MyData(); d.setEmail("ash@smart.com"); d.setFname("Ash"); d.setLname("n/a"); d.setSaying("Good ... Bad ... I'm the guy with the gun"); ds.save(d); // delete record ds.delete(d); // load all MyData[] data = (MyData[]) ds.loadAll(); // find by id MyData o = (MyData) ds.findById("ash@smart.com"); // find by example MyData example = new MyData(); data = (MyData[]) ds.findByExample(example); // close datastore ds.close();
So GO1 covers only the simple stuff, but that might just be enough for simple applications. The only missing piece is to
select records by criteria that are more complex the the findByExample() method will allow.
GWT Sandbox