简体   繁体   中英

How do I use lombok @Builder to store different values?

I have the following JPA entity

@Data
@Builder
public class Post {
  @Id
  @GeneratedValue
  UUID id;

  @OneToMany
  Set<PostTags> tags;

  String content;
}

@Data
public class PostTag {
  @Id
  @GeneratedValue
  UUID id;

  @OneToOne
  Post post;

  String tag;
}

Using lombok @Builder I want to be able to do the following

Post post = Post.builder()
  .tags("hello", "world")
  .content("Hello world")
  .build();

I am presuming I need a custom builder along the lines of

public static class PostBuilder {
  private Set<String> myTags = new HashSet<>();
  public PostBuilder tags(String... tags) {
    myTags.addAll(Arrays.asList(tags));
    return this;
  }
}

From the documentation it appears there ObtainVia annotation that I can use, but I am not sure how to get around it (no example on the doc) and especially since I only want myTags to be a builder specific thing, and not be exposed on the main class itself.

ObtainVia only works for toBuilder , so that won't help much in this case.

I suggest the following approach.

First, add a factory method in PostTag , eg createTag(String) . This method only sets tag in the instance it creates and leaves everything else null . Statically import it into the class where you want to use PostBuilder .

Next, use @Singular on tags . Then you can write:

Post post = Post.builder()
   .tag(createTag("hello"))
   .tag(createTag("world"))
   .content("Hello world")
   .build();

Finally, customize the build() method so that it first creates the Post instance (like an uncustomized build() method would) and then sets this newly created Post instance as post in all PostTag instances. Have a look at the delombok ed code to make sure you use the right builder class and method headers when customizing the builder.

You can use @Accessors for what you're asking:

Post

@Data
@Accessors(chain = true)
public class Post {
    @Id
    @GeneratedValue
    private UUID id;

    @OneToMany
    private Set<PostTags> tags;

    private String content;

    public Post tags(String... tags) {
        Arrays.stream(tags)
                .map(tag -> PostTags.builder().tag(tag).build())
                .forEach(this.tags::add);
        return this;
    }
}

PostTags

@Data
@Builder
public class PostTags {
    @Id
    @GeneratedValue
    private UUID id;
    @OneToOne
    private Post post;
    private String tag;
}

When you using @Accessors(chain = true) , The setters will return this reference instead of void , and then your code will act this way:

Post post = new Post().setId(id).tags("aaa", "bbb");

If you want your code to be more similar to builder then add fluent value to the annotation: @Accessors(chain = true, fluent = true)

It will remove the set<Something> from the setters and just use the name of the fields, and then your code will look like this:

Post post = new Post().id(id).content("hello").tags("aaa", "bbb");

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM