Hibernate throwing ConstraintViolationException when executing delete statement - java

There are tons of questions and answers online regarding to this exception but none of them resolve my issue. I have very simple two entities - one is category and the other one is item. Category has one to many relationship with item. I don't have any problem with insert statement. But when I try to delete Category, hibernate throws "org.hibernate.exception.ConstraintViolationException: could not execute statement" exception because of foreign key constraint ("com.microsoft.sqlserver.jdbc.SQLServerException: The DELETE statement conflicted with the REFERENCE constraint "FK_CATEGORY". The conflict occurred in database "shop", table "dbo.ITEM", column 'CATEGORY_ID'."). So, apparently cascading annotation is not working. What am I missing here?
This is what my code looks like :
int rows = em.createQuery("delete from CATEGORY where id = :id")
.setParameter("id", id)
.executeUpdate();
CATEGORY ENTITY
#Entity
#Table(name = "CATEGORY")
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#Column(name = "NAME")
#Size(max = 50)
private String name;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "category", cascade = CascadeType.ALL)
private Set<Item> items = new HashSet<>();
......//Getters and Setter
ITEM ENTITY
#Entity
#Table(name = "ITEM")
public class Item {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "ID")
private int id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CATEGORY_ID")
private Category category;
#Column(name = "NAME")
#Size(max = 150)
private String name;
....//Getters and Setters
}

For deleting data, you need to use Transaction.
You can use UserTransaction:
UserTransaction utx = entityManager.getTransaction();
try {
utx.begin();
businessLogic();
utx.commit();
} catch(Exception ex) {
utx.rollback();
throw ex;
}
or Spring #Transactional
#Transactional
public void businessLogic() {
... use entity manager inside a transaction ...
}

Turns out, Cascade property has not effect with JPQL because JPA doesn’t support cascade delete (at this point). If you want JPQL to work, cascade needs to be baked into schema. That means removing foreign key constraint and adding cascade delete constraint. Otherwise, foreign key constraint exception will be thrown. If you don't want to bake cascade into schema, another option is use hibernate remove method on each object.

Related

Springboot JPA Hibernate CascadeType confusion

How to make a column cascade enable only for insertion not update and delete.
Here are two class. There are #ManyToOne relationship between Qualification and Department. At the time of insertion I want to insert new department with qualification. But at the time of update of qualification I don't want to update department. How can I configure that??
#Table(name = "department")
public class Department {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String name;
}
#Table(name = "qualification")
public class Qualification implements Serializable {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
Integer id;
private Integer passingYear;
private String result;
#ManyToOne(optional = true, cascade = { CascadeType. ? }, fetch = FetchType.EAGER)
#JoinColumn(name = "department_id", referencedColumnName = "id")
private Department department;
}
CascadeType.PERSIST is what you are looking for. The various CascadeType enums essentially say, which persistence operation of EntityManager persist/merge/remove etc. should cascade to the objects of an association.

Many-to-many relationship JPA - unique constraint

I have 2 entities, Event and Tag, in a many-to-many relationship. Tags should have unique names, so I placed a constraint on it.
It works like expected for unique tag names, I save a batch of new events and entries are automatically inserted in the tag and join tables.
But the moment I try to save an event that has a tag with a duplicate name, an error is thrown due to it violating the constraint.
Is there a way around this that does not involve having to check and insert all the events/tags manually?
Code below:
Event entity:
#Entity
#Table(name = "event")
class Event {
#Id long id;
#ManyToMany(cascade = { CascadeType.ALL })
#JoinTable(name = "event_tag",
joinColumns = #JoinColumn(name = "event_id"),
inverseJoinColumns = #JoinColumn(name = "tag_id"))
private Set<Tag> tags;
// other properties
}
Tag entity:
#Entity
#Table(name = "tag", uniqueConstraints = #UniqueConstraint(columnNames = "name"))
public class Tag {
#Id long id;
#Column(name = "name", unique = true)
private String name;
#ManyToMany(mappedBy = "tags", cascade = { CascadeType.ALL })
private Set<Event> events;
}
I'm using JpaRepository's method saveAll to persist the events. It throws:
java.sql.SQLException: Duplicate entry 'xxxxx' for key 'uq_tag_name'
at org.mariadb.jdbc.internal.protocol.AbstractQueryProtocol.readErrorPacket(AbstractQueryProtocol.java:1694) ~[mariadb-java-client-2.7.3.jar:na]
I have seen a few similar questions but have yet to find a working answer for this.
it happen because of { CascadeType.ALL }
when you set CascadeType.ALL hibernate changes the Tag table and if it need,insert data for Tag.you should remove CascadeType.ALL and if you get (cannot insert transient object) should use flush for handlinf that

Spring data JPA doesn't delete entity

