Mapping API response to another client DTO - spring-boot

[solved]
update: I was using lombok for getter/setters, and forgot to add #Data annotation on that class. Now fixed.
I am calling endpoint of a REST controller from where my Response class has entity like this:
public class Response{
private Long userId;
private Long anotherId;
private Collection<UserInfo> userInfo;
private List<Map<String, List<AnotherDto>>> anotherDto;
//setters and getters
};
Here,
public class UserInfo implements MyInterface{
private Long id;
private String name;
//and #override methods
//setters and getters
}
Now, In my client app,
The DTO I am mapping to the API call response is same like I have described above, except, my UserInfo class does n't implement any interface in client side.
When I make API call, from service side, I have checked that I got all the data as expected, but in client side, I get all except the userInfo for which UserInfo class doesn't implement the interface as I did on service side.
What is the issue here? Any help please.

Related

Spring Boot: Refresh an entity within a service transaction which was updated by an external system call

Given the following workflow in a Spring Boot application:
A RestController receives a request to update the User entity.
The controller delegates the updating to a UserService.
The UserService needs to call an external system which updates the User object on the same database instance. We're dealing with a legacy system here, so we need to stick to this fact.
The UserService needs to save the updated User.
I'm aware of the problem that #4 won't work as #3 has already updated the object.
#RestController
#RequestMapping("/api/user")
#RequiredArgsConstructor
public class UserController {
private final UserService userService;
private final ExternalService externalService;
#PutMapping
public ResponseEntity<UserResponse> createOrUpdateBewerberdaten(#RequestBody #Valid UserRequest userRequest) {
UserResponse userResponse = userService.update(userRequest);
return ResponseEntity.ok(userResponse);
}
#Service
#RequiredArgsConstructor
public class UserService {
private final UserRepository userRepository;
private final ExternalService externalService;
#Transactional
public UserResponse update(UserRequest userRequest) {
User user = userRepository.findById(userRequest.getId());
user.setEmail(userRequest.getEmail());
// this calls an external system via REST updating the user on the same database instance
externalService.update(user);
// this won't work as the user has been updated by externalService
userRepository.save(user)
return new UserResponse(user);
}
}
Is there some pattern I could apply to get the latest version of the User object? My current approach is to call the ExternalService from the controller and put #4 in a dedicated public method, which is also annotated with #Transactional. However this feels like bloating the controller which I would like to keep as simple as possible.

Calling Save() after findAll() in JPA repository

I am working on an SpringBoot Project and Using the Spring JPA.
I have scenario where I need to fetch all the records and then update them after modification.
For example
#Entity
#Table(name="Employee")
public class Employee{
#ID
#Column(name="ID")
Long id;
#Column(name="age")
private int age;
#Column(name="name")
private String name;
#Embedded
private Address address
//TODO getters and setters goes below
}
and then in the repository
#Repository
public interface EmployeeRepository extends JPARepository<Employee, Long>{
}
and in the service Iam trying as below:
#Service
public class EmployeeService{
#Autowired
EmployeeRepository repository;
#Transactional
public void updateEmployee(){
List<Employee> list = repository.findAll();
for(Employee employee :list) {
employee.setAge(employee.getAge()+4);
repository.save(employee); //This is not working.
}
}
}
Save is neither working nor throwing any error.I have also tried saveAndFlush() but not working. Can someone please help me. I want my objects to get updated.
I tried to recreate your issue, but i cant reproduce your error.
My entities get updated with this perfectly fine.
You say you are doing more logic than this, have you tried printing out your entities after you have made the save, to see if something has been updated, and that it fails in another place in your code?
The code above for me updates all entities as expected :)
logging.level.org.hibernate.SQL=TRACE
Add this to application.properties / yaml file for more tracing of SQL statements

I can do PUT but not POST with Spring Data Rest?

