JPA: OneToMany relationship with multiple mappedBy attributes - java

I'm having a problem to map a relationship between instances of one single entity. Let me give you the JPA entities first.
Article entity
#Entity
#Table(name = "article")
public class Article {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "article", orphanRemoval = true)
private Collection<Keyword> keywords;
#OneToMany(cascade = CascadeType.ALL, mappedBy = "article1", orphanRemoval = true)
private Collection<RelatedArticles> relatedArticles;
#Column(name = "content", nullable = false)
#Lob
private String content;
...
}
RelatedArticle entity
#Entity
#Table(name = "related_articles")
public class RelatedArticles {
#Id
#Column(name = "id")
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#JoinColumn(name = "article1_id")
#ManyToOne(optional = false)
private Article article1;
#JoinColumn(name = "article2_id")
#ManyToOne(optional = false)
private Article article2;
private Float weightedJaccardIndex;
...
}
Further explanation
An article can be related to other articles which is realized by the RelatedArticle entity. The article can be referenced by article1 or article2. That means the collection relatedArticles in Article should contain all instances of RelatedArticle where the ID either matches article1 or article2.
Question
How can I map a single collection of RelatedArticles in my Article entity where the origin Article is either article1 or article2?
Alternative solutions are welcome!

Related

Spring Data JPA - Hibernate - OneToMany, ManytoOne bidirectional

Have 2 Entities: Orders and Products. 1 Order can have Many Products and Many Products can belong to 1 Order (Each Product only belongs to 1 Order).
With unidirectional association at Order Entity, I am able to retrieve product details when performing orderRepo.findAll(); In similar fashion, need order details when performing productRepo.findAll();
Tried code:
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "order_details")
public class OrderData {
#Id
#Column(name = "order_id", nullable = false, unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long orderId;
#NotNull
#Column(name = "customer_name", nullable = false)
private String customerName;
#OneToMany(mappedBy = "productId", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<ProductData> products;
}
#Data
#NoArgsConstructor
#AllArgsConstructor
#Entity
#Table(name = "product_details")
public class ProductData {
#Id
#Column(name = "product_id", nullable = false, unique = true)
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long productId;
#NotNull
#Column(name = "product_name", nullable = false)
private String productName;
#ManyToOne(fetch = FetchType.LAZY, optional = false, cascade = CascadeType.ALL)
#JoinColumn(name = "order_id", nullable = false)
private OrderData orderData;
}
While inserting at products; we are getting error: "insert or update on table violates foreign key constraint jpa"
While performing productRep.findAll(): infinite loop for hibernate select queries
Tried #JsonIgnore. This not returning child or parent elements.
Tried #JsonManagedReference vs #JsonBackReference - still no luck.
Please guide me on this
The mappedBy attribute points to the wrong field:
#OneToMany(mappedBy = "productId", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<ProductData> products;
This must be the back reference:
#OneToMany(mappedBy = "orderData", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private Set<ProductData> products;

Hibernate delete all objects associations that reference the object unidirectional using ManyToOne

I have a ModuleData object and a Setting object that references this ModuleData object, but the ModuleData object does not have any reference to the Setting object. Does Hibernate automatically delete the objects that reference ModuleData and if not, how do I accomplish that Hibernate does this.
ModuleData.java
#Entity
#Table(name = "modules")
public class ModuleData {
#Id
#GeneratedValue
#Column(name = "id", updatable = false, nullable = false)
private long id;
#Column(name = "name")
private String name;
#Column(name = "version")
private String version;
}
Setting.java
#Entity
#Table(name = "settings", uniqueConstraints = #UniqueConstraint(columnNames = {"settingKey"}))
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public class Setting {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private long id;
#Column(name = "settingKey", nullable = false)
private String key;
#Column
private String value;
#ManyToOne
#JoinColumn(name = "moduleId", nullable = false)
private ModuleData module;
I will delete by session.delete(moduleData).
You can add the other side of the relation (i.e. add the Setting property to the ModuleData class), and then use Java Persistence 'cascade types' to accomplish this. Specifically the CascadeType.REMOVE will give you the desired behavior. From this great tutorial, your ModuleData class would reference its Setting child similar to this:
#OneToMany(cascade = CascadeType.ALL, mappedBy = "post", orphanRemoval = true)
private List<Comment> comments = new ArrayList<>();
so it would look something like:
#OneToMany(cascade = CascadeType.REMOVE, orphanRemoval = true, ...)
private List<Setting> settings;
Another great tutorial on cascade types:
https://www.baeldung.com/jpa-cascade-types

Hibernate loads list collection twice

I have 3 tables in my database, container, item and item_container which is a join_table of many to one between container and item. Entities in code are mapped using 2 classes: Container and Item.
The relation is unidrectional from Container to Item.
join_table in hibernate is mapped only by annotations in Container class.
#Entity
#Table(name="container")
public class Container {
#Id
#Column(name = "container_id")
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(fetch = FetchType.EAGER, cascade = CascadeType.ALL, orphanRemoval = true)
#JoinTable(name = "item_container",
joinColumns = #JoinColumn(name = "container_id"),
inverseJoinColumns = #JoinColumn(name = "item_id"))
private Set<Item> items;
//getter/setters
}
My problem is: when i have the type of collection Set<Item> items changed to List<Item>, the list contain every Item entity twice. I cant find the reason why, the hashCode and equals methods of Item are based only on the item_id field.
UPDATE:
Code for Item class:
#Entity
#Table(name = "item")
#Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class Item {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id")
private Long id;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "item_template_id", nullable = false)
private ItemTemplate itemTemplate;
#Column(name = "item_group")
#Enumerated(EnumType.STRING)
private ItemGroup itemGroup;
#Column(name = "amount")
private int amount;
//getter/setters
}

