Hibernate and Composite keys
March 22nd, 2009
I like using surrogate keys for most of the database designs for the following reasons:
- Simplifies design and may improve efficiency
- Maintainability: Change to the meaning of the natural key could change the table structures when using natural keys
- Natural key values may be recycled
Here is a partial list of issues I have encountered when using Hibernate with composite keys on a legacy database:
- The documentation (or lack there-of) for Composite Keys usage poses big problems. But, the source code really helps .
- Inserts/Updates are slightly inefficient when compared to surrogate keys if there is no versioning strategy implemented.
- Cannot have relationships (with CRUD operations) mapped in hibernate for each of the keys in the composite primary key. A read-only collection mapping is possible by mapping the relationship with the same column (that is part of the Composite Key) and making insert=”false” and update=”false”.
- It is impossible to determine if the object to be saved is a new one or an existing one when using “assigned” composite keys. Since the keys are “assigned”, the primary key is not null and so it is hard to determine if this is a new object without making another call in a new transaction to the DB. This becomes very cumbersome.
- The Hibernate reference documentation uses various phrases like “discourage it for serious applications” for certain types of Composite-id mappings (embedded composite identifier). There are several issues with using key-many-to-one mapping in the Composite-id elements, etc.
- In older versions of Hibernate (before 3.3.x), you can’t use an IdentifierGenerator to generate composite keys. The suggested approach was to use UserType or CompsiteUserType. A lot of people choose to use “assigned” strategy to avoid the complications with custom types. I think this is resolved in the latest Hibernate version. Again, the documentation here is almost non-existent. In order to make this work in my code, I had debug through the Hibernate source code and also use the test case code attached in this Hibernate CR.
Mock objects and Testing
March 15th, 2009
This has been a much discussed topic over the web in the last couple of years. I was responding to one of the emails at work about mock objects. While organizing my thoughts about Mock objects, I decided to write this post.
Various thought leaders in TDD promote Mock objects. I agree with some of it but not everything they say.
Some caution towards using Mocks extensively:
-
Using Mock objects might affect design decisions:
-
The mocked classes (and the methods used to set up expectations) cannot be final. If called, their normal code may be executed. I prefer making every class either final or abstract unless there is a strong reason to not to do so.
-
A class with only private constructors cannot be instantiated.
-
The behavior for equals() and hashCode() when using mocks may not be what you normally expect.
-
Private methods cannot be mocked. I prefer making every method private unless there is a strong reason to not to do so.
-
-
Trustworthiness & ROI of Mocks: Mocks may hide integration problems and push the burden on other tests. High-level and End-to-end tests give the most ROI, in my opinion. I would value these over the unit tests that use mocks extensively when constrained by Time & Resources. Most of the times, I have seen that tests using mocks extensively completely miss the intent of the test..!
-
Increased complexity and maintenance costs - Setting up mock object expectations create clutter and duplication to test code. Mocking Fine-Grained or Chatty Interfaces results in a lot of brittle unit-test code. High-level tests may not require such extensive changes.
-
It is a well-known fact that unit tests (that use mocks extensively) don’t catch as many bugs as the integration/high-level tests.
I do not want leave an impression that I do not like the idea of using Mock objects for testing. We just need to use it for the right things and right places after understanding it’s rewards and risks, just like any other tool/framework.
Mocking is still suitable for various scenarios that include:
-
Testing sub-systems that don’t exist yet or are being developed separately. But, then after the dependent code is available, I would switch back to real code from mock objects.
-
Legacy code that does not have interfaces and is tightly coupled.
-
Sometimes we may need to mock only some methods of a class and keep the normal behavior of others.
-
Testing against slow/heavyweight components that have significant dependencies.
-
To simplify Unit testing when there are no alternative ways to test the same effectively (for example, if the dependent component is very hard to setup/configure, etc.).
IMO, choosing the right scenarios objects (if any) for using Mock objects is the trickiest part.
I personally avoid using Mock Objects (unless there are no other easy options) by using good OO Design & Programming practices to write my code such that it is testable without having to use Mock Objects.