Open
Description
If we define two entities with many to many relation
@Data
@Entity
@Table(name = "groups")
public class Group {
@Id
@GeneratedValue
@Column(unique = true, updatable = false, nullable = false)
Integer id;
@ManyToMany
Set<User> members;
}
@Data
@Entity
@Table(name = "account_user")
public class User {
@Id
@GeneratedValue
@Column(unique = true, updatable = false, nullable = false)
Integer id;
@Column(updatable = false, nullable = false)
String name;
}
In order to avoid n+1 problem when querying the groups containing some user, we usually use @EntityGraph
or join fetch
in jpql like belows:
@Repository
public interface GroupRepository extends JpaRepository<Group, Integer> {
List<Group> findAll();
@EntityGraph(attributePaths = {"members"})
List<Group> findByMembers(User user);
@Query("select distinct g from Group g join fetch g.members where :user member g.members")
List<Group> findByMembersWithJoinFetch(@Param("user") User user);
}
The join fetch
will return desired groups with their all members in one select statement, but the method of @EntityGraph
will return the groups with only one members based on the query parameter (it does not retrieve all members in that group !)
I write the below test to reproduce this problem with spring-data-jpa:2.7.0, you can find the test program in attachment
@SpringBootTest
class GroupRepositoryTest {
@Autowired
UserRepository userRepository;
@Autowired
GroupRepository groupRepository;
User userA, userB, userC;
@BeforeEach
void setUp() {
userA = new User();
userA.setName("a");
userB = new User();
userB.setName("b");
userC = new User();
userC.setName("c");
userRepository.saveAll(Arrays.asList(userA, userB, userC));
}
@Test
void findByMembersIn() {
//create group
Group group = new Group();
group.setMembers(new HashSet<>(Arrays.asList(userA, userB, userC)));
groupRepository.save(group);
//test
//pass
assertEquals(group.getMembers(), groupRepository.findByMembersWithJoinFetch(userA).get(0).getMembers());
//fail
assertEquals(group.getMembers(), groupRepository.findByMembers(userA).get(0).getMembers());
}
}
I am wondering that is this working as designed or the @EntityGraph
should not have this behavior ?
jpa-join-demo.zip