How to add a where clause to a Hibernate #ManyToMany relationship? - oracle

Given two entities:
PurchaseProductGroup
PurchaseProduct, which has a status column that can be 'A' (active) or 'D' (deleted)
and a many-to-many relationship defined in PurchaseProductGroup:
/** The purchase products linked to this group. */
#ManyToMany
#JoinTable(name = "purchaseprodgrp_purchaseprod",
joinColumns = #JoinColumn(name = "ppg_id"),
inverseJoinColumns = #JoinColumn(name = "ppr_id"))
private List<PurchaseProduct> purchaseProducts;
How can I restrict this so that purchaseProducts with a status of 'D' are excluded?
Things I've tried
Have tried adding the following just below the #ManyToMany annotation but both failed with an "invalid identifier" exception for the column:
#Where(clause = "status <> 'D'")
#WhereJoinTable(clause = "purchaseProduct.status <> 'D'")
Also tried using adding #Where(clause = "status <> 'D'") at the entity level but this doesn't seem to affect the contents of relationship collections - as backed up by this question.

Please try the following code:
#ManyToMany
#JoinTable(name = "purchaseprodgrp_purchaseprod",
joinColumns = #JoinColumn(name = "ppg_id"),
inverseJoinColumns = #JoinColumn(name = "ppr_id"))
#Where(clause = "ppr_status <> 'D'")
private List<PurchaseProduct> purchaseProducts;
This may be similar to something you have tried before, but a critical point to get this working is that ppr_status is the actual column name. Hence the PurchaseProduct entity should have the following:
#Column(name="ppr_status")
public String getStatus() {
return status;
}
If you had named the field ppr_status then the #Column may not be necessary. But based on your comments above we need to tell Hibernate how to map this column.
Reference: Hibernate annotations. #Where vs #WhereJoinTable

Related

Hibernate JoinTableFilter annotation

I'm using Hibernate to map my entities and I'm having a problem. I want my entity to retrieve a list of entity from another table, linked with a join table. I also want to filter on the join table to only retrieve objets whose boolean_value is set to false.
It currently works without filtering, using #JoinTable annotation. I'm facing difficulties when it comes about #FilterJoinTable which seems not to be working.
Here is what I tried to do:
#Entity
#Table(name = "table_a")
#FilterDef(name="checkValue")
#Filter(name = "checkValue")
public class AEntity {
// id ...
#ManyToMany
#JoinTable(name = "my_join_table",
joinColumns = #JoinColumn(name = "a_id"),
inverseJoinColumns = #JoinColumn(name = "b_id"))
#FilterJoinTable(name = "checkValue", condition = "boolean_value = FALSE")
private List<BEntity> objets;
}
Currently it returns all the objects from table_b without filtering.
Any idea of what I'm doing wrong ?
You have to use the #Where annotation, as #FilterJoinTable allows defining Hibernate filters which have to be enabled explicitly with Session#enableFilter(String):
#ManyToMany
#JoinTable(name = "my_join_table",
joinColumns = #JoinColumn(name = "a_id"),
inverseJoinColumns = #JoinColumn(name = "b_id"))
#Where(clause = "boolean_value = FALSE")
private List<BEntity> objets;

"could not initialize proxy - no Session" For Multiple ManyToMany relationships in the parent

