Spring Data JPA delete from many to many relationship problem - java

In my project I use Spring data jpa. I have tables for many to many relationship. My entities:
#Entity
#Table(name = "SPEC")
public class SpecJpa {
#Id
private int id;
#Column(name = "NAME")
private String name;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "Creator_ID", unique = false, nullable = false, updatable = true)
private UsersJpa usersJpa;
#Column(name = "DESCRIPTION")
private String description;
#ManyToMany(fetch = FetchType.EAGER)
#JoinTable(name="SPEC_PARTS",
joinColumns = #JoinColumn(name="ID_PARTS", referencedColumnName="id"),
inverseJoinColumns = #JoinColumn(name="ID_SPEC", referencedColumnName="id")
)
private Set<PartsJpa> partsJpa;
//---------------
And Parts:
#Entity
#Table(name = "PARTS")
public class PartsJpa {
#Id
private int id;
#Column(name = "NAME")
private String name;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "ID_EXPORT", unique = false, nullable = false, updatable = true)
private ExportJpa exportJpa;
#OneToOne(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
#JoinColumn(name = "ID_TYPE", unique = false, nullable = false, updatable = true)
private TypesJpa typesJpa;
#Column(name = "DESCRIPTION")
private String description;
#ManyToMany(fetch = FetchType.EAGER)
private Set<SpecJpa> specJpa;
Now in Controller I try to delete one row from table parts:
#PostMapping("deletePart")
public String deletePart(#RequestParam String id, Model model) {
partsService.deleteById(Integer.parseInt(id));
return "redirect:/parts";
}
But I have exception:
ferential integrity constraint violation:
"FK9Y4MKICYBLJWPENACP4298I49: PUBLIC.PARTS FOREIGN KEY(ID_EXPORT)
REFERENCES PUBLIC.EXPORT(ID) (1)"; SQL statement: /* delete
com.aleksandr0412.demo.entitiesjpa.ExportJpa / delete from EXPORT
where id=? [23503-200]], SQL: / delete
com.aleksandr0412.demo.entitiesjpa.ExportJpa */ delete from EXPORT
where id=? 2020-05-25 19:16:31.630 WARN 13387 --- [nio-8080-exec-4]
o.h.engine.jdbc.spi.SqlExceptionHelper : SQL Error: 23503, SQLState:
23503
In my db for this entities I have 3 tables: Parts, Spec and Spec_parts. As I understand to solve this problem, I firstly should delete rows in table spec_parts, and after this I can delete row from table parts. How can I do this?

In your partsService implementation, I would recommend you first fetch the resource you are about to delete (i.e the PartsJpa) using the given id from the controller.
Next set its specJpa to null or emptySet, then call the delete method afterwards.
For this to work, ensure that method setSpecJpa(SpecJpa specJpa) and setPartJpa(PartJpa partJpa) are properly implemented.
I hope you find this helpful.

Related

Hibernate not respecting nullable=true in #JoinColumn annotation

I have two entities
#Entity
#Table(name = "Documents")
public class Document extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private int id;
#ManyToOne(fetch = FetchType.LAZY, optional = false)
#JoinColumn(nullable = true, name = "asset_id")
private Asset asset;
#ManyToOne( targetEntity = Debt.class,
fetch = FetchType.LAZY, optional = false)
#JoinColumn(nullable = true, name = "debt__id")
private Debt debt;
}
and
#Entity
#Table(name = "debts")
public class Debt extends BaseEntity {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
#OneToMany(mappedBy = "debt", cascade = CascadeType.ALL, fetch = FetchType.LAZY)
private List<Document> documents;
}
This mapping:
#ManyToOne( targetEntity = Debt.class,
fetch = FetchType.LAZY, optional = false)
#JoinColumn(nullable = true, name = "debt_id")
private Debt debt;
was recently added.
On running the app, the app is crashing with an error saying: Error executing DDL "alter table documents add debt__id bigint not null" via JDBC Statement
I checked the sql query hibernate sent to the database and it was: alter table documents add debt_id bigint not null
This query fails because there are records already on the documents table so a non-nullable column without default value could not be added.
So why is the nullable=true in the #JoinColumn annotation ignored by hibernate.
I couldn't find an answer anywhere. This is a spring-boot app, if it helps.
Just remove the optional = false from the #ManyToOne. That is the culprit. Also, you don't need to mention nullable=true in the #JoinColumn, that is by default true
#ManyToOne( targetEntity = Debt.class,
fetch = FetchType.LAZY)
#JoinColumn(name = "debt__id")
private Debt debt;

