Monday, January 16, 2012

Java verbosity, JEE and Lombok

JEE and Lombok are the new Dream Team! Lombok isn't exactly new technology. We use it for some time now to get rid of some Java verbosity, but this library had some major problems that made it nearly unusable in the past. After updating our project settings and Eclipse IDEs we discovered major improvements in Lombok. Let me explain the benefits of Lombok with JPA Entities and JAXB Content Objects and what has changed.

Java Persistence API (JPA)

I really like JPA and the advancements in recent versions. JPA standardizes long proven concepts and fits perfectly into the JEE framework stack with Validation, CDI, EJB and JSF. The Entity Manager APIs and especially the Entity mapping Annotations are mature and powerful. We even mapped some very weird legacy Database schemes without too many JPA provider specific extensions or hacks. Small example Entity with some JPA Annotations:
@Entity
@Table(uniqueConstraints = @UniqueConstraint(columnNames = "name"))
public class Role extends BaseEntity {

 // simple, no further annotations necessary
 private boolean active;

 // additional settings, used for database constraints and form validation
 @NotNull
 @Size(min = 3, max = 50)
 private String name;

 // more complex example
 @OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
 @MapKeyColumn(length = 32)
 @MapKeyEnumerated(EnumType.STRING)
 @Cache(usage = CacheConcurrencyStrategy.TRANSACTIONAL)
 private Map<GroupCategory, Group> groups;

 // start noise...
 public boolean isActive() {
  return this.active;
 }
 public void setActive(final boolean active) {
  this.active = active;
 }
 // etc. etc.

JPA Entities adapt the well known JavaBean conventions with it's setters and getters.
Hey...what about concurrency?
That JavaBeans are the conceptual counterpart to the immutable trend, they do it wrong!
The JPA architecture prevents such problems, e.g. through unshared managed Entities and Transaction handling. That means for instance that the EntityManager isn't shared between threads / requests, that even the 2nd level Cache only stores serialized Entity representations, that you get Concurrent Modification Exceptions and transaction rollbacks if two Entities persist at the same time to the same datastore record etc.

Java verbosity

My major problem with the JavaBeans style is not the mutability concept but the verbosity in Java. Recently there was an article that outlines quite good what I think about many of those "I can't stand Java verbosity anymore" arguments, often used as major justification for learning a new language. You should learn new languages and concepts, but this is really a terrible reason. You can do really well with good structured APIs. Java _is_ more verbose, but at the same time often very readable, even for none Java developers or without the knowledge of mystique DSLs and operators.

Project with many JavaBeans
But there are different kinds of verbosity - for me the IDE code generation of setters and getters that represent 2/3 of a class source is definitely superfluous verbosity that doesn't improve the readability at all. On top of that you often have to provide Javadoc and Tests for this stuff. Why that? Because if you work in customer projects you really want to prevent a discussion in the approval phase whether 100% test and documentation coverage is really a reasonable objective. Today these code metrics are transparent to the customer, tools like Sonar make that simple. But you have to interpret such metrics in context and some people that think in black and white have difficulties with that, especially when they sit on the demanding side.


For me a Java language extension for less verbose setters and getters is at least as important as Lambdas and such an Compiler addition should be much simpler. JavaBeans conventions are as old as SAM types and widely used. I really cannot understand why this isn't a hot Java language topic, especially in combination with many JavaBeans-focused JEE patterns and the JEE resurrection. Many modern languages support alternative concepts.

Lombok to the rescue

With Lombok you can get rid of all those setters and getters through the addition of two annotations:
  • @lombok.Getter
  • @lombok.Setter
The Lombok compiler extension than automatically generates all setters and getters for none-static fields:
@Entity
@XmlRootElement
@Getter
@Setter
public class Role extends BaseEntity {

 @Size(max = 1000)
 private String description;

