How to do bulk delete in JPA when using Element Collections? - java

I am having trouble working out how to do a bulk delete of a Person object using JPA, when the Person objects contain data stored using an #ElementCollection. Any ideas on how to do this would be much appreciated.
#Entity
#Table(name="at_person")
public class Person implements Comparable<Person> {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(name="id")
private long id = 0;
#Column(name="name", nullable = true, length = 128)
private String name = "";
#ElementCollection
#Column(name = "email")
#CollectionTable(name = "person_email", joinColumns = #JoinColumn(name = "person_id"))
private Set<String> email = new HashSet<String>();
}
What I am doing at the moment is this, and it fails with a foreign key constraint error:
Query query=em.createQuery("DELETE FROM Person");
Caused by: java.sql.SQLException: integrity constraint violation:
foreign key no action; FKCEC6E942485388AB table: PERSON_EMAIL
If it can be a pure JPA annotation rather than a Hibernate annotation that would be a bonus!

I'll let you interpret the part of the JPA 2.0 specification that mentions that a bulk delete operation is not cascaded:
4.10 Bulk Update and Delete Operations
...
A delete operation only applies to
entities of the specified class and
its subclasses. It does not cascade to
related entities.
And the fact is that Hibernate won't cascade a delete to a collection table either. This has been reported in HHH-5529 and the suggested approaches are:
You could also (a) clean up the collection table yourself or (b) use cascading foreign keys in the schema.
In other words, (a) use native SQL or (b) use a cascade delete constraint at the database level - and you'll have to add it manually, I don't think you can use #OnDelete with the #ElementCollection annotation (same story as HHH-4301 IMO).

Related

Referential integrity constraint violation: when deleting Entity in Hibernate (H2 in memory DBMS)

