(Hibernate) Can't add entity with many-to-many relationship - spring

Basically, my goal is to have product and factory entity where relationship is many-to-many.
Factory must have some product, while product is not necessary associated with a factory. The problem is that when I try to add Product entity I get this error.
Caused by: org.hsqldb.HsqlException: integrity constraint violation: NOT NULL check constraint; SYS_CT_10103 table: PRODUCT column: FACT_ID
This is my Application class. I want to be able to save product to repository without giving it factory list but I got that error, so I tried adding what's in the comment but the same error remains.
#SpringBootApplication
public class Application implements CommandLineRunner {
#Autowired
private ProductRepo prodRepo;
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
#Override
#Transactional
public void run(String... strings) throws Exception {
Product one = new Product();
/*
List<Product> productList= new ArrayList<Product>();
productList.add(one);
SomeFactory factory_one = new SomeaFactory("one", productList);
List<Factory> factoryList = new ArrayList<Factory>();
factoryList.add(factory_one);
one.setFactoryList(factoryList);
*/
prodRepo.save(one);
}
Product.java
#Entity
#Table(name="PRODUCT")
public class Product {
private int id;
private List<Factory> factoryList = null;
public Product() {}
#Id
#Column(name = "PROD_ID")
#GeneratedValue(strategy = GenerationType.IDENTITY)
public int getId() { return id; }
public void setId(int id) { this.id = id; }
#ManyToMany(fetch = FetchType.LAZY, mappedBy = "productList", targetEntity = Factory.class)
public List<Factory> getFactoryList() {
return factoryList;
}
public void setFactoryList(List<Factory> f) {
this.factoryList = f;
}
}
SomeFactory.java
#Entity
#Table(name ="SOME_FACT")
public class SomeFactory extends Factory {
private String name;
private List<Product> productList;
public SomeFactory() {}
public SomeFactory(String name, List<Product> products) {
this.name = name;
this.productList = products;
}
#Column(name = "FACT_NAME", nullable = false)
public String getName() { return name; }
public void setName(String name) { this.name = name; }
#ManyToMany
#JoinTable(name = "PRODUCT",
joinColumns = #JoinColumn(name = "FACT_ID", nullable = true),
inverseJoinColumns = #JoinColumn(name = "PROD_ID", nullable = false))
public List<Product> getProductList() {
return productList;
}
public void setProductList(List<Product> products) {
this.productList = products;
}
}
Factory.java
#Entity
#Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class Factory {
int id;
public Factory() {}
#Id
#Column(name="FACT_ID")
#GeneratedValue(strategy = GenerationType.TABLE)
int getId() { return id; }
void setId(int id) { this.id = id; }
}

Related

I cannot remove the association in Many To Many bidirectional hibernate

I can't delete the association in the courses_student table of course and student when trying to delete a course, even if I want to cascade delete it does not work for me since there is a foreign key in courses_student, I don't know what the problem is.
I have also tried to remove the association in the courses_student table doing a update.but nothing happened.
DAO
#Override
public boolean deleteCourse(int id) {
Session currentSession = entityManager.unwrap(Session.class);
Courses course = currentSession.load(Courses.class, id);
for(Student student : course.getEstudiantes()) {
course.removeStudent(student);
}
currentSession.delete(course);
if(course.getId() == null)
return true;
else
return false;
}
Courses entity
#Entity
#Table(name = "courses")
public class Courses {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private Integer id;
#Column
private String nombre;
#Column
private String descripcion;
#ManyToMany(mappedBy = "courses")
private Set<Student> Estudiantes = new HashSet<Student>();
public Courses() {
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
public Set<Student> getEstudiantes() {
return Estudiantes;
}
public void setEstudiantes(Set<Student> estudiantes) {
Estudiantes = estudiantes;
}
public void removeStudent(Student student) {
this.Estudiantes.remove(student);
student.getCourses().remove(this);
}
}
Student entity
#Entity
#Table(name = "students")
public class Student {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name="id")
private Integer id;
#Column
private String nombre;
#Column
private String apellido;
#Column
private String dni;
#ManyToMany(fetch=FetchType.LAZY,
cascade= {CascadeType.PERSIST, CascadeType.MERGE,
CascadeType.DETACH, CascadeType.REFRESH})
#JoinTable(
name="courses_students",
joinColumns=#JoinColumn(name="id_student"),
inverseJoinColumns=#JoinColumn(name="id_course")
)
private Set<Courses> courses = new HashSet<Courses>();
public Student() {
}
public Student(String nombre, String apellido, String dni) {
this.nombre = nombre;
this.apellido = apellido;
this.dni = dni;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getNombre() {
return nombre;
}
public void setNombre(String nombre) {
this.nombre = nombre;
}
public String getApellido() {
return apellido;
}
public void setApellido(String apellido) {
this.apellido = apellido;
}
public String getDni() {
return dni;
}
public void setDni(String dni) {
this.dni = dni;
}
public Set<Courses> getCourses() {
return courses;
}
public void setCourses(Set<Courses> courses) {
this.courses = courses;
}
}
EDIT:
apparently it works for me, trying to update since owner side.
#Override
public boolean deleteCourse(int id) {
Session currentSession = entityManager.unwrap(Session.class);
Courses course = currentSession.load(Courses.class, id);
for(Student student : course.getEstudiantes()) {
student.removeCourse(course);
}
currentSession.update(course);
if(course.getId() == null)
return true;
else
return false;
}
It seems to me that you are missing a cascade configuration of your #ManyToMany annotation on Courses which is actually the one you are updating / deleting. Try the following:
#ManyToMany(mappedBy = "courses", cascade = {CascadeType.PERSIST, CascadeType.MERGE})
private Set<Student> Estudiantes = new HashSet<Student>();
Also, given that you have a bi-directional relationship, you should also remove the Course from each Student courses property.

