Cannot delete a parent row: a foreign key constraint fail - java

I have 2 entities (User , Teacher) the User is the father ...
When I want to remove user by native query it give me this error ( Cannot delete or update a parent row: a foreign key constraint fail )
Though I have written:
#OneToOne(mappedBy = "user_id",fetch = FetchType.EAGER,
cascade = CascadeType.ALL , orphanRemoval = true )
at the Teacher reference in the user entity
but when I try to use JPA derived method deleteByEmail() .. it worked!
What is the reason?

Cascading doesn't work with queries.
It works when you change the state of the entities using the methods in the EntityManager or Hibernate ORM Session.
You can see a list of examples in the Hibernate ORM documentation:
5.15. Cascading entity state transitions.

Related

Delete all slaves with an object in HQL

I use following HQL query to delete a specific object in my database.
delete from com.ranking.Footballclub where id = 1
The problem I encounter when I do this, it breaks a foreign key.
Caused by: com.microsoft.sqlserver.jdbc.SQLServerException: The DELETE
statement conflicted with the REFERENCE constraint
"FK_VKLC3OLNFZIT2FCYMMKDO2ERZ4". The conflict occurred in database
"sports", table "dbo.FOOTBALL_PLAYER", column 'CLUB_ID'
A sport teams has a slave of players. I could first delete all players before I delete the Footballclub. But a Footballclub has more than just players. It has for example a list of employees, transfers,... Their foreign keys will break as well.
For this case I'm looking for something in HQL to delete all slave fields in 1 statement.
you can set the ON DELETE CASCADE in the data base explicitly, or mark the required Child entity with the annotation #org.hibernate.annotations.OnDelete.
It will automatically adds the on delete to schema during the schema generation. like -
#OneToMany(fetch = FetchType.EAGER, mappedBy = "YOUR_PARENT",
cascade = CascadeType.REMOVE)
#org.hibernate.annotations.OnDelete(
action = #org.hibernate.annotations.OnDeleteAction.CASCADE)
private List<YOUR_CHILD> CHILDS;
You can only use #OneToMany(cascade=CascadeType.REMOVE) annotation at your Entity and delete it through entity manager. There is no option to do it with hql. Or use constraint in your DDL (foreign key delete cascade);

Used only #mappedBy , not #JoinColumn - join column created in OneToMany relationship, what is then the use of JoinColumn?

I am new to Hibernate. I have a OneToMany relationship with bidirectional mapping between Account and Transaction. I am not using #JoinColumn in either class and using #mappedBy in the non owning Account class. And everything is working fine. Using H2 in memory database, new join column is created in Transaction table. Then what is the use of #JoinColumn in OneToMany relationships? Is it for unidirectional mapping only? Below is the code. I also read for reference JPA JoinColumn vs mappedBy
public class Account {
#OneToMany( mappedBy="account", cascade=CascadeType.ALL)
List<Transaction> list= new ArrayList<Transaction>();
}
public class Transaction {
#ManyToOne
Account account;
}
Application class :
Account a = new Account("savings");
Transaction t1 = new Transaction("shoe purchase", 45);
t1.setAccount(a);
a.getList().add(t1);
accountRepository.save(a);
output:
Transaction table has an entry with foreign key which is account number in that one row in Account table. ACCOUNT_ID column in created in Transaction table.
There are no extra tables created.
Jpa works on the idea of configuration by convention. So, it will perform configuration on your behalf whenever it can. Think of the #Column annotation, you don't have to apply it on every entity attribute, you would need it only when you have to change something about the attributes.
It's the same with #JoinColumn, when you added #ManyToOne, Jpa already knows that you will need the join column and thus was added for you and the default naming convention for the foreign key was applied (attributename_primarykeyoftheothertype).
Use of
mappedBy
is instruct framework to enable bi-directional relationship. Because of #ManyToOne on Transaction class you Your Transaction Table will have foreign key referring to Account table primary key. By default, Hibernate generates the name of the foreign key column based on the name of the relationship mapping attribute and the name of the primary key attribute. In this example, Hibernate would use a column with the name account_id to store the foreign key to the Account entity.
#JoinColum
can be used If you would like override default foreign key name like #JoinColum(name="acc_id")
MappedBy intructs Hibernate that the key used for the association is on the other side of the association.Like in this case ACCOUNT_ID is being created on Account table.
That means that although you associate two tables together, only one table is having foreign key constraint to the other one.
MappedBylets you to still associate from the table not having foreign key constraint to the other table.

