lombok builders does not care of restricted set of method/constructor-properties - builder

I have 3 builder methods:
#Builder(builderMethodName = "b1")
public static User builder1(String name, String age) {
return User.b3().name(name).age(age).build();
}
#Builder(builderMethodName = "b2")
public User(String name, String date, String type) {
super();
this.name = name;
this.date = date;
this.type = type;
}
#Builder(builderMethodName = "b3")
public User(String name, String age, String date, String type) {
super();
this.name = name;
this.age = age;
this.date = date;
this.type = type;
}
When I use b1-builder, I expect that only name and age properties can be set by this builder. However, using this also works (but it should not):
User.b1().name("a").age("1").date("12").type("b").build();
The same goes for b2-builder. The builder should only provide name, date and type, however the builders b1 and b2 support the same properties as b3, even though the declaration of these methods restrict it.
How can I restrict the set properties of b1 and b2-builder?

All your #Builders share the same builder class, because the name of that builder class is generated by concatenating the name of the containing class with "Builder", i.e. it is always "UserBuilder" in this case. Thus, the different #Builders all contribute their setter methods to that single builder class.
You have to let the different #Builders generate different builders classes:
#Builder(builderClassName = "UserBuilder1", builderMethodName = "b1")

Related

Is there a more innovative way to handle null exceptions in the following cases?

Now I have created a Response for Puppy
#Getter
#NoArgsConstructor(access = AccessLevel.PRIVATE)
#AllArgsConstructor(access = AccessLevel.PUBLIC)
#JsonInclude(JsonInclude.Include.NON_NULL)
public class PuppyResponse {
private Long puppyId;
private String name;
private Integer age;
private String breed;
private Long vetId;
private String vetName;
public PuppyResponse(Long puppyId, String name, Integer age, String breed) {
this.puppyId = puppyId;
this.name = name;
this.age = age;
this.breed = breed;
}
public static PuppyResponse of(Puppy puppy) {
Optional<Vet> vet = Optional.ofNullable(puppy.getVet());
if(vet.isPresent()) {
return new PuppyResponse(
puppy.getPuppyId(),
puppy.getName(),
puppy.getAge(),
puppy.getBreed(),
vet.get().getVetId(),
vet.get().getName()
);
}else {
return new PuppyResponse(
puppy.getPuppyId(),
puppy.getName(),
puppy.getAge(),
puppy.getBreed()
);
}
}
public static List<PuppyResponse> listOf(List<Puppy> puppies) {
return puppies.stream()
.map(PuppyResponse::of)
.collect(Collectors.toList());
}
}
It's possible that the dog attribute vet is null.
I designed Puppy to use a different constructor depending on whether it's null or not, but this doesn't seem like a good way to do it.
Of course it works without problems, but I want to design it in a better way. How do I handle nulls?
If you want to just provide new Veet Object if puppy.getVet() is null by using orElseGet
Vet vet = Optional.ofNullable(puppy.getVet()).orElseGet(Vet::new);
If you want to provide default Veet Object if puppy.getVet() is null
Vet vet = Optional.ofNullable(puppy.getVet()).orElseGet(PuppyResponse::getDefaultVet);
This way you don't need to check ifPresent and create responses accordingly
return new PuppyResponse(
puppy.getPuppyId(),
puppy.getName(),
puppy.getAge(),
puppy.getBreed(),
vet.getVetId(),
vet.getName()
);
To provide default Vet Object
private static Vet getDefaultVet(){
Vet v = new Vet();
v.setVetId(0);
v.setName("Default Name");
return v;
}

Use Java 8 stream to convert enum to set

