I would like to create a Builder pattern to create multiple objects at once.
Because with basic builder pattern it can only build one object..
Example of what I did:
package builders;
import java.util.function.Function;
public class UserBuilder {
private List<User> users;
private int userCount;
private UserBuilder() {
}
private UserBuilder(int userCount) {
this.userCount = userCount;
}
private UserBuilder(UserBuilder copy) {
this.users = copy.users;
this.userCount = copy.userCount;
}
public static UserBuilder user() {
return new UserBuilder();
}
public static UserBuilder users(int userCount) {
return new UserBuilder(userCount);
}
public UserBuilder withFirstname(String firstName) {
return withFirstname((unused) -> firstName);
}
public UserBuilder withFirstname(Function<Integer, String> func) {
for (int i = 0; i < users.size(); i++)
users.get(i).setFirstName(func.apply(i));
return this;
}
public UserBuilder withLastName(String lastName) {
return withLastName((unused) -> lastName);
}
public UserBuilder withLastName(Function<Integer, String> func) {
for (int i = 0; i < users.size(); i++)
users.get(i).setLastName(func.apply(i));
return this;
}
public User build() {
return user.get(0);
}
public User build(int index) {
return user.get(index);
}
public List<User> buildAll() {
return users;
}
}
Usage:
// Our builder can be use to create single object like basic builder:
User user = UserBuilder.user()
.withFirstName("toto")
.withLastName("tata")
.build();
// OR it can also build 10 users and set parameters thanks to lambdas
List<User> users = UserBuilder.users(10)
.withFirstName((index) -> "toto" + index)
.withLastName("tata")
.buildAll();
// users will have firstName=toto$index and all will have lastName=tata
What do you think about this pattern?
This is what I want BUT I must duplicate all my methods (with its lambda version..)
Maybe there is a way to delete 50% of methods by adding an annotation or something like that?
Is there a cleaner version?
The more common approach (also used in e.g. JavaFX) would be to stay with a single object builder but re-use the common fields.
In your example you aren't winning much by having the list allocation in your Builder.
public class User {
private String firstName;
private String lastName;
private User(String firstName, String lastName) {
this.firstName = firstName;
this.lastName = lastName;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
#Override
public String toString() {
return "User{" +
"firstName='" + firstName + '\'' +
", lastName='" + lastName + '\'' +
'}';
}
public static class UserBuilder {
private String firstName;
private String lastName;
public UserBuilder firstName(String firstName) {
this.firstName = firstName;
return this;
}
public UserBuilder lastName(String lastName) {
this.lastName = lastName;
return this;
}
public User build() {
return new User(firstName, lastName);
}
}
}
User user = new User.UserBuilder().firstName("toto").lastName("tata").build();
User.UserBuilder commonBuilder = new User.UserBuilder().lastName("tata");
List<User> users = new ArrayList<>(10);
for (int i = 0; i < 10; i++) {
users.add(commonBuilder.firstName("toto" + i).build());
}
List<User> users2 = IntStream.range(0, 10).mapToObj(index -> commonBuilder.firstName("toto" + index).build()).toList();
This may be helpful:
How difficult can it ever be to build a list of objects in Java and Kotlin using the builder pattern?
Part of the code:
class ManufacturerListBuilder {
private String continent;
private final List<Manufacturer> manufacturers = new LinkedList<>();
public ManufacturerListBuilder withContinent(String continent) {
this.continent = continent;
return this;
}
ManufacturerListBuilder add(Function<ManufacturerBuilder, ManufacturerBuilder> builderFunction) {
ManufacturerBuilder builder = ManufacturerBuilder
.apply(new ManufacturerBuilder()
.withContinent(continent))
.build();
this.manufacturers.add(builder);
return this;
}
public Collection<UserFilter> getManufacturers() {
return this.manufacturers;
}
}
Related
This method I created
public class Use {
private
String firstname;
String lastname;
public String output() {
return "Hii my name is " + firstname + lastname;
}
public String getFirstName() {
return firstname.toUpperCase();
}
public void setFirstName(String jl) {
firstname = jl.strip() ;
}
public String getLastName() {
return lastname.toUpperCase();
}
public void setLastName(String FN) {
lastname = FN.strip();
}
}
And I am trying to use the above method in this code:
import java.util.ArrayList;
import java.util.List;
public class MySweetProgram {
public static void main(String[] args) {
String[] firstnames = {"KARRIK", "KESHAV", "Sussy"};
String[] lastnames = {"gulati", "gulati", "smith"};
List <User> users = new ArrayList<User>();
for (int i = 0; i < firstnames.length; i ++) {
User user = new User();
user.setFirstName(firstnames[i]);
user.setLastName(lastnames[i]);
users.add(user);
}
for (User user : users) {
System.out.println(user.getfullname());
}
}
}
on running getting null null null
1: There is no such class 'Use' in 'MySweetProgram' class (Typo: User)
2: There is no such method in 'Use, class as 'getfullname()'
Solution: Change the class name to User And replace 'getfullname()' with 'output()'.
'
import java.util.ArrayList;
import java.util.List;
public class MySweetProgram {
public static void main(String[] args) {
String[] firstnames = { "KARRIK", "KESHAV", "Sussy" };
String[] lastnames = { "gulati", "gulati", "smith" };
List<User> users = new ArrayList<User>();
for (int i = 0; i < firstnames.length; i++) {
User user = new User();
user.setFirstName(firstnames[i]);
user.setLastName(lastnames[i]);
users.add(user);
}
for (User user : users) {
System.out.println(user.getfullname());
}
}
}
//User Class
class User {
private String firstname;
private String lastname;
public String output() {
return "Hii my name is " + firstname + lastname;
}
public String getfullname() {
return firstname + "_" + lastname;
}
public String getFirstName() {
return firstname.toUpperCase();
}
public void setFirstName(String jl) {
// firstname = jl.strip();// for java 11
firstname = jl;
}
public String getLastName() {
return lastname.toUpperCase();
}
public void setLastName(String FN) {
// lastname = FN.strip();//for java 11
lastname = FN;
}
}
I am having trouble understanding why my userRepository is returning null even when there is a record like it in my table. I tried doing it with my demo codes and it works but when I try doing it with user Authentication it does not work.
Security Services
#Path("/securityservice")
public class SecurityServices {
private UserRepository userRepo;
// http://localhost:8990/login/securityservice/security
#GET
#Path("security")
#Produces(MediaType.APPLICATION_JSON)
public Response getOrderById(#QueryParam("orderId") int orderID,
#HeaderParam("Authorization") String authString) throws JSONException {
JSONObject json = new JSONObject();
if (isUserAuthenticated(authString)) {
json.put("INFO", "Authorized User!");
return Response.status(200)
.entity(json.toString())
.type(MediaType.APPLICATION_JSON)
.build();
} else {
json.put("ERROR", "Unauthorized User!");
return Response.status(403)
.entity(json.toString())
.type(MediaType.APPLICATION_JSON)
.build();
}
}
private boolean isUserAuthenticated(String authString) {
//authString = Basic 3hfjdksiwoeriounf
String[] authParts = authString.split("\\s+");
//authParts[0] = Basic
//authParts[1] = 3hfjdksiwoeriounf
String authInfo = authParts[1];
byte[] bytes = Base64.getDecoder().decode(authInfo);
String decodedAuth = new String(bytes);
// decodedAuth = dj:1234
String[] credentials = decodedAuth.split(":");
//credentials[0]=dj
//credentials[1]=1234
System.out.println("HELLO"+credentials[0]);
System.out.println("HELLO"+credentials[1]);
User user = userRepo.findByUsername(credentials[0]); //this line returns null
if (user != null) {
return true;
} else {
return false;
}
}
User class (Getters and setters for the JPA Repo)
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private long id;
#Column(name="firstname")
private String firstName;
#Column(name="lastname")
private String lastName;
private String password;
private String username;
#Column(name="accesstype")
private String accessType;
public User() {
super();
}
public User(String firstName, String lastName, String password,
String username, String accessType) {
super();
this.firstName = firstName;
this.lastName = lastName;
this.password = password;
this.username = username;
this.accessType = accessType;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getAccessType() {
return accessType;
}
public void setAccessType(String accessType) {
this.accessType = accessType;
}
}
I have a class with variables I don't want it to be null or empty. Is there a way to use Lombok builder to set the property? I can use #NonNull but I won't be able to verify if it is empty or not. Obviously the other option is to write my own builder which does all these checks. For example:
class Person {
#NonNull
private String firstName;
#NonNull
private String lastName;
public static class PersonBuilder() {
// .
// .
// .
public Person build() {
//do checks for empty etc and return object
}
}
}
Maxim Kirilov's answer is incomplete. It doesn't check for blank/empty Strings.
I've faced the same issue before, and I realized that in addition to using #NonNull and #Builder from Lombok, overload the constructor with a private access modifier, where you can perform the validations. Something like this:
private Person(final String firstName, final String lastName) {
if(StringUtils.isBlank(firstName)) {
throw new IllegalArgumentException("First name can't be blank/empty/null");
}
if(StringUtils.isBlank(lastName)) {
throw new IllegalArgumentException("Last name can't be blank/empty/null");
}
this.firstName = firstName;
this.lastName = lastName;
}
Also, throwing IllegalArgumentException makes more sense (instead of NPE) when String has blank, empty or null values.
The builder annotation should solve your issue:
#Builder
class Person {
#NonNull
private String firstName;
#NonNull
private String lastName;
}
The generated code is:
class Person {
#NonNull
private String firstName;
#NonNull
private String lastName;
#ConstructorProperties({"firstName", "lastName"})
Person(#NonNull String firstName, #NonNull String lastName) {
if(firstName == null) {
throw new NullPointerException("firstName");
} else if(lastName == null) {
throw new NullPointerException("lastName");
} else {
this.firstName = firstName;
this.lastName = lastName;
}
}
public static Person.PersonBuilder builder() {
return new Person.PersonBuilder();
}
public static class PersonBuilder {
private String firstName;
private String lastName;
PersonBuilder() {
}
public Person.PersonBuilder firstName(String firstName) {
this.firstName = firstName;
return this;
}
public Person.PersonBuilder lastName(String lastName) {
this.lastName = lastName;
return this;
}
public Person build() {
return new Person(this.firstName, this.lastName);
}
public String toString() {
return "Person.PersonBuilder(firstName=" + this.firstName + ", lastName=" + this.lastName + ")";
}
}
}
In this case the null validation will take place during object construction.
I did something like this,
class Person {
private String mFristName;
private String mSecondName;
#Builder
Person(String firstName, String secondName) {
mFristName = PreCondition.checkNotNullOrEmpty(firstName);
mSecondName = PreCondition.checkNotNullOrEmpty(secondName);
}
}
class PreCondition {
static <T> T checkNotNullOrEmpty(T instance) {
if (instance == null || (instance instanceof String && ((String) instance).isEmpty())) {
throw new NullOrEmptyException();
}
return instance;
}
static class NullOrEmptyException extends RuntimeException {
NullOrEmptyException() {
super("Null or Empty");
}
}
}
Have you tried "#NotEmpty"? It's in the javax.validation.constraints package
https://javaee.github.io/javaee-spec/javadocs/javax/validation/constraints/NotEmpty.html
I am trying to learn spring data elasticsearch, and created a sample project. Right now, I am trying to get familiar with the mechanics of sorting.
Simply put, I have a Person class, that has a firstName and lastName field. I am trying to sort on the lastNameField.
Here is my Person class:
#Document(indexName = "entities", type = "person", shards=1, replicas=0)
public class Person {
#Id
private String id;
#Field(type=FieldType.String, index=FieldIndex.not_analyzed)
private String firstName;
#Field(type=FieldType.String, index=FieldIndex.not_analyzed)
private String lastName;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
}
Here is my DAO logic:
#Override
public Page<Person> getPeople(int pageNumber, int pageSize, Direction direction, String...sortFields) {
return personRepository.findAll(new PageRequest(pageNumber, pageSize, direction, sortFields));
}
The personRepository is an empty interface that extends PagingAndSortingRepository
And the actual call:
int pageSize = 1;
int currentPageNumber = -1;
boolean morePages = true;
while(morePages){
Page<Person> results = personDataHandler.getPeople(currentPageNumber+1, pageSize, Direction.DESC, "firstName");
List<Person> people = results.getContent();
if(!CollectionUtils.isEmpty(people)){
for(Person currentPerson: people){
System.out.println("[firstName: "+currentPerson.getFirstName()+", lastName: "+currentPerson.getLastName()+"]");
}
}
currentPageNumber++;
morePages = results.hasNext();
}
Could having a page size of 1 be the issue?
I have two string
private StringProperties firstName;
private StringProperties lastName;
private StringProperties nickName;
the first and last name are picked by user, the nickName is a concatenation of first 3 character of first and lastname
How i can do that?
Actually i initialize it like that (this is the entire class).
public class Person {
private StringProperty firstName;
private StringProperty lastName;
private StringProperty nickName;
private ObservableList<Evento> eventi = FXCollections.observableArrayList();
public Person(String firstName, String lastName) {
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
if (firstName.length() > 2 && lastName.length() > 2)
this.nickName = new SimpleStringProperty(firstName.trim().substring(0,3).concat(lastName.trim().substring(0,3)));
else
this.nickName = new SimpleStringProperty("");
}
public ObservableList<Evento> getEventi() {
return eventi;
}
public String getFirstName() {
if(firstName == null) firstName = new SimpleStringProperty(this,"firstName");
return firstName.get();
}
public StringProperty firstNameProperty() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName.set(firstName);
}
public String getLastName() {
if(lastName == null) lastName = new SimpleStringProperty(this, "lastName");
return lastName.get();
}
public StringProperty lastNameProperty() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName.set(lastName);
}
public String getNickName() {
if(nickName == null) nickName = new SimpleStringProperty(this,"nickName");
return nickName.get();
}
public StringProperty nickNameProperty() {
return nickName;
}
#Override
public String toString() {
return getNickName() + "(" + getLastName() + " " + getFirstName() + ")";
}
}
but when i let the user change first or lastName, the nickName won't update.
You should use ReadOnlyStringProperty for the nickname:
private ReadOnlyStringWrapper nickName= new ReadOnlyStringWrapper();
...
public final String getNickName() {
return nickName.get();
}
public final ReadOnlyStringProperty nickNameProperty() {
return nickName.getReadOnlyProperty();
}
As for binding, you can use utility methods from Bindings class or implement your own binding for any other complicated cases. This example uses createStringBinding() method. It takes Callable functional interface, which will be used to calculate new value, and list of observable properties, which values will be observed for changes:
public Person(String firstName, String lastName) {
this.firstName = new SimpleStringProperty(firstName);
this.lastName = new SimpleStringProperty(lastName);
this.nickName.bind(Bindings.createStringBinding(()->{
if(this.firstName.get().length() > 2 && this.lastName.get().length() > 2) {
return this.firstName.get().substring(0,3).concat(this.lastName.get().trim().substring(0,3));
} else {
return "";
}
}, this.firstName, this.lastName));
}
You can use Bindings.format:
nickName.bind(Bindings.format("%.3s%.3s", firstName, lastName));
The 3 in %.3s is the maximum length of the string.
This won't do any trimming of the strings though, (you could do that before passing the strings to firstName and lastName).
It will also work on strings that are smaller than 3 characters. So, you can get nicknames like FoBar, FooB or Bar (if the first name is an empty string).