Cannot delete or update parent row Hibernate - spring

I have a design and scenario entity.
I'm getting an error when removing a Design that contains one or more scenarios.
The design entity looks like:
#Entity
public class Design {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(columnDefinition = "LONGBLOB")
private byte[] image;
#OneToMany(mappedBy = "design", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private Set<Scenario> ScenarioSet;
The scenario entity looks like:
#Entity
public class Scenario {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "design_ID")
private Design design;
As you can see A design can have more than one scenarios.
And Design is responsible for the relation.
My code to save a scenario:
Design design = this.designService.getDesignById(designID);
scenario.setDesign(design);
this.scenarioService.saveScenario(scenario);
Saving it isn't a problem. I'm saving it this way because the scenario doesn't have an ID at first.
The error i'm getting:
Cannot delete or update a parent row: a foreign key constraint fails (`db`.`scenario`, CONSTRAINT `FKqmttw6jic4aplswy08wtkj5r7` FOREIGN KEY (`design_id`) REFERENCES `design` (`id`)) 0.016 sec
This lets me think that It isn't cascading when I remove the Design.

Add orphanRemoval=true to your scenario list mapping in the Design entity:
#OneToMany(mappedBy = "design", cascade = CascadeType.ALL, orphanRemoval=true, fetch = FetchType.LAZY)
private Set<Scenario> ScenarioSet;
CascadeType.ALL (or precisely CascadeType.REMOVE) serve for cascading remove operation when you take an item from the collection and save the owning entity (Design in this case). To tell Hibernate to remove items in the collection when the owning entity (Design) is removed, you need to use the orphanRemovalattribute:
https://docs.oracle.com/cd/E19798-01/821-1841/giqxy/

Related

Spring Boot Jpa Update- Avoid unnecessary updates of columns & child entities

I have a jpa entity with parent-child relationship(1:n).
Any sort of update on the entity results in jpa trying to update each and every column, even if there is no change in the value. I'm confused about the update behaviour for child enitities, I guess same would be happening with those also.
I went through #DynamicUpdate over entities but I do not know how they behave in case of child-entities.
public class Tutorial extends Auditable<String> implements Serializable {
#Id
#GeneratedValue
private Long id;
private String title;
private string description;
#OneToMany(
fetch = FetchType.LAZY,
mappedBy = "tutorial",
cascade = CascadeType.ALL,
orphanRemoval = true)
private Collection<Modules> Modules = new HashSet<>();
// Getter/Setters
}
How to come up with an approach so that only necessary fields/columns and child entities and their columns are update on each save.

relationship row to be deleted on cascade delete

I have the following entities in a one-to-many relationship:
#Entry
class Parent {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private int id;
...
#OneToMany(cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private Collection<Child> children;
...
}
and
#Entry
class Child {
#Id #GeneratedValue(strategy=GenerationType.AUTO)
private int id;
...
#Transient
private Parent parent;
...
}
So – out of these, there are the following 2 entity tables in the database
parent(id, ...)
child(id, ..)
and the relationship table between these two entities-- due to that #OneToMany relationship
parent_child(parent_id, child_id)
suppose parent_id=4 has the child_id=7 and thus parent_child table has the row (parent_id=4, child_id=7).
When i delete child id =7, isn’t the (parent_id=4, child_id=7) in parent_child table supposed to be deleted as part of it? I’m seeing that row in parent_child table even after the corresponding child is deleted from the child table.
I’m using the repository (implementing CrudRepository) for deleting that child.
//////////////
UPDATE:
by parent_child(parent_id, child_id), i'm referring to the relationship table that hibernate is generating behind the scenes to maintain the relationship between parent and child tables.
went into this table out of curiosity directly on SQL. and these are what i'm seeing. i expected (still do) the (parent_id=4, child_id=7) row would disappear now that child_id=7 fell off the face of the Earth. but didn't.
You have to mapped your entity class like this.
#ManyToOne
#JoinColumn(name = "parent_id", referencedColumnName = "id", nullable = false,cascade = CascadeType.ALL,fetch = FetchType.EAGER)
private Parent parent;
#OneToMany(mappedBy = "parent",cascade = CascadeType.ALL, orphanRemoval = true, fetch = FetchType.EAGER)
private Collection<Child> children;
Remove
#Transient
private Parent parent;
And do #ManyToOne mapping in Child Entity.

Spring Boot + JPA (Hibernate) - how to update some fields