I have an enum looks like:
public enum Movies {
SCIFI_MOVIE("SCIFI_MOVIE", 1, "Scifi movie type"),
COMEDY_MOVIE("COMEDY_MOVIE", 2, "Comedy movie type");
private String type;
private int id;
private String name;
Movies(String type, int id, String name) {
this.type = type;
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
}
I know that I can use stream to create a Set of Movies enum with:
Set<Movie> Movie_SET = Arrays.stream(Movie.values()).collect(Collectors.toSet());
What if I want to create a Set of enum Movies id. Is there a way to do that with stream?
Yes, assuming you have a getter for your id, your Movies enum might look like this:
public enum Movies {
SCIFI_MOVIE("SCIFI_MOVIE", 1, "Scifi movie type"),
COMEDY_MOVIE("COMEDY_MOVIE", 2, "Comedy movie type");
private String type;
private int id;
private String name;
Movies(String type, int id, String name) {
this.type = type;
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
}
Then, you can get the set of ids by using Stream.map():
Set<Integer> movieIds = Arrays.stream(Movies.values()).map(Movies::getId)
.collect(Collectors.toSet());
BTW, an alternative way to create the set of all movies is to use EnumSet.allOf():
Set<Integer> movieIds = EnumSet.allOf(Movies.class).stream().map(Movies::getId)
.collect(Collectors.toSet());
You can use EnumSet implementation for this purpose e.g.
For obtaining the Set of Movies:
Set<Movies> movies = EnumSet.allOf(Movies.class);
For obtaining only movies ids:
Set<Integer> moviesIds = movies.stream().map(Movies::getId).collect(Collectors.toSet());
If you are able to get stream of Enum values then rest could easily be achieved.
You could use custom Collector impl (My favorite of all time), see example below of a custom collector:-
Set<Integer> movieIds = Arrays
.stream(Movies.values())
.collect(
HashSet::new,
(set, e) -> set.add(e.getId()),
HashSet::addAll
);
Or you could use map to fetch ids only and the collect to collect these ids to a Set as usual.
Set<Integer> movieIds = Arrays
.stream(Movies.values())
.map(Movies::getId)
.collect(Collectors.toSet());

Change Spring form tag path value

I need to convert "lastModified" to millisecond.Normally it is Date() format.But i must send it as a millisecond.How I can change path variable from jsp or my controller ? Or you can suggest other way.
You can add a transient long field and use it like below inside your Organization entity class.
public class Organization {
--------------
--------------
#Transient
private long lastModifiedMili;
public long getLastModifiedMili() {
return lastModified.getTime();
}
public void setLastModifiedMili(long lastModifiedMili) {
this.lastModifiedMili = lastModifiedMili;
this.lastModified = new Date(lastModifiedMili);
}
public Organization(Long id, String name, String address, long lastModifiedMili) {
this.id = id;
this.name=name;
this.address = address;
this.lastModifiedMili = lastModifiedMili;
this.lastModified = new Date(lastModifiedMili);
}
}
And inside your form use this:
<form:hidden path="lastModifiedMili" />

Store enum name, not value in database using EBean

I have this enum :
public enum DocumentTypes {
PDF("PDF Document"), JPG("Image files (JPG)"), DOC("Microsoft Word documents");
private final String displayName;
DocumentTypes(final String display) {
this.displayName = display;
}
#Override
public String toString() {
return this.displayName;
}
}
And a model like this :
#Entity
#Table(name = "documents")
public class Document extends Model {
#Id
public Long id;
#Constraints.Required
#Formats.NonEmpty
#Enumerated(EnumType.STRING)
#Column(length=20, nullable=false)
public DocumentTypes type;
#Constraints.Required
#Formats.NonEmpty
#Column(nullable=false)
public String document;
}
I match the enum using this in my controller :
DynamicForm form = form().bindFromRequest();
// ...
Document doc = new Document();
doc.type = DocumentTypes.valueOf(form.field("type").value());
doc.save();
The problem is that in database, it's stored as "Microsoft Word documents", but I would prefer to store it as DOC.
How can I do that?
You can define it very fine granular with the Anotation EnumMapping or EnumValue. This works with the old version org.avaje.ebean.
It seems that there was a complete rewrite of the code. In the actual version there is a different approach.

Use a custom deserializer only on certain fields?

With gson, is it possible to use a custom deserializer / serializer only on certain fields? The user guide shows how to register an adapter for an entire type, not for specific fields. The reason why I want this is because I parse a custom date format and store it in a long member field (as a Unix timestamp), so I don't want to register a type adapter for all Long fields.
Is there a way to do this?
I also store Date values as long in my objects for easy defensive copies. I also desired a way to override only the date fields when serializing my object and not having to write out all the fields in the process. This is the solution I came up with. Not sure it is the optimal way to handle this, but it seems to perform just fine.
The DateUtil class is a custom class used here to get a Date parsed as a String.
public final class Person {
private final String firstName;
private final String lastName;
private final long birthDate;
private Person(String firstName, String lastName, Date birthDate) {
this.firstName = firstName;
this.lastName = lastName;
this.birthDate = birthDate.getTime();
}
public static Person getInstance(String firstName, String lastName, Date birthDate) {
return new Person(firstName, lastName, birthDate);
}
public String toJson() {
return new GsonBuilder().registerTypeAdapter(Person.class, new PersonSerializer()).create().toJson(this);
}
public static class PersonSerializer implements JsonSerializer<Person> {
#Override
public JsonElement serialize(Person person, Type type, JsonSerializationContext context) {
JsonElement personJson = new Gson().toJsonTree(person);
personJson.getAsJsonObject().add("birthDate", new JsonPrimitive(DateUtil.getFormattedDate(new Date(policy.birthDate), DateFormat.USA_DATE)));
return personJson;
}
}
}
When the class is serialized, the birthDate field is returned as a formatted String instead of the long value.
Don't store it as a long, use a custom type with a proper adapter. Inside your type, represent your data any way you want -- a long, why not.

Resources