spring-data-rest, manytomany relation with join table

Is it possible to expose a manytomany relationship that uses a join entity (that contains extra data columns), below is my entities;
I'm trying to get 'purchases' to show in REST, I've put in 'products' as an example of a working REST mapping;
#Entity
public class User {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(fetch = FetchType.LAZY, cascade = CascadeType.ALL, targetEntity = Purchase.class, orphanRemoval = true)
#JoinColumn(name = "user_id", updatable = false)
private List<Purchase> purchases = new ArrayList<>();
#ManyToMany
#JoinColumn(name = "user_id", updatable = false)
private List<Product> products = new ArrayList<>();
}
#Entity
public class Product {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
private String name;
}
#Entity
public class Purchase implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
#ManyToOne
#JoinColumn(name = "user_id", referencedColumnName = "id")
private User user;
#ManyToOne(targetEntity = Prodect.class)
#JoinColumn(name = "product_id", referencedColumnName = "id")
private Product product;
#Column(name = "purchase_date")
private Date purchaseDate;
}
So if i send the REST call;
[GET http://localhost:8080/webapp/users/1]
It returns links for [http://localhost:8080/webapp/users/1/products] but not for [http://localhost:8080/webapp/users/1/purchases]
worked out what the issue was; I need to create a JpaRepository for the Purchase entity. Soon as I added that, the REST links for purchases are available.

Inheritance in JPA not working

I have a parent class:
#MappedSuperclass
public class BaseText implements Serializable {
#Column(name = "LOCALE_CODE", nullable = false)
private String localeCode;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Index
#Column(name = "LOCALIZED_TEXT_ID", nullable = false)
#ForeignKey
private LocalizedText localizedText;
//getters and setters
}
And one of the sub classes:
#Entity
#Table(name = "ASSESSMENT_TEXT")
#AttributeOverride(name = "localeCode", column = #Column(name = "LOCALE_CODE"))
#AssociationOverride(name = "localizedText", joinColumns = #JoinColumn(name = "LOCALIZED_TEXT_ID"))
public class AssessmentText extends BaseText {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private Long id;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#Index
#Column(name = "ASSESSMENT_ID", nullable = false)
#ForeignKey
private Assessment assessment;
//Getters and setters.
}
When I am trying to persist an object I get the following error:
org.apache.openjpa.persistence.ArgumentException: Superclass field "java.lang.Object.localizedText" is mapped in the metadata for subclass "com.my.com.AssessmentText", but is not a persistent field.
What is causing this and how to solve it?
I am using embedded derby database in JUnit and the JPA implementation is OpenJPA.
I am not sure that it is the solution, but I miss an #Inheritance annotation from AssessmentText:
#Entity
#Table(name = "ASSESSMENT_TEXT")
#Inheritance(strategy=InheritanceType.JOINED)
#AttributeOverride(name = "localeCode", column = #Column(name = "LOCALE_CODE"))
#AssociationOverride(name = "localizedText",
joinColumns = #JoinColumn(name = "LOCALIZED_TEXT_ID"))
public class AssessmentText extends BaseText {
I had the same issue, and solved it by adding the MappedSuperClass to the persistence.xml.
I know this is also in the comments somewhere, but the useful one was hidden and I think this should be an answer, not a comment to the question.

Categories