Did we say [ki]DOIT was meant to Keep It Simple and make you Smile?
At least that’s what we are trying…
Did we say [ki]DOIT was meant to Keep It Simple and make you Smile?
At least that’s what we are trying…
For every action, there is an equal and opposite reaction
— Isaac Newton, 1687
This one could, therefore, be called:
Please don’t touch it… unless you know what you are doing…
I’ve always asked myself what makes a programmer – I am a programmer- actually say that. When I have found myself saying this to me – luckily not very often – it feels like giving full control of my life to a machine…
One good specimen of this effect are the [indiscriminate use of]DAOs. Don’t ask me why but DAOs seem to have some viral effect in large projects and their dead bodies tend to survive most generations of programmers involved on the project, i.e. the (very)undesirable DONTTOUCHIT! effect…
In some “advanced” programs we even find a facade for each and every DAO.
Stay calm. I won’t touch it…
One of the (many) nice thing of using a proper ORM is the ability to create automatic database schemas from your domain model. For example, in EclipseLink, this is acheived with the SchemaManager class. All you need is a persistence unit name and let there be it!
If we season this a bit with some little magic from the amazing Liquibase project, with some simple steps we can make automatic structual diffs of our domain model! Let’s take a look at the algorithm we use in [ki]DOIT.
In order to simplify database versioning we have a simple naming convention. Let’s say we have a persistence unit named foo.dev. We’ll (finally) find a (mvn) folder structure as follows:
/src/main/resources/dbchangelog/foo.dev/db.changelog-master.xml/src/main/resources/dbchangelog/foo.dev/db.changelog-initial.xml
These files will sound familiar to you if you’ve already used Liquibase. If not, what are you waiting for?
If you are using the diff project you’ll notice you have a dependency to hsqldb. This is used to simplify db generation without the need of additional installation.
Let’s go with (a simplified version of) the algorithm then:
Too simple? Yes!! And it works like a charm. Currently, the only main issue is some arbitrary changes on indexes if you use them.
During a recent project we faced the (not so uncommon) problem of different serialization needs depending on the runtime context for our domain model. We have mechanisms inplace for this – multiple ifs in the serializer or even ConditionalSerialization.
The issue is that I spend most of my time reading code, and I like meaningful class names. We need to find something better.
Serialization is done in a last phase so this couldln’t be tackled cleanly without some additional help. At first groups – the validation JSR303 way – seemed as the best approach but finally we took benefit of the polymorphic typeAdapter approach taken in GSON and it doesn’t seem like a terrible decision. Speaking in other words, when the annotation SerializationAdapter
is used, the way serialization is handled is done using the following sequence (same applies to deserialization):
TypeAdapter
then give back as is. TypeAdapter
will construct the appropiate serializer and – wishfully – return the correct value.JsonSerializer
then check for XmlTransient
and if not present use default mechanism. Serialize null otherwise.SerializationAdapterCondition
serialize with JsonSerializer
JsonSerializer
XmlTransient
and if not present use default mechanism. Serialize null
otherwiseSince we now have a way to decide which context to use we can make a decision chain with the grouping annotation SerializationAdapters
. The order in which they are specified is the order in which they are questioned for acceptance. It obviestly doesn’t make sense to have a list of adapters and have the first one not implement SerializationAdapterCondition
or always return true since the other adapters will never be used. There is also a slight difference with how the single adapter works: if XmlTransient is present it will never fallback to the default mechanism.
Happy coding!
Examples:
@XmlTransient @OneToOne(cascade = CascadeType.PERSIST) @SerializationAdapter(SerializeWhenUsingActionEngine.class) private InitialStatement initialStatement;
public class SerializeWhenUsingActionEngine implements SerializationAdapterCondition { @Inject private IThreadLocalRegistry threadLocalRegistry; @Override public boolean accepted() { return threadLocalRegistry.getValue(IActionEntityService.class) != null; } }
Starting UUUUUUUUUUUUUUUUUUUUUUUP!