Dreamer
Dreamer

Reputation: 7551

JPA MERGE failed to update entity field value when this field is a collection(using ElementCollection)

Here we have a Manifest class that includes list of students and teachers, both could be null.

class Manifest{
    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name = "MANIFEST_STUDENT")
    List<String> students = new ArrayList<String>();

    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name = "MANIFEST_TEACHER")
    List<String> teachers = new ArrayList<String>();;

    @ElementCollection(fetch = FetchType.EAGER)
    @CollectionTable(name = "MANIFEST_OTHERS")
    List<String> others = new ArrayList<String>();;
}

on the UI, there are two multiple select, one for student and one for teacher that let user choose for current manifest.

here is the problem:

When user deselect all students or teachers from the list(meaning remove all students or teachers from current manifest) and click save, unfortunately nothing can be saved, from UI and database it shows that the multiselect chosen looks the SAME as before.

from service layer, the code is simply like this.

manifest.merge();

It seems we must keep at least one student or teacher for the collection field to make the change valid. So what's going on here and what is the solution? BTW, we are on Openjpa.

Upvotes: 0

Views: 1178

Answers (2)

Richard Sitze
Richard Sitze

Reputation: 8463

Initializing a value in a JPA managed class, such as class Manifest, has no bearing on what, or how, JPA will create the class as JPA maps extracted rows to the class. In particular, the result of:

List<String> students = new ArrayList<String>();

is likely to be:

  • On creation (by JPA) of a new instance, assign an ArrayList<String>() to students.
  • JPA overwrites students with the data it extracts - the empty ArrayList is dereferenced/lost.

If your code is clearing a list, such as students, use obj.getStudents().clear(). More likely to run into problems if you call obj.setStudents(someEmptyList).

The issue here is how the JPA manager handles empty datasets: as null or as an empty list. The JPA spec (old, not sure about the just released update) doesn't take a position on this point. A relevant article here.

From your comments, it's apparent that OpenJPA may not be respecting a null value for a Collection/List, while it happily manages the necessary changes for when the value is set to an empty list instead. Someone knowing more about OpenJPA than I may be able to help at this stage - meanwhile you've got a workaround.

Upvotes: 0

Dreamer
Dreamer

Reputation: 7551

Kind of resolve the issue, more like a work around:

Before calling merge(), place several condition checkers to make sure the collection fields are not null

public void save(Manifest entity) {

    if(entity.getStudents()==null){
        entity.setStudents(new ArrayList<String>());
    }

    if(entity.getTeachers()==null){
        entity.setTeachers(new ArrayList<String>());
    }

    if(entity.getOthers()==null){
        entity.setOthers(new ArrayList<String>());
    }

    entity.merge();
}

Simple as it, it seems the UI returns those collection fields as null even we initiate them as with empty String lists.

cheers.

Upvotes: 1

Related Questions