Home > Mobile >  Set of String is not acting normal in Spring JPA Projection Interface
Set of String is not acting normal in Spring JPA Projection Interface

Time:05-18

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.

  • Related