Reputation: 674
Hello everyone I'm new to Spring world. Actually I want to know how can we use converter to update object instead of updating each element one by one using set and get. Right now in my controller I've :
@PostMapping("/edit/{userId}")
public Customer updateCustomer(@RequestBody Customer newCustomer, @PathVariable final String userId)
{
return customerService.update(userId, newCustomer);
}
and this is how I'm updating the customer object :
@Override
public Customer update(String id, Customer newCustomer) {
Customer customer = customerRepository.findById(id).get();
customer.setFirstName(newCustomer.getFirstName());
customer.setLastName(newCustomer.getLastName());
customer.setEmail(newCustomer.getEmail());
return customerRepository.save(customer);
}
Instead of using each time set and get, I want to use a converter.
Upvotes: 2
Views: 10547
Reputation: 18430
Use ModelMapper
to map one model into another.
First define a function that can map source data into the target model. Use this as a library to use whenever want.
public static <T> void merge(T source, T target) {
ModelMapper modelMapper = new ModelMapper();
modelMapper.getConfiguration().setMatchingStrategy(MatchingStrategies.STRICT);
modelMapper.map(source, target);
}
Use merge
for mapping data
Customer customer = customerRepository.findById(id).get();
merge(newCustomer, customer);
customerRepository.save(customer);
Add dependency in pom.xml for model mapper
<dependency>
<groupId>org.modelmapper</groupId>
<artifactId>modelmapper</artifactId>
<version>2.3.4</version>
</dependency>
Upvotes: 0
Reputation: 1860
The approach of passing the entity's id as a path variable when you're updating it isn't really right. Think about this: you have a @RequestBody
, why don't you include the id inside this body too? Why do you want to specify a path variable for it?
Now, if you have the full Customer
with its id from the body, you don't have to make any calls to your repository because hibernate adds it to a persistent
state already based on its id and a simple
public Customer update(Customer newCustomer) {
return customerRepository.save(newCustomer);
}
should work.
Q: What is a persistent state?
A: A persistent entity has been associated with a database table row and it’s being managed by the current running Persistence Context. ( customerRepository.findById()
is just asking the DB if the entity with the specified id exists and add it to a persistent state. Hibernate manage all this process if you have an @Id
annotated field and is filled, in other words:
Customer customer = new Customer();
customer.setId(1);
is ALMOST the same thing as :
Customer customer = customerRepository.findById(1).get();
)
TIPS: Anyway, you shouldn't have (if you didn't know) a model in the controller layer. Why? Let's say that your Customer
model can have multiple permissions. One possible structure could look like this:
@Entity
public class Customer{
//private fields here;
@OneToMany(mappedBy="customer",--other configs here--)
private List<Permission> permissions;
}
and
@Entity
public class Permission{
@Id
private Long id;
private String name;
private String creationDate;
@ManyToOne(--configs here--)
private Customer customer;
}
You can see that you have a cross reference between Customer
and Permission
entity which will eventually lead to a stack overflow exception (if you don't understand this, you can think about a recursive function which doesn't have a condition to stop and it's called over and over again => stack overflow. The same thing is happening here).
What can you do? Creating a so called DTO
class that you want the client to receive instead of a model. How can you create this DTO? Think about what the user NEEDS to know.
1) Is "creationDate" from Permission
a necessary field for the user? Not really.
2) Is "id" from Permission
a necessary field for the user? In some cases yes, in others, not.
A possible CustomerDTO
could look like this:
public class CustomerDTO
{
private String firstName;
private String lastName;
private List<String> permissions;
}
and you can notice that I'm using a List<String>
instead of List<Permission>
for customer's permissions which are in fact the permissions' names.
public CustomerDTO convertModelToDto(Customer customer)
{
//hard way
CustomerDTO customerDTO = new CustomerDTO();
customerDTO.setFirstName(customer.getFirstName());
customerDTO.setLastName(customer.getLastName());
customerDTO.setPermissions(
customer.getPermissions()
.stream()
.map(permission -> permission.getName())
.collect(Collectors.toList());
);
// easy-way => using a ModelMapper
customerDTO = modelMapper.map(customer,CustomerDTO.class);
return customerDTO;
}
Upvotes: 5