If i have a @OneToMany relationship with @Cascade(CascadeType.SAVE_UPDATE) as follows
public class One {
private Integer id;
private List<Many> manyList = new ArrayList<Many>();
@Id
@GeneratedValue
public Integer getId() {
return this.id;
}
@OneToMany
@JoinColumn(name="ONE_ID", updateable=false, nullable=false)
@Cascade(CascadeType.SAVE_UPDATE)
public List<Many> getManyList() {
return this.manyList;
}
}
And Many class
public class Many {
private Integer id;
/**
* required no-arg constructor
*/
public Many() {}
public Many(Integer uniqueId) {
this.id = uniqueId
}
/**
* Without @GeneratedValue annotation
* Hibernate will use assigned Strategy
*/
@Id
public Integer getId() {
return this.id;
}
}
If i have The following scenario
One one = new One();
/**
* generateUniqueId method will Take care of assigning unique id for each Many instance
*/
one.getManyList().add(new Many(generateUniqueId()));
one.getManyList().add(new Many(generateUniqueId()));
one.getManyList().add(new Many(generateUniqueId()));
one.getManyList().add(new Many(generateUniqueId()));
And i call
sessionFactory.getCurrentSession().save(one);
Before going on
According to Transitive persistence Hibernate reference documentation, you can see
If a parent is passed to save(), update() or saveOrUpdate(), all children are passed to saveOrUpdate()
ok. Now Let's see what Java Persistence With Hibernate book Talks about saveOrUpdate method
Hibernate queries the MANY table for the given id, and if it is found, Hibernate updates the row. If it is not found, insertion of a new row is required and done.
Which can be translated according to
INSERT INTO ONE (ID) VALUES (?)
/**
* I have four Many instances added To One instance
* So four select-before-saving
*
* I DO NOT NEED select-before-saving
* Because i know i have a Fresh Transient instance
*/
SELECT * FROM MANY WHERE MANY.ID = ?
SELECT * FROM MANY WHERE MANY.ID = ?
SELECT * FROM MANY WHERE MANY.ID = ?
SELECT * FROM MANY WHERE MANY.ID = ?
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
INSERT INTO MANY (ID, ONE_ID) VALUES (?, ?)
Any workaround To avoid select-before-saving ??? Yes, You can either
- Add a @Version column (Not applied)
- Implement isTransient method provided by Hibernate interceptor (The option i have)
So as a way to avoid select-before-saving default behavior when using this kind of cascading, i have improved my code by assigning a Hibernate Interceptor to a Hibernate Session whose Transaction is managed by Spring.
Here goes my repository
Before (Without any Hibernate Interceptor): It works fine!
@Repository
public class SomeEntityRepository extends AbstractRepository<SomeEntity, Integer> {
@Autowired
private SessionFactory sessionFactory;
@Override
public void add(SomeEntity instance) {
sessionFactory.getCurrentSession().save(instance);
}
}
After (With Hibernate Inteceptor): something goes wrong (No SQL query is performed - Neither INSERT Nor SELECT-BEFORE-SAVING)
@Repository
public class SomeEntityRepository extends AbstractRepository<SomeEntity, Integer> {
@Autowired
private SessionFactory sessionFactory;
@Override
public void add(SomeEntity instance) {
sessionFactory.openSession(new EmptyInterceptor() {
/**
* To avoid select-before-saving
*/
@Override
public Boolean isTransient(Object o) {
return true;
}
}).save(instance);
}
}
My question is: Why Spring does not persist my Entity and its relationships when using Hibernate Interceptor and what should i do as workaround to work fine ???