Spring Data JPA - find by name of field in collection - java

Two days of thinking and searching and any result - internet and documentation do not answer my question.
I need to construct a Jpa Repository method name to get a Set<Recipe> from database, by field of Ingredient ,ingrName. I am also using a join table and entity RecipeIngredient to store the amount of each ingredient in the Recipe using RecipeIngredient
Help, please.
Thanks!
I try to make something like this:
package com.pck.repository;
import com.pck.Recipe;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.Set;
#Repository
public interface RecipeRepository extends JpaRepository<Recipe, Integer> {
public Set<Recipe> findAllByRecipeIngrs_Ingredient_IngrNameIn(Collection<String> ingredientNames)
}
But it does'nt works.
Recipe:
package com.pck.entity;
import javax.persistence.*;
import java.util.*;
#Entity
#Table(name="recipes")
public class Recipe {
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true, mappedBy = "recipe")
private Set<RecipeIngredient> recipeIngrs;
public Recipe() {}
public Set<RecipeIngredient> getRecipeIngrs() {
return ingredients;
}
public void setRecipeIngrs(Set<RecipeIngredient> recipeIngrs) {
this.recipeIngrs = recipeIngrs;
}
// ... other fields, constructors, getters, setters
}
RecipeIngredient:
package com.pck.entity;
import javax.persistence.*;
import java.util.Objects;
#Entity
#Table(name="recipe_ingredient")
public class RecipeIngredient {
#JsonIgnore
#ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH})
#JoinColumn(name = "recipe_id")
private Recipe recipe;
#ManyToOne(cascade = {CascadeType.DETACH, CascadeType.MERGE, CascadeType.REFRESH})
#JoinColumn(name = "ingredient_id")
private Ingredient ingredient;
#Column(name = "ingredient_amount_grams")
private double ingredientAmountGrams;
public RecipeIngredient() {}
public Recipe getRecipe() {
return recipe;
}
public void setRecipe(Recipe recipe) {
this.recipe = recipe;
}
public Ingredient getIngredient() {
return ingredient;
}
public void setIngredient(Ingredient ingredient) {
this.ingredient = ingredient;
}
public double getIngredientAmountGrams() {
return ingredientAmountGrams;
}
public void setIngredientAmountGrams(double ingredientAmountGrams) {
this.ingredientAmountGrams = ingredientAmountGrams;
}
// ... other fields, constructors, getters, setters
}
Ingredient:
package com.pck.entity;
import javax.persistence.*;
import java.util.*;
#Entity
#Table(name="ingredient")
public class Ingredient{
#Column(name="ingr_name")
private String ingrName;
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.LAZY, orphanRemoval = true, mappedBy = "ingredient")
private Set<RecipeIngredient> recipeIngrs;
public Ingredient() {}
public String getIngrName() {
return ingrName;
}
public void setIngrName(String ingrName) {
this.ingrName = ingrName;
}
public Set<RecipeIngredient> getRecipeIngrs() {
return recipeIngrs;
}
public void setRecipeIngrs(Set<RecipeIngredient> recipeIngrs) {
this.recipeIngrs = recipeIngrs;
}
}

