Hibernate: #PreRemove executed, but records didn't delete - java

There is a binding entity of many-to-many relation. When I delete dependent entities, I expect this entity to be deleted too. The preRemove method is called, but hibernate does not build a deletion request.
What am I doing wrong?
Please help, been 3 days trying to solve the problem((
#Entity
#Data
#Table(name = "SOME_TABLE_NAME")
#IdClass(ExpertAndRequestId.class)
public class ExpertAndRequest implements Serializable {
#Id
private Long managerId;
#Id
private Long requestId;
private String comment;
#ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
#JoinColumn(name = "managerId", updatable = false, insertable = false, referencedColumnName = "id")
//#OnDelete(action = OnDeleteAction.CASCADE)
private Manager manager;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "requestId", updatable = false, insertable = false, referencedColumnName = "id")
private ParticipantRequest request;
#PreRemove
public void preRemove(){
// THIS METHOD EXECUTED
}
}

Related

#ManyToOne in self-referencing entity with composite key - #IdClass, java, hibernate

Spent 3 days looking for a solution and finally I came here for community wisdom.
I have self-referencing entity like follows:
#Getter
#Setter
#AllArgsConstructor
#NoArgsConstructor
#Entity
#IdClass(CompositeUserId.class)
#Table(name = "user", schema = "dbo")
public class User implements Serializable {
#Id
#Column(name = "id")
private Integer id;
#Id
#Column(name = "first_name")
private String firstName;
#Id
#Column(name = "last_name")
private String lastName;
#ManyToOne
#JoinColumn(name = "parent_id", referencedColumnName = "id", insertable = false, updatable = false)
#JoinColumn(name = "first_name", referencedColumnName = "first_name", insertable = false, updatable = false)
#JoinColumn(name = "last_name", referencedColumnName = "last_name", insertable = false, updatable = false)
private User parent;
#OneToMany(mappedBy = "parent", fetch = FetchType.EAGER)
private Set<User> children;
my CompositeUserId.class:
#Getter
#Setter
#EqualsAndHashCode
#AllArgsConstructor
#NoArgsConstructor
public class UserCompositeId implements Serializable {
private Integer id;
private String firstName;
private String lastName;
When I try retrieve all data from my user table I get error:
org.springframework.orm.jpa.JpaObjectRetrievalFailureException: Unable to find ...User with id UserCompositeId#19e66569; nested exception is javax.persistence.EntityNotFoundException:
I suppose there might be some kind of mistake in the #JoinColumn block.
Here is the sql query causing the error:
SELECT *
FROM dbo.user ur1 LEFT OUTER JOIN dbo.user ur2 ON ur1.first_name=ur2.first_name AND ur1.parent_id=ur2.id AND ur1.last_name=ur2.last_name
WHERE ur1.first_name='First Name' AND ur1.id=130 AND ur1.last_name='Last Name'
I ensured that the request does not return anything in the database by running it manually, but found out that if I will change id to parent_id it will return data, so again, probably some mistake in #JoinColumn block
You need you use #NotFound(action=NotFoundAction.IGNORE). If there is no records then it will assign null to it.
#ManyToOne
#JoinColumn(name = "parent_id", referencedColumnName = "id", insertable = false, updatable = false)
#JoinColumn(name = "first_name", referencedColumnName = "first_name", insertable = false, updatable = false)
#JoinColumn(name = "last_name", referencedColumnName = "last_name", insertable = false, updatable = false)
#NotFound(action=NotFoundAction.IGNORE)
private User parent;
The foreign key columns need different names than the primary key columns, e.g. like this (notice the 'parent' prefix in name):
#JoinColumn(name = "parent_id", referencedColumnName = "id", updatable = false, insertable = true)
#JoinColumn(name = "parent_firstname", referencedColumnName = "firstname", updatable = false, insertable = true)
#JoinColumn(name = "parent_lastname", referencedColumnName = "lastname", updatable = false, insertable = true)
#ManyToOne(fetch = FetchType.EAGER)
private UserEntity parent;
Additionally, the insertable value should be true, since otherwise no associations are persisted during the insert.
Be aware that a parent needs to be saved before a child can be associated. Since the parent fk is not updatable, is must be set on a newly created child before it is saved.
Feel free to have a look at this project containing the complete sample code:
Entity:
https://github.com/fladdimir/many-to-one-self-ref-composite-id/blob/master/src/main/java/org/demo/UserEntity.java
Integration-Test:
https://github.com/fladdimir/many-to-one-self-ref-composite-id/blob/master/src/test/java/org/demo/DemoApplicationTests.java
Also it is good practice to synchronize bidirectional associations:
https://vladmihalcea.com/jpa-hibernate-synchronize-bidirectional-entity-associations/
The sample entity could e.g. use some methods like these:
public void addChild(UserEntity child) {
child.parent = this; // sync owning side of the association
this.children.add(child);
}
public void setParent(UserEntity parent) {
parent.addChild(this);
}

jpa one to one relationship issue when persist

I have 2 models with one to one relationship
#Entity
#Table(name = "Form_Item_Production")
public class FormItemProduction {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
#Column(name = "item_id", nullable = false)
private Long itemId;
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "shift_lookup_id", insertable = false, updatable = false)
private AppLookup appLookup;
getter and setter
}
The other one
#Entity
#Table(name = "App_Lookup")
public class AppLookup {
#Id
#GeneratedValue
#Column(name = "Lookup_Id", nullable = false)
private Long lookupId;
#Column(name = "Lookup_Name", length = 30, nullable = false)
private String lookupName;
getter and setter
}
When I try to persist to save the formitemproduction values
public boolean insertItem(List<FormItemProduction> f) {
for (FormItemProduction i : f) {
System.out.println("A" + i.getAppLookup().getLookupId()); // prints the correct id of applookup
i.setItemId(null);
entityManager.persist(i);
}
entityManager.flush();
return true;
}
I get this error
javax.persistence.PersistenceException: org.hibernate.PersistentObjectException: detached entity passed to persist: com.mamee.factory.security.entity.AppLookup
From my understanding this is unidirectional one to one mapping so I don't quite understand why I'm getting the error detached?
You have itemId which can't be null.
#Column(name = "item_id", nullable = false)
private Long itemId;
and you actually set itemId to null
i.setItemId(null);
So this line
entityManager.persist(i);
Is not able to persist your data.
Fixed by changing from:
#OneToOne(cascade = CascadeType.ALL)
#JoinColumn(name = "shift_lookup_id", insertable = false, updatable = false)
private AppLookup appLookup;
to:
#OneToOne
#JoinColumn(name = "shift_lookup_id", insertable = true, updatable = true)
private AppLookup appLookup;
You have set cascade to ALL:
#OneToOne(cascade = CascadeType.ALL)
Which means you are cascading all operations down to the related field AppLookup.
You can set cascade to none, and you will no longer get your error, but no db operations will be executed for AppLookup field.

JoinColumn to an foreign key

I'm trying to achieve this with hibernate:
My codes is as follows:
Device model
#Entity
public class Device {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "fk_user", nullable = true, referencedColumnName = "ID")
private User user;
...
}
Statistics model
#Entity
public class Statistic {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
#Column(nullable = false)
private long id;
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({ #JoinColumn(name = "fk_device", nullable = false, referencedColumnName = "ID"),
#JoinColumn(name = "fk_device_user", nullable = false, referencedColumnName = "fk_user") })
private Device device;
...
}
However, there's an error saying there's no logical column fk_user in device.
Caused by: org.hibernate.MappingException: Unable to find column with logical name: fk_user in device
I suppose it is because fk_user is a foreign key. But how can I solve this? Thanks.
Based on you schema i would map the Device entity as follows:
public class Devince{
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumn(name = "user_id", nullable = true)
private User user;
...
}
}
You dont need the referencedColumnName as you are referring to the User entity primary key. If you would refer to non-primary key column, then that would be necessary.
Regarding the Statistic entity:
public class Statistic {
#ManyToOne(fetch = FetchType.LAZY)
#JoinColumns({ #JoinColumn(name = "device_id", nullable = false),
#JoinColumn(name = "device_user_id", nullable = false,
referencedColumnName = "user_id") })
...
}
}
Again, you only need the referencesColumnName in the second #JoinColumn.