Spring JPA hibernate how to persist children (remove, add, or update) from #OneToMany parent column?

I'm trying to solve this problem since a while and I haven't achieved a 100% solution.
First of all I have to describe my problem. I'm developping a restaurant application, and amoung the Entities, I have the Entity Ingredient and as you know Ingredient can consist of other Ingredient with a specific quantity. So I created an Entity SubIngredient with an Embedded Id.
And to persist subIngredients list I tried a combinations of Cascade and orphanRemoval, each combination worked for some operation but not for the others.
I started by using CascadeType.ALL and the new subIngredient persisted successfuly from the #OneToMany propertiy, But if I try to remove an subIngredient from the subIngredients list and save this error appear.
java.lang.StackOverflowError: null
at com.mysql.cj.NativeSession.execSQL(NativeSession.java:1109) ~[mysql-connector-java-8.0.23.jar:8.0.23]......
I loked in the net for a solution and I find the I have to use orphanremoval = true I tried it but it didn't work until I changed cascade from CascadeType.ALL to CascadeType.PERSIST. But this one make the persistance of new SubIngredient this error aprear
Caused by: javax.persistence.EntityNotFoundException: Unable to find com.example.Resto.domain.SubIngredient with id com.example.Resto.domain.SubIngredientKey#51b11186........
These are my Enities:
#Entity
public class Ingredient {
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY)
#Column(name="ID")
private long id;
#NotNull
#Column(unique=true)
private String name;
private String photoContentType;
#Lob
private byte[] photo;
#JsonIgnoreProperties({"photoContentType","photo"})
#ManyToOne
private IngredientType ingredientType;
#OneToMany(mappedBy = "embId.ingredientId", fetch = FetchType.EAGER,
cascade = CascadeType.ALL /*or orphanRemoval = true, cascade = CascadeType.PERSIST*/ )
private Set<SubIngredient> subIngredients = new HashSet<SubIngredient>();
getters and setters.....
And
#Entity
#AssociationOverrides({
#AssociationOverride(name = "embId.ingredientId",
joinColumns = #JoinColumn(name = "ING_ID")),
#AssociationOverride(name = "embId.subIngredientId",
joinColumns = #JoinColumn(name = "SUB_ING_ID")) })
public class SubIngredient {
#EmbeddedId
private SubIngredientKey embId = new SubIngredientKey();
private double quantity;
getters and setters....
And
#Embeddable
public class SubIngredientKey implements Serializable{
#ManyToOne(cascade = CascadeType.ALL)
private Ingredient ingredientId;
#ManyToOne(cascade = CascadeType.ALL)
private Ingredient subIngredientId;
getters and setters...
The stackoverflow happen because you use a Set<> with Hibernate. When Hibernate retrieves the entities from your DB, it will fill up the Set<> with each entities. In order to that, hashode/equals will be used to determine wether or not the entitie is already present in the Set<>. By default, when you call the hashcode of Ingredient, this happen:
hashcode Ingredient -> hashcode SubIngredient -> hashcode Ingredient
which will result in an infinite call of hashcode method. That's why you have a stackoverflow error.
The same thing will happen with equals/toString.
So to avoid such an issue, it's best to override hashcode, equals and toString.
I have solved the problem by making some changes to may Entities and override equals/hashcode methods thanks Pilpo.
#Embeddable
public class SubIngredientKey implements Serializable{
private Long ingredientId;
private Long subIngredientId;
/**
* #return the ingredientId
*/
#Override
public int hashCode() {
return Objects.hash(ingredientId, subIngredientId);
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof SubIngredientKey)) {
return false;
}
SubIngredientKey other = (SubIngredientKey) obj;
return Objects.equals(ingredientId, other.ingredientId)
&& Objects.equals(subIngredientId, other.subIngredientId);
}
}
#Entity
public class SubIngredient {
#EmbeddedId
private SubIngredientKey embId = new SubIngredientKey();
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("ingredientId")
private Ingredient ingredient;
#ManyToOne(fetch = FetchType.LAZY)
#MapsId("subIngredientId")
private Ingredient subIngredient;
private double quantity;
#JsonIgnore
public SubIngredientKey getId() {
return embId;
}
public void setId(SubIngredientKey id) {
this.embId = id;
}
#JsonIgnoreProperties({"subIngredients","photo","photoContentType","ingredientType"})
public Ingredient getIngredient() {
return ingredient;
}
public void setIngredient(Ingredient ingredient) {
this.ingredient = ingredient;
}
#JsonIgnoreProperties({"subIngredients","photo","photoContentType","ingredientType"})
public Ingredient getSubIngredient() {
return subIngredient;
}
public void setSubIngredient(Ingredient subIngredient) {
this.subIngredient = subIngredient;
}
public double getQuantity() {
return quantity;
}
public void setQuantity(double quantity) {
this.quantity = quantity;
}
#Override
public String toString() {
return "subIngredient= " + getSubIngredient().getName() + " , quantity= " + getQuantity();
}
#Override
public int hashCode() {
return Objects.hash(ingredient,subIngredient);
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof SubIngredient)) {
return false;
}
SubIngredient other = (SubIngredient) obj;
return Objects.equals(ingredient, other.ingredient) && Objects.equals(subIngredient, other.subIngredient);
}
}
#Entity
public class Ingredient {
#Id
#GeneratedValue( strategy = GenerationType.IDENTITY)
#Column(name="ID")
private long id;
#NotNull
#Column(unique=true)
private String name;
private String photoContentType;
#Lob
private byte[] photo;
#JsonIgnoreProperties({"photoContentType","photo"})
#ManyToOne
private IngredientType ingredientType;
#OneToMany(mappedBy = "embId.ingredientId", fetch = FetchType.EAGER, cascade =
CascadeType.ALL, orphanRemoval = true)
private Set<SubIngredient> subIngredients = new HashSet<SubIngredient>();
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getName() {
return this.name;
}
public void setName(String name) {
this.name = name;
}
public String getPhotoContentType() {
return photoContentType;
}
public void setPhotoContentType(String photoContentType) {
this.photoContentType = photoContentType;
}
public byte[] getPhoto() {
return photo;
}
public void setPhoto(byte[] photo) {
this.photo = photo;
}
public IngredientType getIngredientType() {
return this.ingredientType;
}
public void setIngredientType(IngredientType ingredientType) {
this.ingredientType = ingredientType;
}
public Set<SubIngredient> getSubIngredients() {
return subIngredients;
}
public void setSubIngredients(Set<SubIngredient> subIngredients) {
this.subIngredients = subIngredients;
}
public void addSubIngredient(SubIngredient subIngredient) {
this.subIngredients.add(subIngredient);
}
#Override
public String toString() {
String subIngsText = "";
for(var subIngredient:this.subIngredients) {
subIngsText = subIngsText + ", " + subIngredient.toString();
}
return "{id= "+id+",name=" + name +", ingredients="+subIngsText+"}";
}
#Override
public int hashCode() {
return Objects.hash(name);
}
#Override
public boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!(obj instanceof Ingredient)) {
return false;
}
Ingredient other = (Ingredient) obj;
return Objects.equals(name, other.name);
}
}