I am building my first Spring Boot application. I use Hibernate and an H2 in-memory DBMS.
What I am trying to build is a REST API that represents a number of App-Stores. I have an entity called App and another called Store. A store can contain many apps and each app can be contained in more than one store. Apps however, do not know in which stores they are contained. I want to be able to delete apps and stores independently of each other. Just because a store was deleted does not mean the apps therein should be deleted too and vice versa. Apps can exist without being in a store and stores without apps are fine too.
Here is the code for my entities, LpApp is the implementation for an App and LpTemplate is the implementation of a Store:
#Entity
public class LpApp {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, updatable = false)
private Long id;
#NotBlank(message = "An app needs a non-empty name")
#Column(nullable = false, updatable = false, unique = true)
private String appName;
// ... constructors, getters, setters, no further annotations
}
#Entity
public class LpTemplate {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(nullable = false, updatable = false)
private Long id;
#ManyToMany(fetch=FetchType.EAGER)
#JoinTable(name = "template_apps",
inverseJoinColumns = { #JoinColumn(name = "app_id") },
joinColumns = { #JoinColumn(name = "template_id") })
private Set<LpApp> apps = new HashSet<>();
// ... constructors, getters, setters, no further annotations
}
This works well until I attempt to delete an App or Store from my DBMS. At this point I get an org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException.
The exception I get is the following (I trimmed the call stack for brevity):
org.springframework.dao.DataIntegrityViolationException: could not execute statement; SQL [n/a]; constraint ["APP_ID: PUBLIC.TEMPLATE_APPS FOREIGN KEY(APP_ID) REFERENCES PUBLIC.LP_APP(ID) (3)"; SQL statement:
delete from lp_app where id=? [23503-199]]; nested exception is org.hibernate.exception.ConstraintViolationException: could not execute statement
...
Caused by: org.hibernate.exception.ConstraintViolationException: could not execute statement
...
Caused by: org.h2.jdbc.JdbcSQLIntegrityConstraintViolationException: Referential integrity constraint violation: "APP_ID: PUBLIC.TEMPLATE_APPS FOREIGN KEY(APP_ID) REFERENCES PUBLIC.LP_APP(ID) (3)"; SQL statement:
delete from lp_app where id=? [23503-199]
I am obviously doing something wrong, but I don't know where to look. I guess I am not using the #ManyToMany annotation right or perhaps it is the wrong annotation for my use case.
Thank you very much.
You need to add cascade attribute to tell hibernate not to delete the entity on delete operation. There are different options for cascade. Refer to this link to understand the different options.
The short version of the answer is that you are trying to delete a record that has an existing relationship with another record.

Storing data using hibernate for inter related tables

I have two tables (say table A and table B). Table B has foreign key from table A primary key. I generated my java entities using netbeans IDE and i now have something like:
For table A:
#Entity
#Table(name = "WORKFLOW_TRANSACTION")
public class WorkflowTransaction implements {
#OneToMany(mappedBy = "wtId")
private Collection<WorkflowTask> workflowTaskCollection;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "APP_ID")
private BigDecimal appId;
.
.
.
For table B:
#Entity
#Table(name = "WORKFLOW_TASK")
public class WorkflowTask implements Serializable {
#JoinColumn(name = "WT_ID", referencedColumnName = "APP_ID")
#ManyToOne
private WorkflowTransaction wtId;
#Id
#Basic(optional = false)
#NotNull
#Column(name = "TASK_ID")
private BigDecimal taskId;
#Column(name = "STEP_NUM")
private BigInteger stepNum;
.
.
.
Now my questions are:
What is the correct way to save data, should I create object for table B entity and set it in table A entity and then save table A?
I am generating entity beans using netbeans IDE feature. Are there any known disadvantages of it?, if yes, what?
You have bi-directional association, so you need to setTable B property in Table A entity class and also vice-versa. Since you have not declared cascading, you need to save the Table A entity first and then Table B entity.
Alternatively, if you save Table B and then Table A entity classes, the hibernate generates an extra SQL update command to maintain the relationship.
But if you want hibernate to save Table B entity when you save Table A entity then you need to add Cascade property:
#OneToMany(mappedBy = "wtId", cascade=CascadeType.ALL)
I don't think you will have any disadvantages if you use Netbeans for generating the entity classes, it save you time in writing the entities. But if you want to learn then writing entities without Netbeans is good.
To achieve this you can use two type of techniques XML mapping or Annotations
In both these techniques the common point is use
cascade="save-update"
What happens is that you set an attribute in one table getter and it will automatically insert into the many relation when inserting into one relation table

How to cascade delete entities with unidirectional 'ManyToOne' relationship with JPA

I have two entity classes 'User' and 'Department' with unidirectional 'ManyToOne' relationship as below.
public class User{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
#ManyToOne(cascade = CascadeType.ALL, fetch = FetchType.LAZY)
#JoinColumn(name = "DEPARTMENT_ID", nullable = true)
private Department department;
}
public class Department{
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private Long id;
}
If I want to delete some users and cascade remove the referenced departments if not any user references the department, is there any features of JPA to use please?
You can use CascadeType.DELETE, however this annotation only applies to the objects in the EntityManager, not the database. You want to be sure that ON DELETE CASCADE is added to the database constraint. To verify, you can configure JPA to generate a ddl file. Take a look at the ddl file, you'll notice that ON DELETE CASCADE is not part of the constraint. Add ON DELETE CASCADE to actual SQL in the ddl file, then update your database schema from the ddl. This will fix your problem .
This link shows how to use ON DELETE CASCADE on for CONSTRAINT in MySQL. You do this on the constraint. You can also do it in a CREATE TABLE or ALTER TABLE statement. It's likely that JPA creates the constraint in an ALTER TABLE statement. Simply add ON DELETE CASCADE to that statement.
Note that some JPA implementors do provide a means for this functionality.
Hibernate does supply this functionality using the #OnDelete annotation.
You can tell hibernate to delete 'orphan' entries with;
#Cascade({ org.hibernate.annotations.CascadeType.ALL, org.hibernate.annotations.CascadeType.DELETE_ORPHAN })

Hibernate OneToMany with JoinTable ID generation

Could somebody help me in setting appropriate annotation in hibernate for following case:
I have three tables:
Account, Card and AccountCard.
AccountCard is joining table for OneToMany relationship between Card and Account (account has many cards, card is attached to only one account).
I need to add to Account a List cards property and to Card model Account account property. This is the easy thing.
The problem is that I get "Cannot insert null value to AccountCard.id" while persisting Account with Cards.
Also I need to use sequence to generate IDs for joining table but don't know how.
Any help would be very appreciated.
Here is the code in Card:
#ManyToOne(fetch = FetchType.LAZY)
#JoinTable(name = "account_card", joinColumns = #JoinColumn(name = "crd_id"), inverseJoinColumns = #JoinColumn(name = "acc_id"))
private Account account;
I don't want to have a mapping in Account class so List cards is not added.
In your #JoinTable annotation, I see reference to an account_name table and not AccountCard. Is there actually an AccountCard table somewhere?
A join table usually doesn't need an id key of its own, and if you have hibernate autogenerate your table DDL it won't include one.
If you do indeed need an id on the join table, I don't think there's a way or a need to make hibernate aware of it, but you should make the column NOT NULL AUTO_INCREMENT in your SQL DDL.
Did you try to generate the tables first in the database (in my case mysql) and then create the entity with an ide like nebans? An auto increment id column in mysql then ends with:
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Basic(optional = false)
#Column(name = "ID")
private Integer id;
Check out http://netbeans.org/kb/docs/javaee/ecommerce/entity-session.html for a sample how to use netbeans to create entities from database

Using unique constraint on Hibernate JPA2

How can I implement my unique constraints on the hibernate POJO's? assuming the database doesn't contain any.
I have seen the unique attribute in #Column() annotation but I couldn't get it to work?
What if I want to apply this constraint to more than one column?
You can declare unique constraints using the #Table(uniqueConstraints = ...) annotation in your class
#Entity
#Table(uniqueConstraints=
#UniqueConstraint(columnNames = {"surname", "name"}))
public class SomeEntity {
...
}
Bascially, you cannot implement unique constraint without database support.
#UniqueConstraint and unique attribute of #Column are instructions for schema generation tool to generate the corresponsing constraints, they don't implement constraints itself.
You can do some kind of manual checking before inserting new entities, but in this case you should be aware of possible problems with concurrent transactions.
Therefore applying constraints in the database is the preferred choice.
In JPA2, you can add the Unique constraint directly to the field:
#Entity
#Table(name="PERSON_TABLE")
public class Person{
#Id
#Column(name = "UUID")
private String id;
#Column(name = "SOCIALSECURITY", unique=true)
private String socialSecurityNumber;
#Column(name = "LOGINID", unique=true)
private String loginId;
}
IMHO its much better to assign the unique constraint directly to the attributes than at the beggining of the table.
If you need to declare a composite unique key however, then declaring it in the #table annotation is your only option.

Categories

Resources