2

I'd like to update a collection field List<Bar> bars in JPA entity Foo, but without changing the original one - just set the updatedBars to bars field before save. But my code isn't working, probably because the updatedBars are not persistent despite the fact that some Bar objects are from original collection. Any suggestions, please.

P.S. This way of updating collection seems easier to me, because I can just populate an updatedBars in stream copying and updating, creating, and just ignoring values from original collection instead of performing separate operations like remove, update, add on the original collection.

Here is my code example:

@Getter
@Setter
@Entity
@Table(name = "foo")
public class Foo {
    
    @Id
    @Column(name = "id")
    private Long id;
    
    @JoinColumn(name = "foo_id")
    @OneToMany(fetch = FetchType.LAZY, orphanRemoval = true, cascade = CascadeType.ALL)
    private List<Bar> bars;
}
@Getter
@Setter
@Entity
@Table(name = "bar")
public class Bar {
    
    @Id
    @Column(name = "id")
    private Long id;
    
    @Column(name = "foo_id")
    private Long fooId;

    @Column(name = "value")
    private Long value;
}
@Transactional
public void refresh(Long fooId){
    Foo foo = fooRepository.find(fooId);
    List<Bar> updatedBars = getUpdatedBars(foo.getBars());
    foo.setBars(updatedBars);
}

private List<Bar> getUpdatedBars(List<Bar> originalBars) {
    //do any staff to return new list of Bars, not changing the original one
    //some items are removed, some updated (same entity from original list, with changed value field), some added (entities without ids)
}

UPDATE: Updating the content of original collection also did't work.

@Transactional
public void refresh(Long fooId){
    Foo foo = fooRepository.find(fooId);
    List<Bar> originalBars = foo.getBars();
    List<Bar> updatedBars = getUpdatedBars(originalBars);
    originalBars.clear();
    originalBars.addAll(updatedBars);
}

Andrei Yusupau
  • 587
  • 1
  • 11
  • 30

2 Answers2

0

I don't know what without changing the original one should mean, but you could just detach the entity right after loading so that the database state is not updated when you change the collection.

Christian Beikov
  • 15,141
  • 2
  • 32
  • 58
-1

What you are describing is a deep copy. If the type parameter of the list is a primitive, there are a few options but if it's a complex object such as the case an option is iterating source list and creating new objects in destination.

public void copy(List<Bar> dest, List<Bar> source){
    dest.clear();
    for (int i = 0; i < source.size(); i++) {
        dest.add(new Bar(source.get(i)));
    }
}

There are other options such as serializing and deserializing using a library like Jackson or Gson. That would create a new list of objects without need to do it "manually". ref: How do you make a deep copy of an object?

fdev
  • 87
  • 1
  • 7
  • No, my question is definitely not about deep copy. It's about updating persistent collection, replacing it completely or all of its elements with the updated ones. The main problem that changes should be saved to database with Hibernate. – Andrei Yusupau Aug 12 '21 at 08:14
  • If you change the contents of a persistent collection inside a transactional scope it should update on your database because its bound to your session. That is why I recommended doing a deep copy. You will copy the content to a new list and do work on original, that way you have the original contents and any changes to the persistent collection will modify your database. Or do the opposite if wanted.. – fdev Aug 12 '21 at 12:34