JPA/EclipseLink handling of #Version - jpa

In an application using EclipseLink 2.5 and container-managed transactions that needs to merge detached entities from time to time, each entity contains a field with the #Version annotation. Some implementation of optimistic locking is necessary, since enitities are mappted to DTOs and sent to the client, which might then request an update on these entities based on the changes they have made to the corresponding DTOs. The problem I am facing is that whenever persist() or merge() are called on an entity, the corresponding entity being added to the persistence context in the case of persist() or the updated entity returned by merge() do not contain the updated version field. To demonstrate this through an example, suppose we have the following entity:
#Entity
public class FooEntity implements Serializable {
#Id
#GeneratedValue(generator = "MyGenerator")
private Long fooId;
#Version
private Long version;
#Column(nullable = false)
private String description;
// generic setters and getters
}
This entity then gets persisted/merged in an EJB in a fashion similar to the following:
#Stateless
public class FooEjb {
#PersistenceContext(unitName = "FooApp")
private EntityManager entityManager;
public FooEntity create() {
FooEntity entity = new FooEntity();
entity.setDescription("bar");
entityManager.persist(entity);
return entity;
}
public FooEntity update(Long fooId, String description) {
FooEntity entityToUpdate = entityManager.find(FooEntity.class, fooId);
if (entityToUpdate != null) {
entityToUpdate.setDescription(description);
return entityManager.merge(entityToUpdate);
}
return null;
}
}
Calling these EJB methods shows the following behavior:
FooEntity newEntity = fooEjbInstance.create();
newEntity.getFooId(); // returns the generated, non-null value; for the sake of example 43L
newEntity.getVersion(); // returns null
entityManager.find(FooEntity.class, 43L).getVersion(); // returns 1L
// entity with fooId == 42L exists and has a version value of 1L
FooEntity updatedEntity = fooEjbInstance.update(42L, "fhtagn");
updatedEntity.getVersion(); // returns the old value, i.e. 1L
entityManager.find(FooEntity.class, 42L).getVersion(); // returns 2L
This makes the returned entity unsuitable for passing to the client, as any state changes made by the client cannot be persisted due to the merge/persist call rightly causing an OptimisticLockException.
The EJB methods are not explicitly annotated with #TransactionAttribute, which per JPA specs should cause the default value of TransactionAttributeType.REQUIRED to be applied. The current theory is that the phenomenon perceived here has to do with the version field being updated only when the transaction is committed. Since by the time one of the EJB methods above returns, its associated transaction has not yet been committed (and will in fact be committed immediately after the method returns), the version field has not yet been updated. There was a mention of in object vs. in cache storing of the version filed in this question, but I have not been able to find definitive documentation on this. Is this as a whole working as designed, either according to JPA 2.0 or the EclipseLink implementation? If so, how could I best deal with the aforementioned problem?

Merge and persist don't need to immediately go to the database, so operations that depend on, triggers, sequencing and versions might need to call flush or commit to have those values set. Flush forces the context to synchronize with the database, and should set the values appropriately in managed objects. ID generation can be set on persist calls - this can happen when sequences allow for pre-allocation, but not usually when identity objects or triggers are used.
Since an EntityManager context represents a transaction, it is completely isolated from other contexts/transactions. Until the transaction commits, version and other changes cannot be picked up by other processes anyway, so it shouldn't matter when synchronization occurs to other processes. JPA states that most exceptions occur either on the persist/merge call or can be delayed until the context synchronizes with the database (flush/commit) depending on the nature of the operation. Optimistic locking is meant for systems where these collisions are infrequent and retries are less expensive than pessimistic locking every operation

Related

JPA cache behaviour when invoke count() method on Spring Data JPA Repository

