In this post I will discuss several strategies to handle the following situation:
You’re working on a legacy project that uses a company library with DBOs and DAOs for accessing the database.
But the generator for this library is broken and you have to make changes to the DBOs and/or DAOs.
You will face the following three basic cases, when the database changes:
If you have only to add new entities with unidirectional relationships to entities from the common library, everything is fine and in the most cases you don’t have to touch the library. This is also true for adding new queries to the DAOs, thanks to inheritance.
Extending existing entities / Change methods of the DAOs
Extending existing entities with new attributes may be a little bit more complicated, but is also possible without using the generator.
Change the existing methods of a DAO is, on the other hand, a fairly simple case, you can use inheritance. Or introduce a new DAO that only provides the new method and use this DAO instead of the old one.
The interesting case is, when you have destructive changes in the database, like removing columns or foreign keys or whole tables. Because the first and second case are – more or less – simple, I will focus only on strategies for the latter case Destructive changes.
1. Fix the generator
This solution will not touch the current infrastructure of the project. If it is worth to do this task depends
- on the generator you use (in my case I had a very old version of the Hibernate Reverse Engineering project)
- the database changes that will be introduced in the future (as far as I know are Postgres enums still a problem for Hibernate)
- the time you have to fix the generator
- You don’t touch the existing system
- In case of, no known breaking changes from the database point of view for the generator, the generator is working again
- Future changes can be applied fast and easily
- You stick to the old system
- You may add a constraint for the database developers (see postgres enums)
- It may take a long time to fix the generator
2. Replace all DBOs
This option will replace all existing DBOs from within the library with generated, but fully accessible, ones in your project. In case of Java you need to modify the jar archive for this approach to have it working without any potential class path issues.
When a database table or a field that is used within a query is gone, you must also replace the existing DAO within the library with a new one in your project.
- You have the full control over the DBOs back
- While the database evolves you will also gain control over the DAOs back.
- You only have to touch the DAOs when it’s really necessary
- You may modify the library itself
- You change the existing system
- Named queries might be forgotten, while recreating the DBOs.
- In case of removed tables, you must also replace the DAOs. This might break the application, due to unknown side effects of the methods.
To avoid this, it is necessary to have a good test-harness, so that you can discover, if things were broken.
3. Replace the whole library
The following two approaches have in common, that they will replace the whole library. The replacement of the DBOs needs to be done, as described in 2., but the approach for the DAOs differs between both methods.
3.1. Introduce a new framework
I will describe this approach for Java and Spring Data JPA, but I guess in other languages it is also feasible.
Spring Data comes with a very nice feature, that allows you just to specify the query you would like to execute as the method name of an interface. The method name must follow a specific format and language, like:
List<Address> Address.findByCity(String city);
Under the assumption, that the existing DAOs follows such a convention, you can introduce Spring Data JPA. You must now ‘only’ reverse engineer the method signatures and potentially the annotations of the existing DAOs and transfer them to interfaces.
Finally you ‘just’ have to change method names where they might not match.
- Old solution is removed completely and replaced with a well proven and evolvable one
- The work is done once and we don’t have a repeation of small steps
- Less to code, due to autogeneration of entities and, in most of the cases, only creating interface definitions.
- Introcing a new framework / technologie to stack of old – sometimes unmaintained – frameworks
- Risk to break working queries
3.2 Write everything by hand
This approach goes the ususal way of implementing DAOs, manual and mostly without any framework magic.
This approach is feasible when you have no formal language that can be automatically translated to a query or no framework like spring data exists for you language.
- No new framework / technology introduced
- Full control over the DAOs and DBOs
- No “magic” is introduced.
- Risk to break working queries
- Depending on the amount of DAOs the effort to create them is high.
4. Replace DBOs and DAOs only when and where necessary
This option introduces a migration path, that will only create the work, that needs to be done, where and when it is needed.
The process will be like this :
- Reengineer at least annotations, fields and methods from the affected classes
- Remove affected enties ,and if necessary, dao classes.
- Recreate affected entities and dao classes in the specific subpackes
- Add required fields, methods and annotations
- In my particular case, the lowest risk
- Effort is only spend, when really necessary
- Depending on your language / framework, the effort is very low
- The library is not replaced
- In case of replacing a DAO same as for 3.1 and 3.2
- The effort needs to be spend multiple times
- Depending on the language : library needs to be toched everytime when an entity needs to be migrated
We saw several strategies of how to cope with an old library that provides data access, but was generated by an generator that isn’t working anymore. Pros and Cons of each approach reveals that there is no perfect solution and we must decide per case what are the best matching strategy.
In my particular case we decided to use option number 4, because we have already planned to replace the application that uses such a library.