1

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.

Community
  • 1
  • 1
Georgios Syngouroglou
  • 18,813
  • 9
  • 90
  • 92
  • What caching options are you using? If you executed a find or query, the object should have been read into the cache, so a second select wouldn't be necessary. The code you are calling into should be using the same EntityManager for both calls to avoid reading in the entity a second time. – Chris Dec 01 '15 at 15:10
  • Thank you very much for your comment. I edit the question. I include the persistence.xml for the example source code. I am not using cache. – Georgios Syngouroglou Dec 02 '15 at 14:22
  • 1
    I don't think this will work without caching or using some sort of extended persistence unit so that the EntityManager used in find method is the same one used for the save. Are they in the same transaction context? – Chris Dec 02 '15 at 15:19
  • I am not using a service layer and i cannot use the same EntityManager for this two transactions. I implement the business logic into the controller. Separating the business logic to a service layer i can use the same EntityManager for the two repository calls. If i use EntityManager cache (Layer 1) then this is an answer to my question. This SO question helped me also (http://stackoverflow.com/questions/23118789/spring-mvc-controller-shouldnt-be-transactional-why) – Georgios Syngouroglou Dec 02 '15 at 16:04

1 Answers1

2

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.

Georgios Syngouroglou
  • 18,813
  • 9
  • 90
  • 92