I'm writing a transactional junit-based IT test for Spring Data JPA repository.
To check number of rows in table I use side JDBCTemplate.
I notice, that in transactional context invoking of org.springframework.data.repository.CrudRepository#save(S) doesn't take effect. SQL insert in not performed, number of rows in table is not increased.
But If I invoke org.springframework.data.repository.CrudRepository#count after the save(S) then SQL insert is performed and number of rows is increased.
I guess this is behavior of JPA cache, but how it works in details?
Code with Spring Boot:
#RunWith(SpringRunner.class)
#SpringBootTest
public class ErrorMessageEntityRepositoryTest {
#Autowired
private ErrorMessageEntityRepository errorMessageEntityRepository;
#Autowired
private JdbcTemplate jdbcTemplate;
#Test
#Transactional
public void save() {
ErrorMessageEntity errorMessageEntity = aDefaultErrorMessageEntity().withUuid(null).build();
assertTrue(TestTransaction.isActive());
int sizeBefore= JdbcTestUtils.countRowsInTable(jdbcTemplate, "error_message");
ErrorMessageEntity saved = errorMessageEntityRepository.save(errorMessageEntity);
errorMessageEntityRepository.count(); // [!!!!] if comment this line test will fail
int sizeAfter= JdbcTestUtils.countRowsInTable(jdbcTemplate, "error_message");
Assert.assertEquals(sizeBefore+1, sizeAfter);
}
Entity:
#Entity(name = "error_message")
public class ErrorMessageEntity {
#Id
#GeneratedValue(strategy = GenerationType.AUTO)
private UUID uuid;
#NotNull
private String details;
Repository:
public interface ErrorMessageEntityRepository extends CrudRepository<ErrorMessageEntity, UUID>
You are correct this is a result of how JPA works.
JPA tries to delay SQL statement execution as long as possible.
When saving new instances this means it will only perform an insert if it is required in order to get an id for the entity.
Only when a flush event occurs will all changes that are stored in the persistence context flushed to the database. There are three triggers for that event to happen:
The closing of the persistence context will flush all the changes. In a typical setup, this is tight to a transaction commit.
Explicitly calling flush on the EntityManager which you might do directly or when using Spring Data JPA via saveAndFlush
Before executing a query. Since you typically want to see your changes in a query.
Number 3 is the effect you are seeing.
Note that the details are a little more complicated since you can configure a lot of this stuff. As usual, Vlad Mihalcea has written an excellent post about it.
In order to make the test data not pollute the database, when using the unit test of Spring-test, the transaction will be rolled back by default, that is, #Rollback is true by default. If you want to test the data without rolling back, you can set #Rollback(value = false) . If you are using a MySQL database, after setting up automatic rollback, if you find that the transaction is still not rolled back, you can check whether the database engine is Innodb , because other database engines such as MyISAM and Memory do not support transactions.

JPA Entity out of sync with database using multiple sessions

I have a Java EE application with JPA implemented using Eclipselink. I have implemented a basic user login system using the ExternalContext session map. However it seems that often a session becomes out of sync with the database.
The basic process is
1. User A Creates a BidOrder entity.
2. User B creates an AskOrder entity.
3. A monitor checks the two orders match and if so creates an OrderBook entity
4. The changes are Pushed to all users (using Primefaces 5.3 Push)
When I check the prices I use this method in my SessionScoped backing bean for my main view:
public void findLatestPrices()
{
logger.log(Level.INFO, "findLatestPrices with user {0} ",user.getUserId());
findAllOrderBooks();
latestPricesId = new ArrayList<String>();
setLatestPricesResults(request.findLatestPrices());
for (Iterator<OrderBook> it = orderBookResults.iterator(); it.hasNext();)
{
OrderBook orderBook = it.next();
logger.log(Level.INFO, "Found {0} orderbook price", orderBook.getPrice());
}
logger.log(Level.INFO, "End of findLatestPrices with user {0} ",user.getUserId());
}
This calls my RequestScoped Stateful ejb:
public List<OrderBook> findLatestPrices() {
List<OrderBook> orderBooks;
List<OrderBook> orderBooksFiltered;
Map<Member, OrderBook> map = new LinkedHashMap<Member, OrderBook>();
try {
orderBooks = em.createNamedQuery(
"findAllOrderBooks").setHint("javax.persistence.cache.storeMode", "REFRESH")
.getResultList();
for (Iterator<OrderBook> it = orderBooks.iterator(); it.hasNext();) {
OrderBook orderBook = it.next();
Member member = orderBook.getBidOrderId().getMember();
map.put(member, orderBook);
logger.log(Level.INFO, "findLatestPrices orderbook price : {0}",
orderBook.getPrice());
logger.log(Level.INFO, "findLatestPrices orderbook bidorder member : {0}",
orderBook.getBidOrderId().getMember().getMemberId());
logger.log(Level.INFO, "findLatestPrices orderbook lastupdate : {0}",
orderBook.getLastUpdate());
}
...}
I create the EntityManager in the above bean in the following way:
#PersistenceContext
private EntityManager em;
From the logging I can see that sessions return data that is out of sync with the database, i.e. single results when I'd expect two etc. As you can see I've tried setHint to refresh the cache. I've also tried #Cacheable(false) on my OrderBook entity and #Cache(refreshAlways=true) but to no avail.
I'm sending a push event in the #PostPersist of the entity that is created (OrderBook). The javascript event handler in my xhtml page then calls the following remotecommand:
<p:remoteCommand name="updateWidget"
autoRun="false"
actionListener="#{parliamentManager.findLatestPrices}"
update="resultDisplay"
onstart="onStart()"
oncomplete="onComplete()"
onsuccess="onSuccess()"
onerror="onError()">
<f:actionListener binding="#{parliamentManager.findTraders()}" />
<f:actionListener binding="# {parliamentManager.findPortfolios()}" />
</p:remoteCommand>
It seems that often the results of findLatestPrices does not include the latest OrderBook entities for all sessions. Is it possible that an entity is not persisted immediately on a call to #PostPersist, working on the theory that the push is sent to some sessions before the entity is fully persisted and reflected by JPA?
To demonstrate I added a simple command button to call updateWidget() manually. If the session is not updated and I click the button it always updates to the latest data.
Thanks,
Zobbo
There is no locking between sessions, so I'm not quite sure what you mean. Optimistic locking to prevent overwriting stale data is recommended in most JPA provider documentation.
You haven't shown or specified how you are obtaining the EntityManager, or how long it is lived, but there are two levels of caching. The first is the EntityManager itself, which is used to track changes to manage entities and maintain their identity. JPA allows but doesn't mandate a second level of caching, shared at the EntityManagerFactory level. This second level of cache is what the javax.persistence.cache.storeMode is aimed at - it controls what happens when entities are pulled from the shared cache. If the entities are already loaded in the first level cache, because this is meant to represent a transactional scope, they are returned as-is, preserving any unsynchronized changes the application might have been made and JPA provider is required to track.
The only way JPA gives to force a refresh of a managed entity is by calling em.refresh(), though it can also be accomplished by calling em.clear, then re-reading the entity using the javax.persistence.cache.storeMode refresh hint. EclipseLink also has an "eclipselink.refresh" query hint that can be used to force the query to refresh the managed entity instance.

Why do changes to my JPA entity not get persisted to the database?

In a Spring Boot Applicaion, I have an entity Task with a status that changes during execution:
#Entity
public class Task {
public enum State {
PENDING,
RUNNING,
DONE
}
#Id #GeneratedValue
private long id;
private String name;
private State state = State.PENDING;
// Setters omitted
public void setState(State state) {
this.state = state; // THIS SHOULD BE WRITTEN TO THE DATABASE
}
public void start() {
this.setState(State.RUNNING);
// do useful stuff
try { Thread.sleep(2000); } catch(InterruptedException e) {}
this.setState(State.DONE);
}
}
If state changes, the object should be saved in the database. I'm using this Spring Data interface as repository:
public interface TaskRepository extends CrudRepository<Task,Long> {}
And this code to create and start a Task:
Task t1 = new Task("Task 1");
Task persisted = taskRepository.save(t1);
persisted.start();
From my understanding persisted is now attached to a persistence session and if the object changes this changes should be stored in the database. But this is not happening, when reloading it the state is PENDING.
Any ideas what I'm doing wrong here?
tl;dr
Attaching an instance to a persistence context does not mean every change of the state of the object gets persisted directly. Change detection only occurs on certain events during the lifecycle of persistence context.
Details
You seem to misunderstood the way change detection works. A very central concept of JPA is the so called persistence context. It is basically an implementation of the unit-of-work pattern. You can add entities to it in two ways: by loading them from the database (executing a query or issuing an EntityManager.find(…)) or by actively adding them to the persistence context. This is what the call to the save(…) method effectively does.
An important point to realize here is that "adding an entity to the persistence context" does not have to be equal to "stored in the database". The persistence provider is free to postpone the database interaction as long as it thinks is reasonable. Providers usually do that to be able to batch up modifying operations on the data. In a lot of cases however, an initial save(…) (which translates to an EntityManager.persist(…)) will be executed directly, e.g. if you're using auto id increment.
That said, now the entity has become a managed entity. That means, the persistence context is aware of it and will persist the changes made to the entity transparent, if events occur that need that to take place. The two most important ones are the following ones:
The persistence context gets closed. In Spring environments the lifecycle of the persistence context is usually bound to a transaction. In your particular example, the repositories have a default transaction (and thus persistence context) boundary. If you need the entity to stay managed around it, you need to extend the transaction lifecycle (usually by introducing a service layer that has #Transactional annotations). In web applications we often see the Open Entity Manager In View Pattern, which is basically a request-bound lifecycle.
The persistence context is flushed. This can either happen manually (by calling EntityManager.flush() or transparently. E.g. if the persistence provider needs to issue a query, it will usually flush the persistence context to make sure, currently pending changes can be found by the query. Imagine you loaded a user, changed his address to a new place and then issue a query to find users by their addresses. The provider will be smart enough to flush the address change first and execute the query afterwards.

How to use JPA to persist same object to two different db's without distributed transaction?

I want to persist the same entity to a MySQL database and Postgres database (essentially one is a real-time clone of the other). Conceptually, I want to do this in a single method:
EntityManager mysql = ...;
EntityManager postgres = ...;
MyEntity e = new MyEntity();
e.setStuff();
mysql.persist(e);
postgres.persist(e);
The MyEntity class specifies a #GeneratedValue strategy of IDENTITY for its #Id field, and the two data sources are non-XA data sources.
JPA/JTA seems to want to do this in a distributed transaction, which I believe is due to how the transaction boundaries are determined for container-managed transactions, and I get an error because the data sources are non-XA. I can define the data sources as XA sources so the above works as a distributed transaction, but for the immediate need, that's really unnecessary. I don't really care that the two persists are in the same transaction -- In fact, if one fails and the other succeeds, that's fine too (at least for now).
Is there a way to persist the same object to multiple databases with non-XA data sources and still use container-managed transactions? Related, if I want to do a series of persists with multiple objects and multiple data sources in a single method, is there a way to do that with non-XA data sources? I'm using EclipseLink with GlassFish 4.0.
Well, didn't discover how to do it with container-managed transactions, but did it with bean-managed transactions. Injected a UserTransaction resource, and wrapped each persist between a begin/commit pair:
#Stateless
#TransactionManagement(TransactionManagementType.BEAN)
public class MyClass
{
#Resource private UserTransaction utx;
public void doStuff()
{
EntityManager mysql = ...;
EntityManager postgres = ...;
MyEntity e = new MyEntity();
e.setStuff();
try {
utx.begin();
mysql.persist(e);
utx.commit();
utx.begin();
postgres.persist(e);
utx.end();
} catch (...) {
...
}
}
}
I've never tried using bean-managed transactions before, and even though this isn't for production use and isn't especially elegant, if there's something fundamentally wrong with this, I'd appreciate somebody pointing out the proper way.

JPA, scope, and autosave?

I am using JPA and lets say I do something like this
public class MoRun extends Thread {...
public void run() {
final EntityManagerFactory emFactory = Persistence.createEntityManagerFactory("pu");
EntityManager manager = emFactory.createEntityManager();
manager.setFlushMode(FlushModeType.COMMIT);
someMethod(manager);
...
}
public void someMethod(EntityManager manager){
Query query = manager.createNamedQuery("byStates");
List<State> list = query.getResultList();
for (State state : list) {
if(someTest)
state.setValue(...)
}
...
}
So for those objects that pass "someTest" and values are updated are those changes automatically persisted to the db even though there is no transaction and I don't explicitly "manager.save(state)" the object? I ask because it seems like it is and I was wondering if the flush is doing it?
According to the javadoc of FlushMode (I'm assuming this is a JPA 1.0 question), and as pointed out by #Konrad:
If there is no transaction active, the persistence provider must not flush to the database.
Since you're very likely using a transaction-type="RESOURCE_LOCAL" for your persistence unit, since I don't see any begin/commit surrounding your calls to your EntityManager (which is not good, more on this just after), for me there is no transaction active so I wouldn't expect anything to be flushed.
Anyway, as reminded in the nice JPA Concepts page:
With <persistence-unit transaction-type="RESOURCE_LOCAL">
you are responsible for EntityManager
(PersistenceContext/Cache) creating
and tracking...
You must use
the EntityManagerFactory to get an
EntityManager
The resulting
EntityManager instance is a
PersistenceContext/Cache
An
EntityManagerFactory can be injected via the
#PersistenceUnit annotation only (not #PersistenceContext)
You are
not allowed to use #PersistenceContext to refer to a unit
of type RESOURCE_LOCAL
You
must use the EntityTransaction API to begin/commit around every call to your
EntityManger
Calling
entityManagerFactory.createEntityManager()
twice results in two separate
EntityManager instances and therefor
two separate PersistenceContexts/Caches.
It is
almost never a good idea to have more than one instance of an
EntityManager in use (don't create a
second one unless you've destroyed the
first)
So, in my opinion, you should fix your code here, there is no real point at wondering about unexpected behavior if your code is not correct. Just performs calls to your EntityManager inside a transaction.
How do you know there is no transaction? Are you using it from EJB? In that case I bet there is a transaction.
From docs (http://java.sun.com/javaee/6/docs/api/javax/persistence/FlushModeType.html):
If FlushModeType.COMMIT is set, the
effect of updates made to entities in
the persistence context upon queries
is unspecified.
If there is no transaction active, the
persistence provider must not flush to
the database.
If you are in transaction, attached entities (i.e. those loaded in the same transaction) are automatically recorded to database.