A better approach is to use the DAO (data-access object) pattern, 8 which encapsulates the data-access logic within a DAO class. A DAO defines methods for persisting, loading, and deleting objects. It also defines finder methods, which execute queries and are discussed in more detail later. The DAO methods are invoked by the business logic and call the ORM framework to access the database.
Figure 3b shows an example of a Hibernate DAO for the Account domain class. This DAO consists of the AccountDao interface, which defines the public methods, and an AccountDaoImpl class, which implements the interface and calls Hibernate to access the database.
The DAO pattern simplifies the business logic and decouples it from the ORM framework, but it has some drawbacks. The first problem is that many DAOs consist of cookie-cutter code that is tedious to develop and maintain. This has caused some developers to abandon the DAO pattern and write business logic that directly calls the ORM framework, despite the drawbacks of doing so.
One way to reduce the amount of cookie-cutter code is to use a generic DAO. 9 This consists of a superinterface, which defines the CRUD (create, read, update, delete) operations, and a superclass, which implements them. The superinterface and the superclass are parameterized by the entity class, which makes them strongly typed. Application DAOs extend the generic DAO interface and implementation class. Using a generic DAO eliminates some but not all of the cookie-cutter code, so it’s only a partial solution.
Another problem with using DAOs is that some application classes might not be able to reference them. Modern Java EE applications resolve inter-component references using a mechanism known as dependency injection. 5 When the application starts up, an assembler instantiates each application component and injects it with references to the required components. Resolving inter-component references in this way simplifies the components and promotes loose coupling.
One limitation of dependency injection, however, is that it does not easily allow noncomponents such as domain
objects to obtain references to components such as DAOs. Domain objects are instantiated by the application rather than by the component assembler. It’s tricky, although not impossible, 13 for the component assembler to intercept the instantiation of such objects and inject dependencies. As a result, business logic residing in domain objects cannot always reference components such as DAOs.
There are a couple of ways to work around this limitation. Components such as services, which can use dependency injection, pass DAOs as method parameters to domain classes, which cannot. This works well in some situations, but in more complex cases the code becomes cluttered with extra parameters. Another workaround is to move the code that needs to use the DAOs into components where it can use dependency injection. The trouble with moving business logic out of the entities is that it degrades the design and results in an anemic domain model.
Dynamic persistence methods in GORM. GORM provides a different style of persistence API. Rather than providing an API object, it injects methods for saving, loading, and deleting persistent objects into domain classes. This mechanism decouples the business logic from the underlying ORM framework without having to use DAOs. It also eliminates the need for application code to obtain references to the ORM framework API objects or DAOs.
GORM injects several methods into domain classes, including save(), which saves a newly created object; get(), which loads an object by its primary key; and delete(), which deletes an object. Here is an example that uses these methods:
Customer c = new Customer(“John Doe”)
if ( !c.save()) fail “save failed”
Customer c2 = Customer.get( c.id)
c2.delete()
assertNull Customer.get( c.id)
This example creates a Customer object and saves it in the database by
APriL 2009 | voL. 53 | no. 4 | communicAtionS of the Acm
53
References:
Archives