Question

Multiple constructors annotated with Lombok @Builder cause a problem?

This is my first question, so please feel free to correct anything that I missed writing this question:)

I am having trouble with using the @Bulider with my DTO objects. I have an abstract class "EventDTO" inherited by a class type "CreateTaskLogDTO".

I have two constructors each on both classes. The constructors inside the CreateTaskLogDTO inherits the ones inside the EventDTO.

strangely, the constructor annotated with the
@Builder(builderMethodName = "buildWithSpecificParamsNoID") works properly, but the one with @Builder(builderMethodName = "buildWithOperationStringID") fails to instantiate the following fields : String OperationString, Long id

Source code for EventDTO (parent class)

package com.wwme.wwme.log.domain.DTO;

import com.wwme.wwme.group.domain.Group;
import com.wwme.wwme.log.domain.OperationType;
import com.wwme.wwme.task.domain.Task;
import com.wwme.wwme.user.domain.User;
import lombok.*;
import lombok.experimental.SuperBuilder;

import java.time.LocalDateTime;

@Getter
@Setter
public abstract class EventDTO {

    public EventDTO(Long id, User user, Group group, OperationType operationTypeEnum, LocalDateTime operationTime, String operationString, Task task) {
        this.id = id;
        this.user = user;
        this.group = group;
        this.operationTypeEnum = operationTypeEnum;
        this.operationTime = operationTime;
        this.operationString = operationString;
        this.task = task;
    }

    public EventDTO(User user, Group group, OperationType operationTypeEnum, LocalDateTime operationTime, Task task) {
        this.user = user;
        this.group = group;
        this.operationTypeEnum = operationTypeEnum;
        this.operationTime = operationTime;
        this.task = task;
    }

    protected Long id;
    protected User user;
    protected Group group;
    protected Task task;
    protected OperationType operationTypeEnum;
    protected LocalDateTime operationTime;
    protected String operationString;


    //Calculate Operation String based on given parameters
    public abstract void setOperationStr();

    @Override
    public String toString() {
        return "EventDTO{" +
                "id=" + id +
                ", user=" + user.getNickname() +
                ", group=" + group.getGroupName() +
                ", task=" + task.getTaskName() +
                ", operationTypeEnum=" + operationTypeEnum +
                ", operationTime=" + operationTime +
                ", operationString='" + operationString + '\'' +
                '}';
    }
    //set specific fields for 
    public abstract void setSpecificFields();
    public abstract String convertToString();
}

Source code for CreateTaskLogDTO (child class)

package com.wwme.wwme.log.domain.DTO;

import com.wwme.wwme.group.domain.Group;
import com.wwme.wwme.log.domain.OperationType;
import com.wwme.wwme.task.domain.Task;
import com.wwme.wwme.user.domain.User;
import lombok.*;
import lombok.experimental.SuperBuilder;
import lombok.extern.slf4j.Slf4j;

import java.time.LocalDateTime;

@Slf4j
@Getter
public class CreateTaskLogDTO extends EventDTO{
    private String newTaskName;

    @Builder(builderMethodName = "buildWithSpecificParamsNoID")
    public CreateTaskLogDTO(User user, Group group, OperationType operationTypeEnum, LocalDateTime operationTime, String newTaskName, Task task) {
        super(user, group, operationTypeEnum, operationTime, task);
        this.newTaskName = newTaskName;
        setOperationStr();
    }

    @Builder(builderMethodName = "buildWithOperationStringID") //buildWithOperationStringID
    public CreateTaskLogDTO(Long id, User user, Group group, OperationType operationTypeEnum, LocalDateTime operationTime, String operationString, Task task) {
        super(id, user, group, operationTypeEnum, operationTime, operationString, task);
        setSpecificFields();
    }

    @Override
    public String toString() {

        return super.toString() +  "CreateTaskLogDTO{" +
                "newTaskName='" + newTaskName + '\'' +
                ", operationString='" + operationString + '\'' +
                '}';
    }

    @Override
    public void setOperationStr() {
        this.operationString = newTaskName;
    }

    @Override
    public void setSpecificFields() {
        this.newTaskName = this.getOperationString();
    }

