DTO to JPA Entity in onetomany bidirectional relationship with orika - java

I am copying incoming DTO objects to my JPA entities having bidirectional OneToMany relation, using orika library(tried different other libraries like Dozer,SpringBeanUtils etc and all have same effect), though copy works fine but persisting entity is not updating foreign key of child entities. I am aware that it's happening due to missing child entity synchronisation with parent entity.
But the whole idea of using orika or any similar library is to avoid boilerplate code of copying each entity/objects separately. So I want to know is there anyway I can do this synchronisation during copy itself?
Following are my entity and DTO class
#Entity
#Data
public class Parent
{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
#OneToMany(mappedBy=“parent”,cascade=CascadeType.ALL,OrphanRemoval=true)
private List<Child> childs;
public void addChild(Child child)
{
childs.add(child);
child.setParent(this);
}
public void removeChild(Child child)
{
childs.remove(child);
child.setParent(null);
}
}
#Entity
#Data
public class Child
{
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
private Long id;
private String name;
#ManyToOne(fetch=FetchType.LAZY)
#JoinColumn(name=“parent_id”)
private Parent parent;
}
#Data
public class ParentDto
{
private Long id;
private String name;
private List<ChildDto> childs;
}
#Data
public class ChildDto
{
private Long id;
private String name;
}
And Orkia copy logic -
DefaultMapperFactory factory=new DefaultMapperFactory().Builder().build();
factory.classMap(ParentDto.class,Parent.class).byDefault().register();
Parent parentEntity=factory.getMapperFacade().map(parentDtoObject,Parent.class);
//parentEntity.getChilds().forEach(child -> child.setParent(parentEntity));
repository.save(parentEntity);
When repository.save() is completed foreign_key of child table parent_id is persisted as null instead of actual value. But if I uncomment the line commented it works fine, which I don't want to do as my entity has many child objects and looping will have performance impact and code looks ugly as well. Is there a better way of doing this? Or having unidirectional OneToMany will work ?