You can leverage jpql:
#Query("select r from Recipe r inner join r.recipeIngrs ri inner join ri.ingredient i where i.ingrName in :names")
public Set<Recipe> findAllByIngrNameIn(#Param("names") Collection<String> ingredientNames)

Related

How can we sort the result by date in Hibernate?

I am using Hibernate to fetch the JSON object from DB. I want that JSON object should contain all the details of the only top two of most recently created VGIs. But I don't know how to limit the result set and then sort on the basis of created date when no HQL query is used. It is fetching all the details from MySQL db.
import java.util.Date;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.Table;
#Entity
#Table(name = "vgi", catalog = "coworkee5")
public class VGI {
#ManyToOne
#JoinColumn(name = "employee_id", referencedColumnName = "id", insertable = false, updatable = false)
private Employees employees;
public Employees getEmployees() {
return employees;
}
public void setEmployees(Employees employees) {
this.employees = employees;
}
#OneToMany(cascade = CascadeType.ALL, targetEntity = VgiGoals.class, mappedBy = "vgi")
private List<VgiGoals> vgiGoals;
public List<VgiGoals> getVgiGoals() {
return vgiGoals;
}
public void setVgi_goals(List<VgiGoals> vgiGoals) {
this.vgiGoals = vgiGoals;
}
public VGI() {
}
#Id
#Column(name = "id")
private String id;
#Column(name = "title")
private String vgi;
#Column(name = "employee_id")
private String employee_id;
#Column(name = "created_on")
private String created;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getvgi() {
return vgi;
}
public void setvgi(String vgi) {
this.vgi = vgi;
}
public String getEmployee_id() {
return employee_id;
}
public void setEmployee_id(String employee_id) {
this.employee_id = employee_id;
}
public String getCreated() {
return created;
}
public void setCreated(String created) {
this.created = created;
}
}
Add #OrderBy annotation to sort like below.
#OneToMany(cascade = CascadeType.ALL, targetEntity = VgiGoals.class, mappedBy = "vgi")
#OrderBy("createdOn DESC")
private List<VgiGoals> vgiGoals;
NOTE : replace createdOn with your date field in VgiGoals class.
And for limit use below method in the query.
query.setFirstResult(1).setMaxResults(10);

JPA OneToMany MappedBy relationships

I have three classes, Site, GoupIP and IP
A Site has one or many GrouIPs.
A GroupIP has one or many IPs.
Here is the code:
Site
#Entity
#Table(name = "site")
public class Site implements Serializable {
private Set<GroupIp> groups;
#OneToMany(mappedBy = "site", fetch = FetchType.EAGER, cascade =CascadeType.ALL)
public Set<GroupIp> getGroups() {
return groups;
}
public void setGroups(Set<GroupIp> groups) {
this.groups = groups;
}
}
GroupIP
#Entity
#Table(name = "groupip")
public class GroupIp implements Serializable {
private Set<Ip> ips;
private Site site;
#ManyToOne(cascade = CascadeType.MERGE)
#JoinColumn(name = "site_id")
public Site getSite() {
return site;
}
#OneToMany(mappedBy = "groupip", fetch = FetchType.EAGER, cascade =CascadeType.ALL)
public Set<Ip> getIps() {
return ips;
}
public void setIps(Set<Ip> ips) {
this.ips= ips;
}
}
IP
#Entity
#Table(name = "ip")
public class Ip implements Serializable {
private GroupIp groupIp;
#ManyToOne(targetEntity = GroupIp.class,cascade = CascadeType.MERGE)
#JoinColumn(name = "groupip_id", nullable=false)
public GroupIp getGroupIp() {
return groupIp;
}
public void setGroupIp(GroupIp groupIp) {
this.groupIp = groupIp;
}
}
On GroupIp class, I m getting:
In attribute 'ips', the "mapped by" value 'groupip' cannot be resolved to an attribute on the target entity.
Whats wrong on my code ??
The mappedBy name that you have to put in the relationship is the name of the class attribute, not the table name.
So put #OneToMany(mappedBy = "groupIp",... (note the uppercase) instead of #OneToMany(mappedBy = "groupip",...

java persistence cross object method access

I am using an Employee and Company class as an example. In a Java persistence environment (Hibernate), when a bean is processing an Employee class with its' own Entity Manager, how can I call a Company class bean method when that object has its' own Entity Manager? DO I have to do a one to one mapping or can I call the method via the Application Config somehow?
Thanks.
You can use one to one mapping. I have implemented recently with product and team class. I have marked product as entity and team as entity. Below is the code which you would need to make it work. There are other ways to configure as well. In the below config you would require one table to store product, one table to store team and third team to store the productid and teamid.
***Product Class**
import java.io.Serializable;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.ManyToMany;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.OneToOne;
import javax.persistence.OrderBy;
import javax.persistence.Table;
import javax.persistence.Transient;
import org.codehaus.jackson.annotate.JsonBackReference;
import org.codehaus.jackson.annotate.JsonIgnoreProperties;
#Entity
#Table(name="Product")
#JsonIgnoreProperties({"hibernateLazyInitializer", "handler"})
public class Product implements Serializable{
/**
*
*/
private static final long serialVersionUID = -5392649457041674962L;
#Id
#Column(name="productId")
#GeneratedValue
private Long productId;
#Column(name="productName")
private String productName;
#Column(name="productHasVariations")
private String productHasVariations;
#Column(name="productImgPath")
private String productImgPath;
#Column(name="productDesc")
private String productDesc;
// //---------------------------------------item mapped to league------------------------------------------//
#JsonBackReference
#OneToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
#JoinTable(
name="ProductLeague",
joinColumns= #JoinColumn(name="productId"),
inverseJoinColumns = #JoinColumn(name="leagueId")
)
private League league;
// //--------------------------------------------------------------------------------------------------------//
//
// //---------------------------------------item mapped to category------------------------------------------//
#JsonBackReference
#OneToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinTable(
name="ProductCategory",
joinColumns= #JoinColumn(name="productId"),
inverseJoinColumns = #JoinColumn(name="categoryId")
)
private Category category;
// //--------------------------------------------------------------------------------------------------------//
//
// //---------------------------------------item mapped to team------------------------------------------//
#JsonBackReference
#OneToOne(cascade = CascadeType.ALL,fetch = FetchType.EAGER)
#JoinTable(
name="ProductTeam",
joinColumns= #JoinColumn(name="productId"),
inverseJoinColumns = #JoinColumn(name="teamId")
)
private Team team;
// //--------------------------------------------------------------------------------------------------------//
//
// //---------------------------------------item mapped to flags such as featured, sale, hot, new------------//
#JsonBackReference
#OneToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
#JoinTable(
name="ProductFlag",
joinColumns= #JoinColumn(name="productId"),
inverseJoinColumns = #JoinColumn(name="flagId")
)
private Flag flag;
//--------------------------------------------------------------------------------------------------------//
//
//---------------------------------------item mapped to sizes ------------//
#JsonBackReference
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
#OrderBy("sizeId asc")
#JoinTable(
name="ProductSize",
joinColumns= #JoinColumn(name="productId"),
inverseJoinColumns = #JoinColumn(name="sizeId")
)
private Set<Size> size;
// //--------------------------------------------------------------------------------------------------------//
//---------------------------------------item mapped to prices ------------//
#JsonBackReference
#OneToMany(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
#OrderBy("priceId asc")
#JoinTable(
name="ProductPrice",
joinColumns = { #JoinColumn(name="productId")
},
inverseJoinColumns = #JoinColumn(name="priceId")
)
private Set<Price> price;
// //--------------------------------------------------------------------------------------------------------//
// //--------------------------------------------------------------------------------------------------------//
//---------------------------------------item mapped to discounts ------------//
#JsonBackReference
#OneToOne(cascade = CascadeType.ALL,fetch = FetchType.LAZY)
#JoinTable(
name="ProductDiscount",
joinColumns = { #JoinColumn(name="productId")
},
inverseJoinColumns = #JoinColumn(name="discountId")
)
private Discount discount;
// //--------------------------------------------------------------------------------------------------------//
#Transient
private Long productQuantity;
#Transient
private String productPriceBeforeDiscount;
#Transient
private String productPriceAfterDiscount;
#Transient
private String productSelectedSize;
public Flag getFlag() {
return flag;
}
public void setFlag(Flag flag) {
this.flag = flag;
}
public Long getProductId() {
return this.productId;
}
public void setProductId(Long productId) {
this.productId = productId;
}
public String getProductName() {
return this.productName;
}
public void setProductName(String productName) {
this.productName = productName;
}
public void setProductHasVariations(String productHasVariations) {
this.productHasVariations = productHasVariations;
}
public String getProductHasVariations() {
return productHasVariations;
}
public void setLeague(League league) {
this.league = league;
}
public League getLeague() {
return league;
}
public Category getCategory() {
return this.category;
}
public void setCategory(Category category) {
this.category = category;
}
public Team getTeam() {
return this.team;
}
public void setTeam(Team team) {
this.team = team;
}
public String getProductImgPath() {
return productImgPath;
}
public void setProductImgPath(String productImgPath) {
this.productImgPath = productImgPath;
}
public void setProductDesc(String productDesc) {
this.productDesc = productDesc;
}
public String getProductDesc() {
return productDesc;
}
public void setSize(Set<Size> size) {
this.size = size;
}
public Set<Size> getSize() {
return size;
}
public void setPrice(Set<Price> price) {
this.price = price;
}
public Set<Price> getPrice() {
return price;
}
public void setProductQuantity(Long productQuantity) {
this.productQuantity = productQuantity;
}
public Long getProductQuantity() {
return productQuantity;
}
public void setDiscount(Discount discount) {
this.discount = discount;
}
public Discount getDiscount() {
return discount;
}
public void setProductPriceAfterDiscount(String productPriceAfterDiscount) {
this.productPriceAfterDiscount = productPriceAfterDiscount;
}
public String getProductPriceAfterDiscount() {
return productPriceAfterDiscount;
}
public void setProductPriceBeforeDiscount(String productPriceBeforeDiscount) {
this.productPriceBeforeDiscount = productPriceBeforeDiscount;
}
public String getProductPriceBeforeDiscount() {
return productPriceBeforeDiscount;
}
public void setProductSelectedSize(String productSelectedSize) {
this.productSelectedSize = productSelectedSize;
}
public String getProductSelectedSize() {
return productSelectedSize;
}
}
****TeamClass********
import java.util.Set;
import javax.persistence.CascadeType;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.JoinTable;
import javax.persistence.OneToMany;
import javax.persistence.Table;
import java.io.Serializable;
#Entity
#Table(name="Team")
public class Team implements Serializable{
/**
*
*/
private static final long serialVersionUID = 5969057417203282157L;
#Id
#Column(name="teamId")
#GeneratedValue
private Integer teamId;
#Column(name="teamName")
private String teamName;
#OneToMany(cascade=CascadeType.ALL,fetch=FetchType.LAZY)
#JoinTable(
name="ProductTeam",
joinColumns = #JoinColumn(name="teamId"),
inverseJoinColumns = #JoinColumn(name="productId")
)
public Set<Product> product;
public Integer getTeamId() {
return this.teamId;
}
public void setTeamId(Integer teamId) {
this.teamId = teamId;
}
public String getTeamName() {
return teamName;
}
public void setTeamName(String teamName) {
this.teamName = teamName;
}
public Set<Product> getProduct() {
return product;
}
public void setProduct(Set<Product> product) {
this.product = product;
}
}

