Geertjan is a DZone Zone Leader and has posted 467 posts at DZone. You can read more from them at their website. View Full User Profile

Loosely Coupled Data Layers for CRUD Applications

05.07.2011
| 10345 views |
  • submit to reddit

Let's add a further level of decoupling to our application. Right now (after part 1, part 2, part 3, and part 4), though our user interface is no longer tightly coupled to our database, there is still a link to it. And that is because, throughout the user interface, we refer to the "TripQuery" object, which makes use of the "TripQueryDAO" object for making connections to a MySQL database, via JPA (Eclipselink, with a "persistence.xml" file). Now, let's say we want to provide for the possibility that the user doesn't have access to a MySQL database, but to an Oracle database, for example. Or maybe the user doesn't want to work with a database at all. Instead, the user wants to persist trips to a text file.

We can also imagine a different situation, not from the point of view of the user, but of the developer. At work, the developer has access to a database, but while working at home, that database isn't present. So it would be handy if all the nice capabilities we've created could still be used, though with text files instead of databases. Or, another scenario, we quickly want to throw together some very simple code that simulates data access, so that we can then focus on the user interface and come back to the data access functionality later or delegate that work to another developer on our team.

For all these various scenarios, a loosely coupled data layer is very handy. We're going to start by creating a new interface, named "ITripQuery", in our API module:

Here's the definition of "ITripQuery":

import java.util.List;
import org.my.libs.Trip;
import org.openide.util.Lookup;

public interface ITripQuery {

Lookup getLookup();

List getTrips();

}

That interface is nice and simple. Each different type of TripQuery will puts its capabilities into "getLookup" and return its current list of Trip objects via "getTrips". (That's also, by the way, how the NetBeans Project API works. It has a "getLookup" method and a "getProjectDirectory" method. Don't believe me? Click here! It is thanks to this very generic API definition for projects that the Grails and Griffon support in NetBeans IDE is able to recognize Grails and Griffon projects simply by its project structure, without the need to create some kind of artificial folder or file inside those projects, i.e., convention-over-configuration frameworks benefit from the looseness of the Project API, which is in turn enabled by the "getLookup" method, which allows for a random collection of capabilities to be added dynamically.)

Now we can create multiple different modules that implement this service. Below, you see a service named "TripDummy" and another service named "TripSQL", as examples:

The "TripSQL" works using the database code provided in the previous 4 parts of this series. The "TripDummy" doesn't access any database at all. It has a bunch of dummy code, just to provide some very simplistic functionality so that the developer can work on the application without requiring a database, like this, and take note of the first statement which registers this implementation in "META-INF/services", where "Lookup.getDefault" will find it:

@ServiceProvider(service = ITripQuery.class)
public class DummyTripQuery implements ITripQuery {

private List trips;
private Lookup lookup;
private InstanceContent instanceContent;

@Override
public org.openide.util.Lookup getLookup() {
trips = new ArrayList();
// Create an InstanceContent to hold abilities...
instanceContent = new InstanceContent();
// Create an AbstractLookup to expose InstanceContent contents...
lookup = new AbstractLookup(instanceContent);
instanceContent.add(new ReloadableEntityCapability() {
@Override
public void reload() throws Exception {
Random generator = new Random();
Trip trip = new Trip();
trip.setPersonid(new Person(generator.nextInt()));
trip.setDepcity("Amsterdam");
trip.setDestcity("Prague");
Triptype tt = new Triptype();
tt.setDescription("Description 1");
tt.setTriptypeid(generator.nextInt());
tt.setName("John Smith");
tt.setLastupdated(new Date());
trip.setTriptypeid(tt);
trip.setTripid(generator.nextInt());
getTrips().add(trip);
}
});
instanceContent.add(new CreatableEntityCapability() {
@Override
public void create(Trip trip) throws Exception {
Random generator = new Random();
trip.setPersonid(new Person(generator.nextInt()));
Triptype tt = new Triptype();
tt.setDescription("Desctiption 2");
tt.setTriptypeid(generator.nextInt());
tt.setName("Harry Jones");
tt.setLastupdated(new Date());
trip.setTriptypeid(tt);
trip.setTripid(generator.nextInt());
getTrips().add(trip);
}
});
instanceContent.add(new RemovableEntityCapability() {
@Override
public void remove(Trip trip) throws Exception {
getTrips().remove(trip);
}
});
instanceContent.add(new SaveableEntityCapability() {
@Override
public void save(Trip trip) throws Exception {
StatusDisplayer.getDefault().setStatusText("Saved...");
}
});
return lookup;
}

@Override
public List getTrips() {
return trips;
}

}

Of course, the developer could simply hardcode some dummy values into the user interface. However, that's exactly the problem we're trying to avoid here. We'd like to separate our data access code from the user interface so that we can plug different data sources into it without requiring any changes in our user interface. For example, in the viewer TopComponent, I have this snippet, which passes in whatever is currently registered as an implementation of "ITripQuery" to the creation of the node hierarchy:

ITripQuery query = Lookup.getDefault().lookup(ITripQuery.class);
node = new RootNode(query);
em.setRootContext(node);

For the use case that we're trying to cover here, there'll always only be one implementation of ITripQuery registered, hence we only need to cater for one implementation. Nice and easy and clean. Throughout the application, I can now change all references to 'TripQuery' to be 'ITripQuery' instead. And then, with actually very little work at all, I have a loosely coupled data layer in my CRUD application.

Thanks Toni Epple from Eppleton for advice and help on this scenario!

Previous parts in this series on loosely coupled features for CRUD applications:

Published at DZone with permission of its author, Geertjan Wielenga.

Comments

Antonio Vieiro replied on Sat, 2011/05/07 - 5:19am

Great series, Geertjan!!

I'm glad to see that capability-oriented programming is making room in NetBeans Platform development. I think this is one of the greatest features the platform has to offer.

A small suggestion: change the name of "ITripQuery" to "ITripCollection", that may clarify things a little: the queries are inside the DAOs, the "ITripQuery" just holds a list of Trips, so "ITripCollection" may be a better name.

Cheers,

Antonio

james baker replied on Tue, 2011/05/10 - 7:01am

Hi, Just wanted to say nice post but please never ever use "I" to prefix an interface.
If you need to use "I" to distinguish between the interface and implementation then you probably didn't need an interface in the first place.
More appropriate names would be TripQuery for the interface and concrete implementations SQLTripQuery and DummyTripQuery (or replace Query with Collection as Antonio said).

Cheers,

James (Lt. in the Naming Police) :)

Comment viewing options

Select your preferred way to display the comments and click "Save settings" to activate your changes.