I have a Parent User Class that has multiple ManyToMany Relationships.
#Table(name = "user")
public class User {
..
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.DETACH})
#JoinTable(
name = "user_address",
joinColumns = { #JoinColumn(name = "user_id")},
inverseJoinColumns = { #JoinColumn(name = "address_id")}
)
#JsonIgnore
private final List<Address> addresses = new ArrayList<Address>();
#ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.MERGE, CascadeType.DETACH})
#JoinTable(
name = "reports",
joinColumns = { #JoinColumn(name = "user_id")},
inverseJoinColumns = { #JoinColumn(name = "reports_id")}
)
#JsonIgnore
private final List<Reports> reports = new ArrayList<Reports>();
}
When I access the FIRST ManyToMany property, everything works fine. However, immediately after
accessing the first, when I try to access the SECOND ManyToMany Property I get the "could not initialize proxy - no Session" exception:
#Component
public class Combiner {
public void combineData() {
...
List<Address> addresses = user.getAddress(); // This works
List<Reports> reports = user.getReports(); // Get the error here
..
}
}
The Address and Reports classes have the inverse relationship as many ManyToMany back to the User Entity Above.
public class Address {
#ManyToMany(mappedBy = "addresses", fetch = FetchType.LAZY)
private final List<User> users = new ArrayList<User>();
}
public class Reports {
#ManyToMany(mappedBy = "reports", fetch = FetchType.LAZY)
private final List<User> users = new ArrayList<User>();
}
I tried searching SO for the same error where there are MULTIPLE relationships like mine and the first passes but second fails, but could'nt find a post (or google couldn't understand the search terms, if anyone knows a pre-existing one - please let me know).
Could someone assess what else Im missing?
I've tried these so far to no avail:
Added #Transactional to the parent Service class that calls Combiner above
Made the second failing relationship EAGER. (as i understand it you cant make BOTH EAGER since i get a multiple bags error probably because of Cartesian join)
AM Using SpringBoot (2.2.4) with Hibernate Core {5.4.10.Final}
Approach one:
Make #ManyToMany uni-directional. The exception clearly says it can not initialize the collection of role you have in User class.
As you asked in the comment section Why can't this use case be Bi Directional - You can make this bi-directional as well.
Approach two: make collection of role EAGER or use Hibernate.initialize() to initialize the collection.
Bonus: you can make both collection EAGER by using Set not List.

Hibernate #OneToMany don't remove child from list when updating parent

I have create mapping between Paper and Mcq question as below.
public class Paper {
#OneToMany(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE})
#JoinTable(name = "paper_mcq",
joinColumns = {#JoinColumn(name = "paper_id")},
inverseJoinColumns = {#JoinColumn(name = "mcq_id")})
#JsonIgnore
private Set<Mcq> mcqs = new HashSet<>();
}
When I'm updating Paper entity it's deletes all MCQ.
SQL Output:
Hibernate: delete from paper_mcq where paper_id=?
I believe your paper object in paperRepo.save(paper) don't have mcqs at this time, and the cascading sees that as a deletion. I'm just assuming that you're receiving your object from json and the #JsonIgnore simply ignores the deserialization.
So there are multiple options to solve that:
- Query the mcqs and set them before updating
- remove #JsonIgnore and add those in your json
- remove the cascading and set it manually

How to create join table with extra column with JPA annotations?

I need for a project to join 2 SQL tables implemented like this :
I know that I'm not supposed to implement the table IngredientList as an object cause it's only here for SQL structure.
My code goes like this :
#Entity
#Table(name="recipe")
public class Recipe {
#Id
#GeneratedValue(strategy=GenerationType.AUTO)
#Column(name="id_recipe")
private Long id;
#OneToMany
#JoinTable(name="liste_ingredients", joinColumns = #JoinColumn(name = "id_recette",
referencedColumnName = "id_recette"),
inverseJoinColumns = #JoinColumn(name = "id_ingredient",
referencedColumnName = "id_ingredient"))
List<Ingredient> ingredients;
/* Getter/Setter/Constructor */
}
Which is the classic way but with that I lose the Quantity attribute that I want to associate with ingredient. And I don't get how I can work around this without creating an object IngredientList.
Thanks in advance.
Nevermind that I got my answer gonna edit it soon with code, for anyone with the same question.

Join table data gets deleted when updating an entity in ManyToMany Relationship

I have this Recipe entity with ManyToMany relationship with Category entity
#ManyToMany
#JoinTable(name = "recipe_category", joinColumns = #JoinColumn(name =
"recipe_id"),
inverseJoinColumns = #JoinColumn(name = "category_id"))
private Set<Category> categories;
The relationship in the Category entity is this.
#ManyToMany(mappedBy = "categories")
private Set<Recipe> recipes;
The Recipe, Category, Recipe_Category gets created along with a bunch of other tables based on other entities. However, when I edit Recipe, the data in the join table Recipe_Category gets deleted.
Why is this behavior and what I need to do to fix it?
Thanks in advance.

Resources