Hibernate Use of #OneToMany or #ManyToMany targeting an unmapped class where the collection is pointing to a super class that doesn't have a table

I am having this error:
sesion.org.hibernate.AnnotationException: Use of #OneToMany or #ManyToMany targeting an unmapped class: entities.Elaborado.componentes[entities.Producto]
This is the super class:
package entities;
import javax.persistence.*;
#MappedSuperclass
#Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class Producto {
#Id
protected Integer numero;
protected String descripcion;
public Integer getNumero() {
return numero;
}
public void setNumero(Integer numero) {
this.numero = numero;
}
public String getDescripcion() {
return descripcion;
}
public void setDescripcion(String descripcion) {
this.descripcion = descripcion;
}
And the subclass is:
package entities;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.*;
#Entity
#Table(name="elaborados")
public class Elaborado extends Producto {
private float precioVenta;
private int porcentajeGanancia;
#ManyToOne
private Unidad unidad;
#OneToMany
#JoinTable(
name="compuestoDe",
joinColumns = #JoinColumn( name="codProductoE"),
inverseJoinColumns = #JoinColumn( name="codProductoSM")
)
private List<Producto>componentes;
public float getPrecioVenta() {
return precioVenta;
}
public void setPrecioVenta(float precioVenta) {
this.precioVenta = precioVenta;
}
public int getPorcentajeGanancia() {
return porcentajeGanancia;
}
public void setPorcentajeGanancia(int porcentajeGanancia) {
this.porcentajeGanancia = porcentajeGanancia;
}
public Unidad getUnidad() {
return unidad;
}
public void setUnidad(Unidad unidad) {
this.unidad = unidad;
}
public List<Producto> getComponentes() {
return componentes;
}
public void setComponentes(ArrayList<Producto> componentes) {
this.componentes = componentes;
}
This is an exercise I have to resolve for Collage. The problem is that I have some restrictions. If I add #Entity to the super class it asks for the table Producto which I don't have and I can't create.
I can't also change the inheritance type to SINGLE_TABLE because the teacher gave me 2 different tables for subclasses and 0 for the superclass.
I am sorry that the names of the classes and attributes are on Spanish. If you need me to translate them let me know.
From the spec:
A mapped superclass, unlike an entity, is not queryable and must not be passed as an argument to
EntityManager or Query operations.
But querying for Producto is what this code does:
#OneToMany
#JoinTable(
name="compuestoDe",
joinColumns = #JoinColumn( name="codProductoE"),
inverseJoinColumns = #JoinColumn( name="codProductoSM")
)
private List<Producto>componentes;
You'll have to change #MappedSuperclass to #Entity, and keep #Inheritance(strategy=InheritanceType.TABLE_PER_CLASS) to match the two tables you have.
InheritanceType.TABLE_PER_CLASS will need one table per concrete class, so no table is required for the abstract class Producto.

