Why Hibernate is running Update instead of Delete - spring

i am trying to delete a row from my child entity . Loan is my parent class and DVLoanParticipants is child class. My Loan Object is looking like this to run delete .
Loans(loanId=196777, dvLoanParticipantsMap={})
My Expectation is to delete the row from DVLoanParticipants , but hibernate trying to run an update statement and failing.
#Entity
public class Loans {
#Id
#Column(name = "LOAN_ID")
private Long loanId;
#OneToMany(targetEntity = DVLoanParticipants.class, cascade =
CascadeType.ALL,orphanRemoval=true )
#JoinColumn(name = "LOAN_ID")
#MapKey(name = "dvLoanParticipantsId.dvpParticipantName")
#LazyCollection(LazyCollectionOption.FALSE)
private Map<String, DVLoanParticipants> dvLoanParticipantsMap;
}
#Entity
public class DVLoanParticipants implements Serializable{
private static final long serialVersionUID = 1L;
#EmbeddedId
private DVLoanParticipantsId dvLoanParticipantsId;
}
#Embeddable
public class DVLoanParticipantsId implements Serializable {
private static final long serialVersionUID = 1L;
#Column(name = "LOAN_ID")
private Long loanId;
#Column(name = "DVP_PARTICIPANT_NAME")
private String dvpParticipantName;
}
The Update Statement is trying to execute is
update DV_LOAN_PARTICIPANTS set LOAN_ID=null where LOAN_ID=?
But why its running Update statement ? what i can do to run the delete statement?

CascadeType.ALL (specifically CascadeType.REMOVE here) doesn't mean the children will be removed if removed from the collection. It means that every state transition of the parent is cascaded to his child. Children are only removed automatically when the parent is deleted.
So by deleting the DVLoanParticipants in the collection, you are not asking Hibernate to delete the entity. You are asking him to update the record to point to no Loans, which results in a SQL update with NULL in the foreign key.
You need to had #OneToMany(orphanRemoval=true) for Hibernate to remove a child when he is separated from his parents (it must be detached from all parents for it to work).

Related

Not able to delete child entities when OneToMany relationship is with the same entity. Cascade not working

I am not able to delete child entities when OneToMany relationship is with the same entity. Cascade is not working.
If OneToMany relationship is between 2 different entities then cascade works fine.
How to enforce cascade.delete when the relationship is between same entities.
Cascade works fine during save.
#Entity
#Data
public class QuestionBank implements Serializable{
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long questionBankId;
private String questionSeriesId;
private String questionShortText;
#OneToMany(mappedBy="parentQuestionBank", cascade = CascadeType.ALL, orphanRemoval = true)
private List<QuestionBank> childQuestionBankList;
#ManyToOne()
private QuestionBank parentQuestionBank;
}
#DeleteMapping("/getQB/{id}")
public void deleteQB(#PathVariable Long id) {
QuestionBank qb = queBankRepo.findById(id).get();
queBankRepo.delete(qb);
}

Spring Boot Jpa Update- Avoid unnecessary updates of columns & child entities

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.

Why Value is not getting assigned in JPA for insert statement

