Showing posts with label Hibernate. Show all posts
Showing posts with label Hibernate. Show all posts

Friday, December 20, 2013

An Annotation Nightmare

@XmlElementWrapper(name="orders")
@XmlJavaTypeAdapter(OrderJaxbAdapter.class)
@XmlElements({
   @XmlElement(name="order_2",type=Order2.class),
   @XmlElement(name="old_order",type=OldOrder.class)
})
@JsonIgnore
@JsonProperty
@NotNull
@ManyToMany
@Fetch(FetchMode.SUBSELECT)
@JoinTable(
    name = "customer_order",
    joinColumns = {
        @JoinColumn(name = "customer_id", referencedColumnName = "id")
    },
    inverseJoinColumns = {
        @JoinColumn(name = "order_id", referencedColumnName = "id")
    }
)
private List orders;

Wait. What? Is this really what we have come to? I can't even see the damn property under this bloat. How did this happen? Yeah ok - we had to get rid of the old xml configuration horror somehow. But this? This is even WORSE. This class is supposed to be a goddamn pojo with a bunch of properties. Short and concise, easy to read. I, as a reader of this cass, am not interested at all how the database table is joining customers to orders. I'm neither interested how its being serialized. This is just implementation details. Reading this class, i am living in an object world and i want to know what data and behaviour the object has. Not more, not less. I don't care about column names, fetchtypes or json serialization for the moment. And i don't want to read, change or recompile this class for the sake of a tablename change. I don't want to add another annotation for storing this entity in a mongoDB neither. The entity should not have responsibility for these details. We are not only violating the Single Responsibility Principle here, we are doing a f****** responsibility party.

Ok ok, enough of the rage. How do we deal with this issue? Some duplicate the entity for various layers with different annotation purposes. They map the entity onto the next layers related entity using an automated mapper like Dozer. Some even write that mapping themselves. But this is by no means a solution. It is just replacing one code smell with another one: Duplication.

So please, focus on frameworks that don't force you to clutter your code. jOOQ is a nice solution to map database records to entities without annotations. Also, hibernate allows you to define your mappings in XML.

Private Field Injection


@Inject
private MyService myService

This is used quite often, while it shouldn't even be possible. The myService field is private, thus it's inaccessible from outside the class. Nevertheless it's possible and people do it. In reality it is a hack. The DI-framework sets the field using reflections doing setAccessible(true). You don't want hacks in your code, do you? Lets have a look at the alternatives:

Setter Injection
Well, at least its better than the private field injection, since its using a public method instead of hacking a private field. But still, ask yourself this: 'Is this class even supposed to live without the injected value?' Because if it's not, there is no reason what so ever for the class to get constructed without an instance of MyService. You want to implement this constraint on the class level and inside the constructor, not on the framework level.

Constructor Injection
This is usually the way to go. It allows you to
  • make the field immutable (there is usually no need to change it).
  • implement the constraint, that the class is not instantiatable without a given MyService in the right place.
Of course it means that you cannot inject by annotation. But why would you want to? The class doesn't need to know, if it gets injections by a DI-Container or a Factory Class. It should not know anything of this. No @Autowired, no @Qualifier. All it needs to know is its own behaviour. Everything else should be handled outside of the class.
One could use a configuration class or file for the actual injection.

A DI-Container is a usefull tool that helps you wire your classes together. Use it for this purpose, but don't let it dictate your code. Uncle Bob wrote a great post where he explained how to use DI-Frameworks without having them dictate your code.

@RunWith(SpringJUnit4ClassRunner.class) in UnitTests

Why would you need this in unittests? Because it is automatically generated by your IDE/app template? No! You want to test the behaviour of a class, living in isolation in unittests. Not if the DI-Conainer is injecting a fields accordingly. Just inject yourself in a setup method. No DI-Container needed. By the way, all this testrunner does is these 3 lines of code.

private TestContextManager testContextManager;
//..
this.testContextManager = new TestContextManager(getClass());
this.testContextManager.prepareTestInstance(this);

They are not worth blocking your only TestRunner slot. You want to keep it free for something like parameterized @RunWith(JUnitParamsRunner.class) or concurrency @RunWith(ConcurrentJunitRunner.class) tests.

@Override

Really, my IDE already knows if i am correctly overriding a method. To me, its just clutter.

@SuppressWarnings

... Don't even get me started

tl;dr
Annotations have become more harmful than helpful these days. We should get back to pojos and focus on keeping our code as clutterless and framework-agnostic as possible to make it more readable and reuseable. Don't let frameworks dictate your codebases, since they should be exchangable tools. Beware of what a class should know, and what not. Some annotations are useful, most aren't.

@DevNull({
 @SuppressWarnings
 @Autowired, 
 @Inject, 
 @Override,  
 @XmlElementWrapper,
 @XmlJavaTypeAdapter,
 @XmlElement,
 @JsonIgnore,
 @JsonProperty,
 @ManyToMany,
 @Fetch,
 @JoinTable
})

Wednesday, April 10, 2013

I18n of JPA Entities

Sadly the support for i18n for Entities in JPA is actually very poor to non-existent. There's just basically no out of the box solution provided by Hibernate & Co, although i18n is a feature that could be considered mandatory for a powerful ORM Framework. If you take a look at PHP for example Doctrine supports i18n very well. So besides some really unpopular i18n extensions on forums/github you are basically on your own.

So how do you do it?

There isn't really a general solution to achieve i18n. There are many different ways and you have to choose which is actually best for you. If you do have a relatively small amount of data and ordering by translations is not necessary you can store "keys" in your tables that are being translated by Spring i18n for example. Spring i18n could store the translations in separate property files. Since you wont have to worry about performance, this is a very easy way to achieve i18n.

But lets say you have lots of data, and you need to order it on the database side. With such requirements you are better off storing translations inside your database tables. While there are guides, and also an opensource framework on the internet that implement i18n with one centralized translation-table that holds all translations i highly recommend not to follow that idea. The problem with this solution is, that you will have to add one more join for every field that has to be translated, whereas multiple joins for a single table with lots of data can easily become a performanceissue.
Its better to follow the approach to create a separate translation-table for every entity. This means that you would have one entity containing simple data, and another one containing only the fields that require translation. Like so:

This schema is pretty normalized and you only have a single join on fetching an entity, while you can still order appropriate.

Your entities would look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
@Entity
public class Book {

    @Id
    private int isbn;

    private String author;

    private Date releaseDate;

    @OneToMany
    @JoinColumn(name = "isbn")
    private List<BookTranslation> translations;

    // constructor, getters and setters -...

}

and that

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
@Entity
public class BookTranslation implements Serializable {

    @Id
    private int isbn;

    @Id
    private String locale;

    private String title;

    private String description;

    // constructor, getters and setters -...

}

You could then fetch a book with its translations with:

select b from Book b join fetch b.translations bt where bt.locale = 'de';

If you cannot accept your ServiceLayer to deliver two entities for every Book only because of translations, i suggest you to implement some DTO like this BookTO which holds all values.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
public class BookTO {

    private int isbn;

    private String author;

    private Date releaseDate;

    private String title;

    private String description;

}

You'd just need to map the values of Book and BookTranslation to BookTO within your ServiceLayer and also return BookTO instead of Book in your ServiceLayer.

This solution might not always be the most appropriate, but it definitely comes in handy sometimes.