Reputation: 978
I've been using Eclipselink in my project for a long time, but today I came across a problem, and I can not solve it.
The problem is this: at first I search my "Function" object in an instance of my program, after that in another instance I search and change that same record (changed the "Interval" and "Date" column) and saved. After this in the first instance I change only the "Interval", but in the database the "Date" field has been changed even if I have not changed, Eclipselink (in the first instance) generates in the UPDATE the change of the field "Date" only because the "Function" of the first instance is outdated with the database, whereas changes were made to the registry in the meantime, between searching the object and saving it.
When analyzing the querys generated in the database i noticed that Eclipselink generates a SELECT before each UPDATE, but in my case i would not need it, how can I make Eclipselink only add in the UPDATE the fields that had changes? And not those that differ from the database.
My Eclipselink configuration:
"javax.persistence.jdbc.driver", "com.mysql.cj.jdbc.Driver"
"javax.persistence.jdbc.url", "jdbc:mysql://{IP}:3306/{USER}?useTimezone=true&serverTimezone=America/Sao_Paulo&autoReconnect=true&zeroDateTimeBehavior=convertToNull"
"javax.persistence.jdbc.user", user
"javax.persistence.jdbc.password", password
"eclipselink.cache.shared.default", "false"
"eclipselink.logging.level", "WARNING"
"eclipselink.query-results-cache", "false"
"eclipselink.refresh", "true"
"eclipselink.weaving", "static"
"connection.autoReconnectForPools", "true"
"connection.autoReconnect", "true"
Code:
Function function = FunctionDAO.getFunctionByName("A");
function.setInterval(0);
...
EntityManager manager = {config};
manager.getTransaction().begin();
manager.merge(function);
manager.getTransaction().commit();
manager.close();
import java.io.Serializable;
import java.util.Date;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import javax.persistence.Temporal;
import javax.persistence.TemporalType;
@Entity
@Table(name = "function")
public class Function extends ModelAudit implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "name")
private String name;
@Column(name = "interval")
private Integer interval;
@Temporal(TemporalType.TIMESTAMP)
@Column(name = "date")
private Date date;
...
getters and setters
Upvotes: 2
Views: 795
Reputation: 21145
I don't like DAO objects as this problem is common one when using the find/save pattern, and why optimistic locking is strongly recommended. Any number of modifications could have occurred from the time you read in the entity up until the time you call save/merge on it, and if your method is wrapped in its own transaction and just uses em.merge(), your instance overwrites what is in the database entirely. The longer you keep an entity around, the better the chance some other process has changed it and made your copy of the data stale.
Options:
Use some form of optimistic locking. When reading in an entity, it will have versioning so that when you call em.merge if the version in the database is ahead of what you pass in, it will give an exception to notify you of a potential conflict.
Implement DAO changeValue methods that only update selection of fields within the entity. For example a setIntervalByName(String name, Integer interval) method which could use a JPA update query to avoid the select if you didn't care to read in the entity, or use the same functionality similar to your getFunctionByName method.
Using both allows passing around the object for complex changes such as for REST api that might serialize the entity for get/put requests, while still allowing optimization to avoid reading in the entity graph for simple changes elsewhere.
Upvotes: 2