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.
Related
I have a spring boot JPA project with an entity called Customers and another one CustomerReports
#Entity
public class Customers {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
private String Name;
#JsonIgnore
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "customer_id", referencedColumnName = "id")
private Reports reports;
//getter and setters..etc
}
#Entity
public class CustomerReports {
#Id
#GeneratedValue(strategy= GenerationType.IDENTITY)
private int id;
private BigDecimal monthlyPayment;
//done
#JsonIgnore
#OneToOne(mappedBy = "reports")
private Customers customers;
//constructors, getters...etc.
}
I want whenever I insert a Customer, a report to also be generated for that customer. The column "monthlyPayment" in reports is also generated through a reference from another table so I don't want to insert those columns manually if that makes sense.
Is there a way to do that? I'm not sure what to google so it would be great if anyone can give me an idea
If I understand your question properly, you can derive CustomerReports entity based on Customers via simple java utility method & then call save if you are using jparepository :
CustomerReports customerReports=reportUtil(customerEntity);
jpaRepository.save(customerEntity);
jpaRepository.save(customerReports);
...
private CustomerReports reportUtil(Customers customerEntity){
/*Derive values for CustomerReports based on Customers & return*/
}
Or if you don't want to do by this way then check if your underlying database support triggers which you can use for inserting data into CustomerReports while doing insert to Customers
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.
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.
I have created two entities Book and Book_Category with one-to-many relationship. When I issued BookCategoryRepository.findAll(), I expected hibernate to use 'INNER JOIN' query. But it just issued query to take data from Book_Category.
What I am missing? What should I do to make hibernate issue JOIN query?
Book.java
#Entity
public class Book {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#ManyToOne
#JoinColumn(name = "book_category_id")
private BookCategory bookCategory;
}
BookCategory.java
#Entity
#Table(name = "book_category")
public class BookCategory {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private int id;
private String name;
#OneToMany(mappedBy = "bookCategory", fetch = FetchType.EAGER, cascade = CascadeType.ALL)
private Set<Book> books;
}
BookCategoryRepository.java
public interface BookCategoryRepository extends JpaRepository<BookCategory, Integer> {
}
bookCategoryRepository.findAll()
Hibernate uses by default a second query to retriev the child collection. One reason for this is a proper limit query. Otherwise, there would be more rows in the result set, than entities for the 1 side, if at least one has more than 1 child.
There exists an annotation to change this behaviour in hibernate which is ignored by the Spring Data Jpa Repositories. The annotation is #Fetch(FetchMode.JOIN). You might consider How does the FetchMode work in Spring Data JPA if you really need this behaviour.
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/