Many to One Relationship with #IdClass

Using Spring Data JPA & Hibernate, I am saving an object Company, that has 0 to Many AccountMapping. The AccountMappings Primary Key is a composite of a String accountNumber and the Company Primary Key. When I save a new company the COMP_NUM from the Company Object is not set into the AccountMapping object. When I use long companyNumber it is zero, and Long it is NUM. Hibernate is executing the insert statement first, but how to get it to set the primary key from company into child object ?
#Entity
#Table(name = "COMPANY")
public class Company implements Serializable {
#Id
#Column(name = "COMP_NUM")
#SequenceGenerator(name = "comp_num_seq", sequenceName = "comp_num_seq", allocationSize = 1)
#GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "comp_num_seq")
private long number;
#OneToMany(mappedBy = "companyNumber", fetch = FetchType.LAZY, cascade = CascadeType.ALL)
private List<AccountMapping> accountMappings;
public Company() {
super();
}
public long getNumber() {
return this.number;
}
public void setNumber(long id) {
this.number = id;
}
public List<AccountMapping> getAccountMappings() {
return accountMappings;
}
public void setAccountMappings(List<AccountMapping> accountMappings) {
this.accountMappings = accountMappings;
}
}
#Entity
#IdClass(value = AccountMappingPK.class)
#Table(name = "ACCOUNT_MAPPING")
public class AccountMapping implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "ACCNT_NUM")
private String accountNumber;
#Id
#Column(name = "COMP_NUM")
private Long companyNumber;
#Column(name = "IS_PRIMARY")
private Boolean isPrimary;
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNumber(String accountNumber) {
this.accountNumber = accountNumber;
}
public Long getCompanyNumber() {
return companyNumber;
}
public void setCompanyNumber(Long companyNumber) {
this.companyNumber = companyNumber;
}
public Boolean getIsPrimary() {
return isPrimary;
}
public void setIsPrimary(Boolean isPrimary) {
this.isPrimary = isPrimary;
}
}
public class AccountMapping implements Serializable {
#Column(name = "EA_ACCNT_NUM", nullable = false)
private String accountNumber;
#Column(name = "COMP_NUM", nullable = false)
private Long companyNumber;
public AccountMapping() {
// default constructor
}
public String getAccountNumber() {
return accountNumber;
}
public void setAccountNum(String accountNumber) {
this.accountNumber = accountNumber;
}
public Long getCompanyNumber() {
return companyNumber;
}
public void setCompanyNumber(Long companyNumber) {
this.companyNumber = companyNumber;
}
#Override
public boolean equals(Object obj) {
if (obj instanceof AccountMappingPK) {
AccountMappingPK accntPk = (AccountMappingPK) obj;
if (!(accountNumber.equals(accntPk.getAccountNumber()))) {
return false;
}
if (!(accntPk.getCompanyNumber() == (companyNumber))) {
return false;
}
return true;
}
return false;
}
#Override
public int hashCode() {
int hash = (accountNumber == null ? 1 : accountNumber.hashCode());
return (int) (hash * companyNumber);
}
}
#Entity
#IdClass(value = AccountMappingPK.class)
#Table(name = "ACCOUNT_MAPPING")
public class AccountMapping implements Serializable {
#Id
#Column(name = "ACCNT_NUM")
private String accountNumber;
#Id
#ManyToOne
#JoinColumn(name = "COMP_NUM")
private Company company;
...
}
// No annotations in this class
public class AccountMappingPK implements Serializable {
private String accountNumber;
private Company company;
...
// All the getter/setter, constructors, and so on ...
}
The Hibernate ORM documentation has more details about mapping with #IdClass: See Example 134. IdClass with #ManyToOne