Hibernate JPA IdentifierGenerationException: null id generated for class with #embeddedid

I am having trouble mapping my database domain model to the program entities in one case where the entity is essentially a join table (a period) which combines two other entities (a timeslot and a day). Another entity (a lesson) then has a reference to this period entity, determining when it occurs.
When I try to save a lesson with a new period using saveOrUpdate(lesson) hibernate throws an IdentifierGenerationException
org.hibernate.id.IdentifierGenerationException: null id generated for:class com.trials.domain.Period
The database looks like below (not the real database, just the key tables and columns)
In the java hibernate model, I have used an embedded id for the primary key of the period class and the lesson class then has a reference to a period.
Period.java
#Entity
#Table(name = "period")
public class Period{
#EmbeddedId
private PeriodId periodId;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "day_idday", nullable = false, insertable = false, updatable = false)
private Day day;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "timeslot_idtimeslot", nullable = false, insertable = false, updatable = false)
private Timeslot timeslot;
//constructors, getters, setters, hashcode, and equals
}
And the embedded id just has the primary key columns:
PeriodId.java
#Embeddable
public class PeriodId implements Serializable {
#Column(name = "timeslot_idtimeslot")
private int timeslotId;
#Column(name = "day_idday")
private int dayId;
//constructors, getters, setters, hashcode, and equals
}
Then there is the lesson class that uses the period defined as:
Lesson.java
#Entity
#Table(name = "lesson")
public class Lesson {
#Id
#Column(name = "idlesson")
private int lessonId;
#ManyToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
#JoinColumns({#JoinColumn(name = "period_timeslot_idtimeslot", nullable = false, updatable = false), #JoinColumn(name = "period_day_idday", nullable = false, updatable = false)})
private Period period;
//constructors, getters, setters, hashcode, and equals
}
The Timeslot and Day entity classes are both very basic pojos, and their ids use GenerationType.AUTO. So my problems are:
What causes this IdentifierGenerationException
How to avoid it while keeping the same database model
Thanks in advance
Put those guys
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "day_idday", nullable = false, insertable = false, updatable = false)
private Day day;
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "timeslot_idtimeslot", nullable = false, insertable = false, updatable = false)
private Timeslot timeslot;
inside the PeriodId class and throw away those ints. I have done a mapping similar to yours this way and it works.
I was able to create the following mapping for my case (scala code) and could totally throw away the #Embeddable class:
#Entity
#Table(name = "payment_order_item", schema = "pg")
#IdClass(classOf[PaymentOrderItem])
final class PaymentOrderItem extends Serializable{
#Id
#ManyToOne
#JoinColumn(name = "order_item_id", referencedColumnName = "id")
var orderItem: OrderItem = _
#Id
#ManyToOne
#JoinColumn(name = "payment_id", referencedColumnName = "id")
var payment: Payment = _
}
So the following should work for you then
#Entity
#Table(name = "period")
#IdClass(Period.class)
public class Period extends Serializable{
#Id
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "day_idday", referencedColumnName = "id", nullable = false)
private Day day;
#Id
#ManyToOne(fetch = FetchType.EAGER)
#JoinColumn(name = "timeslot_idtimeslot", referencedColumnName = "id", nullable = false)
private Timeslot timeslot;
//constructors, getters, setters, hashcode, and equals
}
On a first glance,
You're missing the generated value annotation in the embedded id class.
#Embeddable
public class PeriodId implements Serializable {
#GeneratedValue
#Column(name = "timeslot_idtimeslot")
private int timeslotId;
#GeneratedValue
#Column(name = "day_idday")
private int dayId;
//constructors, getters, setters, hashcode, and equals
}