I have a problem with deleting entity from database. Whatever I do anyway it doesn't delete.
Driver class
#Entity
#Table(name = "drivers")
public class Driver {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "driver", fetch = FetchType.EAGER)
#JsonSerialize(using = RatingsSerializer.class)
private List<Rating> ratings;
// other fields. Getters and Setters...
}
Rating class
#Entity
#Table(name = "ratings")
#JsonDeserialize(using = RatingDeserializer.class)
public class Rating {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#ManyToOne
#JoinColumn(name = "driver_id")
private Driver driver;
#ManyToOne
#JoinColumn(name = "client_id")
private Client client;
private int mark;
private Date createdAt;
//Getters and Setters ...
}
First one what I do is annotate ratings with #OneToMany(mappedBy = "driver", fetch = FetchType.EAGER, orphanRemoval = true, cascade = CascadeType.REMOVE) and when call driverRepository.delete(driver) it throws:
org.postgresql.util.PSQLException: ERROR: update or delete on table "drivers" violates foreign key constraint "fk3raf3d9ucm571r485t8e7ew83" on table "ratings"
Ok, choose another way. Try to delete each rating object using ratingRepository, but never happens, it just iterate thorough each rating item and throw again error
org.postgresql.util.PSQLException
Next step was to set for each rating item Client and Driver to null. Now driver entity is deleted from database but rating entity remain in database.
What happens?
Spring Data JPA version: 1.5.7
It looks that your Foreign Key error is related to Client table which is linked according to your code line:
#ManyToOne
#JoinColumn(name = "client_id")
private Client client;
So, if you add cascade = CascadeType.REMOVE within the annotation, it may works. But, that' up to you if you want to delete everything on cascade including Client row. If not, then update that column value first to null.

How to avoid Hibernate generating two queries for an update with OneToMany?

I'm facing the same situation as in this question, that has no useful answer.
When I add a new element to the many part of my one-to-many relation, Hibernate generates two queries, one to insert and one to update the foreign key to the parent.
Why does it need the second query for? Isn't the parent's id set in the insert?
Is there any way of avoiding this?
Hibernate:
/* insert mydomain.LanguageKnowledge */
insert
into
languageKnowledge
(language_fk, level_fk, personId_fk)
values
(?, ?, ?)
Hibernate:
/* create one-to-many row mydomain.Person.offeredLanguages */
update
languageKnowledge
set
personId_fk=?
where
id=?
public class LanguageKnowledge {
#Id
#GeneratedValue(strategy = IDENTITY)
private Integer id;
#Enumerated(STRING)
#Column(name = "language_fk")
private LanguageIso639_3 language;
#Enumerated(STRING)
#Column(name = "level_fk")
private LanguageLevel level;
protected LanguageKnowledge() {
}
}
public class Person {
#Id
#GeneratedValue(strategy = IDENTITY)
private Integer id;
#OneToMany(fetch = EAGER, cascade = {ALL}, orphanRemoval = true)
#JoinColumn(name = "personId_fk", referencedColumnName = "id", nullable = false)
private final Set<LanguageKnowledge> offeredLanguages = new HashSet<>();
public Person(Set<LanguageKnowledge> offeredLanguages) {
addOfferedLanguages(offeredLanguages);
}
protected Person() {
}
public void addOfferedLanguages(Set<LanguageKnowledge> offeredLanguages) {
this.offeredLanguages.addAll(offeredLanguages);
}
public void removeOfferedLanguages(Set<LanguageKnowledge> offeredLanguagesToRemove) {
this.offeredLanguages.removeAll(offeredLanguagesToRemove);
}
}
The association is uni-directional, so Person is the owning side (because it's the only side).
Make the association bidirectional and make LanguageKnowledge the association owner. That way you will avoid redundant updates because the foreign key values will be specified as part of insert statements for LanguageKnowledge.

Hibernate, JPA cant delete one-to-many relation

I have one-to-many relation:
#Entity
#Table(name = "Users")
public class User {
#Id
#Column(name = "user_id", nullable = false)
#GeneratedValue(strategy = GenerationType.AUTO)
private Integer id;
#Column(name = "login", nullable = false)
private String login;
#Column(name = "password", nullable = false)
private String password;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "role_id", nullable = false)
private Role role;
#OneToMany(fetch = FetchType.LAZY, mappedBy = "user", cascade = javax.persistence.CascadeType.ALL)
private Set<Contacts> contacts = new HashSet<Contacts>();
And I'm trying to delete User object with all Contacts; I tried to use:
cascade = javax.persistence.CascadeType.ALL
cascade =
javax.persistence.CascadeType.REMOVE
#Cascade(CascadeType.DELETE) from org.hibernate.annotations
#Cascade(CascadeType.DELETE_ORPHAN) from org.hibernate.annotations
but nothing helped. I always get exception:
org.hibernate.util.JDBCExceptionReporter - Cannot delete or update a
parent row: a foreign key constraint fails
(contactmanager.contact, CONSTRAINT contact_ibfk_1 FOREIGN KEY
(user_id) REFERENCES
UPD
Code that deletes a User is as follows:
#Transactional
public void removeUser(User user) {
sessionFactory.getCurrentSession().delete(user);
}
I'll appreciate any help! Thanks.
My recommendation here would be to do the relationship management yourself. Cascading removes can be tricky (especially in a situation like yours where the owner of your bi-directional relationship is not the one declaring the cascade) and often times quite dangerous so I usually prefer to avoid them. Especially if you are running a version of JPA pre-2.0 then you don't have too much of a choice. I would just change the removal method to something like:
#Transactional
public void removeUser(User user) {
Set<Contacts> contacts = user.getContacts();
for (Contact contact : contacts) {
sessionFactory.getCurrentSession().delete(contact);
}
contacts.clear();
sessionFactory.getCurrentSession().delete(user);
}

Categories