Hibernate get parent child lazy loading with addtional check

I have two tables Company and Employees, one-to-many mapping. Company table contains composite primary key.
I want to search from company table based on primary id but want to put an additional check on the child table.
I want to load only a particular type of employees which I will get in the request. How it can be done in Sprongboot JPA with findById("id");
class Company{
#Id
private String companyId;
#Id
private String stateId;
private String company Name;
#OneToMany(targetEntity = Employees.class, fetch = FetchType.LAZY, cascade = {
CascadeType.ALL }, mappedBy = "company")
private Set<Employees> empList;
}
class Employees{
#Id
private String id;
//foreign key
private String companyId;
//foreign key
private String stateId;
#ManyToOne
#JoinColumns({
#JoinColumn(name = "companyId", referencedColumnName = "companyId", insertable = false, updatable = false, nullable = true),
#JoinColumn(name = "stateId", referencedColumnName = "stateId", insertable = false, updatable = false, nullable = true) })
private Company company;
private int salary;
private String type;
}
Use Filter, which is an alternative of #Where where you can set dynamic value.
Here is the sample
#FilterDef(
name = "employeeTypeFilter",
parameters = #ParamDef(name = "type", type = "string")
)
#Filter(
name = "employeeTypeFilter",
condition = "type > : type"
)
public class Employees {
}
You can enable or disable filter from your code dynamically based on your requirement.
You can use #Where for fixed type
#Where(clause = "type = 'anyEmployeeType'")
private Set<Employees> empList;
For dynamically fetch you can query in Employees repository
List<Employees> findByTypeAndCompany(String type, Company company);

JPA HBM2DDL creating invalid foreign key constraints

