How can I map/change/implements methods from one layer to another? - java

Good evening, I'm learning about using layers in the spring and I've encountered some problems that I don't know how to solve...
I know that the entity is exposed in the Repository, where we use Hibernate to perform operations.
In the service layer we have to use an Object to "hide" the entity, but how do we map Classes / Entities by methods?
I mean, we have this example, but I want to use JPA, and I can't use a ProductData id to search to a Product Repository, for example, how can I modify these methods to work without errors, please?
public ProductData findById(Integer id){
ProductEntity product = productRepo.findById(id);
ProductData data = new ProductData(product.getId(), product.getName(), product.getCeva());
return data;}

Related

Hibernate Bytecode Instrumentation: Set fetched property values to their fields right away

I'm working on an enterprise application that uses Hibernate and EJB, and I'm utilizing Hibernate's Bytecode instrumentation to implement true lazy-loading of properties having bidirectional #OneToOne associations. I have a service method implemented using EJB, and as we all know, EJB uses RMI, which uses native Java serialization and deserialization to facilitate RPC invocations. The service method returns an entity with needed properties already fetched using JPQL, but since Hibernate doesn't set the fetched properties eagerly to their target fields, clients calling the service method end up receiving entities with properties having null values. Aside from calling the property getters manually before returning the entity, is there a way to tell Hibernate to set fetched property values automatically to their corresponding fields?
I'm using Hibernate 5.3.15 and JBoss EAP 7.2.8.
First of all, EJB doesn't necessarily use RMI, I guess what you mean is EJB remoting. There is no way that I know of to force field initialization except for initializing the fields through some means (access). One way to overcome this is to use DTOs that simply do not do any lazy initialization.
I think that this might be a bug in the serialization code of Hibernate for such bytecode enhanced proxies, so please create an issue in the issue tracker(https://hibernate.atlassian.net) with a test case(https://github.com/hibernate/hibernate-test-case-templates/blob/master/orm/hibernate-orm-5/src/test/java/org/hibernate/bugs/JPAUnitTestCase.java) that reproduces the issue.
If you want to take the DTO approach, I think this is a perfect use case for Blaze-Persistence Entity Views.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(User.class)
public interface UserDto {
#IdMapping
Long getId();
String getName();
UserDetailsDto getDetails();
#EntityView(UserDetails.class)
interface UserDetailsDto {
#IdMapping
Long getId();
String getFirstname();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
UserDto a = entityViewManager.find(entityManager, UserDto.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
Page<UserDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!

How to deal with transient entities after deserialization

Let's say I have a simple REST app with Controller, Service and Data layers. In my Controller layer I do something like this:
#PostMapping("/items")
void save(ItemDTO dto){
Item item = map(dto, Item.class);
service.validate(item);
service.save(item);
}
But then I get errors because my Service layer looks like this:
public void validate(Item item) {
if(item.getCategory().getCode().equals(5)){
throw new IllegalArgumentException("Items with category 5 are not currently permitted");
}
}
I get a NullPointerException at .equals(5), because the Item entity was deserialized from a DTO that only contains category_id, and nothing else (all is null except for the id).
The solutions we have found and have experimented with, are:
Make a special deserializer that takes the ids and automatically fetches the required entities. This, of course, resulted in massive performance problems, similar to those you would get if you marked all your relationships with FetchType.EAGER.
Make the Controller layer fetch all the entities the Service layer will need. The problem is, the Controller needs to know how the underlying service works exactly, and what it will need.
Have the Service layer verify if the object needs fetching before running any validations. The problem is, we couldn't find a reliable way of determining whether an object needs fetching or not. We end up with ugly code like this everywhere:
(sample)
if(item.getCategory().getCode() == null)
item.setCategory(categoryRepo.findById(item.getCategory().getId()));
What other ways would you do it to keep Services easy to work with? It's really counterintuitive for us having to check every time we want to use a related entity.
Please note this question is not about finding any way to solve this problem. It's more about finding better ways to solve it.
From my understanding, it would be very difficult for modelMapper to map an id that is in the DTO to the actual entity.
The problem is that modelMapper or some service would have to do a lookup and inject the entity.
If the category is a finite set, could use an ENUM and use static ENUM mapping?
Could switch the logic to read
if(listOfCategoriesToAvoid.contains(item.getCategory())){ throw new IllegalArgumentException("Items with category 5 are not currently permitted"); }
and you could populate the listOfCategoriesToAvoid small query, maybe even store it in a properties file/table where it could be a CSV?
When you call the service.save(item), wouldn't it still fail to populate the category because that wouldn't be populated? Maybe you can send the category as a CategoryDTO inside the itemDTO that populated the Category entity on the model.map() call.
Not sure if any of these would work for you.
From what I can gather the map(dto, Item.class) method does something like this:
Long categoryId = itemDto.getCategoryId();
Category cat = new Category();
cat.setId(categoryId);
outItem.setCategory(cat);
The simplest solution would be to have it do this inside:
Long categoryId = itemDto.getCategoryId();
Category cat = categoryRepo.getById(categoryId);
outItem.setCategory(cat);
Another option is since you are hardcoding the category code 5 until its finished, you could hard-code the category IDs that have it instead, if those are not something that you expect to be changed by users.
Why aren't you just using the code as primary key for Category? This way you don't have to fetch anything for this kind of check. The underlying problem though is that the object mapper is just not able to cope with the managed nature of JPA objects i.e. it doesn't know that it should actually retrieve objects by PK through e.g. EntityManager#getReference. If it were doing that, then you wouldn't have a problem as the proxy returned by that method would be lazily initialized on the first call to getCode.
I suggest you look at something like Blaze-Persistence Entity Views which has first class support for something like that.
I created the library to allow easy mapping between JPA models and custom interface or abstract class defined models, something like Spring Data Projections on steroids. The idea is that you define your target structure(domain model) the way you like and map attributes(getters) via JPQL expressions to the entity model.
A DTO model for your use case could look like the following with Blaze-Persistence Entity-Views:
#EntityView(Item.class)
// You can omit the strategy to default to QUERY when using the code as PK of Category
#UpdatableEntityView(strategy = FlushStrategy.ENTITY)
public interface ItemDTO {
#IdMapping
Long getId();
String getName();
void setName(String name);
CategoryDTO getCategory();
void setCategory(CategoryDTO category);
#EntityView(Category.class)
interface CategoryDTO {
#IdMapping
Long getId();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
ItemDTO a = entityViewManager.find(entityManager, ItemDTO.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
Page<ItemDTO> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
And in your case of saving data, you can use the Spring WebMvc integration
that would look something like the following:
#PostMapping("/items")
void save(ItemDTO dto){
service.save(dto);
}
class ItemService {
#Autowired
ItemRepository repository;
#Transactional
public void save(ItemDTO dto) {
repository.save(dto);
Item item = repository.getOne(dto);
validate(item);
}
// other code...
}

Returning Entity from Service method is a bad practice?

I've heard when you want to return some object from a service method, you have to define a DTO object (or POJO object generated with JSON Schema) instead of using an Entity.
To make it clear, here is the example:
We have an entity and a jpa repository for it:
#Data
#Entity
#Table(name = "tables")
public class Table {
#Id
private Long id;
private String brand;
}
This is a bad practice:
#Service
public class MyService {
#Autowired
private TableRepository tableRepository;
#Transactional
public Table create() {
Table table = new Table();
// Some logic for creating and saving table
return table;
}
}
This is a good practice:
#Service
public class MyService {
#Autowired
private TableRepository tableRepository;
#Transactional
public TableDTO create() {
Table table = new Table();
// Some logic for creating and saving table
// Logic for converting Table object to TableDTO object
return tableDTO;
}
}
Why is this so?
Thank you!
Probably you mean a DTO (Data Transfer Object), not DAO (Data Access Object). Let me clarify this:
Data Transfer Object:
A Pojo that represents a piece of information. Usually it has aggregated data in it.
Data Access Object:
An object that performs access to some kind of persistence storage for retrieving information, someone considers it a synonim of Repository, someone not.
Entity:
An object that represents data that has been retrieved from the database.
Why is returning an Entity from the Service considered a bad practice?
The reason is that the Entity is something that is very close to the database. It contains primary key, someone could guess your database structure from it and the set of the data in case of query can be verbose. Hence, it is preferable to have some kind of logic, usually a mapper, that hides primary key and aggregates data to be less verbose and to not expose the db structure. Also, while the Entity is built on the table structure, the DTO can be customized in base of caller needs. Usually it contains exactly the data that is needed for some action and nothing more than this. Suppose you have thirdy party software that calls your backend services: you should not expose the db structure (Entities) to this service. It is better to define a contract, with the minimal information needed for this thirdy party service to operate, and expose only this part of the information, hiding all the rest.
Hope that's a little bit more clear now.
Edit:
Of course there are other good reasons for using DTOs instead of Entities, this is only an introductory explanation to the subject.

REST development backend DTO vs frontend DTO

What is the best approach when developing (JSON-based) REST services in terms of Data objects.
Should I split database model (back-end) from the front-end model?
Is it a good practice to always keep the DB related JPA Entities up to a specific layer and then convert them into a set of DTOs for the front-end?
For example, 3 layer architecture:
Controller
Service
Repository
Should I confine the DB entities (annotated with JPA annotations) to Repository and Service layers
And then make the Controller only operate with another set of UI 'entities' (DTOs)?
This would require some kind of mapping between the 2 either automatic or 'manual'.
This allows for 'thin' front end entities.
For example in Backend we have the JPA annotations only and we have the owner as an Account reference:
#Entity
public class Job {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne(fetch = FetchType.LAZY)
private Account owner;
But in the front-end layer I would have Jackson specific annotations. I don't need the whole Account object, but only its id in the UI:
class Job {
long id;
String name;
long ownerId;
}
Update
After experimenting with "manual" mapping, the conclusion is that it quickly becomes a mess.
In my case, I wanted the Repository layer to return Entity (JPA) and the Service layer to do the mapping and return the Dto. So for getting data from the DB, this seems pretty affordable, there is only 1 mapping involved (from Entity to Dto). But, when it comes to creating / saving entities, the problem is bigger with composite objects. For example:
Let's say I POST a UserDto (which contains UserProfileDto as a composite object) from the API client to the Controller. Now the Service layer will accept UserDto, but it will have to find the UserProfileEntity corresponding to that UserProfileDto.
Also, Repository's .save() method returns the newly persisted Entity. Now this has to be mapped to Dto and then back to Entity if I want to use it further as part of another object (otherwise I will get the object references an unsaved transient instance - save the transient instance before flushing error).
For example, if I do:
repository.save(profileEntity) this will return a newly persisted ProfileEntity, but now I need to map it to ProfileDto in order to make it part of UserDto before mapping again UserDto to UserEntity and persisting.
Note: Dto here are objects I am planning to use as a response to the client (with JSON related annotations). These live in the Service and Controller layers, whereas Entity are JPA related objects that only live in the Repository and Service layers.

What is the proper way to create and validate an Entity Model and its DTO in a RESTful API?

I am developing my first RESTful API from scratch and with Spring Boot.
I have already created the endpoints, models and JPA repositories for "standalone" entities. But now that I started linking them together and after doing some research I got to the conclusion that I may have to create DTOs. I don't think everytime I'm creating a new Order with a POST request I should make the client send the whole Customer and Employee objects inside the request as nested objects of Order (if I am also wrong in this please let me know). I am thinking about creating a DTO by just replacing the class relations with just IDs.
This is how my entity is currently defined:
#Data
#Entity
#Table(name = "Orders")
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.SEQUENCE)
private Long id;
#NotBlank
#NotNull
private String description;
#NotBlank
#NotNull
private Status status;
#NotNull
#ManyToOne
#JoinColumn(foreignKey = #ForeignKey(name = "employee_id_fk"))
private Employee employee;
#NotNull
#ManyToOne
#JoinColumn(foreignKey = #ForeignKey(name = "customer_id_fk"))
private Customer customer;
protected Order() {}
public Order(String description) {
this.description = description;
this.status = Status.IN_PROGRESS;
}
}
And my endpoint (this is what I must change):
#PostMapping("/orders")
ResponseEntity<EntityModel<Order>> createOrder(#Valid #RequestBody Order order) {
order.setStatus(Status.IN_PROGRESS);
Order newOrder = repository.save(order);
return ResponseEntity
.created(linkTo(methodOn(OrderController.class).getOrder(newOrder.getId())).toUri())
.body(assembler.toModel(newOrder));
}
Now, how should I validate the requests with this format?
Previously, as you can see, I would simply use #Valid and it would automatically get validated when the endpoint is called against the Order model. However, if I create the DTO, I would have to validate the DTO with the same methodology and duplicate all the annotations from its model (#NotNull, #NotBlank, etc.). Maybe I should validate the entity model after mapping it from the DTO but I don't know how straightforward that would be and whether that is a good practice of validating requests. I also can't remove the validations from the entity model because I'm using Hibernate to map them to tables.
Great questions!
I don't think everytime I'm creating a new Order with a POST request I should make the client send the whole Customer and Employee objects inside the request as nested objects of Order (if I am also wrong in this please let me know).
You're right. It's not because we can save bits and bytes (as it may look like), but because the lesser information you can ask from the client, the better the experience he/she would get (whether it's an external integrator or front-end/back-end application within the same company). Fewer amounts of data to encompass = easier to comprehend and less room for an error. It also makes your API cleaner from the design perspective. Is it possible to process your request without the field? Then it shouldn't be in your API.
Now, how should I validate the requests with this format? Previously, as you can see, I would simply use #Valid and it would automatically get validated when the endpoint is called against the Order model. However, if I create the DTO, I would have to validate the DTO with the same methodology and duplicate all the annotations from its model (#NotNull, #NotBlank, etc.).
You can also use #Valid to kick in validation for DTO inside the controller within the method mapped to endpoint. But as you mentioned correctly, all validated fields within DTO should be annotated with #NotNull, #NotBlank, etc. As a solution, to the "duplication" problem, you can create a base class and define all validations in there and inherit DTO and Entity from it. But please, don't do that!
Having the same fields and validation rules within DTO and Enity isn't considered duplication since they are separate concepts and each one of serves its specific purpose within its layer (DTO - top tier, Entity - most often lowest, Data tier). There are a lot of examples demonstrating it (e.g. here and here)
Maybe I should validate the entity model after mapping it from the DTO but I don't know how straightforward that would be and whether that is a good practice of validating requests.
It's a best practice to validate the request and a lot of projects are following it. In your example, it's very straightforward (direct mapping from DTO to Entity), but very often you would have a service layer that does some business logic before handing it off to a data layer (even in your example I recommend moving out your code from controller to a service layer). You don't want malformed request pass beyond the controller to handle it later with excessive if statements, null checks (that leads to a defensive code that's hard to follow and it's also error-prone).
Another note: you shouldn't sacrifice client experience and tell them or force yourself to add two more fields because it allows having one Object serving as DTO and Entity and simplifies development.
The last note: To map fields from DTO to Entity you can use one of the object mapper libraries.

Categories

Resources