I am using Projection Interface to return a partial view of an Entity, and the partial view contains a Set of String. I expect the Set works as normal but its not: when I add a new String to it, it ignores the String, see the example code below:
@Entity public class MyEntity {
@Id private Long id;
@ElementCollection private Set<String> strings;
// more fields
// getters and setters
}
public interface MyEntityRepo extends CrudRepository<MyEntity, Long> {
MyEntityPartial getMyEntityPartialById(Long id);
}
public interface MyEntityPartial {
Set<String> getStrings();
}
@DataJpaTest public class MyTest {
@Autowired private MyEntityRepo repo;
@BeforeEach private void setup() {
MyEntity myEntity = new MyEntity();
myEntity.setId(1L);
Set<String> strings = new HashSet<>();
strings.add("A");
myEntity.setStrings(strings);
repo.save(myEntity);
}
@Test public void exampleTest() {
MyEntityPartial myEntityPartial = repo.getMyEntityPartialById(1L);
assertThat(myEntityPartial, is(notNullValue()));
assertThat(myEntityPartial.getStrings(), containsInAnyOrder("A"));
myEntityPartial.getStrings().add("B");
assertThat(myEntityPartial.getStrings(), containsInAnyOrder("A", "B"));
}
}
Here the test fails at the last assert as myEntityPartial.getStrings()
still contains only "A" but not "A" and "B". I checked the Set
of the projection instance myEntityPartial
is not a normal HashSet
but a org.hibernate.collection.internal.PersistentSet
. Wondering if that is the issue? And how do I tell Hibernate or Spring to use HashSet
in a projection interface?
CodePudding user response:
Note : I haven't worked with projections before in Spring Data JPA. So thank you for sharing this! :TIL
I think problem is with myEntityPartial.getStrings().add("B");
.
It does not update the actual MyEntity
managed by MyRepo
. And every execution of myEntityPartial.getStrings()
I guess returns latest projection of entity in db.
But if we store myEntityPartial.getStrings()
in a local set, it works!
@Test public void exampleTest() {
MyEntityPartial myEntityPartial = repo.getMyEntityPartialById(1L);
Set<String> partialSet = myEntityPartial.getStrings();
assertNotNull(myEntityPartial);
assertTrue(partialSet.contains("A"));
partialSet.add("B");
assertTrue(partialSet.contains("A"));
assertTrue(partialSet.contains("B"));
}
I am assuming this is not the real intention of test though ?
So if entity managed by repository is updated, then it works
@Test public void exampleTest2() {
MyEntityPartial myEntityPartial = repo.getMyEntityPartialById(1L);
assertNotNull(myEntityPartial);
assertTrue(myEntityPartial.getStrings().contains("A"));
//update the entity in db
updateEntity("B");
assertTrue(myEntityPartial.getStrings().contains("A"));
assertTrue(myEntityPartial.getStrings().contains("B"));
}
/*
* Updating actual entity, returns B with partial projection
*/
private void updateEntity(String newStrings){
MyEntity entity = repo.findById(1L).get();
Set<String> existing = entity.getStrings();
existing.add(newStrings);
entity.setStrings(existing);
repo.save(entity);
}
Also, myEntityPartial.getStrings()
showed type LinkedHashSet.
Hope this helps in resolution.