| Persistence layer is important for any enterprise architecture and the options are vast.
Requirements
There are several requirements at this layer– many of them closely tied to those given in the data model layer.
- A large degree of database independence without loss of db-optimizations
- Declarative optimistic concurrency support
- Extensive and flexible code generation support
- Ability to introduce customizations/optimizations where required – for example be able to introduce complex SQL queries etc.
- Ability to extend the CRUD operations to introduce a history or auditing mechanism if needed. Most persistence framework allow pluggable lifecycle listeners that can "hook" into the CRUD operations. (for an example using Hibernate click here).
- Ability to handle relationships
- Ability to handle transactions, including distributed transactions
- Good support for complex optimized custom queries with support for pagination (this may have to be built into a higher level service). Click here for best practices in advanced search and pagination.
Persistence Layer Options
1. CMP 2.0 Entity Beans
Pros
- Easy to achieve database independence
- Extensive code generation support through XDoclet, Middlegen, EJBGen etc.
- Popular
- Lots of support from the application server vendors
Cons
- Persistence classes/mechanism is tied to the J2EE framework.
- CMP 2.0 does not have a standardized mechanism for handling optimistic concurrency (there is server-specific support for example in Weblogic).
- Introducing a history/auditing mechanism will involve additional work – cannot be done “under the hood” unless database triggers are used
- Not feasible for complex iteration type queries. Too much overhead involved.
2. JDO
Pros
- Standardized – vendor neutral
- Suitable for both fine-grained and course-grained access
- Contains advanced features such as caching and optimistic concurrency support
- Good code generators based on XDoclet and Middlegen
Cons
- No sane open source implementation I know of
- API and Learning curve not as simple as it could have been
3. Direct JDBC with DAO/Externalized SQL framework (iBATIS)
There is only one sensible option here – iBATIS. It's a great toolset that allows us to abstract the SQL out into XML files and map POJO's to the SQL results.
Pros
- Uses POJO's not tied to any framework – these POJO's can double as value objects.
- Simple, simple, simple and a very short learning curve
- Nice built-in support for pagination of large resultsets for such a simple framework
- Many developers are already familiar with SQL - no new OQL to learn
- Can accommodate complex SQL's, optimizations and existing legacy SQL's
- You don't have to make the tough decision of mixing up SQL with another Object based query language because the latter didn't give you all the facilities you needed (such as the legacy complex SQL queries mentioned above).
Cons
- No known code generator (not that I know of) for the simple CRUD's – I'd love to see a middlegen plugin for this.
- Unless you keep your SQL's standards compliant, no guarantee that they will run across different databases. In the worst case, you will have to maintain multiple SQL maps (as the externalized SQL's are referred to in the framework) per database.
4. Other non-JDO compliant open source options
These include Castor JDO, Hibernate, Apache OJB, Torque, Jaxor and a lot of others. Hibernate seems to be the most feasible option primarily because of the points mentioned below.
Pros
- Works on plain old java objects (POJO's) not bound to the framework –these POJO's can double as value or data transfer objects
- Great following and support
- Code generation options using XDoclet and Middlegen – including named queries using hibernate.query attributes – highly maintainable code
- SQL pass-through facility
Cons
- Learn yet another query language
- Typical special-case defects and issues common with any open source framework with a significant code-base
- Some enterprise production UNIX environments could have security problems with the byte-code enhancement work that Hibernate needs to do under the hood.
Conclusion
There is no clear winner in the persistence arena and the correct option depends on the nature of the project. Entity beans themselves are not a bad choice for achieving high levels of scalability if they are not misused or you don't mind making some of the compromises mentioned (they're definitely not my personal favourite). Given the fact that I'm biased towards open source options, the decision for me for most cases is between Hibernate and iBATIS based on the size of the project and capabilities of the team.
Best Practices
- Always use a facade layer to access persistence data objects. DO NOT expose transactional persistence objects to the client tier. There are several recommended patterns for using Hibernate with a layered architecture.
You would typically use the
thread-local session pattern with Hibernate.
- Use Data Transfer Objects (or Value Objects) to pass data between the facade layer and client. If the persistence layer allows POJO's to be used, the persistence domain objects can double as value or data transfer objects. This is a good idea provided you have taken sufficient precautions to avoid returning transactional objects. It also avoids additional object-instantiation.
- Use container managed transactions for handling transactions - I always find it useful to introduce transactions at a
separate layer even if that means writing transactional wrappers for most of the non-transactional services that need to get exposed. Layering in this way also helps to streamline exception handling and logging and also to introduce nice AOP aspects that enforce these layering constraints.
- It's always a good choice to insulate your business services from the specific persistence layer you are using in case you change your mind in the future. This can be done by writing generic wrappers around the specific persistence and exception classes as given in this article on OR Mapping with Apache OJB. Ofcourse this is a non-issue if you use an IoC container.
- Use a clear and consistent code-generation strategy to minimize
human errors and increase productivity. Example strategies for
Hibernate: Writing XDoclet enabled domain classes that generate the
mapping files automatically. Using middlegen hibernate plugin to create
XDoclet-enabled domain classes from the data model, and using
HibernateSynchronizer plugin to generate classes based on the mapping
file. The latter approach is the one I prefer as it is easily applicable
for both legacy and green-field projects. See the
development stack for details.
- If you decide to use direct-JDBC, the following best practices apply:
- Seperate the data access code into a seperate layer using the DAO pattern. (You can achieve this by using the iBATIS DAO framework)
- Externalize the SQL to support maintainability and additional database support. (You can achieve this by using iBATIS SQL Maps)
- Using prepared statements vs. statements gives you several advantages:
- Insulation from statement manipulation attacks
- Cleaner, more database independant and more reliable code because you don't have to deal with issues such as date conversions, quote variations, SQL escape sequences within variables etc.
- If you repeatedly execute the same prepared statement avoids multiple SQL parsing in the database.
- Also if you are using direct JDBC, use statement batching to improve performance by reducing network roundtrips to the database. Note that not all JDBC drivers would implement this feature in the optimal manner.
- Don't leave out stored procedures or database-specific import/export tools for doing high volume batch operations. Doing such operations using any client or middle tier logic can incur a heavy performance expense. (join/view the debate)
- Read about advanced search and pagination best practices
Resources
JDO Persistence - contains a good comparison of the overall set of technology options (not vendor options)
Object Relational Mapping with Apache Jakarta OJB - good article to get started on Apache OJB
Carl's Weblog - he blogs about his experiences with Persistence Layers and settles for Jaxor
Go back to the Architecture Page
|
|