Hibernate: Problem with cascade delete for one to many relation - sql

I have following entity model.
#Entity
#Table(name="product")
public class ProductEntity {
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
private UUID id;
...
#OneToMany(mappedBy = "productEntity", cascade = CascadeType.ALL)
private List<ProductAddonEntity> productAddonEntities;
}
#Entity
#Table(name="product_addon")
public class ProductAddonEntity {
#Id
#GeneratedValue(generator = "uuid2")
#GenericGenerator(name = "uuid2", strategy = "uuid2")
private UUID id;
...
#ManyToOne()
#JoinColumn(name = "addon_id")
private ProductEntity addonEntity;
}
I want to delete product, and that deletion should also delete all ProductAddon entities, connected with this product. So I declare one to many relation with all cascade types.
But when I try to delete some product, at the beginning Hibernate tries to set null addon_id in product_addon table. But this column have non-null constraint, so deletion fails.
So I added to annotation #ManyToOne parameters
#JoinColumn(name = "addon_id", nullable = false, updatable = false)
But now hibernate just tries to delete product, before deleting product_addon entities, connected with this product. And this deletion fails because of foreign key constraint (Cannot delete or update a parent row: a foreign key constraint fails).
What can be a problem here? This application also uses liquibase, so foreign keys generated not by hibernate. For example, foreign key for addon_id doesn't have actions on delete, but I'm thinking that hibernate does not need these actions, because it works on higher data layer

Orphan removal is aggressive remove cascading mode to remove child object whenever the parent object needs to be deleted(#OneToOne and #OneToMany relationships).
This feature added from JPA 2.0 version.JPA deletion operation
The difference between the two settings is in the response to disconnecting a relationship. For example, such as when setting the address field to null or to another Address object.
#Entity
class Employee {
#OneToOne(cascade=CascadeType.REMOVE)
private Address address;
}
If only cascade=CascadeType.REMOVE is specified no automatic action is taken since disconnecting a relationship is not a remove operation.
#Entity
class Employee {
#OneToOne(orphanRemoval=true)
private Address address;
}
If orphanRemoval=true is specified the disconnected Address instance is automatically removed. This is useful for cleaning up dependent objects (e.g. Address) that should not exist without a reference from an owner object (e.g. Employee).

Related

How to create insert statements for table with Collection

I have a Spring boot JPA project that has an entity Entitya. This entity has a collection attribute
#ElementCollection(targetClass=String.class)
private List<String> options = new ArrayList<String>(10);
with get/set
I see tables created - Entitya and Entitya_options [looks like entitya doesnt know anything about Entitya_options. But entitya_options has foreign key to Entitya]
I don't need the extra table, but it's ok
I want to insert data using import.sql, how can I do?
Insert into "ENTITYA" (FORMULA,NOTE,ACTION_ID) values (....);
How to insert Entitya_options too , when inserting Entitya ?
EDIT
Question:
Should I use the below annotations? as mentioned - How to persist a property of type List<String> in JPA?
#ElementCollection // 1
#CollectionTable(name = "my_list", joinColumns = #JoinColumn(name = "id")) // 2
#Column(name = "list") // 3
private List<String> list;
I need complete insert statements, that can be put in import.sql
You can use import.sql (Hibernate support) or data.sql (Spring JDBC support) files to load data.
Ff you use JPA and an embedded datasource, your schema.sql won’t be taken into effect, because Hibernate’s DDL generation has priority over it.
A solution to that problem is to set the following property:
spring.jpa.hibernate.ddl-auto=none
Since Spring boot 2, the schema is only initialized by default for embedded datasources. To allow loading data for all types of datasources, you have to set the following property:
spring.datasource.initialization-mode=always
spring.datasource.data=classpath:my_script1.sql, classpath:my_script2.sql
Update
#Component
public class MyEntityInitializer implements CommandLineRunner{
#Autowired
EntityaRepository entityaRepository;
#Override
public void run(String... args) {
Entitya entitya = new Entitya();
entitya.setOptions(Arrays.asList("op1","op2"));
entityaRepository.save(entitya);
}
}
By this you can your entries inserted every time your application is started.
Make sure to check for already existing records, otherwise you get primary key exception.

JPA: single table inheritance possible performance tradeoff

I have following MS SQL DB tables, where I am storing school related data:
HighSchools<---GradeLevels<---Courses<---CourseGrades
The relations between the tables looks like
#Entity
#Table(name = "GradeLevels", schema = "dbo")
public class HighSchoolGradeLevel {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "AttendedId", nullable = false)
private HighSchoolAttended highSchoolAttended;
#OneToMany(mappedBy = "highSchoolGradeLevel")
#OrderBy("createdDate ASC")
private List<HighSchoolCourse> highSchoolCourses = new ArrayList<>(0);
// other fields and getter/setter methods....
}
#Entity
#Table(name = "Courses", schema = "dbo")
public class HighSchoolCourse {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "GradeLevelId", nullable = false)
private HighSchoolGradeLevel highSchoolGradeLevel;
#OneToMany(fetch = FetchType.EAGER, mappedBy = "highSchoolCourse", cascade={CascadeType.REMOVE, CascadeType.PERSIST})
#OrderBy("createdDate ASC")
private List<HighSchoolCourseGrade> highSchoolCourseGrades = new ArrayList<>(0);
// other fields and getter/setter methods....
}
#Entity
#Table(name = "CourseGrades", schema = "dbo")
public class HighSchoolCourseGrade {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "CourseId", nullable = false)
private HighSchoolCourse highSchoolCourse;
// other fields and getter/setter methods....
}
The java web-app users can do CRUD operations with the tables data.
The new requirement is to consume the data with the same structure from a third party. The consumed data SHOULD NOT be displayed for the web-app users.
Therefore, I am thinking to use JPA single table inheritance which allows me to:
avoid adding clones of the existing tables
avoid cloning existing business logic which is tight to existing
entity types
avoid code refactoring of existing CRUD ops in case implementing own discriminator column.
The Single table inheritance looks reasonable to use in my, but there is one thing I am not sure about "What is the performance trade off of using Single Table Inheritance, especially while operating on the Parent and its Childs entities?". The question may sound dummy, knowing the fact that Hibernate will update SQL queries with corresponding discriminator statements. But still it would be nice to improve the current knowledge and be sure that the existing users of wep-app will not get bad usage experience.
The existing web-app DAO layer uses with Spring Data JPA. Currently, each of existing tables contains more than ~500k records.

