I am using Spring Data(ver1.9) - JPA - EclipseLink(2.6.1) - Apache Tomcat. I want to update an entity object without checking for its existence.
Example Entity,
@Entity
@Table(schema = "customSchema")
public class Person implements Serializable {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false)
private Integer id;
@Column(nullable = false, length = 60)
private String firstName;
public Person() {
this.id = null;
this.firstName = "";
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getFirstName() {
return firstName;
}
public void setFirstName(String firstName) {
this.firstName = firstName;
}
}
Example Spring Data Repository,
@Repository
public interface PersonRepository extends JpaRepository<Person, Integer> {
}
The code that i execute and the sql that is generated,
int id = 5; // This id is existing in my database.
Person Person = personRepository.findOne(id);
// SQL: One SELECT is executed. This is normal.
person.setFirstName("aNewFirstName");
personRepository.saveAndFlush(person);
// SQL: One SELECT is executed and then an update is executed.
The persistence.xml, (layer 2 cache is disabled for thread safety, it can be enabled selectively using @Cacheable annotation)
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.1" xmlns="http://xmlns.jcp.org/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence http://xmlns.jcp.org/xml/ns/persistence/persistence_2_1.xsd">
<persistence-unit name="my_peristenceUnit" transaction-type="RESOURCE_LOCAL">
<provider>org.eclipse.persistence.jpa.PersistenceProvider</provider>
<class>package.to.entity.Person</class>
<exclude-unlisted-classes>false</exclude-unlisted-classes>
<shared-cache-mode>NONE</shared-cache-mode>
</persistence-unit>
</persistence>
My problem is existing in the saveAndFlush command. EclipseLink is checking the fields from the table row to determine which of them have changed using a SELECT sql command. This helps to create an update command that contains only the edited fields.
The same happens with the delete command!
Question
Is there any way to avoid the select sql execution of the update(saveAndFlush) or delete(delete) commands?
What to do if i prefer to update the whole row of the table? Without checking which fields have changed?
I have seen another SO question similar to this but this solution is not working for my example. I used the @ExistenceChecking(value = ExistenceType.ASSUME_EXISTENCE) annotation for the Person Entity and nothing changed when the saveAndFlush command was executed.
EclipseLink has two different cache levels.
The first one is the Persistence Unit Cache. This is a shared cache (L2) or Layer 2 cache. This cache is the one you disabled,
<shared-cache-mode>NONE</shared-cache-mode>
Even if you disable it you can use it explicitly using the @Cacheable annotation.
The second is the Persistence Context Cache. This is an isolated cache (L1) that services operations within an EntityManager. This cache level is existing in your application. This cache is the key to avoid this behavior from the EclipseLink provider.
You call two different times your repository methods. To make use of the L1 cache this calls have to use the same EntityManager. In your case you are using different EntityManagers. Below an example to use the same EntityManger for your two different repository calls.
@Service
public class MyEnityService {
@Transactional
public Boolean create() {
int id = 5; // This id is existing in my database.
Person Person = personRepository.findOne(id);
// SQL: One SELECT is executed. This is normal AND CACHED FROM YOUR ENTITY MANAGER.
person.setFirstName("aNewFirstName");
personRepository.saveAndFlush(person);
// SQL: ONLY ONE update is executed. NOTHING ELSE.
return true;
}
}
Be carefull, the @Transactional annotation is not working on a Controller method. You have to use a service for this.