I have to update my entity - when placeA or/and placeB is updated, I have to also update points.
SO I fetch route object from database and modify one or two fields (placeA, placeB). The problem is that I have to update points accordingly -> in Point object I have to also update pointAddress (point.pointAddress has to be updated to route.placeA or route.placeB values):
public class Route{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "place_a_id")
private Address placeA;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "place_b_id")
private Address placeB;
#OneToMany(mappedBy = "route", cascade = CascadeType.ALL)
List<Point> points;
public class Point{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "point", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<PointDetail> pointDetails;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "route_id", nullable = false)
private Route route;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "route_address_id", nullable = false)
private Address pointAddress;
public class PointDetail{
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "point_id", nullable = false)
private Point point;
And I fetch Route entity from db and from now this object is in persistent state (everything within the same transaction) so I don't need to invoke repository.save(myChangedRoute) explicitly.
The question is how to update route.points[0].pointAddress?
Is it sufficient?
Route route = repository.findRouteById(1L);
route.setPointA("new place");
route.getPoints().get(0).setPointAddress("new place")
And what with route.getPoints().get(0).getRoute() and route.getPoints().get(0).getPointDetails() objects? For example should I also update route.getPoints().get(0).getPointDetails() object? PointDetail objects have a field point that maybe should be also updated?
I have related (nested) objects in my Route object (dependencies) so my question is how to update my object structure properly to not overwrite new values with the old nested ones that are not updated, e.g. I updated:
route.getPoints().get(0).setPointAddress("new place")
but I haven't updated route.getPoints().get(0).getPointDetails().get(0).setPoint(MY NEW UPDATED AND NOT YET SAVED Point object)???
SO we have a circular dependencies route -> point -> pointDetail -> point and the question is if it's sufficient to update only pointAddress in my route.point object or I have to also update pointAddress in route.point.pointDetail.point?
First of all: point -> pointDetail -> point is not a curricular dependecny. The relation between pointDetail and point is BiDirectional dependency.
PointDetail objects have a field point that maybe should be also updated? Of course not.
and the question is if it's sufficient to update only pointAddress in my route.point? Yes that is enough.
Hibernate has the first level cache it ensures that objects will be loaded only once per session. So no override can occur. Of course, is up to you and your code how will you update properties and in which order.

Hibernate mapping user relation to entities

Let's se we have Hibernate entity User with basic fields such as username, password, roles etc..
Now we have an entity such as Car.
User has a OneToOne relationship with Car, cause he can own a car. But he also has besides this a OneToMany relationship to Car, because he also owns the cars of his children. But in the frontend I want to know which cars he owns for himself and which cars he owns for his children. The same applies to the relationship between User and motorbike (his own, his childrens, etc...)
How would the User entity class look like? Is it good to have the relationships mapped in an "Helper" entity such as UserData:
#Entity
#Data
#Table( name = "users",
uniqueConstraints = {
#UniqueConstraint(columnNames = "username")
})
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Size(max = 150)
private String username;
#NotBlank
#Size(max = 120)
private String password;
#OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinColumn(name = "USER_DATA_ID")
private UserData userData;
UserData:
#Entity
#Data
#Table( name = "user_data")
public class UserData {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinColumn(name = "OWN_CAR_ID")
private Car ownCar;
#OneToOne(cascade = {CascadeType.ALL}, fetch = FetchType.EAGER)
#JoinColumn(name = "PARTNER_CAR_ID")
private Car partnerCar;
#ManyToMany(fetch = FetchType.LAZY)
#JoinTable( name = "user_children_cars",
joinColumns = #JoinColumn(name = "user_data_id"),
inverseJoinColumns = #JoinColumn(name = "car_id"))
private Set<Car> childrenCars = new HashSet<>();
public boolean addToChildrenCarSet(Car c) {
return childrenCars.add(c);
}
public UserData() {
}
}
As you ask for an opinion, I would say it gets unnecessary complicated if you use the intermediate entity user_data. :-) There is no real drawback to add more fields and keys into the user class - performance is probably also better then using the EAGER fetching. If performance is an issue, better optimize querys later on then splitting the table now.
Also the #ManyToMany I would avoid - better create the intermediate table and relations yourself. You can check out https://bootify.io and create your database schema there. There is no EAGER fetching and also no CascadeType.ALL (both only good ideas in special cases), you would probably add more problems with that then actual helping in any way.
So the addToChildrenCarSet method would end up in a #Service class, in a method with #Transactional, in my proposal.

How to send POST request with Many-to-many relationship in Spring?

I'm trying to add a order with equipment list, here's my entities:
the order entity
#Entity #Table(name = "orders") public class Order extends Ticket{
#OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
private Set<OrderEquipment> orderEquipments = new HashSet<>();}
the equipment entity
#Entity #Table(name = "equipments") public class Equipment extends DateAudit {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#NotBlank
#Size(max = 30)
private String name;
#NotNull
private Long nbr_piece ;
#OneToMany(mappedBy = "equipment", cascade = CascadeType.ALL, orphanRemoval = true)
private Set<OrderEquipment> orderEquipments = new HashSet<>();}
and the order_equipment entity
#Entity #Table(name = "order_equipment") public class OrderEquipment extends DateAudit { #Id
#ManyToOne
#JoinColumn(name = "order_id")
private Order order;
#Id
#ManyToOne
#JoinColumn(name = "equipment_id")
private Equipment equipment;
#NotBlank
#Column(name = "quantity")
private Long quantity;}
here is the add function in the orderController
#PostMapping("/orders")
public Order createOrder(#Valid #RequestBody Order Order){
Order.setObservateurEmail(Order.getObservateurEmail());
Order.setObject(Order.getObject());
Order.setDescription(Order.getDescription());
return orderRepository.save(Order);
}
I have seen a mistakes there, lemme try to help you. Since you issue is not clear, please lemme know if it does/does not work:
You have two bidirectional mappings there:
Order (with ALL cascade) <-> OrderEquipment
Equipment (with ALL cascade) <-> OrderEquipment
You are using #JoinColumn for both of them, even though they are bidirectional. Please take a look at this. You should always use the mappedBy attribute when defining bidirectional relationships.
Now, you are receiving an Order object from a POST request, making changes to 3 attributes and then saving it. Since the mapping between the Order and OrderEquipment have the CascadeType.ALL attribute, any save on the Order object will save all OrderEquipment children associated. If the Order object you are receiving already have OrderEquipment children, your method will also save/update them.
Your POST mapping looks good to me, just take care with your table relationship definitions.
Take a look at this answer to check how a lits of entities should be formatted on a JSON POST.

Resources