C. Rich
C. Rich

Reputation: 53

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,

Upvotes: 2

Views: 2244

Answers (2)

Christian Beikov
Christian Beikov

Reputation: 16400

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.

Upvotes: 0

Khaled Ahmed
Khaled Ahmed

Reputation: 1134

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);
}

Upvotes: 1

Related Questions