EclipseLink Remove Cascade

I have been developing a web application running on the Glassfish Server. I use JPA with EclipseLink implementation.
These are my two entity classes. I represent the relation between them below. When I start the Glassfish server and delete the lesson entity, Cascade works. It deletes all of the Test entities of it. Then I add lesson entity and test entity related to it. But when I try to delete the Lesson entity, at this time Cascade does not work and throws "foreign key constraint" error. After Server restarting, Cascade again works. ??? What is the difference? Why do Cascade operation only work at the startup?
Thank you.
#Entity
public class Lesson implements Serializable {
...
#OneToMany( fetch = FetchType.LAZY, cascade = { CascadeType.MERGE, CascadeType.REMOVE }, mappedBy = "lesson" )
private List< Test > tests;
...
}
#Entity
public class Test implements Serializable {
...
#ManyToOne( targetEntity = Lesson.class, fetch = FetchType.LAZY )
#JoinColumn( name = "lessonNo", insertable = true, updatable = true, nullable = false )
private Lesson lesson;
...
}

How to prevent hibernate to save same object

I have tables named Country, City and People. What can I prevent hibernate to save same people object? Here is my classes and the problem is when I try to save a country object, Hibernate tries to save or update same people objects as expected.(City's people object and Country's people object has same PK)
org.hibernate.NonUniqueObjectException: a different object with the same identifier value was already associated with the session
Classes;
class Country{
.....
#JoinColumn(.....)
#ManyToOne(optional = false, fetch = FetchType.EAGER)
#Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
private City city;
#JoinColumn(.....)
#ManyToOne(optional = false, fetch = FetchType.EAGER)
#Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
private People people;
}
class City{
....
#JoinColumn(.....)
#ManyToOne(optional = false, fetch = FetchType.EAGER)
#Cascade(org.hibernate.annotations.CascadeType.SAVE_UPDATE)
private People people;
}
Here is my save method;
public void save(){
....
getCurrentSession().saveOrUpdate(customer); //Hibernate session
}
2 solutions here :
Use merge instead of save
or remove save cascade annotation.
Using both save and save cascade basically tells Hibernate "please persist this new object along with all his relationships which are also new". You don't want that.

NHibernate many-to-one relationship

We have the following Domain objects :-
public class UserDevice : BaseObject
{
// different properties to hold data
}
public class DeviceRecipient:BaseObject
{
public virtual UserDevice LastAttemptedDevice{get;set;}
}
Hence the sql schema created based on this using fluent nhibernate automapper is like
DeviceRecipient's table is having primary key of UserDevice as a foreign key i.e UserDevice_Id.
Now, When we try to delete UserDevice object it gives a sql exception for foreign key constraint. What we want to do is :-
Delete the UserDevice object , hence the UserDevice row without deleting the DeviceRecipient as it will be used somewhere else in domain model. We just want to set null to UserDevice_Id column of DeviceRecipient when we delete UserDevice.
We want to do it using fluent nhibernate conventions as we use Automapping.
Any help will be appreciable.. Thanks in advance.!
As I can see you have uni-direction many-to-one relation. So firstly you have to write following override:
public class DeviceRecipientOverride : IAutoMappingOverride<DeviceRecipient>
{
public void Override(AutoMapping<DeviceRecipient> mapping)
{
mapping.References(x => x.LastAttemptedDevice)
.NotFound.Ignore(); // this doing what you want.
}
}
Secondly you could convert it to automapping convention, if you have more places with this behavior.
public class ManyToOneNullableConvention : IReferenceConvention
{
public void Apply(IManyToOneInstance instance)
{
var inspector = (IManyToOneInspector) instance;
// also there you could check the name of the reference like following:
// inspector.Name == LastAttemptedDevice
if (inspector.Nullable)
{
instance.NotFound.Ignore();
}
}
}
EDIT:
From the NHibernate reference
not-found (optional - defaults to exception): Specifies how foreign
keys that reference missing rows will be handled: ignore will treat a
missing row as a null association.
So when you set not-found="ignore" SchemaExport/SchemaUpdate will just not create the FK for you. So if you have the FK then you need to delete it or set OnDelete behavior of the FK to Set Null. Assuming that you are using Microsoft Sql Server:
ALTER TABLE [DeviceRecipient]
ADD CONSTRAINT [FK_DeviceRecipient_LastAttemptedDevice]
FOREIGN KEY ([LastAttemptedDevice_ID])
REFERENCES [UserDevice]
ON DELETE SET NULL