Hi I have couple of Entity classes as below, using lombok for getter and setters
Parent Entity Class have
#Table(name = "PARTY")
#Entity
public class Party {
#Id
#Column(name = "PARTY_ID")
private Long partyId;
#OneToMany(targetEntity = DVLoanParticipants.class,cascade = CascadeType.ALL)
#JoinColumn(name = "PARTY_ID")
#MapKey(name="dvpParticipantName")
#LazyCollection(LazyCollectionOption.FALSE)
private Map<String, DVLoanParticipants> dvLoanParticipantsMap;
}
Child Entity Class have
#Table(name = "DV_LOAN_PARTICIPANTS")
#Entity
public class DVLoanParticipants implements Serializable {
#Id
#Column(name = "PARTY_ID")
private Long partyId;
#Id
#Column(name = "DVP_PARTICIPANT_NAME")
private String dvpParticipantName;
#Column(name = "DVP_PARTICIPANT_TYPE")
private String dvpParticipantType;
}
In service class i am calling save operation as
repository.save(parentEntityObject);
I am able to execute update statements ,but when i try to insert new row for child entity class i am getting an error saying
cannot insert NULL into ("ABC"."DV_LOAN_PARTICIPANTS"."PARTY_ID")
But if i print the parentEntityObject just before the save operation i see the values like
(partyId=12345678, dvpParticipantName=XYZ, dvpParticipantType=VKP)
I see the query formed as
insert
into
DV_LOAN_PARTICIPANTS
(DVP_PARTICIPANT_TYPE, PARTY_ID, DVP_PARTICIPANT_NAME)
values
(?, ?, ?)
Just before te save i am seeing valules in the Object
Builder=DVLoanParticipants(partyId=123456, dvpParticipantName=Builder,
dvpParticipantType=Individual)
Update
This is the setting part for values
DVLoanParticipants dvLoanParticipants = new
DVLoanParticipants();
dvLoanParticipants.setPartyId(Long.valueOf(partyId));
dvLoanParticipants.setDvpParticipantName("Builder");
dvLoanParticipants.setDvpParticipantType("Individual");
Party party = new Party();
Map<String, DVLoanParticipants> dvLoanParticipantsMap = new
java.util.HashMap<>();
dvLoanParticipantsMap.put("Builder", dvLoanParticipants);
party.setPartyId(Long.valueOf(partyId));
party.setDvLoanParticipantsMap(dvLoanParticipantsMap);
repository.save(party);
What is the mistake i am doing ?
The root cause of your problem in this part:
#OneToMany(targetEntity = DVLoanParticipants.class,cascade = CascadeType.ALL)
#JoinColumn(name = "LOAN_ID")
#MapKey(name="dvpParticipantName")
private Map<String, DVLoanParticipants> dvLoanParticipantsMap;
actually for your case the column name in the #JoinColumn means:
If the join is for a unidirectional OneToMany mapping using a foreign key mapping strategy, the foreign key is in the table of the target entity.
So, assuming for the clarity that you want to map the following schema:
create table PARTY
(
PARTY_ID int,
-- ...
primary key (PARTY_ID)
);
create table DV_LOAN_PARTICIPANTS
(
PARTY_ID int,
DVP_PARTICIPANT_NAME varchar(50),
DVP_PARTICIPANT_TYPE varchar(10),
-- ...
primary key (PARTY_ID, DVP_PARTICIPANT_NAME),
foreign key (PARTY_ID) references PARTY(PARTY_ID)
);
You can use the following mapping:
#Entity
#Table(name = "PARTY")
public class Party
{
#Id
#Column(name = "PARTY_ID")
private Long partyId;
// I use fetch = FetchType.EAGER instead of deprecated #LazyCollection(LazyCollectionOption.FALSE)
// targetEntity = DVLoanParticipants.class is redundant here
#OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "PARTY_ID") // this is DV_LOAN_PARTICIPANTS.PARTY_ID column
#MapKey(name = "dvpParticipantName")
private Map<String, DVLoanParticipants> dvLoanParticipantsMap;
public Party()
{
dvLoanParticipantsMap = new HashMap<>();
}
// getters / setters
public void addParticipant(DVLoanParticipants p)
{
this.dvLoanParticipantsMap.put(p.getDvpParticipantName(), p);
p.setPartyId(getPartyId());
}
}
#Entity
#Table(name = "DV_LOAN_PARTICIPANTS")
public class DVLoanParticipants implements Serializable
{
#Id
#Column(name = "PARTY_ID")
private Long partyId;
#Id
#Column(name = "DVP_PARTICIPANT_NAME")
private String dvpParticipantName;
#Column(name = "DVP_PARTICIPANT_TYPE")
private String dvpParticipantType;
// getters / setters
}
and example how to save:
Party party = new Party();
party.setPartyId(2L);
// ...
DVLoanParticipants part1 = new DVLoanParticipants();
part1.setDvpParticipantName("Name 3");
part1.setDvpParticipantType("T1");
DVLoanParticipants part2 = new DVLoanParticipants();
part2.setDvpParticipantName("Name 4");
part2.setDvpParticipantType("T1");
party.addParticipant(part1);
party.addParticipant(part2);
repository.save(party);
and several notes:
The LazyCollectionOption.TRUE and LazyCollectionOption.FALSE values are deprecated since you should be using the JPA FetchType attribute of the #OneToMany association.
You use hibernate specific approach for mapping сomposite identifiers. As it's mentioned in the hibernate documentation:
The restriction that a composite identifier has to be represented by a primary key class (e.g. #EmbeddedId or #IdClass) is only JPA-specific.
Hibernate does allow composite identifiers to be defined without a primary key class via multiple #Id attributes.
But if you want to achieve more portability you should prefer one of the jpa allowed approaches.

Spring Boot JPA - OneToMany relationship and database structure

I wonder what database structure would be the best option in my case:
I have entity Questionnaire:
#Table(name = "questionnaire")
public class Questionnaire extends BaseEntity {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#OneToMany(mappedBy = "fieldStatus")
private List<QuestionnaireField > fieldStatusList;
}
#Table(name = "questionnaire_field")
public class QuestionnaireField extends BaseEntity {
private static final long serialVersionUID = 1L;
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#Column(name = "questionnaire_id")
private Long questionnaireId;
#Column(name = "field_id")
private Long fieldId; //this is id related to the other table Field
#Column(name = "completed")
private boolean completed; //because I need some additional informations like completed I think I can't use ManyToMany between Questionnaire and Field
#ManyToOne(cascade = {CascadeType.PERSIST, CascadeType.REMOVE})
#JoinColumn(name = "questionnaire_id")
private Questionnaire questionnaire;
As you see each Questionnaire can have multiple QuestionnaireFields, BUT each QuestionnaireField is of type Field (hence I added private Long fieldId). Table Field can have 10.000 different fields.
Summary:
one questionnaire can have e.g. 10 Fields, the second one 20 another Fields etc. To store fields related to some particular Questionnaire I created QuestionnaireField table with 2 columns: private Long questionnaireId; and private Long fieldId; . The question is if it is a good approach? That are plain columns not related to any Foreign Key... I try to find the best solution to save Questionnaire with related QuestionnaireFields that are a subset of a big Field table...

EntityNotFoundException in Hibernate Many To One mapping however data exist

I'm getting an error
Caused by: javax.persistence.EntityNotFoundException: Unable to find tn.entities.AgenceBnq with id 01
when I get AgenceBnq through Employee
Employee class:
#Table(name = "EMPLOYEE")
#NamedQuery(name = "Employee.findById", query = "SELECT e FROM Employee e WHERE e.employeMat = ?1"),
public class Employee implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name = "EMPLOYEE_MAT", unique = true, nullable = false, length = 15)
private String employeeMat;
...
#ManyToOne
#JoinColumn(name = "AGENCE_COD")
private AgenceBnq agenceBnq;
}
#Entity
#Table(name="AGENCEBNQ")
public class AgenceBnq implements Serializable {
private static final long serialVersionUID = 1L;
#Id
#Column(name="AGENCE_COD", unique=true, nullable=false, length=10)
private String agenceCod;
...
//bi-directional many-to-one association to Employee
#OneToMany(mappedBy="agenceBnq")
private Set<Employee> employees;
}
I'm calling namedQuery Employee.findById in DAO to retrieve data and I have to get AgenceBnq from Employee but get this error while calling query.getResultList()
#NotFound( action = NotFoundAction.IGNORE) isn't useful for me because data exist in AGENCEBNQ table and I have to retrieve date through Employee.
Is this a bug in hibernate ? I'm using hibernate version 3.6.7.Final
Firstly, You dont need query for it, the EnityManger.find(Employee.class, YOUR_ID) will do the job.
Secondly dont use ? in your queries but names (e.employeMat = :id) as it is easier to debug and less error prones for complicated queries.
Finally, check your DB table if the AGENCE_COD column in Employee table really contains the valid ID for your entitity that crashes (and that it length matches the ID length of AgenceBnq). It should work, the typical reason why it doesnt will be that your Employe.AGENCE_COD has defualt value and when creatubg the new EMploye you add it only to the Agence but you did not set Agence in the Employ.

Resources