The main question is how to convert DTOs to entities and entities to Dtos without breaking SOLID principles.
For example we have such json:
{ id: 1,
name: "user",
role: "manager"
}
DTO is:
public class UserDto {
private Long id;
private String name;
private String roleName;
}
And entities are:
public class UserEntity {
private Long id;
private String name;
private Role role
}
public class RoleEntity {
private Long id;
private String roleName;
}
And there is usefull Java 8 DTO conveter pattern.
But in their example there is no OneToMany relations. In order to create UserEntity I need get Role by roleName using dao layer (service layer). Can I inject UserRepository (or UserService) into conveter. Because it seems that converter component will break SRP, it must convert only, must not know about services or repositories.
Converter example:
#Component
public class UserConverter implements Converter<UserEntity, UserDto> {
#Autowired
private RoleRepository roleRepository;
#Override
public UserEntity createFrom(final UserDto dto) {
UserEntity userEntity = new UserEntity();
Role role = roleRepository.findByRoleName(dto.getRoleName());
userEntity.setName(dto.getName());
userEntity.setRole(role);
return userEntity;
}
....
Is it good to use repository in the conveter class? Or should I create another service/component that will be responsible for creating entities from DTOs (like UserFactory)?
Try to decouple the conversion from the other layers as much as possible:
public class UserConverter implements Converter<UserEntity, UserDto> {
private final Function<String, RoleEntity> roleResolver;
#Override
public UserEntity createFrom(final UserDto dto) {
UserEntity userEntity = new UserEntity();
Role role = roleResolver.apply(dto.getRoleName());
userEntity.setName(dto.getName());
userEntity.setRole(role);
return userEntity;
}
}
#Configuration
class MyConverterConfiguration {
#Bean
public Converter<UserEntity, UserDto> userEntityConverter(
#Autowired RoleRepository roleRepository
) {
return new UserConverter(roleRepository::findByRoleName)
}
}
You could even define a custom Converter<RoleEntity, String> but that may stretch the whole abstraction a bit too far.
As some other pointed out this kind of abstraction hides a part of the application that may perform very poorly when used for collections (as DB queries could normally be batched. I would advice you to define a Converter<List<UserEntity>, List<UserDto>> which may seem a little cumbersome when converting a single object but you are now able to batch your database requests instead of querying one by one - the user cannot use said converter wrong (assuming no ill intention).
Take a look at MapStruct or ModelMapper if you would like to have some more comfort when defining your converters. And last but not least give datus a shot (disclaimer: I am the author), it lets you define your mapping in a fluent way without any implicit functionality:
#Configuration
class MyConverterConfiguration {
#Bean
public Mapper<UserDto, UserEntity> userDtoCnoverter(#Autowired RoleRepository roleRepository) {
Mapper<UserDto, UserEntity> mapper = Datus.forTypes(UserDto.class, UserEntity.class)
.mutable(UserEntity::new)
.from(UserDto::getName).into(UserEntity::setName)
.from(UserDto::getRole).map(roleRepository::findByRoleName).into(UserEntity::setRole)
.build();
return mapper;
}
}
(This example would still suffer from the db bottleneck when converting a Collection<UserDto>
I would argue this would be the most SOLID approach, but the given context / scenario is suffering from unextractable dependencies with performance implications which makes me think that forcing SOLID might be a bad idea here. It's a trade-off
If you have a service layer, it would make more sense to use it to do the conversion or make it delegate the task to the converter.
Ideally, converters should be just converters : a mapper object, not a service.
Now if the logic is not too complex and converters are not reusable, you may mix service processing with mapping processing and in this case you could replace the Converter prefix by Service.
And also it would seem nicer if only the services communicate with the repository.
Otherwise layers become blur and the design messy : we don't know really any longer who invokes who.
I would do things in this way :
controller -> service -> converter
-> repository
or a service that performs itself the conversion (it conversion is not too complex and it is not reusable) :
controller -> service -> repository
Now to be honest I hate DTO as these are just data duplicates.
I introduce them only as the client requirements in terms of information differ from the entity representation and that it makes really clearer to have a custom class (that in this case is not a duplicate).
personally, converters should be between your controllers and services, the only things DTOs should worry about is the data in your service layer and how which information to expose to your controllers.
controllers <-> converters <-> services ...
in your case, you can make use of JPA to populate roles of your users at the persistence layer.
I suggest that you just use Mapstruct to solve this kind of entity to dto convertion issue that you are facing. Through an annotation processor the mappings from dto to entity and vice versa are generated automatically and you just have to inject a reference from your mapper to your controller just like you normally would do with your repositories (#Autowired).
You can also check out this example to see if it fit your needs.
That's the way I'd likely do it. The way I'd conceptualize it is that the User converter is responsible for user / user dto conversions, and as such it rightly shouldn't be responsible for role / role dto conversion. In your case, the role repository is acting implicitly as a role converter that the user converter is delegating to. Maybe someone with more in-depth knowledge of SOLID can correct me if I'm wrong, but personally I feel like that checks out.
The one hesitation I would have, though, would be the fact that you're tying the notion of conversion to a DB operation which isn't necessarily intuitive, and I'd want to be careful that months or years into the future some developer doesn't inadvertently grab the component and use it without understanding the performance considerations (assuming you're developing on a larger project, anyways). I might consider creating some decorator class around the role repository that incorporates caching logic.
I think the way to do it cleanly is to include a Role DTO that you convert to the RoleEntity. I might use a simplified User DTO in case that it is read only. For example, in case of unprivileged access.
To expand your example
public class UserDto {
private Long id;
private String name;
private RoleDto role;
}
with the Role DTO as
public class RoleDto {
private Long id;
private String roleName;
}
And the JSON
{
id: 1,
name: "user",
role: {
id: 123,
roleName: "manager"
}
Then you can convert the RoleDto to RoleEntity while converting the User in your UserConverter and remove the repository access.
Instead of creating separate convertor clas, you can give that responsibility to Entity class itself.
public class UserEntity {
// properties
public static UserEntity valueOf(UserDTO userDTO) {
UserEntity userEntity = new UserEntity();
// set values;
return userEntity;
}
public UserDTO toDto() {
UserDTO userDTO = new UserDTO();
// set values
return userDTO;
}
}
Usage;
UserEntity userEntity = UserEntity.valueOf(userDTO);
UserDTO userDTO = userEntity.toDto();
In this way you have your domain in one place. You can use Spring BeanUtils to set properties.
You can do the same for RoleEntity and decide whether to lazy/eager load when loading UserEntity using ORM tool.
Related
For example in rust, we can use type-safe json! macro like this:-
let value = json!({
"user": json!({
"data" : json!({"
filled": false
})
})
});
What is the similar way in java without creating POJO or string literal?
In spring, you normally don't need lines like your RUST-example. Spring is doing a lot behind the scenes for you.
Two common use-cases
1. Provide a REST-API
Normally, when you use spring, then you want to build a REST-API. A simple API looks like this:
// User.java
#Data // <- Lombok annotation, which creates getter/setter/equals/hashcode for us
public class User {
private String name;
}
// UserController.java
#Controller
#RequiredArgsConstructor
#RequestMapping("/users")
public class UserController {
private final UserRepository userRepository;
#GetMapping("/{id}")
public User getUser(#PathVariable("id") String id) {
// spring will serialize the user automatically for you
// there is no need to build a json yourself
return userRepository.getById(id);
}
2. Consume a REST-API
On the other hand, we want to consume a REST-API. But also here: Spring and his ecosystem is doing the JSON-part for you.
// User.java
#Data
public class User {
private String name;
}
// some random method in a class with a webClient
private User consumeRest() {
// even here: no manual json parsing at all
// Spring-magic is working for you
return this.webClient.get()
.uri(someUriHere)
.retrive()
.bodyToMono(User.class)
.block();
}
tl;dr
Spring (and java) requires a strong typing. You need almost always a type-class (aka POJO). The classes can be really complex. Hundred of fields and nested fields? No problem.
There are some "hacks" to bypass the type. Like the link in the comments or a Map<String, Object> but you will loose the type-safety.
If that is too much "boilerplate", than Java is not perhaps the right choice for you.
So I'm writing a personal project to learn about web programing and I came across the DAO pattern. I'd built some classes (models) and like almost any program, they are nested (ex: the class Payment has a reference to an Author instance).
Just for the reference, I'm not using any mappers (will add them on a later iteration, and I'm using JDBC, not JPA).
My question is this:
When I create the PaymentJdbcDao I had a method what will return some Payment, but in order to create this payment from the database stored object I must reach also the Author (stored in a separated table).
Should I call UserJdbcDao from the PaymentJdbcDao in order to get the payment's author, should I alter the query with a join to retrieve the fields from both entities, should the PaymentJdbcDao call the UserService (I think this isn't good since the services are on the layer abobe the daos), or should I remove the author reference as an object and just hold a reference to the author_id?
Which of the abobe is the more appropriate way to accomplish this? Or is any other way which is a better practice?
Thanks.
I call my DAOs (DataAccessObjects) "Repositories".
Spring Data JPA is doing this as well.
So I would create a UserRepository and a PaymentRepository.
Repositories can be called by other Repositories or Services.
Services should never be called by Repositories.
UI -> Service -> Repository.
Your PaymentRepository could return an Entity like this
public class PaymentEntity{
private long id;
private DateTime dateTime;
private UserEntity user;
}
Your UserRepository could return an Entity like this
public class UserEntity{
private long id;
private DateTime lastLogin;
private List<PaymentEntity> payments;
}
Your Repositories could look like this.
public interface PaymentRepository{
PaymentEntity getPaymentById(long id);
List<PaymentEntity> getAllPayments();
}
public interface UserRepository{
UserEntity getUserById(long id);
List<UserEntity> getAllUsers();
}
So your PaymentRepository would call the UserRepository to get the User for your Payment.
And your UserRepository would call the PaymentRepository to get all users Payments
I hope I was able to help you
spring-data-rest provides a fantastic way how to specify the view of an entity - Spring projections. I am curious if there is a way for achieving similar functionality without using spring-data-rest - just repositories and simple rest controllers.
Let's say I have an entity:
#Entity
public class Customer {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String firstname;
private String lastname;
// …
}
and two controller endpoints. One should return the full entity and the second only subset of the entity (eq. just firstname, although the real example is a bit more complex). Basically, I would like to avoid returning nested collections from some endpoints.
I know that I can achieve this by creating a second entity pointing to the same table that contains only the values needed but the problem is that I would have to create a separate repository for it as well and it creates a lot of unnecessary boilerplate.
So my question is, do I need to have two entities with two separate repositories or is there some more elegant way how to do this in Spring?
You can use Spring's property filters to filter out few fields from response to an API:
#RequestMapping(...)
public MappingJacksonValue getUserEntities(...)
Page<UserEntity> entities = service.findAll();
MappingJacksonValue mappingJacksonValue = new MappingJacksonValue(entities);
FilterProvider filters = new SimpleFilterProvider()
.addFilter("UserEntity", SimpleBeanPropertyFilter
.filterOutAllExcept("fieldName"));
mappingJacksonValue.setFilters(filters);
return mappingJacksonValue;
}
You could use JSON Views to filter out the fields you need. Here are some examples.
Alternatively you could create a DTO instead of a full entity but that is, IMHO an elegant approach.
I'm in the middle of migrating a project from:
Spring 4.2.1 -> 5.0.0
Spring Data Gosling -> Kay
Hibernate 4.3.8 -> 5.8.0
And I'm running getting "org.hibernate.LazyInitializationException: could not initialize proxy - no Session" when accessing an object coming from my database in a controller method.
Here's a stripped down version of my code:
// CustomUser.java
#Entity
#Access(AccessType.FIELD)
#Table(name = "users")
public class CustomUser implements Serializable {
...
#Id
#GeneratedValue//details omitted
#GenericGenerator//details omitted
#Column(name = "id", insertable = true, updatable = true, unique = true, nullable = false)
private Long id;
#Column(name = "name")
private String name;
public String getName() { return name; }
}
// UserController.java
#RequestMapping(value = "/user/{userId}/", method = RequestMethod.GET)
public String showUser(#PathVariable("userId") CustomUser user) {
System.out.println("user name is [" + user.getName() + "]");
return "someTemplate";
}
// UserService.java
#Service
public class UserServiceImpl implements UserService {
#Autowired UserRepository userRepository;
#Override
public User findUserById(Long userId) {
return userRepository.getOne(userId);
}
}
// UserRepository.java
public interface UserRepository extends JpaRepository<CustomUser, Long> { }
// UserConverter.java
#Component
public class UserConverter implements Converter<String, CustomUser> {
#Autowired UserService userService;
#Override
public CustomUser convert(String userId) {
CustomUser user = userService.findUserById(SomeUtilClass.parseLong(userId));
return user;
}
}
There's also a #Configuration WebMvcConfigurerAdapter class that autowires a UserConverter instance and adds it to a FormatterRegistry.
Prior to starting this upgrade, I could hit:
http://server:port/user/123/
and Spring would take the "123" string, the UserConverter::convert method would fire and hit the Postgres database to look up a user with that id, and I'd get back a CustomUser object in my controller's "showUser" method.
But, now I am getting the org.hibernate.LazyInitializationException. This is occurring when I attempt to print out the user's name in the "showUser" method - or even just "println(user)" without accessing a field.
Most of the info I've been able to turn up from searching suggests that this exception comes from having an object having a lazily loaded collection of sub objects (like if my CustomUser had a collection of Permission objects or something that mapped to a different database table). But in this case I'm not even doing that, this is just a field on the object.
My best guess at the moment is this is due to some kind of hibernate session being terminated after the Converter does its work, so then back in the controller I don't have a valid session. (although again, I don't know why the CustomUser object is coming back unusable, I'm not attempting to fetch a subcollection).
I have added the Hibernate annotation "#Proxy(lazy = false)" to my CustomUser.java and if I do that the problem goes away. But, I'm not sure this is a good solution - for performance reasons, I really don't think I want to go down the road of eagerly fetching EVERYTHING.
I've also tried annotating various things (the service method, the controller method, etc.) with #Transactional; I haven't gotten that to work but I am still reasonably new to Spring and I may be trying that in the wrong place or misunderstanding what that should do.
Is there a better way to handle this than just "#Proxy(lazy = false)" on all of my Entity classes?
The immediate problem comes from the use of userRepository.getOne(userId). It is implemented in the SimpleJpaRepository using EntityManager.getReference. This method returns just a Proxy, which only contains its id. And only when a property gets accessed those get lazy loaded. This includes simple properties like name in your case.
The immediate fix is to use findOne which should load all the eager properties of your entity which should include simple properties, so the exception goes away.
Note that this will slightly change the behavior of your converter. The current version will not fail when the id is not existent in the database because it never checks. The new one will obtain an empty Optional and you'll have to convert it to null or throw an exception.
But there is (or might be) a bigger problem hiding: The entity is still detached because in absence of an explicit transaction demarcation the transactions only span single repository calls. So if you later want to access lazy loaded properties, you might get the same problem again. I see various options to consider:
Leave it as it is, being aware that you must not access lazy loaded properties.
Make sure a transaction is started before the converters get invoked.
Mark your controllers with #Transactional and loading the user (again) in the controller.
The question is where it is better (or in other words: where do you prefer) to put business validation logic of Jpa Entities.
Two ideas are:
In the EntityListener that before save or update would validate the entity
In the service that provides access to jpa persisting methods.
There are pros and cons of both.
When using approach No. 2 it is easier to test as you may just mock the jpa provider and test the validation logic. On the other hand with approach No. 1 the validation would happen at the same moment with validations like #NotNull etc.
I would love to know how do you solve validations in your projects and which is the better way to go.
Thanks.
Here's a general thumb rule that I follow:
When using bean validation, specify
rules that do not require dependencies
on other beans. The moment you depend
on another bean, get your service
layer to handle that dependency.
In other words, if you have a reference to a bean inside another, avoid putting in that #NotNull constraint. Your service layer is best used for that, for you're catching the violation much earlier, and at a more logical point (since other business validations would assume that the beans are available).
As an example, consider the following entity (apologies for it wont compile)
#Entity
public class User
{
#Id
private int id;
#NotNull
private String fullName;
#NotNull
private String email;
private Set<Role> roles; //No bean validation constraints here.
...
public boolean mapRoleToUser(Role role)
{ //Validation is done here. Including checks for a null role.
}
}
#Entity
public class Role
{
#Id
private int id;
#NotNull
private String name;
}
The service layer in this case, is the one that should validate whether the user has a role attached or not. Verification in the pre-persist or pre-update phase is a bit too late, especially when there is a distinct service layer that has business logic, and the rest of the business logic in the domain model (sadly, I haven't seen a good enough application with all of the logic in the domain model alone).