You can do that with Blaze-Persistence Entity-Views which was designed with this in mind.
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(Parent.class)
#UpdatableEntityView
public interface ParentDto {
#IdMapping
Long getId();
String getName();
void setName(String name);
#UpdatableMapping
Set<ChildDto> getChilds();
#EntityView(Child.class)
#UpdatableEntityView
interface ChildDto {
#IdMapping
Long getId();
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.
ParentDto a = entityViewManager.find(entityManager, ParentDto.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<ParentDto> findAll(Pageable pageable);
The best part is, it will only fetch the state that is actually necessary!
Saving the state is also easy:
entityViewManager.save(entityManager, parentDto);

Related

How to define only selected foreign attributes in spring entity

Supposedly I have two entities joined as such following:
#Entity
public class User {
#Id
private Long id;
private String username;
private String password;
#ManyToOne
#JoinColumn(name="role_id", referencedColumnName = "id") //Table user in database has foreign key role_id
private Role role;
}
#Entity
public class Role {
#Id
private Long id;
private String name;
}
How do I only create User entity with only one of role attributes instead of the whole? (for example, role name only)
I expect something like
#Entity
public class User {
#Id
private Long id;
private String username;
private String password;
// Some prefix or annotation maybe?
private String role_name;
}
Like you read in the comments already, you will need a DTO approach for this and 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 getUsername();
String getPassword();
#Mapping("role.name")
String getRoleName();
}
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!

JPA Entity that has a OneToMany relationship and want to use DTO

I have an entity called "Review" that has a OneToOne relationship with a "User" entity and a OneToMany relationship with a "ReviewStage" entity. I have implemented a DTO pattern so, I also have ReviewDTO which is actually what is being sent to the UI. I am using mapstruct to map the entity to dto. All is working well however, I would rather use the UserDTO and ReviewStageDTO in the relationship mappings.
This works well:
#Entity
#Getter #Setter #NoArgsConstructor
public class Review {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long reviewId;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "ownerId")
private User owner;
#OneToMany(mappedBy = "reviewId")
private Set<ReviewStage> stages;
}
For fun, I tried this but, obviously doesn't work:
#Entity
#Getter #Setter #NoArgsConstructor
public class Review {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long reviewId;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "ownerId")
private UserDTO owner;
#OneToMany(mappedBy = "reviewId")
private Set<ReviewStageDTO> stages;
}
I just need a nudge in the right direction. Thanks,
The relationships should be between entities only and if you want to make a dto for Review and inside this dto you want to return the UserDto for example you should create a mapstruct class to map between UserEntity to UserDTO
Example
class UserDto {
/// put any fields here that you want to map
}
class ReviewDto {
UserDto user;
}
#Mapper(componentModel = "spring")
class UserMapper {
UserDto map(User user);
}
#Mapper(componentModel = "spring", uses={UserMapper.class})
class ReviewMapper {
ReviewDto map(Review review);
}
If you are concerned about the performance, I can recommend you take a look at what Blaze-Persistence Entity Views has to offer.
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(Review.class)
public interface ReviewDTO {
#IdMapping
Long getReviewId();
UserDTO getOwner();
Set<ReviewStageDTO> getStages();
#EntityView(User.class)
interface UserDTO {
#IdMapping
Long getId();
String getName();
}
#EntityView(ReviewStage.class)
interface ReviewStageDTO {
#IdMapping
Long getId();
String getName();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
ReviewDTO a = entityViewManager.find(entityManager, ReviewDTO.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
Optional<ReviewDTO> findByReviewId(long reviewId);
Note that this will only fetch the state that is actually required. With MapStruct or other bean-mapping solutions you have to handle efficient fetching yourself.

Hibernate - Entity to JSON

I need to convert to json an Entity with JsonManagedReference and JsonBackReference implementations:
#Entity
#Table(name = "myparenttable", schema = "myschema", catalog = "mydb")
#JsonIgnoreProperties(ignoreUnknown = true)
public class Parent implements Serializable {
private Integer id_parent;
private String name;
#JsonManagedReference
#JsonInclude(JsonInclude.Include.NON_NULL)
#JsonProperty(access = JsonProperty.Access.WRITE_ONLY)
private List<Child> children;
//getters and setters
}
#Entity
#Table(name = "mychildtable", schema = "myschema", catalog = "mydb")
public class Child implements Serializable {
private Integer id_child;
private String description;
#JsonBackReference
private Parent parent;
//getters and setters
}
With this setup, the persist function is straightforward, I just perform a
em.persist(parent);
and both entities are inserted into the database; but also I need to convert those entities into json for audit purposes. I get a infinite recursion error when doing this:
ObjectMapper mapper = new ObjectMapper();
String jsonString = mapper
.writerWithDefaultPrettyPrinter()
.writeValueAsString(parent);
Is there a way to do both?
You may want to annotate your parent object into Child class with
#JsonIgnore
private Parent parent;
in this way the reference of the parent object isn't put into the serialized json object.
Check if you realy need to implement Serializable interface
This is a perfect use case for using DTOs with 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(Parent.class)
public interface ParentDto {
#IdMapping
Integer getId();
String getName();
List<ChildDto> getChildren();
#EntityView(Child.class)
interface ChildDto {
#IdMapping
Integer getId();
String getDescription();
}
}
Querying is a matter of applying the entity view to a query, the simplest being just a query by id.
ParentDto a = entityViewManager.find(entityManager, ParentDto.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
On top solving your serialization issue, using Blaze-Persistence Entity-Views will also improve performance because it only select the columns that are actually needed.

Dynamic fetching for relations in Spring JPA

I want to be able to dynamically load the relations of my entity, depending on which RestService got called.
Entity classes:
#Entity
public class Order {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#ManyToOne
private Buyer buyer;
// some more attributes
}
#Entity
public class Buyer {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
// some more attributes
}
RestController class:
#GetMapping
public Iterable<Order> getAll() {
// here I want JPA to NOT load the buyers for the order
return orderRepository.findAll();
}
#GetMapping("/{id}")
public Order get(#PathVariable("id") String id) {
// here I want JPA to load the buyers for the order
return orderRepository.findById(Long.parseLong(id)).orElseThrow();
}
None of the two fetchtypes LAZY and EAGER or json annotations (like #JsonIgnore, #JsonIdentityInfo, #JsonManagedReference and #JsonBackReference) seem to make this possible as far as I understood and tried.
If this is not possible, maybe someone can explain how to solve this problem then. On the one hand I sometimes need those relations in my frontend to display some values and on the other hand when I always load them I get huge performance problems or infinity recursions.
I don't think JPA supports your use case directly.
One option is to create the same entity twice - one with eager and the other with lazy. Switch them in the methods.
Another option is to use a DTO (Data Transfer Object) as the response, instead of the entity class itself. You will have to write a mapper logic to convert an entity to DTO though.

Hibernate extending entity for read with additional joins

I have an entity that corresponds to some table in DB
#Entity
#Table
public class MyEntity {
#Id
private Long id;
#Column
private String description;
#OneToMany
private List<Foo> foos;
...
//getters setters
...
}
And I would like to extend that entity for read-only and have additional joins to another table. I need this joins only in one case for specific read user-case:
#Entity
public class ExtendedEntity extends MyEntity {
#ManyToMany(fetch = FetchType.EAGER)
private List<Bar> bars;
}
Just using extensions will create a discriminator column in DB, which I don't need since I'm using the ExtendedEntity only for the read. I found I hack with using #DiscriminatorFormula that will be resolved to the same Integer value like this:
#DiscriminatorFormula("0")
#DiscriminatorValue("0")
public class MyEntity
#DiscriminatorValue("00")
public class ExtendedEntity extends MyEntity
But this really looks like a hack. With using #MappedSuperclass I cannot have associations, but this is the main reason I need to extend my entity.
What other options do I have and what are the best practices for this use-case?

Categories

Resources