Inconsistent datatypes: expected TIMESTAMP got NUMBER

I am using Hibernate 5.3.10 as my ORM in the Spring boot based project. Suppose that we have the following entities:
#Entity
#Table(name = "parent")
#Inheritance(strategy = InheritanceType.JOINED)
public class Parent {
#EmbeddedId
private EmbId id;
public Id getId() { return id; }
public void setId(Id id) { this.id = id; }
}
#Entity
#Table(name = "child")
#PrimaryKeyJoinColumns({
#PrimaryKeyJoinColumn(name = "id"),
#PrimaryKeyJoinColumn(name = "date_time")
})
public class Child {
#EmbeddedId
private EmbId id;
private String name;
public Id getId() { return this.id; }
public void setId(Id id) { this.id = id; }
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
}
and the embeddable key as follow:
#Embeddable
public class EmbId {
#Column(name = "id")
private Long id;
#Column(name = "date_time")
private Date dateTime;
public Long getId() { return this.id; }
public void setId(Long id) { this.id = id; }
public Date getDateTime() { return this.dateTime; }
public void setDateTime(Date dateTime) { this.dateTime = dateTime; }
}
When I want to polymorphic query on Child entity, the JPA faces with ORA-00932 Inconsistent datatypes: expected TIMESTAMP got NUMBER and the following sql have seen in the console:
select child0_.id as date_time1_25_0, child0_.date_time as id2_25_0, child0_1_.name as name3_3_0 from my_schema.child child0_ inner join my_schema.parent child0_1_ on child0_.id = child0_1_.date_time and child0_.date_time = child0_1_.id
It seems that the equality of IDs is displaced. What happened and what should I do to resolve that?
Thanks in advance.
Fortunately, I found that adding referencedColumnName attribute to #PrimaryKeyJoinColumn could be guide hibernate to use the IDs in the right place.
So, the Child class should be as follow:
#Entity
#Table(name = "child")
#PrimaryKeyJoinColumns({
#PrimaryKeyJoinColumn(name = "id"),
#PrimaryKeyJoinColumn(name = "date_time")
})
public class Child {
#EmbeddedId
private EmbId id;
private String name;
public Id getId() { return this.id; }
public void setId(Id id) { this.id = id; }
public String getName() { return this.name; }
public void setName(String name) { this.name = name; }
}

