Lombok — предоставить поля @EmbeddedId для @SuperBuilder

#java #spring #lombok

Вопрос:

есть ли способ выставить свойства, определенные в @EmbeddedId to @SuperBuilder ?

 // Composite id class
@Data
@Builder(builderMethodName = "having")
@NoArgsConstructor
@AllArgsConstructor
@Embeddable
public class TheId {
    @Column(name = "name", insertable = false)
    private String name;

    @Column(name = "description", insertable = false)
    private String description;
}

// Base entity class
@SuperBuilder 
@Getter
@Immutable
@MappedSuperclass
@AllArgsConstructor
@NoArgsConstructor
public class BaseEntity implements Serializable {
    @Delegate
    @EmbeddedId
    private TheId id;

    @Column(name = "long_description", insertable = false)
    private String longDescription;
}

// Concreate entity over database view
@Entity
@Table(name = "view_number_1")
@Getter
@Setter
@Immutable
@AllArgsConstructor(staticName = "of")
@SuperBuilder(builderMethodName = "having")
public class ConcreteEntity1 extends BaseEntity {}
 

Я хотел бы иметь возможность писать такой код:

 ConcreateEntity1.having()
    .name("the name")
    .description("something")
    .longDescription("akjsbdkasbd")
    .build();
 

вместо

 ConcreateEntity1.having()
    .id(TheId.having()
        .name("the name")
        .description("something")
        .build())
    .longDescription("akjsbdkasbd")
    .build();
 

Причина, лежащая в основе всей концепции:
одноименные столбцы присутствуют в нескольких представлениях, поэтому логично иметь один базовый класс для них всех. Хотя сами объекты являются неизменяемыми (на основе представления БД) Я хотел бы использовать builder в тестах, поэтому я хотел бы иметь их, как указано выше.

Ответ №1:

Не существует автоматического способа вставки делегатов в @(Super)Builder . Однако, если ваш класс делегата ( TheId в данном случае) не содержит слишком много полей, вы можете вручную добавить соответствующие методы установки в класс builder. Просто добавьте правильный заголовок класса builder, поместите в него свой код, и Lombok добавит весь оставшийся код, который вы не пишете вручную.

По общему признанию, это немного сложно. Генерируемый код @SuperBuilder довольно сложный, и вы не хотите добавлять слишком много ручного материала, потому что вы хотите сохранить преимущества Lombok: если вы что-то измените в своем аннотированном классе, вам не захочется переписывать все свои ручные методы. Таким образом, это решение пытается сохранить большую часть автоматизации за счет небольшого снижения производительности / потери памяти.

 @SuperBuilder(builderMethodName = "having")
public class BaseEntity {
    @Delegate
    private TheId id;

    private String longDescription;

    public static abstract class BaseEntityBuilder<C extends BaseEntity, B extends BaseEntityBuilder<C, B>> {
        private TheId.TheIdBuilder theIdBuilder = TheId.having();
        
        // Manually implement all delegations.
        public B name(String name) {
            theIdBuilder.name(name);
            // Instantiating every time you call the setter is not optimal, 
            // but better than manually writing the constructor.
            id = theIdBuilder.build();
            return self();
        }
        public B description(String description) {
            theIdBuilder.description(description);
            id = theIdBuilder.build();
            return self();
        }
        
        // Make this method invisible. 
        @SuppressWarnings("unused")
        private B id() {
            return self();
        }
    }
}
 

Стоит ли оно того? Это вопрос вкуса и насколько это улучшает применимость / удобство использования / читаемость конструктора в вашем случае.

Комментарии:

1. Да, такое ощущение, что подобная пользовательская реализация не стоит затраченных усилий. Я думал, что в Lombok есть какая-то встроенная функция, которую я не смог найти.

Ответ №2:

Поскольку вы использовали аннотацию @Delegate в id, вы должны иметь возможность устанавливать / получать поля ID непосредственно из класса BaseEntity.

Подробнее об этом можно прочитать здесь.