Skip to content
Advertisement

Hibernate simply adding elements to manytomany from JSON

I want to add elements in my manytomany association via a request without loading each added element. I am using a custom join table. [Code will be added at the bottom]

So for example this is the JSON I get:

{
    "id": 122,
    "materials": [
        {
            "id": {
                "materialId": 62,
                "homeworkId": 122
            },
            "position": 1
        }
    ]
}

So there is one HomeworkMaterial. If I send this as POST to the server nothing happens (as expected). For all following examples, I’m just using homeworkRepository.save(homework)

Also the following will work. (All HomeworkMaterials will be removed as expected)

{
    "id": 122,
    "materials": []
}

But if I want to add elements to the materials like this:

{
        "id": 122,
        "materials": [
            {
                "id": {
                    "materialId": 62,
                    "homeworkId": 122
                },
                "position": 1
            },
            {
                "id": {
                    "materialId": 162,
                    "homeworkId": 122
                },
                "position": 1
            }
        ]
    }

There will be an error.

attempted to assign id from null one-to-one property [schooling.api.courses.homework.ids.HomeworkMaterial.homework]

So the problem is, that the materials are not loaded from the DB and I have just the ID of HomeworkMaterial. So I am pretty sure that if I just loop through the HomeworkMaterials and load all the Materials from the DB it works. But this is really not a good solution in my opinion, because I think I should not need to access the DB for adding those HomeworkMaterials since I did this on the client and have the IDs of the materials and if something is wrong there would be just an exception thrown.

So my question is, how can I easily save more HomeworkMaterials without having the materials fetched?

I have also tried looping through them and saving them in their own repository, but this does not work too. (got the same exception as with the previous approach)

So now my Code:

Homework.java

@Getter @Setter @NoArgsConstructor
@JsonIdentityInfo(generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")
@Entity
@Table(name = "homework")
public class Homework {
private static final String sequenceName = "homework_id_sequence";

@Id
@Column(nullable = false)
@SequenceGenerator(allocationSize=10,  sequenceName=sequenceName, name=sequenceName)
@GeneratedValue(generator=sequenceName, strategy= GenerationType.SEQUENCE)
protected Long id;

@OneToMany(mappedBy = "homework", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@Fetch(value = FetchMode.SUBSELECT)
@JsonIgnoreProperties(value = {"homework", "material"})
List<HomeworkMaterial> materials = new ArrayList<>();

public void addMaterial(Material material, int position) {
    HomeworkMaterial homeworkMaterial = new HomeworkMaterial(material, this, position);
    materials.add(homeworkMaterial);
    material.getHomeworks().add(homeworkMaterial);
}

public void removeMaterial(Material material) {
    for (int i = 0; i < materials.size(); i++) {
        HomeworkMaterial current = materials.get(i);
        if(current.getHomework().equals(this) && current.getMaterial().equals(material)) {
            current.getHomework().getMaterials().remove(current);
            current.setHomework(null);
            current.setMaterial(null);
            materials.remove(current);
        }
    }
}
// Equals and Hash omitted
}

Material.java

@Getter @Setter @NoArgsConstructor
@JsonIdentityInfo(scope = schooling.api.courses.materials.Material.class, generator = ObjectIdGenerators.PropertyGenerator.class, property = "id")

@Entity
@Table(name="material")
public class Material implements FileOwner {
private static final String sequenceName = "material_id_sequence";

@Id
@Column(nullable = false)
@SequenceGenerator(allocationSize=10,  sequenceName=sequenceName, name=sequenceName)
@GeneratedValue(generator=sequenceName, strategy=GenerationType.SEQUENCE)
protected Long id;

@OneToMany(mappedBy = "material", cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.LAZY)
@Fetch(value = FetchMode.SUBSELECT)
@JsonIgnoreProperties(value = {"homework", "material"})
List<HomeworkMaterial> homeworks = new ArrayList<>();

public void addHomework(Homework homework, int position) {
    HomeworkMaterial homeworkMaterial = new HomeworkMaterial(this, homework, position);
    homeworks.add(homeworkMaterial);
    homework.getMaterials().add(homeworkMaterial);
}

public void removeHomework(Homework homework) {
    for (int i = 0; i < homeworks.size(); i++) {
        HomeworkMaterial current = homeworks.get(i);
        if(current.getHomework().equals(homework) && current.getMaterial().equals(this)) {
            current.getHomework().getMaterials().remove(current);
            current.setHomework(null);
            current.setMaterial(null);
            homeworks.remove(current);
        }
    }
}
}

HomeworkMaterial.java

@Getter @Setter @NoArgsConstructor
@Entity
@Table(name = "homework_material")
public class HomeworkMaterial {
@EmbeddedId
private HomeworkMaterialId id;

@ManyToOne(fetch = FetchType.LAZY)
@MapsId("materialId")
private Material material;

@ManyToOne(fetch = FetchType.LAZY)
@MapsId("homeworkId")
private Homework homework;

@Column(nullable = false)
private int position;

public HomeworkMaterial(Material material, Homework homework, int position) {
    this.material = material;
    this.homework = homework;
    this.position = position;
    this.id = new HomeworkMaterialId(material.getId(), homework.getId());
}
}

HomeworkMaterialId.java

@Embeddable
@NoArgsConstructor @AllArgsConstructor @Getter @Setter

public class HomeworkMaterialId implements Serializable {

@Column(name = "material_id")
private long materialId;

@Column(name = "homework_id")
private long homeworkId;

}

As said above, for saving I’m just simply using homeworkRepository.save(homework)

Advertisement

Answer

I found a solution to this, I can just “fetch” the Homework and Material with getById so the DB is not accessed, but I have a reference to them and I can then save it.

User contributions licensed under: CC BY-SA
4 People found this is helpful
Advertisement