How do we specify LockMode in EJB3 Persistence NamedQuery? I want to add Pessimistic LockMode to my existing select so that I can update if necessary but surprisingly Query object doesnot have setLockMode(xxx) method ( My understanding was if JPA, asubset of EJB3 persistence, exposes setLockMode, EJB3 persistence should have the method available too).
Query query = em.createNamedQuery("findOptoutStudent");
query.setParameter("optoutIndicator", optoutIndicator);
List<Student> students = query.getResultList();
return students.get(0);
I would assume I dont have to change the query manually to "select for update".
Thanks
Kevin
Pessimistic Lock Mode :
PESSIMISTIC_READ which represents a shared lock. Lock request fails when another entity manager has acquired PESSIMISTIC_WRITE.
PESSIMISTIC_WRITE which represents an exclusive lock. Lock request fails when another entity manager has acquired either of the locks on the object.
Applying lock on the object
entityManager.lock(object, LockModeType.PESSIMISTIC_READ)
Releasing the lock afterwards
entityManager.lock(object, LockModeType.NONE)
Related
I'm trying to use Kundera as a JPA implementation over cassandra (namely for the transaction management capabilities).
2 questions:
FIRST QUESTION
is it true that transaction management does not support native queries? i.e. if I do:
EntityManagerFactory emf = Persistence.createEntityManagerFactory("piccandra", getCassandraProperties());
EntityManager em = emf.createEntityManager();
em.setFlushMode(FlushModeType.COMMIT);
em.getTransaction().begin();
Query q = em.createNativeQuery("insert into photographer (photographer_id, photographer_name) values ('id1', 'name1')");
q.executeUpdate();
em.getTransaction().rollback();
the rollback will not work?
SECOND QUESTION
how do I perform optimistic locking (aka CAS) on persist?
In cassandra I can run:
UPDATE photographer SET name = 'x' WHERE id = 1 AND update_date <= some-timestamp
When using Kundera I want to persist an entity only if it hasn't changed by someone else on the base store. can I do that?
EDIT:
I see that JPA does support optimistic locking, but Kundera does not imeplement it.
Rollbacks will not work in case of native queries, as there is no entity object mapped to the query is available in persistence context (required for maintaining the state of the object in transactions).
Kundera does not support optimistic locking as of now. If you are looking for a workaround you can use native querying on Cassandra.
I am using JPA for data persistence.
I am unable to explain a behaviour in my program.
I have an entity A which has another entity B as its member.In my code I create new instance of A and set an instance of B (fetched from database) in A,and then I save A using EntityManager. I am using container managed transaction, hence all transactions are supposed to commit at end of the method.
In very same method, after persisting A, I try to fetch an entity of class C. C, like A, has B as its member. I use a JQPL query to fetch C for id of B's instance I associated with A's instance previously.
Issue is that while fetching C, JPA is also executing SQL query to save A. I expect that to happen at end of transaction (ie when method ends).
But its happening while I try to fetch C. If I don't fetch C, then SQL query for saving A is issued when method ends.
What can be the reason for this behaviour?
JPA provider needs to flush the persistence context before query execution if there is the possibility that query results would not be consistent with the current persistent context state.
You can set flush mode to COMMIT for the desired (or all) sessions. Just keep in mind to manually flush the session if a query depends on the dirty persistence context state. Default flush mode is AUTO, meaning that persistence context may be flushed before query execution.
The reason is the database isolation level. It's read_commited by default.
Read more about isolation levels here:
https://en.wikipedia.org/wiki/Isolation_%28database_systems%29#Read_committed
So to not break this isolation JPA MUST execute all SQL statements in the buffer that all data in the transaction has reached the database.
From JPA documentation, PESSIMISTIC_READ lock on an entity can be acquired if no one is holding a PESSIMISTIC_WRITE lock on the entity. However, as I tested using OpenJPA 2.0.0, WebSphere and MSSQL (as well as DB2), it seems that 2 services cannot acquire PESSIMISTIC_READ lock on the same entity at the same time.
This code (in ConfigEJB) was used to lock the entity:
ConfigEntity configEntity = this.getEntityById(1); // successfully get the entity
this.entityManager.lock(configEntity, LockModeType.PESSIMISTIC_READ);
2 instances of ConfigEJB were invoked. The first instance could acquire the lock successfully. However, the second instance couldn't get the lock, and was blocked until the first instance finished its transaction (I expected it to successfully get the lock).
Has anyone ever encountered this problem? Or is this an expected behavior of JPA? How to let the services obtain PESSIMISTIC_READ locks properly?
Not found in reference, but Pro JPA2 saying:
Some databases support locking mechanisms to get repeatable read isolation without acquiring a write lock. A PESSIMISTIC_READ mode can be used to pessimistically achieve repeatable read semantics when no writes to the entity are expected. The fact that this kind of situation will not be encountered very often, combined with the allowance that providers have of implementing it using a pessimistic write lock, leads us to say that this mode is not one to be easily picked up and commonly used.
So, seems your database provider replace PESSIMISTIC_READ with PESSIMISTIC_WRITE
I am using Java SE and learning about the use of a persistence API (toplink-essentials) to manage entities in a Derby DB. Note: this is (distance learning) university work, but it is not 'homework' this issue crops up in the course materials.
I have two threads operating on the same set of entities. My problem is that every way I have tried, the entities within a query result set (query performed within a transaction) in one thread can be modified so that the result set is no longer valid for the rest of the transaction.
e.g. from one thread this operation is performed:
static void updatePrices(EntityManager manager, double percentage) {
EntityTransaction transaction = manager.getTransaction();
transaction.begin();
Query query = manager.createQuery("SELECT i FROM Instrument i where i.sold = 'no'");
List<Instrument> results = (List<Instrument>) query.getResultList();
// force thread interruption here (testing non-repeatable read)
try { Thread.sleep(2000); } catch (Exception e) { }
for (Instrument i : results) {
i.updatePrice(percentage);
}
transaction.commit();
System.out.println("Price update commited");
}
And if it is interrupted from another thread with this method:
private static void sellInstrument(EntityManager manager, int id)
{
EntityTransaction transaction = manager.getTransaction();
transaction.begin();
Instrument instrument = manager.find(Instrument.class, id);
System.out.println("Selling: " + instrument.toFullString());
instrument.setSold(true);
transaction.commit();
System.out.println("Instrument sale commited");
}
What can happen is that when the thread within updatePrices() resumes it's query resultSet is invalid, and the price of a sold item ends up being updated to different price to that at which it was sold. (The shop wishes to keep records of items that were sold in the DB). Since there are concurrent transactions occuring I am using a different EntityManager for each thread (from the same factory).
Is it possible (through locking or some kind of context propagation) to prevent the results of a query becoming 'invalid' during a (interrupted) transaction? I have an idea that this kind of scenario is what Java EE is for, but what I want to know is whether its doable in Java SE.
Edit:
Taking Vineet and Pascal's advice: using the #Version annotation in the entity's Class (with an additional DB column) causes the large transaction ( updatePrices() ) to fail with OptimisticLockException. This is very expensive if it happens at the end of a large set of query results though. Is there any way to cause my query (inside updatePrices() ) to lock the relevant rows causing the thread inside sellInstrument() to either block or abort throw an exception (then abort)? This would be much cheaper. (From what I understand I do not have pessimistic locking in Toplink Essentials).
Thread safety
I have a doubt about the way you manage your EntityManager. While a EntityManagerFactory is thread-safe (and should be created once at the application startup), an EntityManager is not and you should typically use one EntityManager per thread (or synchronize accesses to it but I would use one per thread).
Concurrency
JPA 1.0 supports (only) optimistic locking (if you use a Version attribute) and two lock modes allowing to avoid dirty read and non repeatable read through the EntityManager.lock() API. I recommend to read Read and Write Locking and/or the whole section 3.4 Optimistic Locking and Concurrency of the JPA 1.0 spec for full details.
PS: Note that Pessimistic locking is not supported in JPA 1.0 or only through provider specific extensions (it has been added to JPA 2.0, as well as other locking options). Just in case, Toplink supports it through the eclipselink.pessimistic-lock query hint.
As written in the JPA wiki, TopLink Essentials is supposed to support pessimistic locking in JPA 1.0 via a query hint:
// eclipselink.pessimistic-lock
Query Query = em.createQuery("select f from Foo f where f.bar=:bar");
query.setParameter("bar", "foobar");
query.setHint("eclipselink.pessimistic-lock", "Lock");
query.getResultList();
I don't use TopLink so I can't confirm this hint is supported in all versions. If it isn't, then you'll have to use a native SQL query if you want to generate a "FOR UPDATE".
You might want to take a look at the EntityManager.lock() method, which allows you to obtain an optimistic or a pessimistic lock on an entity once a transaction has been initialized.
Going by your description of the problem, you wish to lock the database record once it has been 'selected' from the database. This can be achieved via a pessimistic lock, which is more or less equivalent to a SELECT ... FROM tbl FOR UPDATE statement.
I have a method that retrieves Entities using a NamedQuery. I update a value of each entity and then run another named query (in the same method and Transaction) filtering by the old value and it returns the same Entities as if I had not changed them.
I understand that the EntityManager needs to be flushed and also that it should happen automatically but that doesn't make any difference.
I enabled hibernate SQL logging and can see that the Entities are not updated when I call flush but when the container transaction commits.
EntityManager entityManager = getPrimaryEntityManager();
MyEntity myEntity = entityManager.find(MyEntityImpl.class, allocationId);
myEntity.setStateId(State.ACTIVE);
// Flush the entity manager to pick up any changes to entity states before we run this query.
entityManager.flush();
Query countQuery = entityManager
.createNamedQuery("MyEntity.getCountByState");
// we're telling the persistence provider that we want the query to do automatic flushing before this
// particular query is executed.
countQuery.setParameter("stateId", State.CHECKING);
Long count = (Long) countQuery.getSingleResult();
// Count should be zero but isn't. It doesn't see my change above
To be honest I'm not that familiar with JPA, but I ran into similar problems with Hiberate's session manager. My fix was to manually remove the specified object from Hibernate's session before querying on it again so it's forced to do a lookup from the database and doesn't get the object from cache. You might try doing the same with JPA's EntityManager.
I've just had the same issue and discovered two things:
Firstly, you should check the FlushMode for the persistence context
and / or the query.
Secondly, make sure that the entity manager is
exactly the same object for both transaction management and query
execution. In my case, I had Mockito spy on the entityManager, which
was enough to break the transaction management.