Home > database >  Can I map properties directly in Java Stream?
Can I map properties directly in Java Stream?

Time:07-11

In my Spring Boot project, I use the following map:

public void create(List<EmployeeRequest> request) {
    final List<Employee> employees = request.stream()
            .map(r -> new Employee(r.getName(),
                    r.getEmail(),
                    r.getCountry(),
                    r.getAge()))
            .collect(Collectors.toList());

    employeeRepository.saveAll(employees);
}

Here is the related classes:

public class EmployeeRequest {
    String name;
    String email;
    String country;
    int age;
}

public class Employee {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    private String name;

    private String email;

    private String country;

    private int age;

    public Employee(String name, String email, String country, int age) {
        this.name = name;
        this.email = email;
        this.country = country;
        this.age = age;
    }

As the field names are the same, I tried to make a mapping directly using class instead of each field as shown below, but I make a mistake I think related to Functional method:

public void create(List<EmployeeRequest> request) {
    final List<Employee> employees = request.stream()
            .map(Employee::new) // Cannot resolve constructor 'Employee'
            .collect(Collectors.toList());

    employeeRepository.saveAll(employees);
}

But I am getting "Cannot resolve constructor 'Employee'" error on the following line:

.map(Employee::new)

So, is it possible to make this kind of mapping via Java stream?


Update: I moved toEntity() method to a separate class a shown below, but get error if I don't make it static:

public class EmployeeRequestMapper {

    public Employee mapToEntity(EmployeeRequest request) {
        return new Employee(request.getName(), request.getEmail(), request.getCountry(), request.getAge());
    }
}

And I use it as shown below:

public void create(List<EmployeeRequest> request) {
    final List<Employee> employees = request.stream()
            .map(EmployeeRequestMapper::mapToEntity)
            .collect(Collectors.toList());
    employeeRepository.saveAll(employees);
}

So, do I have to make mapToEntity static or is there a proper way using that method in my service?

CodePudding user response:

I assume that it's a rest API and your Request class functions as a DTO. So what I like to do is just create a method toEntity in the Request/DTO class and then I have a pretty easy mapping and the responsibility is on the Request/DTO side. In your case it would look something like this:

public class EmployeeRequest {
    String name;
    String email;
    String country;
    int age;

    public Employee toEntity() {
        return new Employee(this.name, this.email, this.country, this.age);
    }
}

Now you can use it like this:

public void create(List<EmployeeRequest> request) {
    final List<Employee> employees = request.stream()
            .map(EmployeeRequest::toEntity)
            .collect(Collectors.toList());

    employeeRepository.saveAll(employees);
}

But if you want to make your way work you could create a constructor in Employee that just takes EmployeeRequest. But I don't like this solution because I think the Entity should not need to know about the other class. But in case you don't care about this, just add a constructor in your Employee:

public Employee(EmployeeRequest request) {
    this.name = request.getName();
    this.email = request.getEmail();
    this.country = request.getCountry();
    this.age = request.getAge();
}

CodePudding user response:

map accepts a function that is supposed to receive the EmployeeRequest and returns back the instance of Employee

Something like this:

Employee createEmployeeFromRequest(EmployeeRequest request) {...}

So if you want to use a constructor, you should create one that maps the request to the Employee:

public class Employee {

      public Employee(EmployeeReqeuest request) {
          ...
      }
}

Then you can map with Employee::new which is a method reference here - it "refers" this constructor - without that it won't work

  • Related