Reputation: 462
Before I start I checked few posts and none of them resolved my problem.
Please can someone guide me here. I wanted to establish a Plant(1) to Inventories(n) Relationship.
I created couple of models, one for Plant as below where I mention the OneToMany relationship
@Entity
public class Plant implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long plantID;
@OneToMany(mappedBy = "plant", cascade = CascadeType.ALL, orphanRemoval = true)
private List<Inventory> listInventory = new ArrayList<>();
getter and setter....
And another one for Inventory where I mention ManyToOne relationship
@Entity
public class Inventory implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.AUTO)
private long inventoryId;
@ManyToOne
@JoinColumn(name = "plantID", nullable = false)
private Plant plant;
When I try to persist the data like below, it saves the plant (parent) but not its children's.
As part of persisting the data, I did something like below
public Plant addPlant(String plantName, List<Inventory> listMaterial, PlantRepository plantRepository, InventoryRepository inventoryRepository) {
Plant plant = new Plant();
List<Inventory> listInventory = new ArrayList<>();
plant.setPlantName(plantName);
for (Inventory inventory : listMaterial) {
inventory.setPlant(plant);
listInventory.add(inventory);
}
plant.setListInventory(listInventory);
return plantRepository.save(plant);
}
After reading few posts I found that I should set the association of plant with the inventory before persisting. So I did added the code for the same but it went on infinite or just my STS hanged.
I am sure I am doing something wrong but not sure where. Please can someone guide me through.
My expectation is that Inventory will be a reference table where I will have list of inventories. When I add a new plant, I will map few of the inventories to this plant. Similarly this will happen for many plants and many inventories.
As part of persistence, I will have to save plant and its inventories. I should also be able to pass the plant ID and retrieve the corresponding Inventories.
UPDATE 1: Also I am not sure if the relation I am using is fair enough for this scenario. As my inventory is a reference table and at the same time, when a plant is mapped to multiple inventories, each inventory can be modified before persisting.
I tried @ManyToMany and it stores the relation in a 3rd table with a unique reference to both the tables but I wont be able to get the details of each inventory record. With @ManyToMany, when I fetch it bring the values from the reference table and not the modified one which was persisted with the parent(plant) Please any advise
UPDATE 2
I tried with the same models but I changed the way I persist the data as below.
public Plant addPlant(String plantName, List<Inventory> listMaterial, PlantRepository plantRepository, InventoryRepository inventoryRepository) {
Plant plant = new Plant();
List<Inventory> listInventory = new ArrayList<>();
plant.setPlantName(plantName);
for (Inventory inventory : listMaterial) {
plant.addInventoryToPlant(inventory);
}
return plantRepository.save(plant);
}
Here is the add method in my plant model
public void addInventoryToPlant(Inventory inventory) {
listInventory.add(inventory);
inventory.setPlant(this);
}
It is just overwriting the inventory table with different plant IDs but not creating a reference table or join table to maintain all the possible mappings. If I try to add a plant with 2 inventories, it maps them first to the Inventory table. If I add another plant then this is getting overwritten. I was in an assumption that it will create a third table to maintain this entity
Upvotes: 3
Views: 10884
Reputation: 10726
This answers the UPDATE 2 part:
As I understand, Plant
and Inventory
are in a many-to-many relationship, but there are additional properties that are to be stored along with the information that a specific Plant
is holding a specific Inventory
item.
In that case, you need an additional entity (let's call it StockItem
) that will be used to hold that additional state. Both Plant
and Inventory
will then be in a one-to-many relationship with the new entity.
Your mapping will then become:
class Plant {
@OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
@JoinColumn(name = "plant_id")
private Set<StockItem> stockItems;
...
}
class StockItem {
@ManyToOne(mappedBy = "stockItems")
private Plant plant;
@ManyToOne(cascade = {CascadeType.MERGE, CascadeType.PERSIST})
@JoinColumn(name = "inventory_id")
private Inventory inventory;
//put any fields here that may vary from one plant to plant to another
private int quantity;
}
class Inventory {
@OneToMany(mappedBy = "inventory")
public Set<StockItem> stockItems;
// leave any fields that will NOT vary from one plant to another here
private String name;
}
Things to note:
This mapping assumes that you will be adding new StockItem
s to a Plant
(new items added to Inventory.stockItems
will be ignored by JPA), in which case it will be enough to set the StockItem.inventory
field to a proper value, add the StockItem
to the Plant.stockItems
list, and save the Plant
entity
The StockItem.plant
and Inventory.stockItems
are not absolutely necessary, remove them if you do not need them
Upvotes: 2
Reputation: 26572
I have a feeling that those Inventory entities are detached and the Persistence Provider is not considering them during flush. You do not get an exception as @OneToMany is a special kind of relationship in flush algorithm because the act of persisting the owning entity does not depend on the target thus Hibernate will proceed and persist only the Plant entity.
Try using merge instead of persist:
plant.setListInventory(listInventory);
return plantRepository.merge(plant);
Update
You can also merge each inventory one by one using save as Spring JPA implicitly checks whether entity should be saved or merged:
for (Inventory inventory : listMaterial) {
Inventory mergedInventory = inventoryRepository.save(inventory);
mergedInventory.setPlant(plant);
listInventory.add(mergedInventory);
}
plant.setListInventory(listInventory);
return plantRepository.save(plant);
Upvotes: 1