JPA/Hibernate tries to store too many parameters in entity with composite Id (and doubling properties)

I have entity with composite Id
#Entity
#IdClass(value = BorrowId.class)
#Table(name = "BORROW")
public class Borrow {
#Id
#Column(name = "BOOK_ID", insertable = false, updatable = false)
private long bookId;
#Id
#Column(name = "BORROWER_ID", insertable = false, updatable = false)
private long borrowerId;
#Id
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
#JoinColumn(name = "BOOK_ID")
private Book book;
#Id
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST, CascadeType.MERGE, CascadeType.REFRESH})
#JoinColumn(name = "BORROWER_ID")
private Borrower borrower;
#Id
#Column(name = "BORROW_DATE")
private Date borrowDate;
#Column(name = "RETURN_DATE")
private Date returnDate;
BorrowId has properties (everything with getters/setters)
private long bookId;
private long borrowerId;
private Date borrowDate;
and equals,hashCode methods
when I'm trying to persist Borrow entity (book and borrower properties set to corresponding entities, then calling entityManager.persist(borrow); )
I've got in my logs:
Hibernate: insert into BORROW (BORROWER_ID, BOOK_ID, RETURN_DATE,
BORROW_DATE) values (?, ?, ?, ?) // this insert statement is correct
(that's a look of my table) Invalid value "5" for parameter
"parameterIndex" [90008-174]
So it looks like someone is doing something wrong :)
How to solve this? is my entity declaration faulty? (I wanted to use CompositeId to learn something about using it)
Thanks to #JB nizet
#Id
#Column(name = "BOOK_ID", insertable = false, updatable = false)
private long bookId;
#Id
#Column(name = "BORROWER_ID", insertable = false, updatable = false)
private long borrowerId;
#MapsId("bookId")
#ManyToOne(fetch = FetchType.LAZY, cascade = {CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.REFRESH})
#JoinColumn(name = "BOOK_ID")
private Book book;
#MapsId("borrowerId")
#ManyToOne(fetch = FetchType.EAGER, cascade = {CascadeType.PERSIST,
CascadeType.MERGE,
CascadeType.REFRESH})
#JoinColumn(name = "BORROWER_ID")
private Borrower borrower;
#Id
#Column(name = "BORROW_DATE")
private Date borrowDate;
Solves the problem, strange, because nowhere, in any example/tutorial there was no #MapsId used with #IdClass.
I'm not sure if now #JoinColumn is needed

Categories