Lombok - 将@EmbeddedId 字段公开给@SuperBuilder

Lombok - expose @EmbeddedId fields to @SuperBuilder

有没有办法将 @EmbeddedId 中定义的属性公开给 @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();

整个概念背后的原因: 同名列存在于多个视图中,因此为所有视图设置一个基础 class 是合乎逻辑的。尽管实体本身是不可变的(基于数据库视图),但我想在测试中使用 builder 这就是为什么我希望它们像上面那样。

既然您在 id 中使用了 @Delegate 注释,那么您应该能够 set/get TheId 字段直接来自 BaseEntity Class.

您可以阅读 here 更多相关信息。

没有自动将委托插入 @(Super)Builder 的方法。 但是,如果您的委托人 class(在本例中为 TheId)不包含太多字段,您可以手动将相应的 setter 方法添加到构建器 class。只需添加正确的构建器 class header,将您的代码放入其中,Lombok 将添加您未手动编写的所有剩余代码。

诚然,这有点棘手。 @SuperBuilder 生成的代码非常复杂,您不想添加太多手动内容,因为您想保留 Lombok 的优点:如果您在注释 class 中更改某些内容,您就不会不想重写您所有的手动方法。所以这个解决方案试图以一点点 performance/memory 浪费为代价来保持大部分自动化。

@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();
        }
    }
}

值得吗?这是一个品味问题,以及它在多大程度上提高了你的情况下构建器的 applicability/usability/readability。