Home > Blockchain >  Return nested collection using JPA Specification
Return nested collection using JPA Specification

Time:09-02

I'm new in JPA Specification and I'm trying to get objects from nested collection by user id.

There are 2 entities:

@Entity
@Table(name = "message")
public class Message implements Serializable
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private int id;

    @Column(name = "title")
    private String title;
    
    @Column(name = "message")
    private String message;

    // getters and setters
}

@Entity
@Table(name = "user")
public class User implements Serializable
{
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Basic(optional = false)
    @Column(name = "id")
    private int id;

    @ManyToMany(fetch = FetchType.LAZY, cascade = CascadeType.MERGE)
    @JoinTable(
            name = "user_message_items",
            joinColumns = { @JoinColumn(name = "user_id") },
            inverseJoinColumns = { @JoinColumn(name = "message_id") }
    )
    private List<Message> sentMessages = new ArrayList<>();

    // getters and setters
}

If I use JPQL it works

@Repository
public interface MessageRepository extends JpaRepository<Message, Integer>, JpaSpecificationExecutor<Message>
{
    
    @Query(value = "select messages from User user inner join user.sentMessages messages where user.id = :userId")
    Page<Message> findSentMessagesByUserId(@Param("userId") int userId, Pageable pageable);
}

But I'd like to have Specification instead to filter values in the future, so I made one.

public Page<Message> getMessagePageByUserId(int pushUserId)
{
    Pageable paging = new OffsetLimitRequest(0, 10);
    Specification<Message> specification = (root, query, criteriaBuilder) -> {
        Root<Message> messageRoot = root;
        Root<User> userRoot = query.from(User.class);
        Expression<List<Message>> messages = userRoot.get("sentMessages");
        return criteriaBuilder.and(criteriaBuilder.equal(userRoot.get("id"), pushUserId), criteriaBuilder.isMember(messageRoot, messages));
    };
    return messageRepository.findAll(specification, paging);
}

Unfortunately this method returns collection as Set :( In collection can be duplicates messages. I need to get collection with duplicates. Could you help me please to rewrite code and find a solution. Many thanks.

CodePudding user response:

If duplicity matters to you, you should really model this by making the messages unique through some sort of surrogate key. I guess you have a join table for sentMessages? Probably it's better to model this as an entity and work with that instead of with User. Then you'd have to create a SentMessageRepository, but it would work with the Spring Data restrictions. Also, it is semantically more correct IMO.

  • Related