2

I was asked to create a request that will list customers based on their genders. However the request method has to be POST and I was adviced to use a dto and a mapper to achieve this goal. I'll give some examples to further explain my problem.

My Customer entity is as follows:

@Data
@Entity
@Table(name = "customer", schema = "public")
public class Customer implements Serializable {
    @Id
    @Column(name = "id_", unique = true)
    private Integer id_;
    @Column(name = "name_", nullable = false)
    private String name_;
    @Column(name = "surname", nullable = false)
    private String surname;
    @Column(name = "phone", nullable = false)
    private String phone;
    @Column(name = "email", nullable = false)
    private String email;
    @Column(name = "gender", columnDefinition = "text", nullable = false)
    private String gender;
    @JsonBackReference
    @OneToMany(mappedBy = "customer")
    Set<PurchaseOrder> purchaseOrder = new HashSet();

    public Customer() {

    }

This is an example for customer stream based on my code:

{
        "id_": 1,
        "name_": "Laura",
        "surname": "Blake",
        "phone": "95334567865",
        "email": "Bulvar 216 PF Changs",
        "gender": "W"
    }

I am asked to give this stream as input:

{ "gender": "W" }

As an output I am expected to receive a list of customer entities with gender 'W'. So, I have created a CustomerDto class:

@Data
public class CustomerDto {
    private String gender;
}

This is the method I'm going to use defined in CustomerRepository:

@Repository
public interface CustomerRepository extends JpaRepository<Customer, Integer> {

    List<Customer> findAllByGender(Customer customer);
}

This is what I have on my controller and service, respectively:

@RequestMapping(method= RequestMethod.POST, value="/customers/gender")
    public List<Customer> getCustomersByStream(@RequestBody @Valid Customer customer) {
        return service.getCustomersByGender(customer);
    }

public List<Customer> getCustomersByGender(Customer customer) {
        return repo.findAllByGender(customer);
    }

I added ModelMapper to my dependencies and I tried several methods both with customer and customerDto inputs. But I failed to successfully list customers by gender. I'd appreciate a code answer with proper explanations so that I can understand what's going on.

EDIT:

This is the answer without using ModelMapper. In case anyone is searching for a solution:

Controller:

@RequestMapping(method= RequestMethod.POST, value="/customers/gender")
    public List<Customer> getCustomersByStream(@RequestBody @Valid CustomerDto dto) {
        String gender = dto.getGender();
        return service.getCustomersByGender(gender);
    }

Repository:

@Repository
public interface CustomerRepository extends JpaRepository<Customer, Integer> {

    List<Customer> findAllByGender(String gender);
}

Service:

public List<Customer> getCustomersByGender(String gender) {
        return repo.findAllByGender(gender);
    }
Dilara Daria
  • 63
  • 1
  • 7
  • The Customer take gender field as a string and not customerDTO. Can you try changing `repo.findAllByGender(customer.getGender())` and changing `List findAllByGender(String gender);` – Deepak Patankar Sep 08 '20 at 11:09
  • Thank you for your kind answer Deepak. I came up with a similar solution to your suggestion and added it to my question as an edit. However, it doesn't use mapper. I'll ask my superior whether it is acceptable. – Dilara Daria Sep 08 '20 at 11:22
  • You don't always need to send some values for a post request. You can still send empty post request. Also, Your superior must have asked to use DTO for response where you're accepting it as body in the request. I understood what you superior want and it could be pretty large structure of the code if I write so – Amit Mishra Sep 08 '20 at 11:39
  • Thank you for your interest Amit. I am pretty new to this subject so I might be missing something as you stated. But he specifically asked me to give `{ "gender": "W" }` or `{ "gender": "M" }` as an input. Which I assume means it is not an empty request. Still, I appreciate the information you gave regarding empty requests. – Dilara Daria Sep 08 '20 at 11:48
  • I am adding a response! It has a lot of explanation so it's taking some time lol – Amit Mishra Sep 08 '20 at 11:49
  • Oh, no worries! I'll definitely check out your response. I am here to learn after all. Take your time – Dilara Daria Sep 08 '20 at 11:54
  • @Dilara, feel free to approach if you want some different solution – Amit Mishra Sep 08 '20 at 12:07
  • Thank you very much @AmitMishra ! I think i'm all set for now, fingers crossed. – Dilara Daria Sep 08 '20 at 12:19

1 Answers1

1

Okay, that's pretty simple.

In Your Controller

//Pass a parameter geneder = someValue and you will get that in the gender variable
@RequestMapping(method= RequestMethod.POST, value="/customers/gender")
public List<Customer> getCustomersByStream(@RequestBody AnotherDTO gender) {
    return service.getCustomersByGender(anotherDTO.getGender());
}

DTO's are meant to be used to accept a request body if they have a large data payload or for casting custom responses. As I think your superior would have asked to use a DTO for customizing response. Put only those varibles which you want to be there in the response.

ResponseDTO.java

public class CustomerDto {
    private String gender;
    private Long id;
    private String name;
    private String surname;
    private String phone;
    private String email;
    //Standard Setters and getters
    //Also, you need to make sure that the variable name in the Entity should 
    //be exactly same as your Entity. In DTO you just need to put those 
    //variables which you want to be in response.
}

Your Repo

@Repository
public interface CustomerRepository extends JpaRepository<Customer, Integer> {

    List<Customer> findAllByGender(String gender);
}

Your Business layer. Some Java class.

public List<Customer> getCustomersByGender(String gender) {
    List<Customer> response = new ArrayList<>();
    List<Customer> list = repo.findAllByGender(gender);
    //Autowire Object mapper of manually create a new instance.
    ObjectMapper mapper = new ObjectMapper();
    list.forEach(customer ->{
         YourDTO ref = mapper.map(list, YourDTO.class);
         response.add(ref);
    });
   return response;
}

Now, you can simply return the response that you've received from the service layer.

Amit Mishra
  • 498
  • 5
  • 16
  • I hope this will help you anyway – Amit Mishra Sep 08 '20 at 12:00
  • Your answer was easy to follow and informative. I understand what you mean but I was asked to create a custom input rather than a custom output. However, your example made me understand the use of mapper better. I am starting to think it was some type of miscommunication and a mapper isn't necessary for my case. I upvoted the answer because it would still list the customers based on gender. I didn't accept it because of the `@RequestParam` annotation. Because I was specifically asked to give the input as a body. Hopefully your answer will guide more people through their struggles. – Dilara Daria Sep 08 '20 at 12:16
  • I think this turned out to be a better example to learn from :) – Dilara Daria Sep 08 '20 at 12:25