    @Override
    public String convertToString() {
        log.info(this.getUser().getNickname());
        log.info(this.getNewTaskName());

        return "\"" + this.getUser().getNickname() + "\" 님이 " + "\"" + this.getNewTaskName() +"\""
                + "과제를 생성하였습니다.";

    }


}

Thanks!

I tried to running test codes to properly identify the problem.

This is the test that does not work (using the buildWithOperationStringID builder method) it fails to set up the fields : Id and OperationString. everything else sets up fine.

   @Test
    void createTaskLogDTO_builder_withID_test(){
        String operationString = "Task1";
        LocalDateTime now = LocalDateTime.now();

        when(mockTask.getTaskName()).thenReturn("Task1");
        when(mockUser.getNickname()).thenReturn("User1");
        when(mockGroup.getGroupName()).thenReturn("Group1");

        CreateTaskLogDTO createTaskLogDTO =  CreateTaskLogDTO.buildWithOperationStringID()
                .id(1L)
                .task(mockTask)
                .user(mockUser)
                .group(mockGroup)
                .operationString(operationString)
                .operationTime(now)
                .operationTypeEnum(OperationType.CREATE_TASK)
                .build();

        assertEquals(createTaskLogDTO.getId(),1L);
        assertEquals(createTaskLogDTO.getTask().getTaskName(),mockTask.getTaskName());
        assertEquals(createTaskLogDTO.getUser().getNickname(),mockUser.getNickname());
        assertEquals(createTaskLogDTO.getGroup().getGroupName(),mockGroup.getGroupName());
        assertEquals(createTaskLogDTO.getOperationTime(),now);
        assertEquals(createTaskLogDTO.getOperationTypeEnum(),OperationType.CREATE_TASK);
        assertEquals(operationString,createTaskLogDTO.getOperationString());
    }

And this is the test of the builder method that works (buildWithSpecificParamsNoID) It properly sets all fields.

    @Test
    void createTaskLogDTO_build_withoutID_test(){
        when(mockTask.getTaskName()).thenReturn("Task1");
        when(mockUser.getNickname()).thenReturn("User1");
        when(mockGroup.getGroupName()).thenReturn("Group1");

        String operationString = "Task1";
        LocalDateTime now = LocalDateTime.now();

        CreateTaskLogDTO createTaskLogDTO = CreateTaskLogDTO.buildWithSpecificParamsNoID()
                .task(mockTask)
                .user(mockUser)
                .group(mockGroup)
                .operationTime(now)
                .operationTypeEnum(OperationType.CREATE_TASK)
                .newTaskName(mockTask.getTaskName())
                .build();

        assertEquals(createTaskLogDTO.getTask().getTaskName(),mockTask.getTaskName());
        assertEquals(createTaskLogDTO.getUser().getNickname(),mockUser.getNickname());
        assertEquals(createTaskLogDTO.getGroup().getGroupName(),mockGroup.getGroupName());
        assertEquals(createTaskLogDTO.getOperationTime(),now);
        assertEquals(createTaskLogDTO.getOperationTypeEnum(),OperationType.CREATE_TASK);
        assertEquals(operationString,createTaskLogDTO.getOperationString());
        assertEquals(mockTask.getTaskName(),createTaskLogDTO.getNewTaskName());

    }

 3  51  3
1 Jan 1970

Solution

 4

I think this is because both @Builder tries to generate a builder class with the same name. Only one of them successfully generates this, and the other would see that an existing builder class already exists, and does not generate the builder class again.

In addition to having different builder method names, the two @Builder annotations should also have different builderClassNames:

@Builder(
    builderMethodName = "buildWithSpecificParamsNoID",
    builderClassName = "SpecificParamsNoIDBuilder"
)
public CreateTaskLogDTO(User user, Group group, OperationType operationTypeEnum, LocalDateTime operationTime, String newTaskName, Task task) {
    ...
}

@Builder(
    builderMethodName = "buildWithOperationStringID",
    builderClassName = "OperationStringIDBuilder"
)
public CreateTaskLogDTO(Long id, User user, Group group, OperationType operationTypeEnum, LocalDateTime operationTime, String operationString, Task task) {
    ...
}
2024-07-14
Sweeper