 ... // no trivial setters and getters follow!
The generated JVM Bytecode methods don't differ from ordinary getters and setters with classic noisy source code (only some details like debug line numbers). This is fully transparent to all other frameworks. Lombok isn't required as runtime library. So you can also use this e.g. for JAXB Content Objects (tutorial) which also heavily follow the JavaBeans style. The above example contains @Entity and @XmlRootElement, that means this JavaBean is usable for O/R datastore persistence as well as XML marshalling and unmarshalling without any boilerplate code.

You can use your own setters and getters if more logic is necessary (e.g. lazy initialize empty fields), Lombok will skip the generation of existing methods. You can also annotate single fields instead of the whole class, e.g. the BaseEntity in the above example contains an auto generated id and doesn't provide an id setter. JPA doesn't really need the setters for populating the Entity:
@MappedSuperclass
public abstract class BaseEntity implements Serializable {

 @Transient
 @Getter
 @Setter
 private boolean selected;

 @Id
 @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "ident_Seq")
 @Getter
 private Long id;

 ... // no trivial setters and getters follow!


That's all great, but by no means a new message - why I'm so excited about it?

We are using Lombok for some time now. Even if it's benefits are undeniable we often thought about removing it from our projects. The problem was, that Lombok had many problems with the Eclipse auto format features (I heard from problems with other IDEs too).
If you used certain Save actions than you got Eclipse errors and jumbled invalid source code. The auto formatting and sorting often didn't work at all with Lombok annotations in place.
So... deactivate auto format actions?!
Better not do that! If you develop applications in teams with multiple developers you commonly use a source version control system like SVN, Git etc. It's very important that you can follow code changes (who changed what / why / when) in the history. If you reformat half the code with your flavor-of-the-day code style you lose this important information. It's important to have Programming Style Guidelines in the company and for the project but you must also enforce them before each checkin. IDE Save actions with preconfigured auto format rules are very powerful for this.

So this is not an option, but I was surprised how smooth many things work after our recent IDE and library updates for a project. A short look into the Lombok release notes shows severe activities around fixing those IDE problems. We still have some minor issues if you have own methods in your class and method sorting active, but no real show stopper.

Don't overuse technology

Lombok has many other annotations and you may find some other interesting as well. I will not repeat the documentation here. Especially @ToString, @EqualsAndHashcode, @...Constructor and the aggregation @Data look promising for JPA Entities and JAXB Content Objects. They create toString(), equals(), hashCode() etc. for all none-static and none-transient fields.

Beware: The generated methods trigger the lazy loading of collection properties and also iterate over arrays and collections. This leads to endless cycles or you will load, print and compare half of your database if you have an object model with many associations. You can restrict the used fields for the method generation - but not with the @Data all-you-can-eat, which seems so convenient but is absolutely useless for Entities. We don't use this annotations but you can give it a try.

Very short Lombok Setup Guide

  • Download lombok.jar.
  • The JAR is executable, specifiy IDE location in the appearing Installer dialog. This will add Lombok as Java agent to Eclipse or other IDEs
    (for Eclipse these lines are added to eclipse.ini: -javaagent:lombok.jar, -Xbootclasspath/a:lombok.jar)
  • Add this to your Maven POM:
  •    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>0.11.2</version>
        <scope>provided</scope>
       </dependency>
    
  • profit

2 comments:

  1. Good post and Thanks for sharing information. I know Java has been criticized for its verbosity but isn't it this verbosity which gives idea and control on what's going on e.g. var file = new file() doesn't give you stream level details which you have while doing same thing in Java. though some time it irates but nothing is perfect.

    Thanks
    10 Example of Enum in Java

    ReplyDelete
  2. I've found that Lombok is great for getting quickly building up a project with a minimal code base. Going along, I tend to remove more Lombok annotations or make them more fine grained. For example, replacing @Data with @Setter and @Getter annotations to have more control. I love that it's great for productivity but still allows you to scale back from using it.

    ReplyDelete