'How to customize lombok's superbuilder?

I have an existing data model which was (unfortunately) written with bidirectional relationships. Currently, I'm trying to refactor it using Lombok. I've added the @SuperBuilder annotation, but the generated builder does not call my custom setter methods (the ones that ensure that the bidirectionality remain intact).

After running delombok and investigating the resulting code, it appears that a constructor is created on the class being built that takes an instance of the builder to use to set the values. Unfortunately, it simply assigns the field values directly. So I thought maybe I could just implement that constructor myself, and make the calls to the setters as required. Of course, this does not work. When I build I get an error because there are now apparently two implementations of that same method in my class (in other words SuperBuilder implemented it even though it was already implemented in the class).

Does anyone know how to override that constructor (or any other mechanism that would allow me to get the setters called when constructing my object using the SuperBuilder annotation)?

Edit: added code as requested

The entity class I'm trying to refactor to using lombok is:


@Entity
@Table(name = "APPLICATION_USER", uniqueConstraints = @UniqueConstraint(columnNames = { "PRINCIPAL_NAME", "APPLICATION", "SITE_ID" }))
@AttributeOverrides(@AttributeOverride(name = "id", column = @Column(name = "APP_USER_ID")))
@Filters({ @Filter(name = FilterQueryConstants.SITE_ID_FILTER_NAME, condition = FilterQueryConstants.SITE_ID_FILTER) })
@SuperBuilder
public class ApplicationUser extends User
{
    private static final long serialVersionUID = -4160907033349418248L;

    @Column(name = "APPLICATION", nullable=false)
    private String application;

    @ManyToMany(mappedBy = "applicationUsers", targetEntity = Group.class)
    @Filters({ @Filter(name = FilterQueryConstants.GROUP_FILTER_NAME, condition = FilterQueryConstants.GROUP_FILTER),
            @Filter(name = FilterQueryConstants.SITE_ID_FILTER_NAME, condition = FilterQueryConstants.SITE_ID_FILTER) })
    @MappingTransform(operation = DTOSecurityOperation.ASSIGN_GROUP)
    @Builder.Default
    private Set groups = new HashSet ( );

    // Other methods omitted for brevity

When I run the delombok, the resulting constructor looks like the following:

protected ApplicationUser(final ApplicationUserBuilder b) {
    super(b);
    this.application = b.application;
    if (b.groups$set) this.groups = b.groups;
    else this.groups = ApplicationUser.$default$groups();
}

So I thought I could just basically copy this code into my ApplicationUser class and modify it to call my setter method when it sets the value for groups (rather than just doing a direct assignment). I was thinking of something like this:

protected ApplicationUser(final ApplicationUserBuilder b) {
    super(b);
    this.application = b.application;
    if (b.groups$set) this.setGroups(b.groups);
    else this.setGroups(ApplicationUser.$default$groups());
}

Originally, when using 1.18.8, I was getting an error stating that this constructor already exists. Since updating to 1.18.22, I now get this:

error: cannot find symbol
        if (b.groups$set) this.setGroups(b.groups);
                                          ^
  symbol:   variable groups
  location: variable b of type ApplicationUserBuilder


Solution 1:[1]

Customizing @SuperBuilder only works in more recent lombok version; you should always use the most recent one, which is v1.18.22 at the time of the writing of this answer.

With that version, customizing a @SuperBuilder constructor is possible. However, you are using code as a basis for your constructor that has been delomboked with v1.18.8. That does not work any more with current lombok versions. lombok v1.18.10 introduced that the actual field value for @Default fields are stored in the builder in fields like fieldName$value, not simply fieldName.

Thus, your customized constructor has to look as follows:

protected ApplicationUser(final ApplicationUserBuilder<?, ?> b) {
    super(b);
    this.application = b.application;
    if (b.groups$set) this.setGroups(b.groups$value);
    else this.setGroups(ApplicationUser.$default$groups());
}

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Jan Rieke