I'm having an issue with JPA and Hibernate. I have 2 classes (actually more but those are the 2 that are giving me a headache).
They have a OneToMany/ManyToOne relationship, and I have specified on the OneToMany that I want it to cascade every possible change (CascadeType.ALL).
However, when I let HBM2DDL run in "update" mode, it is creating foreign key constraints that are not fitting what I want to achieve. It is creating a constraint in RESTRICT mode, which is absolutely not what I have specified. Perhaps the error is in my code ? I've joined it below.
#Entity
public class Department {
#Id
#Column(name = "id", nullable = false)
private int id;
#Column(name = "name", nullable = false)
private String name;
#OneToMany(mappedBy = "departmentByDepartment", cascade = CascadeType.ALL)
private Collection<Article> productsById;
#OneToMany(mappedBy = "departmentByDepartment", cascade = CascadeType.ALL)
private Collection<User> usersById;
And then :
#Entity
#Table(name = "product", schema = "qlog_project", catalog = "")
public class Article {
#Id
#Column(name = "id", nullable = false)
private int id;
#Column(name = "name", nullable = false)
private String name;
#Column(name = "description", nullable = false)
private String description;
#Column(name = "price", nullable = false)
private double price;
#Column(name = "image", nullable = false)
private String image;
#ManyToOne
#JoinColumn(name = "department", referencedColumnName = "id", nullable = false)
private Department departmentByDepartment;
#OneToMany(mappedBy = "productByProductId", cascade = CascadeType.ALL)
private Collection<Stock> stocksById;
Also, I cannot seem to be able to delete an Article instance with EntityManager.remove(). It's not producing any query and not doing anything. Maybe it is linked ?
Thanks in advance,
Regards

eclipselink manytoone not save

i have a persit problem with elcipselink
here is my model :
#Entity
#Table(name = "TBL_ASSOC_LANGUE_CODE_BE_GARE")
public class AssocLangueCodeBeGare {
#AttributeOverrides({ #AttributeOverride(name = "id_langue", column = #Column(name = "ID_LANGUE") ),
#AttributeOverride(name = "id_gare", column = #Column(name = "ID_GARE") ) })
#EmbeddedId
private AssocLangueCodeBeGareFK key;
#Column(name = "CODE_BE", length = 4)
private String codeBe;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(nullable = false, name = "ID_GARE", referencedColumnName = "ID_GARE", insertable = false, updatable = false)
private StopPoint stopPoint;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_LANGUE", referencedColumnName = "ID_LANGUE", insertable = false, updatable = false)
private Langue langue;
in StopPoint i don t reference table AssocLangueCodeBeGare, i don t need it.
then when i do :
this.serviceStopPoint.save(currentStopPoint);
for (AssocLangueCodeBeGare assoc : listAssocLangueCodeBeGare) {
assoc.setStopPoint(currentStopPoint);
this.serviceAssocLangueCodeBeGare.save(assoc);
}
save is
#Override
public void save(T entityToSave) {
this.getEntityManager().persist(entityToSave);
}
I m using batch insert for writing and sometimes when i save another entity flush is done and i get :
Caused by: org.h2.jdbc.JdbcBatchUpdateException: NULL not allowed for column "ID_GARE"; SQL statement:
INSERT INTO TBL_ASSOC_LANGUE_CODE_BE_GARE (CODE_BE, ID_GARE, ID_LANGUE) VALUES (?, ?, ?) [23502-192]
at org.h2.jdbc.JdbcPreparedStatement.executeBatch(JdbcPreparedStatement.java:1208)
at org.eclipse.persistence.internal.databaseaccess.DatabasePlatform.executeBatch(DatabasePlatform.java:2134)
at org.eclipse.persistence.internal.databaseaccess.DatabaseAccessor.executeJDK12BatchStatement(DatabaseAccessor.java:871)
does i miss something in the mapping?
does i need to add something like this in stoppoint entity:
#OneToMany(mappedBy = "stopPoint", cascade = CascadeType.ALL)
List<AssocLangueCodeBeGare> assocLangueCodeBeGares;
#EDIT1
I think its caused by my EmbeddedId because it was null!! the mapping does'nt set correct value in the embedable object?
here is the embedable object :
#Embeddable
public class AssocLangueCodeBeGareFK implements Serializable {
private String id_langue;
private Long id_gare;
#Override
public int hashCode() {
thanks a lot!
The insertable = false and updatable = false prevent the value from the mapping from being written to the database. You need to have another mapping to that database column with the value set, or it won't get into the database. While making your OneToMany the ID works, so would the MapsId annotation in the current code as JPA will then set the value in your embeddable ID. Or you could have manually added the value from the referenced class into the appropriate AssocLangueCodeBeGare.key fields.
Finaly i find a solution :
I use #IdClass(AssocLangueCodeBeGareFK.class on AssocLangueCodeBeGare
And i put #id on
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(nullable = false, name = "ID_GARE", referencedColumnName = "ID_GARE")
private StopPoint stopPoint;
#Id
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "ID_LANGUE", referencedColumnName = "ID_LANGUE")
private Langue langue;
I rename on my AssocLangueCodeBeGareFK class name of my property to be the same as the AssocLangueCodeBeGare class :
private Long stopPoint;
private String langue;
now all work like a charm.
if it helps...

JPA Delete with 2 Interlocking Entity Classes

I have 2 EJB 3 Entity Beans:
#Entity
public class Organisation
{
#Id
#Column(length = 64)
private String guid;
private String name;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
#JoinColumn(name = "home_unit_fk", nullable = true)
private Unit homeUnit;
}
#Entity
public class Unit
{
#Id
#Column(length = 64)
private String guid;
private String name;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "organisation_fk", nullable = false)
private Organisation organisation;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "parent_unit_fk", nullable = true)
private Unit parentUnit;
#OneToMany(mappedBy = "parentUnit", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE)
#OrderBy("shortName")
#OptimisticLock(excluded = true)
private Set<Unit> childUnits;
}
If I do a delete on the Organisation using standard Dao :
public int deleteByGuid(final String guid)
{
final Query query = entityManager.createQuery("delete from " + getPersistentClass().getName() + " where guid = :guid");
query.setParameter("guid", guid);
return query.executeUpdate();
}
But I get the following exception:
com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Cannot delete or update a parent row: a foreign key constraint fails (config.unit, CONSTRAINT FK27D184F5D4393D FOREIGN KEY (organisation_fk) REFERENCES organisation (guid))
I don't get it. What am I doing wrong? Shouldn't JPA/Hibernate perform deletes on both the Unit and the Organisation within the same transaction?
A bulk delete query does not load objects into memory and it bypasses any cascade specified on associations.
I would code your delete method as:
public int deleteByGuid(final String guid){
Organization org = entityManager.find(Organization.class, guid);
entityManager.remove(org);
}
If you use a Query to do bulk updates, the operation is delegated to the database directly. If you wish to delete child objects, you have to set a DELETE CASCADE trigger at the "database" level.
By loading the object and removing it, Hibernate will trigger the cascade at the "object" level.
More info available at: http://twasink.net/blog/2005/04/differences-in-behaviour-between-hibernate-delete-queries-and-the-old-way/

Categories

Resources