Hibernate ManyToMany deleted object would be re-saved by cascade

For some reason I can't delete an object that belongs to a many to many relationship. I get the following error:
Exception in thread "main" org.hibernate.ObjectDeletedException: deleted object would be re-saved by cascade (remove deleted object from associations): [edu.cs157b.hibernate.AppointmentRequest#11]
at org.hibernate.internal.SessionImpl.forceFlush(SessionImpl.java:1232)
Here are my three classes that map the many to many relationship. Essentially, Doctor has many Patients through AppointmentRequest & vice versa. Here are the classes
Doctor
package edu.cs157b.hibernate;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.*;
#Entity
#Table(name="DOCTOR_INFO")
#NamedQueries (
{
#NamedQuery(name = "Doctor.getAll", query = "from Doctor"),
#NamedQuery(name = "Doctor.findByName", query = "from Doctor where name = :name")
}
)
public class Doctor implements Person {
private int id;
private String name;
private Specialty specialty;
private List<AppointmentRequest> appointmentRequests = new ArrayList<AppointmentRequest>();
#Id
#GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Column(unique=true)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#ManyToOne (fetch = FetchType.EAGER, cascade= CascadeType.PERSIST)
#JoinColumn(name="specialty_id")
public Specialty getSpecialty() {
return specialty;
}
public void setSpecialty(Specialty specialty) {
this.specialty = specialty;
}
#OneToMany(mappedBy="doctor", targetEntity = AppointmentRequest.class,
fetch=FetchType.EAGER, orphanRemoval=true, cascade= CascadeType.ALL)
public List<AppointmentRequest> getAppointmentRequests() {
return this.appointmentRequests;
}
public void setAppointmentRequests(List<AppointmentRequest> appointmentRequests) {
this.appointmentRequests = appointmentRequests;
}
#Transient
public List<Patient> getPatients() {
List<Patient> patients = new ArrayList<Patient>();
for(AppointmentRequest appointment:appointmentRequests) {
patients.add(appointment.getPatient());
}
return patients;
}
}
Patient
package edu.cs157b.hibernate;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.*;
#Entity
#Table(name="PATIENT_INFO")
#NamedQueries (
{
#NamedQuery(name = "Patient.getAll", query = "from Patient"),
#NamedQuery(name = "Patient.findByName", query = "from Patient where name = :name")
}
)
public class Patient implements Person {
private int id;
private String name;
private String medical_record;
private List<AppointmentRequest> appointmentRequests = new ArrayList<AppointmentRequest>();
public String getMedical_record() {
return medical_record;
}
public void setMedical_record(String medical_record) {
this.medical_record = medical_record;
}
#Id
#GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#Column(unique=true)
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#OneToMany(mappedBy="patient", targetEntity = AppointmentRequest.class,
fetch=FetchType.EAGER, orphanRemoval=true, cascade= CascadeType.ALL)
public List<AppointmentRequest> getAppointmentRequests() {
return this.appointmentRequests;
}
public void setAppointmentRequests(List<AppointmentRequest> appointmentRequests) {
this.appointmentRequests = appointmentRequests;
}
#Transient
public List<Doctor> getDoctors() {
List<Doctor> doctors = new ArrayList<Doctor>();
for(AppointmentRequest appointment:appointmentRequests) {
doctors.add(appointment.getDoctor());
}
return doctors;
}
}
ApppointmentRequest
package edu.cs157b.hibernate;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.TimeZone;
import javax.persistence.*;
import org.hibernate.annotations.Type;
import java.util.List;
#Entity
#Table(name="APPOINTMENT_REQUEST")
#NamedQueries (
{
#NamedQuery(name = "AppointmentRequest.getAll", query = "from AppointmentRequest"),
#NamedQuery(name = "AppointmentRequest.findByDoctorId", query = "from AppointmentRequest where doctor_id = :doctor_id"),
#NamedQuery(name = "AppointmentRequest.findByPatientId", query = "from AppointmentRequest where patient_id = :patient_id"),
#NamedQuery(name = "AppointmentRequest.findByID", query = "from AppointmentRequest where id = :id")
}
)
public class AppointmentRequest {
private int id;
private Doctor doctor;
private Patient patient;
private boolean fulfilled = false;
private Calendar time;
private final SimpleDateFormat timestampFormat = new SimpleDateFormat("MM/dd/yyyy h a");
public Calendar getTime() {
return time;
}
#Transient
public String getFormattedTime() {
String result = timestampFormat.format(time.getTime());
return result;
}
public void setTime(Calendar time) {
this.time = time;
}
public boolean isFulfilled() {
return fulfilled;
}
public void setFulfilled(boolean fulfilled) {
this.fulfilled = fulfilled;
}
#Id
#GeneratedValue
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
#ManyToOne (fetch = FetchType.EAGER, cascade= CascadeType.PERSIST)
#JoinColumn(name="doctor_id")
public Doctor getDoctor() {
return doctor;
}
public void setDoctor(Doctor doctor) {
this.doctor = doctor;
}
#ManyToOne (fetch = FetchType.EAGER, cascade= CascadeType.PERSIST)
#JoinColumn(name="patient_id")
public Patient getPatient() {
return patient;
}
public void setPatient(Patient patient) {
this.patient = patient;
}
}
Doctor Delete Method
public void deleteDoctor(String doctor_name) {
Session session = sessionFactory.openSession();
Doctor doctor = new Doctor();
try {
session.beginTransaction();
Query query = session.getNamedQuery("Doctor.findByName");
query.setString("name", doctor_name);
doctor = (Doctor) query.uniqueResult();
if(doctor == null) {
throw new NullPointerException();
}
List<AppointmentRequest> appointments = doctor.getAppointmentRequests();
for(AppointmentRequest appointment:appointments) {
appointment.setDoctor(null);
}
session.delete(doctor);
session.getTransaction().commit();
}
finally {
session.close();
}
}
What this exception really means is you are telling Hibernate to remove object from database but at the same time this object still exist (that means still exist in java or database) in mapped collection via Persistent entity which has CascadeType.PERSIST annotated over it.
It's like having something tied through elastic rubber on the window and then poke it hoping it will drop. Hibernate is smart it is saving you from doing meaningless stuff, it tells you what to do
deleted object would be re-saved
by cascade (remove deleted object from associations)
Sine you are doing appointment.setDoctor(null); it will remove object from collection (only in java as you are not explicitly or implicitly updating appointment).You have CascadeType.PERSIST on doctor that means when hibernate is going to commit the transaction it will find that appointment has association to doctor you just deleted that means if you remove that doctor from table, hibernate has to go and create same doctor as you have not told him to make appropriate changes in appointment as he follows all the entity rules set by you. Since hibernate is smart he knows this and he will throw a exception for you saying don't be an oxymoron and do the right thing.
Now there are more than one solution that I can think of here
Use cascade={CascadeType.PERSIST,CascadeType.REMOVE} or cascade=CascadeType.ALL on getDoctor() in AppointmentRequest
As mentioned in hibernate document here
It doesn't usually make sense to enable cascade on a #ManyToOne or
#ManyToMany association. Cascade is often useful for #OneToOne and
#OneToMany associations.
remove cascade from getDoctor
Since you have FetchType.EAGER on getDoctor() with cascade specified it is little complicated for me interpret the behaviour of hibernate but in this questions they have solved by using FetchType.LAZY am not sure if it will work out for you.
You can do session.saveOrUpdate(appointment) on all the AppointmentRequest which has this doctor and then go for session.delete(doctor);
Hope you this would solve your problem.

Categories