I have two simple entity like this:
public class Agent extends BasedEntity {
private String firstname;
private String lastname;
#ManyToOne
#JoinColumn(name="agency_id", nullable=true)
Agency agency;
}
and
public class Agency extends BasedEntity {
private String name;
private String address;
#OneToMany(mappedBy="agency")
private Set<Agent> agents;
}
#RepositoryRestResource
public interface AgencyRespository extends JpaRepository<Agency, Long> {
}
#RepositoryRestResource
public interface AgentsRespository extends JpaRepository<Agent, Long> {
}
When I do a PUT with
https://localhost:8080/api/v1/agents/64/agency
body:https://localhost:8080/api/v1/agencies/50
it goes through but if I do a POST to
https://localhost:8080/api/v1/agents/64/agency
body:https://localhost:8080/api/v1/agencies/50
I get a
org.springframework.web.HttpRequestMethodNotSupportedException: Request method 'POST' not supported
You are using an old version of Spring Data Rest. POST is allowed from 2.3.x.
The latest version is 3.2.x. You should definetely ugrade to a newer version...
----------- Edit
I've just realized that the exception is NOT the inner HttpRequestMethodNotSupportedException from the RepositoryPropertyReferenceController class, but the 'default' org.springframework.web.HttpRequestMethodNotSupportedException.
This exception is never raised directly from the SRD package.
Maybe you have a filter which deny POST request or some kind of security settings.

#CreatedBy becomes null when updating

I have this entity:
#Entity
#EntityListeners( AuditingEntityListener.class )
public class Employee {
#Id
#GeneratedValue
int id;
private String name;
...
#LastModifiedBy
private String modifiedBy;
#CreatedBy
private String createdBy;
}
And i have this config class:
#Configuration
#EnableJpaAuditing
public class DataConfig {
#Bean
public AuditorAware<String> auditorAware() {
return () ->
SecurityContextHolder.getContext().getAuthentication().getName();
}
}
The problem is:
When updating entity, the created_by becomes null.
Any help please.
I'd suggest to you to ensure if your spring boot app is scanning the DataConfig class.
In addition, well in case of having a REST Service (I don't know because that info is not added to the question) but bear in mind a REST Service is Stateless, and you need fetch the Authorization from the request to add it to the spring security context BEFORE executing the request.
But if your spring boot app is just a Spring MVC one with basic Authorization, be sure you have an open session once the data is updated/created

Spring Boot JPA #Transactional #Service does not update, but #Transactional in controller does

I have a very basic Spring Boot/JPA stack app, with a controller, service layer, and repository that does not persist updates as I understand it should.
A trivial Entity:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
protected Customer() {}
public Customer(String name) { this.name = name; }
// standard getters,setters //
}
A trivial Repository:
#Repository
public interface CustomerRepository extends CrudRepository<Customer, Long> {}
A simple Service layer:
// If the service is #Transactional and the controller is not, the update does NOT occur
#Transactional
#Service
public class CustomerService {
private static final Logger LOG = getLogger(CustomerService.class);
#Autowired
private CustomerRepository customerRepository;
boolean updateCustomerName(Long id, String name) {
Customer customer = customerRepository.findOne(id);
if (customer == null) { return false; }
// Modifies the entity
customer.setName(name);
// No explicit save()
return true;
}
}
And a REST controller that uses it all:
// If the controller is #Transactional and the service is not, the update occurs
#RestController
#RequestMapping("/mvc")
public class CustomerController {
#Autowired
private CustomerService customerService;
#RequestMapping(path = "{id}", method = RequestMethod.PUT)
public ResponseEntity updateCustomerName(#PathVariable Long id, #RequestParam("name") String name) {
customerService.updateCustomerName(id,name);
return ResponseEntity.noContent().build();
}
}
These are wired together with a simple one-liner SpringBootApplication
I have SQL debug logs enabled and see the selects, update, etc.
With the code above: When the service method is invoked by the controller, the modified entity is not persisted. SQL logs show the select of the entity but no update.
There is also no update if nothing is marked #Transactional
However, simply by moving the #Transactional annotation from the service class to the controller class, the SQL update does occur.
If I add an explicit customerRepository.save(customer) to the service method, the update also occurs. But my understanding is that the ORM should automatically save modified persistent entities.
I'm sure the issue has something to do with the EntityManager lifecycle in the web request, but I'm puzzled. Do I need to do additional configuration?
Complete example at https://github.com/monztech/SO-41515160
EDIT: This was solved, see below. Per the Spring spec #Transactional does not work in package-private methods and mistakenly did not make the update service method public.
The update will occur if the method is public and the service class has the #Transactional annotation.
I do have another question, however. Why is the #Transactional annotation necessary? (the update does not occur without it) Shouldn't the entity manager still persist the object because of the open session in view mechanism that Spring uses, independent of any transaction?
Make your updateCustomerName method public.

Resources