Spring JPARepository querying many to many intersection table

I have 3 entity classes as follows (Example taken from https://hellokoding.com/jpa-many-to-many-extra-columns-relationship-mapping-example-with-spring-boot-maven-and-mysql/)
Book class
#Entity
public class Book{
private int id;
private String name;
private Set<BookPublisher> bookPublishers;
public Book() {
}
public Book(String name) {
this.name = name;
bookPublishers = new HashSet<>();
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(mappedBy = "book", cascade = CascadeType.ALL, orphanRemoval = true)
public Set<BookPublisher> getBookPublishers() {
return bookPublishers;
}
public void setBookPublishers(Set<BookPublisher> bookPublishers) {
this.bookPublishers = bookPublishers;
}
}
Publisher class
#Entity
public class Publisher {
private int id;
private String name;
private Set<BookPublisher> bookPublishers;
public Publisher(){
}
public Publisher(String name){
this.name = name;
}
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(mappedBy = "publisher")
public Set<BookPublisher> getBookPublishers() {
return bookPublishers;
}
public void setBookPublishers(Set<BookPublisher> bookPublishers) {
this.bookPublishers = bookPublishers;
}
}
Intersection Table
#Entity
#Table(name = "book_publisher")
public class BookPublisher implements Serializable{
private Book book;
private Publisher publisher;
private Date publishedDate;
#Id
#ManyToOne
#JoinColumn(name = "book_id")
public Book getBook() {
return book;
}
public void setBook(Book book) {
this.book = book;
}
#Id
#ManyToOne
#JoinColumn(name = "publisher_id")
public Publisher getPublisher() {
return publisher;
}
public void setPublisher(Publisher publisher) {
this.publisher = publisher;
}
#Column(name = "published_date")
public Date getPublishedDate() {
return publishedDate;
}
public void setPublishedDate(Date publishedDate) {
this.publishedDate = publishedDate;
}
}
I want to query 2 things,
Get list of books belonging to a particular publisher e.g. get all books associated with publisher 100
Get list of books not associated with a particular publisher e.g. get all books not associated with publisher 100
I want to achieve this using a simple JPARepository query if possible like findByXYZIn(...) etc.
Please let me know if querying a many to many relation is possible using JPA repository queries and if yes, whether I can do it directly or would it require any changes in the entity classes
In BookRepository
Get publisher's books
findBooksByBookPublishersPublisherId(Long publisherId)
Get books not published by publisher
findBooksByBookPublishersPublisherIdNot(Long publisherId)
IMHO Publication is much more apropriate name then BookPublisher in your case as Publisher by itself could be BookPublisher (a published that publishing books)
I'm not sure if you can make it just by method name. But you definitely can use JPA query. Something like this: "SELECT b FROM Book b JOIN b.bookPublishers bp JOIN bp.publisher p WHERE p.id = ?1". and with not equal for the second case
Well you can use named Queries to fulfill your requirements:
#Query("select b from Book b where b.publisher.idd = ?1")
Book findByPublisherId(int id);
#Query("select b from Book b where b.publisher.idd <> ?1")
Book findByDifferentPublisherId(int id);
Take a look at Using #Query Spring docs for further details.

Resources