I’m currently learning Spring-Boot and Spring-Data-JPA.
I’m using a postgresql database for storing the data.
My goal is to store ingredients with a unique and custom ID (you just type it in when creating it), but when another ingredient with the same ID gets inserted, there should be some kind of error. In my understanding, this is what happens when I use the @Id annotation, hibernate also logs the correct create table statement.
This is my Ingredient class:
public class Ingredient {
@Id
@Column(name = "ingredient_id")
private String ingredient_id;
@Column(name = "name")
private String name;
@Column(name = "curr_stock")
private double curr_stock;
@Column(name = "opt_stock")
private double opt_stock;
@Column(name = "unit")
private String unit;
@Column(name = "price_per_unit")
private double price_per_unit;
@Column(name = "supplier")
private String supplier;
-- ... getters, setters, constructors (they work fine, I can insert and get the data)
}
My controller looks like this:
@RestController
@RequestMapping(path = "api/v1/ingredient")
public class IngredientController {
private final IngredientService ingredientService;
@Autowired
public IngredientController(IngredientService ingredientService) {
this.ingredientService = ingredientService;
}
@GetMapping
public List<Ingredient> getIngredients(){
return ingredientService.getIngredients();
}
@PostMapping
public void registerNewStudent(@RequestBody Ingredient ingredient) {
ingredientService.saveIngredient(ingredient);
}
}
And my service class just uses the save() method from the JpaRepository to store new ingredients.
To this point I had the feeling, that I understood the whole thing, but when sending two post-requests to my application, each one containing an ingredient with the id "1234", and then showing all ingredients with a get request, the first ingredient just got replaced by the second one and there was no error or smth. like that in between.
Sending direct sql insert statements to the database with the same values throws an error, because the primary key constraint gets violated, just as it should be. Exactly this should have happened after the second post request (in my understanding).
What did I get wrong?
Update:
From the terminal output and the answers I got below, it is now clear, that the save() method can be understood as "insert or update if primary key is already existing".
But is there a better way around this than just error-handle every time when saving a new entry by hand?
3
Answers
When saving a new ingredient, jpa will perform an update if the value contained in the “id” field is already in the table.
A nice way through which you can achieve what you want is
You can return an error if the entity is already in the table otherwise (empty lambda), you can save the new row
The save method will create or update the entry if the id already exists. I’d switch to auto generating the ID when inserting, instead of manually creating the IDs. That would prevent the issue you have
This is a downside to using CrudRepository save() on an entity where the id is set by the application.
Under the hood EntityManager.persist() will only be called if the id is null otherwise EntityManager.merge() is called.
Using the EntityManager directly gives you more fine grained control and you can call the persist method in your application when required