How to avoid Hibernate to create foreign key restrictions on oneToMany relationship

I have these two entities: Hotel, HotelPhoto
And the following relationship:
Hotel
#OneToMany(fetch = FetchType.EAGER, mappedBy="idHotel")
#OnDelete(action = OnDeleteAction.CASCADE)
public Set<HotelPhoto> getHotelPhotos() {
return hotelPhotos;
}
When I run this code the hibernate is creating a foreign key restriction with RESTRICT on delete and on update. I would like to have a CASCADE restriction on delete and on update. Is there a way to do that and avoid automatically creating a RESTRICT restriction?

#Where annotation in hibernate on child table

This is my schema:
event_details:
- id (PK)
- name
- description
event_ticket_types:
- id (PK)
- event_id (References id (event_details)
- ticket_name
EventDetail
//bi-directional many-to-one association to EventTicketType
#OneToMany(mappedBy="eventDetail", cascade = CascadeType.ALL)
private Set<EventTicketType> eventTicketTypes;
in my EventDetail entity, I have added #Where(clause = "deleted_at is NULL")
I've also added the same in EventTicketType
This is how I am getting the data in service.
EventDetail eventDetail = eventDetailRepository.getById(eventId);
Set<EventTicketType> eventTicketTypes = eventDetail.getEventTicketTypes();
And then I am mapping entity and dto.
In the query log - I see that parent table (event_details) executes query with where condition i.e where deleted_at is NULL, but the child table (event_ticket_types) does not have a where condition
Where is it that I am doing wrong?
You're not showing where the #Where annotations are but I'm guessing they're both at class level only. Try also adding the #Where annotation to the #OneToMany method above.
The annotation works at entity level when you select the entity directly, but you need it on the relationship method also if you want it to work for joins.

How to batch delete using bulkUpdate

I have a common User / Role setup, with a user_role join table. I'm trying to use Spring's HibernateTemplate to mass delete all locked users like this:
getHibernateTemplate().bulkUpdate("delete from User where locked=?", true);
If the user being deleted does not have any roles (no record in the user_role table), then everything goes fine; however if the user does have a role record, I'm getting the following error:
integrity constraint violated - child
record found
Roles are defined in User.java like this:
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name = "user_role", joinColumns = { #JoinColumn(name = "user_id") }, inverseJoinColumns = #JoinColumn(name = "role_id"))
private Set<Role> roles = new HashSet<Role>();
So how can I batch delete users even if a user has child records? Thanks!
Bulk delete operations are not cascaded to related entities as per the JPA specification:
4.10 Bulk Update and Delete Operations
Bulk update and delete operations
apply to entities of a single entity
class (together with its subclasses,
if any). Only one entity abstract
schema type may be specified in the
FROM or UPDATE clause.
...
A delete operation only applies to
entities of the specified class and
its subclasses. It does not cascade
to related entities.
However, I'd expect the JPA provider to deal with join tables. Sadly, Hibernate doesn't and this is logged in HHH-1917. I'm afraid you'll have to fall back on native SQL to clean up the join table yourself or to use cascading foreign keys in the schema.
Application-level cascading (cascading through hibernate annotations or JPA annotations) only work if the actual entity is actually loaded from the db. When you use the hibernate template with HQL, you'll notice that the entities are not loaded, and the HQL is directly converted to SQL to be executed.
If you want to batch delete you have to use an HQL query to delete all relevant tables (ie roles) before deleting the parent table data.
I'm not entirely sure because it's hard for me to recreate this problem, but I think you might need to add a cascade to your #ManyToMany
#ManyToMany(cascade = CascadeType.ALL)
Since you want to bulk delete something that have a ManyToMany related items, you first have to delete the relation (in the join table), or do a loop and for each item, delete manually (insane and too much heavy).
So, since JPQL does not allow to do it, a possible way is to make a native SQL query for deleting the id you want in the related table, and then, do the bulk delete.

Categories