persistence@glassfish.java.net

Re: multiple threads trying to modfiy an entity

From: Gordon Yorke <gordon.yorke_at_oracle.com>
Date: Thu, 23 Apr 2009 09:40:06 -0300

You can set the persistence unit property "toplink.logging.exceptions"
to false.
--Gordon

Mauro Almeida wrote:
>
> I'm trying to fix this via the Optimistic Locking.
>
>
>
> Things are better... but the OptimistLockException thrown by top link
> is always logged by toplink. How do I like to turn that off?
>
>
>
> Logger
>
> oracle.toplink.essentials.session.file:/home/flexivoice/sailfin/domains/xxx-domain/applications/j2ee-apps/xx-xx-xx-.x0x/xxx-rrr-eee_jar/-xxx-xx-persistence-unit
>
>
>
> Complete Message
>
> Exception [TOPLINK-5006] (Oracle TopLink Essentials - 2.1 (Build
> b60g-fcs (01/08/2009))):
> oracle.toplink.essentials.exceptions.OptimisticLockException Exception
> Description: The object [Stats[value=1]] cannot be updated because it
> has changed or been deleted since it was last read. Class>
> com.localmatters.xx.xx.xx.stats.Stat Primary Key> [1] at
>
> oracle.toplink.essentials.descriptors.VersionLockingPolicy.validateUpdate(VersionLockingPolicy.java:737)
> at
> oracle.toplink.essentials.internal.queryframework.DatabaseQueryMechanism.updateObjectForWriteWithChangeSet(DatabaseQueryMechanism.java:1309)
> at
>
>
>
> In my application catch the exception and treat it. But since several
> conflicts may occur,, I don't want to have that logged everytime
>
>
>
> Thanks !
>
>
>
> Mauro Almeida
>
>
>
> ------------------------------------------------------------------------
>
> *From:* Gordon Yorke [mailto:gordon.yorke_at_oracle.com]
> *Sent:* Tuesday, April 21, 2009 12:45 PM
> *To:* persistence_at_glassfish.dev.java.net
> *Subject:* Re: multiple threads trying to modfiy an entity
>
>
>
> First issue I see here is you are mixing access types. In JPA 1.0 you
> must annotate the class attributes or the getter methods but not both.
> But because you are using pessimistic locking the optimistic locking
> is not needed for this case.
> Second try using a new query instead of a named query.
> Third, although it will have no impact on this example you should not
> use a cache type of NONE. Instead if you want no caching us
> shared-cache.default = false.
> --Gordon
>
> Mauro Almeida wrote:
>
> Gordon, I still cannot make this work
>
>
>
> I've added a TBLOCK column to my JavaDB table
>
>
>
> I've added to my Entity
>
>
>
> @Column (name = "TBLOCK");
>
> private Integer tblock;
>
>
>
>
>
> @Version Integer getTblock() {
>
> return tblock;
>
> }
>
>
>
> Also tried annotating the variable with @Version, but would get an
> exception
>
>
>
>
>
> My PU in persistence.xml
>
> Has: toplink.refresh set to true and toplink.cache.type.default set to
> NONE
>
>
>
> And in my façade.
>
>
>
> I inject emf with my PU name.
>
>
>
> IN The method:
>
>
>
> em = emf.createEntiryManager();
>
>
>
> em.joinTransaction();
>
>
>
> q = em.createNamedQuery(queryHere);
>
> q.setHint("QueryHints.PESSIMISTIC_LOCK", "PessimisticLock.Lock");
>
> // also tried q.setHint("TopLinkQueryHints.PESSIMISTIC_LOCK",
> "PessimisticLock.Lock");
>
> // also tried q.setHint("toplink.pessimistic-lock", "Lock");
>
>
>
> s = (Entry) q.getSingleResult ();
>
>
>
> If a get NoResultException , I create an entry and persist it
>
> Else I just merge()
>
>
>
> I still have the wrong updates/dirty reads...
>
>
>
> Any other ideas....
>
>
>
> I tried using em.lock () but no luck, since it always throws a
> PersistenceException()
>
>
>
>
>
>
>
> Mauro Almeida
>
>
>
> ------------------------------------------------------------------------
>
> *From:* Gordon Yorke [mailto:gordon.yorke_at_oracle.com]
> *Sent:* Friday, April 17, 2009 8:45 AM
> *To:* persistence_at_glassfish.dev.java.net
> <mailto:persistence_at_glassfish.dev.java.net>
> *Subject:* Re: multiple threads trying to modfiy an entity
>
>
>
> You should not serialize the access that would prevent concurrent
> access to your application. What you need to ensure is that you are
> managing concurrent access through appropriate locking. If these
> conflicts occur rarely then I recommend using optimistic locking. Add
> @Version field to your entity (there are EclipseLink extensions to JPA
> if you can not update your entities). The @Version field will ensure
> any conflicts will be reported through an OptimisticLockException. If
> these conflicts are not rare then I recommend using pessimistic
> locking. JPA 1.0 did not provide support for pessimistic locking but
> if you switch your find to a JPQL query and add the hint.
> query.setHint(QueryHints.PESSIMISTIC_LOCK, PessimisticLock.Lock);
> Then each thread will have exclusive access to the corresponding row.
> --Gordon
>
> Mauro Almeida wrote:
>
> In the error scenario the db has one row, that's updated everytime an event happens in the application.
>
> basically, a counter is increment by one.
> When the app sends this sessionbean, let's say 24 messages, to have it update it counter, some msg are 'lost'
>
> Counter shows only 23, 14, etc.... instead of 24.
>
> How can I serialize the access?
>
> Another way to say is: how can several client updated concurrently the DB, based on it's previous state.,
> Thanks !
>
>
> yes s and s1 was just an was just an email issue..
>
>
>
> -----Original Message-----
> From: Gordon Yorke [mailto:gordon.yorke_at_oracle.com]
> Sent: Thu 4/16/2009 3:33 PM
> To: persistence_at_glassfish.dev.java.net <mailto:persistence_at_glassfish.dev.java.net>
> Subject: Re: multiple threads trying to modfiy an entity
>
> If any number of threads perform a find within different transactions
> then these threads will not find the Entity with the specified ID and
> these threads will attempt to persist an Entity with that same ID and
> all but the first to write the data to the database will fail. This is
> expected behaviour. You will need to retry that transaction.
> How many updates are lost? Can you provide specifics on what and when
> the updates are lost.
>
> I see your code also has a typo (s1 vs s) but this must be an email
> issue as this code would not compile otherwise.
> if (s == null) {
>
> Stats *s* =
> createStat(id);
>
> // Now increment the proper count, based on the type
> o msg received
>
> switch (type) {
>
> case TOTAL:
>
> *s1*.setTotal(*s1*.getTotal()+1);
>
> break;
>
> case PARTIAL:
>
> *s1*.setPartial(*s1*.getPartial()+1);
>
> break;
>
> }
>
> em.persist(s1);
>
> em.flush();
>
> } else {
>
>
> --Gordon
>
>
>
> Mauro Almeida wrote:
>
>> The App is the one creating the ID. It is not auto sequentially generated
>>
>>
>>
>> That's why I was first doing a em.find() to see if entry already
>> exists. (99% of my cases, it will exist) than I just em.merge().
>>
>>
>>
>> If em.find returned me null I would try to em.persit(). As you can see
>> in the snippet below.
>>
>>
>>
>> The problems are:
>>
>> When table is emptyl.. I get a duplicated entry (2 threads trying to
>> persist the same id)
>>
>> When there are entries...... Some updates are lost. The counter in the
>> DB do not add up to the number of messages received by the sessionbean
>>
>> Thanks!
>>
>>
>>
>>
>>
>> Mauro Almeida
>>
>> ------------------------------------------------------------------------
>>
>> *From:* Gordon Yorke [mailto:gordon.yorke_at_oracle.com]
>> *Sent:* Thursday, April 16, 2009 3:03 PM
>> *To:* persistence_at_glassfish.dev.java.net <mailto:persistence_at_glassfish.dev.java.net>
>> *Subject:* Re: multiple threads trying to modfiy an entity
>>
>>
>>
>> For the duplicate inserts who is creating the Id? If multiple threads
>> attempt to insert the same PK then you should expect Unique Constraint
>> violations as multiple threads would have attempted to insert the same
>> object in different transaction.
>> What are the problems you are still experiencing with this SessionBean?
>> --Gordon
>>
>> Mauro Almeida wrote:
>>
>> Gordon,
>>
>>
>>
>> Thanks.. I think I'm on the right track but it still not working.
>>
>>
>>
>> So. I'm injecting the the emf using @PersistenceUnit into the class
>>
>> Every method call, emf.createEntityManager()
>>
>> Then I join em.joinTransaction() before I do my find(), merge(), persist()
>>
>>
>>
>> I still have some race condition. I also have a different façade to a
>> different Entity, and the results a much more off (the updates seems
>> to get lost more often)
>>
>> I continue to have cache turned off on persistence.xml
>>
>>
>>
>> Any Ideas...?? THANKS !
>>
>>
>>
>>
>>
>> Following the code
>>
>>
>>
>> @Stateless
>>
>> public class MyFacade implements IMyFacade ()
>>
>>
>>
>> @PersistenceUnit(unitName="myUnit")
>>
>> Private EntityManagerFactory emf;
>>
>>
>>
>> public void updateEntry (StatsType type, int id)
>> {
>>
>>
>>
>> EntityManager em = emf.CreateEntityManager()
>>
>> em.joinTransaction()
>>
>> try {
>>
>> Stats s = em.find(Stats.class, id);
>>
>> // if could not find entry in the DB, create it
>>
>> if (s == null) {
>>
>> Stats s =
>> createStat(id);
>>
>> // Now increment the proper count, based on the
>> type o msg received
>>
>> switch (type) {
>>
>> case TOTAL:
>>
>> s1.setTotal(s1.getTotal()+1);
>>
>> break;
>>
>> case PARTIAL:
>>
>> s1.setPartial(s1.getPartial()+1);
>>
>> break;
>>
>> }
>>
>> em.persist(s1);
>>
>> em.flush();
>>
>> } else {
>>
>> switch (type) {
>>
>> case TOTAL:
>>
>> s.setTotal(s.getTotal()+1);
>>
>> break;
>>
>> case PARTIAL:
>>
>> s.setPartial(s.getPartial()+1);
>>
>> break;
>>
>> }
>>
>> em.merge(s);
>>
>> em.flush();
>>
>> }
>>
>> } catch (Exception ex) {
>>
>> ex.printStackTrace();
>>
>> }
>>
>> If (em.isOpen())
>>
>> em.close();
>>
>> }
>>
>>
>>
>>
>>
>> }
>>
>>
>>
>> ------------------------------------------------------------------------
>>
>> *From:* Gordon Yorke [mailto:gordon.yorke_at_oracle.com]
>> *Sent:* Thursday, April 16, 2009 8:53 AM
>> *To:* persistence_at_glassfish.dev.java.net <mailto:persistence_at_glassfish.dev.java.net>
>> <mailto:persistence_at_glassfish.dev.java.net>
>> *Subject:* Re: multiple threads trying to modfiy an entity
>>
>>
>>
>> Stateless session beans can be used by more than one client. The best
>> practise is to inject an EntityManagerFactory and then for each
>> invocation create an EntityManager within the method. Otherwise
>> multiple threads may be using the same EM.
>> --Gordon
>>
>> Mauro Almeida wrote:
>>
>> Hi,
>>
>>
>>
>> I have the following problem.
>>
>>
>>
>> I have a facade (a stateless bean, CMT) that has an entity Manager and
>> a method where several threads can call to have for an entry updated..
>>
>>
>>
>> What is happening is that the database is not updated properly.
>> Sometimes the counter is not incremented, which seems to me something
>> related to the attachment of the entity with the DB. Also, when the
>> table is initially empty and all N updates come at the same to the
>> same row, it tells me it has a duplicate key.
>>
>>
>>
>> I'm using sailfing build 60g. The DB is a JavaDB database
>>
>>
>>
>> The façade looks like:
>>
>> @Stateless
>>
>> @TransactionManagement(TransactionManagermentType.CONTAINER)
>>
>> public class MyFacade implements IMyFacade ()
>>
>>
>>
>> @PersistenceContext(unitName="myunit")
>>
>> private EntityManager em;
>>
>>
>>
>> @TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)
>>
>> public void updateEntry (StatsType type, int id)
>> {
>>
>> try {
>>
>> Stats s = em.find(Stats.class, id);
>>
>> // if could not find entry in the DB, create it
>>
>> if (s == null) {
>>
>> Stats s =
>> createStat(id);
>>
>> // Now increment the proper count, based on the
>> type o msg received
>>
>> switch (type) {
>>
>> case TOTAL:
>>
>> s1.setTotal(s1.getTotal()+1);
>>
>> break;
>>
>> case PARTIAL:
>>
>> s1.setPartial(s1.getPartial()+1);
>>
>> break;
>>
>> }
>>
>> em.persist(s1);
>>
>> em.flush();
>>
>> } else {
>>
>> switch (type) {
>>
>> case TOTAL:
>>
>> s.setTotal(s.getTotal()+1);
>>
>> break;
>>
>> case PARTIAL:
>>
>> s.setPartial(s.getPartial()+1);
>>
>> break;
>>
>> }
>>
>> em.merge(s);
>>
>> em.flush();
>>
>> }
>>
>> } catch (Exception ex) {
>>
>> ex.printStackTrace();
>>
>> }
>>
>> }
>>
>>
>>
>> The transactionManagement, the em.flush and TransactionAttributes
>> annotation I've put then after my original attempts failed.
>>
>>
>>
>> persistence.xml looks like:
>>
>> <?xml version="1.0" encoding="UTF-8"?>
>>
>> <persistence version="1.0"
>> xmlns="http://java.sun.com/xml/ns/persistence" <http://java.sun.com/xml/ns/persistence>
>> <http://java.sun.com/xml/ns/persistence> xmlns:xsi=*MailScanner has
>> detected a possible fraud attempt from "www.w3.org <http://www.w3.org>" claiming to be*
>> *MailScanner has detected a possible fraud attempt from "www.w3.org" claiming to be* "http://www.w3.org/2001/XMLSchema-instance" <http://www.w3.org/2001/XMLSchema-instance>
>> <http://www.w3.org/2001/XMLSchema-instance>
>> xsi:schemaLocation="http://java.sun.com/xml/ns/persistence <http://java.sun.com/xml/ns/persistencehttp:/java.sun.com/xml/ns/persistence/persistence_1_0.xsd>
>> _http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd" <http://java.sun.com/xml/ns/persistencehttp:/java.sun.com/xml/ns/persistence/persistence_1_0.xsd>_
>> <http://java.sun.com/xml/ns/persistencehttp:/java.sun.com/xml/ns/persistence/persistence_1_0.xsd>>
>>
>> <persistence-unit name="myunit" transaction-type="JTA">
>>
>> <jta-data-source>jdbc/castats</jta-data-source>
>>
>> <properties>
>>
>> <property name="toplink.jdbc.user" value="username"/>
>>
>> <property name="toplink.jdbc.password" value="passed"/>
>>
>> <!-- <property name="toplink.cache.type.default"
>> value="NONE"/> -->
>>
>> </properties>
>>
>> </persistence-unit>
>>
>> </persistence>
>>
>>
>>
>>
>>
>> Can someone point in the right direction? Why Do I have this race
>> condition?
>>
>>
>>
>>
>>
>>
>>
>> Mauro Almeida
>>
>>
>>
>>
>
>