JHipster does not use lombok. Why?

renanfranca

Renan Franca

Posted on August 26, 2022

JHipster does not use lombok. Why?

Originally published at renanfranca.github.io

After I run the JHipster generator for the first time I realized that the back end implemented with spring boot doesn't use Lombok. So I decided to customize the generated code to use lombok than I realized it wasn't the best way to use JHipster.

Customize JHipster

When I realized how awesome JHipster was I thought that I should change the generated code to be like the one I used to type manually. So I saw the opportunity to create my own blueprint with the following custom annotations:

  • Add Lombok - @lombok
  • Hide comment - @noCodeComment
  • Change the database scheme - @schema
  • Add Id GeneratedValue to entity - @generatedValue(identity)
  • Define column database name - @column(ds_field)
  • Use unsupported types - @newFieldTypeImport(import_class_package_type) and @newFieldType(type)

I opened source my blueprint which customize the server side code:

Here is a jdl file example which uses my blueprint:

@lombok  
@schema(MyProject)  
@noCodeComment 
entity RenanClasse (RenanOk) {  
@generatedValue(identity)  
id Long  
@column(ds_name)  
name String
@newFieldTypeImport(java_time_LocalDateTime)  
@newFieldType(LocalDateTime)  
birthday ZonedDateTime
}  

// Use Data Transfer Objects (DTO)  
dto * with mapstruct  

// Service layer  
service all with serviceClass  
Enter fullscreen mode Exit fullscreen mode

JHipster choose not use Lombok

I found an issue at jhipster generated project that list the the reasons to not use lombok. I will quote some of them:

  1. On one project, a new version of the Lombok plugin caused the IDE to crash (I think this was Intellij). So nobody could work anymore.

  2. On another project, Lombok made the CI server crash (and would have probably caused the production server to crash), as it triggered a bug in the JVM

  3. On a third project, we achieved 30% performance increase by recoding the equals/hashcode from Lombok

  4. Then, for JHipster, the story is also that we can't ask people to install a plugin on their IDE:

    • 1st goal is to have a smooth experience: you generate the app and it works in your IDE, by default

    • 2nd goal is that you can use whatever IDE you want. And some people have very exotic things, for example I just tried https://codenvy.com/ -> no plugin for this one, of course

Entity implementation

In the following example you can see that JHipster implements manually sets and gets. Beyond that you can see it's implement what is called fluent method as I learn it from the JHipster generator source code and from this issue which discuss fluent setters implementation at jhipster.

@Entity
@Table(name = "nap")
@Cache(usage = CacheConcurrencyStrategy.READ_WRITE)
public class Nap implements Serializable {

    private static final long serialVersionUID = 1L;

    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "sequenceGenerator")
    @SequenceGenerator(name = "sequenceGenerator")
    @Column(name = "id")
    private Long id;

    @Column(name = "start")
    private ZonedDateTime start;

    @Column(name = "jhi_end")
    private ZonedDateTime end;

    @Enumerated(EnumType.STRING)
    @Column(name = "place")
    private Place place;

    @ManyToOne
    private BabyProfile babyProfile;

    @ManyToOne
    private Humor humor;

    // jhipster-needle-entity-add-field - JHipster will add fields here

    public Long getId() {
        return this.id;
    }

    public Nap id(Long id) {
        this.setId(id);
        return this;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public ZonedDateTime getStart() {
        return this.start;
    }

    public Nap start(ZonedDateTime start) {
        this.setStart(start);
        return this;
    }

    public void setStart(ZonedDateTime start) {
        this.start = start;
    }

    public ZonedDateTime getEnd() {
        return this.end;
    }

    public Nap end(ZonedDateTime end) {
        this.setEnd(end);
        return this;
    }

    public void setEnd(ZonedDateTime end) {
        this.end = end;
    }

    public Place getPlace() {
        return this.place;
    }

    public Nap place(Place place) {
        this.setPlace(place);
        return this;
    }

    public void setPlace(Place place) {
        this.place = place;
    }

    public BabyProfile getBabyProfile() {
        return this.babyProfile;
    }

    public void setBabyProfile(BabyProfile babyProfile) {
        this.babyProfile = babyProfile;
    }

    public Nap babyProfile(BabyProfile babyProfile) {
        this.setBabyProfile(babyProfile);
        return this;
    }

    public Humor getHumor() {
        return this.humor;
    }

    public void setHumor(Humor humor) {
        this.humor = humor;
    }

    public Nap humor(Humor humor) {
        this.setHumor(humor);
        return this;
    }

    // jhipster-needle-entity-add-getters-setters - JHipster will add getters and setters here

    @Override
    public boolean equals(Object o) {
        if (this == o) {
            return true;
        }
        if (!(o instanceof Nap)) {
            return false;
        }
        return id != null && id.equals(((Nap) o).id);
    }

    @Override
    public int hashCode() {
        // see https://vladmihalcea.com/how-to-implement-equals-and-hashcode-using-the-jpa-entity-identifier/
        return getClass().hashCode();
    }

    // prettier-ignore
    @Override
    public String toString() {
        return "Nap{" +
            "id=" + getId() +
            ", start='" + getStart() + "'" +
            ", end='" + getEnd() + "'" +
            ", place='" + getPlace() + "'" +
            "}";
    }
}
Enter fullscreen mode Exit fullscreen mode

source: Nap.java

So you can create this object like when you use the builder pattern but it's not immutable.

Nap nap = new Nap().id(1l).start(ZonedDateTime.now()).end(ZonedDateTime.now().plusHours(1));
Enter fullscreen mode Exit fullscreen mode

I enjoyed this implementation and that's enough for me to keep my code less verbose.

This is the way

I gave up to customize JHipster generated code.
I realized that I should accept the jhipster generated code pattern because If I try to customize everything I am creating my own JHipster and I am going to lose the productivity which JHipster could bring to my workflow.
It isn't a trivial job to maintain a blueprint up to date with the latest jhipster generator version. So I advise you to only create a blueprint if it is extremely necessary.

💖 💪 🙅 🚩
renanfranca
Renan Franca

Posted on August 26, 2022

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related