JPA - Fetch child object from REST service - java

I'm writing a Spring Boot (with JPA) REST application where one of the entities that compose my model is owned by a different/external service, which can be fetch using a REST api.
#Entity
public class ManagedEntity {
#Id
private UUID id;
private ExternalEntity external; // I can be fetch from a REST api
}
I'd like to know how should I map my entity in order to have my JPA implementation to load it from the REST api, if possible.
Or, what would be the best way to model my entity/application in order to have a rich model?

It is not possible to do at data-access-layer. You can load the data in the business logic. Change your #Entity class adding
#Transient
private ExternalEntity external;
Fetch the data from the DB first and then try to make a service call to populate ExternalEntity

You could mark entire ExternalEntity with #Transient and initialize it in a service logic.
But the best way in returning data is to return view objects instead of JPA domain objects

Since you marked the question with a Spring tag and since you seem to be looking for the easy-done-for-you suggestion, why not use Spring-Data-REST? From the front page ...
Spring Data REST
Spring Data REST is part of the umbrella Spring Data project and makes it easy to build hypermedia-driven REST web services on top of Spring Data repositories.

Related

filter Data in Spring Boot Data REST

I use Spring Boot Data REST, yes, I write something like below:
#RepositoryRestResource
public interface ExerciseRepository extends JpaRepository<Exercise, Integer> {}
then I open 127.0.0.1/exercises. It will show all exercises.
But I want only show some appointed exercises(eg. exercise id < 100, or other complicated logic) on the 127.0.0.1/exercises.
I know I can use #RestController, but how can I do this with Spring Boot Data REST?
#RepositoryRestResource(path="exercises",collectionResourceRel = "exercises")
can you edit this according to your own code ? I think this will work for you
You can declare an interface method, for example:
#RepositoryRestResource
public interface ExerciseRepository extends JpaRepository<Exercise, Integer> {
List<Exercise> findByIdLessThan(#Param("id") Integer id);
}
In this case, the query is derived from the method name directly, but you can also write a query manually using #Query, for more details check the documentation.
To invoke the method use the following API request:
GET http://localhost:8080/exercises/search/findByIdLessThan?id=100
For reference, Spring Data REST - Useful notes.
EDIT:
If you use Hibernate as your persistence provider, you can use #Where for static filtering, and #Filter for dynamic filtering where filters are defined and configured at runtime, according to Hibernate User Guide.
For example, you can annotate the entity with #Where and define a condition that will be applied to all queries related to that entity:
#Where(clause = "id<100")
#Entity
public class Exercise{
//...
}

map DTO to Backend Entities

I am developing a rest application where the data in DB is loaded in Entities then some transformations are made on the data while being filled in corresponding DTOs then returned back to the consumer.
According to the consumer and some other parameters, a different subset of the data should be returned to the user, for example if user is inquiring on his personal info, level of details returning will be different than if a manager is inquiring on the data of his employees, etc ...
My question:
Is there any framework to handle this custom mapping (i.e. an xml based file that determines which field in which BE Entity should be mapped to which DTO in which condition ? instead of making a custom code in each case? thanks in advance.
I am using spring rest + hibernate
About XML file mapping, I do not know any. But what I find really useful and very customizable is MapStruct. It is a very useful library and the docs and examples are very good.
A simple example:
#Mapper
public interface CarMapper {
CarMapper INSTANCE = Mappers.getMapper( CarMapper.class );
#Mapping(source = "numberOfSeats", target = "seatCount") // Here is one of the functionalities that you wanted...
CarDto carToCarDto(Car car);
}
And there is IDE and Lombok support also.

DDD implementation with Spring Data and JPA + Hibernate problem with identities

So I'm trying for the first time in a not so complex project to implement Domain Driven Design by separating all my code into application, domain, infrastructure and interfaces packages.
I also went with the whole separation of the JPA Entities to Domain models that will hold my business logic as rich models and used the Builder pattern to instantiate. This approach created me a headache and can't figure out if Im doing it all wrong when using JPA + ORM and Spring Data with DDD.
Process explanation
The application is a Rest API consumer (without any user interaction) that process daily through Scheduler tasks a fairly big amount of data resources and stores or updates into MySQL. Im using RestTemplate to fetch and convert the JSON responses into Domain objects and from there Im applying any business logic within the Domain itself e.g. validation, events, etc
From what I have read the aggregate root object should have an identity in their whole lifecycle and should be unique. I have used the id of the rest API object because is already something that I use to identify and track in my business domain. I have also created a property for the Technical id so when I convert Entities to Domain objects it can hold a reference for the update process.
When I need to persist the Domain to the data source (MySQL) for the first time Im converting them into Entity objects and I persist them using the save() method. So far so good.
Now when I need to update those records in the data source I first fetch them as a List of Employees from data source, convert Entity objects to Domain objects and then I fetch the list of Employees from the rest API as Domain models. Up until now I have two lists of the same Domain object types as List<Employee>. I'm iterating them using Streams and checking if an objects are not equal() between them if yes a collection of List items is created as a third list with Employee objects that need to be updated. Here I've already passed the technical Id to the domain objects in the third list of Employees so Hibernate can identify and use to update the records that are already exists.
Up to here are all fairly simple stuff until I use the saveAll() method to update the records.
Questions
I alway see Hibernate using INSERT instead of updating the list of
records. So If Im correct Hibernate session is not recognising the
objects that Im throwing into it because I have detached them when I
used the convert to domain object?
Does anyone have a better idea how can I implement this differently or fix
this problem?
Or should I stop using this approach as two different objects and continue use
them as rich Entity models?
Simple classes to explain it with code
EmployeeDO.java
#Entity
#Table(name = "employees")
public class EmployeeDO implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
public EmployeeDO() {}
...omitted getter/setters
}
Employee.java
public class Employee {
private Long persistId;
private Long employeeId;
private String name;
private Employee() {}
...omitted getters and Builder
}
EmployeeConverter.java
public class EmployeeConverter {
public static EmployeeDO serialize(Employee employee) {
EmployeeDO target = new EmployeeDO();
if (employee.getPersistId() != null) {
target.setId(employee.getPersistId());
}
target.setName(employee.getName());
return target;
}
public static Employee deserialize(EmployeeDO employee) {
return new Country.Builder(employee.getEmployeeId)
.withPersistId(employee.getId()) //<-- Technical ID setter
.withName(employee.getName())
.build();
}
}
EmployeeRepository.java
#Component
public class EmployeeReporistoryImpl implements EmployeeRepository {
#Autowired
EmployeeJpaRepository db;
#Override
public List<Employee> findAll() {
return db.findAll().stream()
.map(employee -> EmployeeConverter.deserialize(employee))
.collect(Collectors.toList());
}
#Override
public void saveAll(List<Employee> employees) {
db.saveAll(employees.stream()
.map(employee -> EmployeeConverter.serialize(employee))
.collect(Collectors.toList()));
}
}
EmployeeJpaRepository.java
#Repository
public interface EmployeeJpaRepository extends JpaRepository<EmployeeDO, Long> {
}
I use the same approach on my project: two different models for the domain and the persistence.
First, I would suggest you to don't use the converter approach but use the Memento pattern. Your domain entity exports a memento object and it could be restored from the same object. Yes, the domain has 2 functions that aren't related to the domain (they exist just to supply a non-functional requirement), but, on the other side, you avoid to expose functions, getters and constructors that the domain business logic never use.
For the part about the persistence, I don't use JPA exactly for this reason: you have to write a lot of code to reload, update and persist the entities correctly. I write directly SQL code: I can write and test it fast, and once it works I'm sure that it does what I want. With the Memento object I can have directly what I will use in the insert/update query, and I avoid myself a lot of headaches about the JPA of handling complex tables structures.
Anyway, if you want to use JPA, the only solution is to:
load the persistence entities and transform them into domain entities
update the domain entities according to the changes that you have to do in your domain
save the domain entities, that means:
reload the persistence entities
change, or create if there're new ones, them with the changes that you get from the updated domain entities
save the persistence entities
I've tried a mixed solution, where the domain entities are extended by the persistence ones (a bit complex to do). A lot of care should be took to avoid that domain model should adapts to the restrictions of JPA that come from the persistence model.
Here there's an interesting reading about the splitting of the two models.
Finally, my suggestion is to think how complex the domain is and use the simplest solution for the problem:
is it big and with a lot of complex behaviours? Is expected that it will grow up in a big one? Use two models, domain and persistence, and manage the persistence directly with SQL It avoids a lot of caos in the read/update/save phase.
is it simple? Then, first, should I use the DDD approach? If really yes, I would let the JPA annotations to split inside the domain. Yes, it's not pure DDD, but we live in the real world and the time to do something simple in the pure way should not be some orders of magnitude bigger that the the time I need to to it with some compromises. And, on the other side, I can write all this stuff in an XML in the infrastructure layer, avoiding to clutter the domain with it. As it's done in the spring DDD sample here.
When you want to update an existing object, you first have to load it through entityManager.find() and apply the changes on that object or use entityManager.merge since you are working with detached entities.
Anyway, modelling rich domain models based on JPA is the perfect use case for Blaze-Persistence Entity Views.
Blaze-Persistence is a query builder on top of JPA which supports many of the advanced DBMS features on top of the JPA model. I created Entity Views on top of it to allow easy mapping between JPA models and custom interface defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure the way you like and map attributes(getters) via JPQL expressions to the entity model. Since the attribute name is used as default mapping, you mostly don't need explicit mappings as 80% of the use cases is to have DTOs that are a subset of the entity model.
The interesting point here is that entity views can also be updatable and support automatic translation back to the entity/DB model.
A mapping for your model could look as simple as the following
#EntityView(EmployeeDO.class)
#UpdatableEntityView
interface Employee {
#IdMapping("persistId")
Long getId();
Long getEmployeeId();
String getName();
void setName(String name);
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
Employee dto = entityViewManager.find(entityManager, Employee.class, id);
The Spring Data integration allows you to use it almost like Spring Data Projections: https://persistence.blazebit.com/documentation/entity-view/manual/en_US/index.html#spring-data-features and it can also be saved back. Here a sample repository
#Repository
interface EmployeeRepository {
Employee findOne(Long id);
void save(Employee e);
}
It will only fetch the mappings that you tell it to fetch and also only update the state that you make updatable through setters.
With the Jackson integration you can deserialize your payload onto a loaded entity view or you can avoid loading alltogether and use the Spring MVC integration to capture just the state that was transferred and flush that. This could look like the following:
#RequestMapping(path = "/employee/{id}", method = RequestMethod.PUT, consumes = MediaType.APPLICATION_JSON_VALUE)
public ResponseEntity<String> updateEmp(#EntityViewId("id") #RequestBody Employee emp) {
employeeRepository.save(emp);
return ResponseEntity.ok(emp.getId().toString());
}
Here you can see an example project: https://github.com/Blazebit/blaze-persistence/tree/master/examples/spring-data-webmvc

Spring Boot and Hibernate: Still fetching one-to-many relationships even when filtered out from serializing

I have a Spring Boot project using a mysql database. It has a data model (Project) that has a one-to-many relationship with another model (Files), specified with a fetching approach of FetchType.LAZY.
#Entity
public class Project extends BaseEntity {
#OneToMany(fetch = FetchType.LAZY)
#JoinColumn(name="projectId")
private List<Files> files;
...
}
I have an endpoint that serializes a list of projects to JSON /projects. And another endpoint that provides me with the details from a single project /projects/[projectid]. I do not need the list of Files for each and every Project for the first endpoint. But I do for the latter.
To allow me to determine whether to fetch the Files associated with a project at the controller-level, I have created a filter for the /projects endpoint, to strip out the files JSON field, Following some of the guidance in the question here: Ignore fields from Java object dynamically while sending as JSON from Spring MVC
Making my Project model look like this:
#JsonFilter("FilterOutFiles")
#Entity
public class Project extends BaseEntity{
...
}
and the controller's endpoint as so:
#Slf4j
#RestController
public class ProjectsController {
...
#JsonRequestMapping(value = "/projects", method = RequestMethod.GET)
public MappingJacksonValue getUserProjects(#CurrentAccount final UserRuntime userRuntime) {
User user = userRuntime.getUser();
List<Project> ownProjectList = projectRepository.findAllByOwner_UserId(user.getId());
SimpleFilterProvider filters = new SimpleFilterProvider();
filters.addFilter("FilterOutFiles", SimpleBeanPropertyFilter.serializeAllExcept("files"));
MappingJacksonValue mapping = new MappingJacksonValue(ownProjectList);
mapping.setFilters(filters);
return mapping;
}
...
}
This seems to work fine in stripping out the list of files from the JSON. No problem there. My assumption was that with the fetch type set to "lazy" the retrieval of the files would only happen during serialization (if required!), and with it filtered out by the serializer, it wouldn't be required, therefore wouldn't be fetched.
In the course of executing the controller's function, I can see one SQL request to get the list of projects when calling projectRepository.findAllByOwner_UserId. Which is as expected. However, it seems that after returning from my controller function (I presume during the serializing process), hibernate makes queries to retrieve the list of files for each project. One query for each project. Because files themselves also have one-to-many relationships with other models, this quickly balloons into hundreds, sometimes thousands of SELECT statements. Instead of just one.
How can I prevent Hibernate from resolving this lazy fetch on data I do not require which has now been filtered out by the serializer?

Better approach for DTOs?

I am developing a simple forum web application using SpringMVC, JPA2.
I have created JPA entities like User, Forum, Post etc which reflects the DB table structure.
But while displaying the data on UI I need DTOs as I can't always hold the data to be displayed on UI using Entities.
For ex: Change Password screen. Here I need to hold Old Pwd, New Password and Confirm New Pwd. But User entity won't have Old/New/Confirm Pwd fields, it just has Password. So I need to create DTOs which are just data carriers between Web and service layers.
My question is while creating DTO objects, should I put all the properties in DTO itself or wrap the Entity in DTO and add additional properties needed?
Ex: For Edit User Screen,
public class UserDTO
{
private User user; // User is a JPA entity
// setters & getters
}
With this I can pass the underlying User entity to my service layer. But while binding UI properties to DTO I need to associate PropertyEditors.
(or)
public class UserDTO
{
private String userId;
private String userName;
private String password;
// setters & getters
}
With this approach, I need to convert & copy the DTO properties into JPA entities and pass to Service layer.
Which approach is better? Or is there any other approach without DTOs altogether?
Your first approach still carries the Entity object itself to the presentation layer. If you have additional parameters that are not coming from the database, and your persistence context is still available, then this approach is sufficient.
The second approach requires duplication of code, which is not ideal.
If the persistence context is not available, I would suggest detaching the Entity from the persistence context using EntityManager.detach(), rather than creating a parallel hierarchy of beans.
On the other hand, if data is coming in from the presentation layer, you will need to load the Entity from the database (using find() or something similar) and update it, or merge() it into the persistence context.

Categories