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.