[ 
https://issues.apache.org/jira/browse/OPENJPA-891?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12675510#action_12675510
 ] 

Albert Lee commented on OPENJPA-891:
------------------------------------

The JIRA implements the new JPA2 LockModeType features.

The following is a summary of the design points and considerations in the 
OpenJPA implementation:

Support the following new LockModeType and property map combination for find, 
lock and refresh methods in the EntityManager interface:
Optimistic (Same as Read)
Optimistic_Force_Increment (Same as Write)
Pessimistic_Read
Pessimistic_Write
Pessimistic_Force_Increment

Since OpenJPA already support both optimistic and pessimistic lock managers, 
the basic design goal is to "reuse" as much as possible to ensure stability, 
compatibility and semantics of existing lock managers behaviors as well as the 
new JPA 2 support..

A new JPA2LockManager is introduced to support the new lock mode semantics. 
"jpa2" is the alias name for the openjpa.LockManager properties. This will be 
the default for OpenJPA 2.0.0.

There are 3 aspects in supporting the new features:
The front end - EntityManagerImpl:
The EntityManagerImpl needs to implement the new interface methods.
Since the em method's LockMode and properties Map arguments are transient and 
only apply during the method call, the front end code needs to save, apply the 
setting for the current method call and restore the previous values when the 
method exit.
Translate the relevant property values and apply to the fetch configuration for 
use downstream. Only the following properties will be processed
javax.persistence.lock.timeout
openjpa.LockTimeout
openjpa.ReadLockMode
openjpa.WriteLockMode

public <T> T find(Class<T> cls, Object oid, LockModeType mode,
        Map<String, Object> properties) {
        assertNotCloseInvoked();
        if (mode != LockModeType.NONE)
            _broker.assertActiveTransaction();

        boolean fcPushed = pushLockProperties(mode, properties);
        try {
            oid = _broker.newObjectId(cls, oid);
            return (T) _broker.find(oid, true, this);
        } finally {
            popLockProperties(fcPushed);
        }
    }

The back end - Lock Manager
The role of the JPA2LockManager is to route the requested LockMode to the lock 
manager and set the version check options to be processed by the delegated lock 
manager:

    protected void lockInternal(OpenJPAStateManager sm, int level, int timeout, 
Object sdata) {
        if (level >= LOCK_PESSIMISTIC_FORCE_INCREMENT) {
            setVersionCheckOnReadLock(true);
            setVersionUpdateOnWriteLock(true);
            super.lockInternal(sm, level, timeout, sdata);
        } else if (level >= LOCK_PESSIMISTIC_READ) {
            setVersionCheckOnReadLock(true);
            setVersionUpdateOnWriteLock(false);
            super.lockInternal(sm, level, timeout, sdata);
        } else if (level >= LOCK_READ) {
            setVersionCheckOnReadLock(true);
            setVersionUpdateOnWriteLock(true);
            optimisticLockInternal(sm, level, timeout, sdata);
        }
    }

Exception handling
New LockTimeoutException and PessimisticLockException require differentiation 
its statement-level and transaction-level rollback semantics respectively. 
Application may recover and re-try when the former exception is received.
Since detecting these conditions is very database specific. the 
DBDictionary.narrow() method will delegate to the associated Dictionary 
subclass to examine the SQLException (SQLState, SQLCode and message text etc.) 
thrown by the database and to compute if the exception is a recoverable 
exception. The existing sqlstate mapping to StoreException's subtype remains 
unchanged (except a few corrections). The StoreException is used to encapsulate 
the recoverable attribute of the SQLException and will be processed during 
exception translation.. The only subtype that is affected is 
StoreException.LOCK type.

OpenJPAException narrow(String msg, SQLException ex) {
        Boolean recoverable = null;
        int errorType = StoreException.GENERAL;
        for (Integer type : sqlStateCodes.keySet()) {
            Set<String> errorStates = sqlStateCodes.get(type);
            if (errorStates != null) {
                recoverable = matchErrorState(type, errorStates, ex);
                if (recoverable != null) {
                    errorType = type;
                    break;
                }
            }
        }
        StoreException storeEx;
        switch (errorType) {
        case StoreException.LOCK:
            storeEx = new LockException(msg);
            break;
E.g.
DB2:
    @Override
    protected Boolean matchErrorState(int subtype, Set<String> errorStates,
        SQLException ex) {
        Boolean recoverable = null;
        if (errorStates.contains(errorState)) {
            recoverable = Boolean.FALSE;
            if (subtype == StoreException.LOCK && errorState.equals("57033")
                && ex.getMessage().indexOf("80") != -1) {
                recoverable = Boolean.TRUE;
            }
        }
        return recoverable;
    }
Derby:
    @Override
    protected Boolean matchErrorState(int subtype, Set<String> errorStates,
        SQLException ex) {
        Boolean recoverable = null;
        String errorState = ex.getSQLState();
        int errorCode = ex.getErrorCode();
        if (errorStates.contains(errorState)) {
            recoverable = Boolean.FALSE;
            if (subtype == StoreException.LOCK && errorCode < 30000) {
                recoverable = Boolean.TRUE;
            }
        }
        return recoverable;
    }

The LockException will be translated to the proper em method exception as in :

private static Throwable translateStoreException(OpenJPAException ke) {
        .......
        } else if (ke.getSubtype() == StoreException.LOCK || cause instanceof 
LockException) {
                LockException lockEx = (LockException)(ke instanceof 
LockException ? ke : cause);
                if( lockEx != null && lockEx.isPessimistic()) {
                    if( lockEx.isRecoverable()) {
                        e = new org.apache.openjpa.persistence
                            .LockTimeoutException(ke.getMessage(), 
getNestedThrowables(ke), getFailedObject(ke), ke.isFatal());
                    } else {
                        e = new org.apache.openjpa.persistence
                            .PessimisticLockException(ke.getMessage(), 
getNestedThrowables(ke), getFailedObject(ke), ke.isFatal());
                    }
                } else {
                    e = new org.apache.openjpa.persistence
                        .OptimisticLockException(ke.getMessage(), 
getNestedThrowables(ke), getFailedObject(ke), ke.isFatal());
                }
        } else if (ke.getSubtype() == StoreException.OBJECT_EXISTS

The LockTimeoutException and QueryTimeoutException will be exempted from being 
marked RolledbackOnly in PersistenceExceptions

    public static RuntimeExceptionTranslator getRollbackTranslator(
        final OpenJPAEntityManager em) {
        return new RuntimeExceptionTranslator() {
            private boolean throwing = false;
            public RuntimeException translate(RuntimeException re) {
                RuntimeException ex = toPersistenceException(re);
                if (!(ex instanceof NonUniqueResultException)
                    && !(ex instanceof NoResultException)
                    && !(ex instanceof LockTimeoutException)
                    && !(ex instanceof QueryTimeoutException)
                    && !throwing) {
                    try {
                        throwing = true;

openjpa.ReadLockLevel and WriteLockLevel are enhanced in parallel to the new 
lock modes. (i.e.
5.53.  openjpa.ReadLockLevel 
Property name: openjpa.ReadLockLevel 
Resource adaptor config-property: ReadLockLevel 
Default: read 
Possible values: none, read, write, optimistic, optimistic-force-increment, 
pessimistic-read, pessimistic-write, pessimistic-force-increment, numeric 
values for lock-manager specific lock levels 
Description: The default level at which to lock objects retrieved during a 
non-optimistic transaction. Note that for the default JDBC lock manager, read 
and write lock levels are equivalent. 

For the em methods that take both LockModeType and a property Map, we may run 
into situation where "openjpa.ReadLockMode/WriteLockMode" are specified and 
create a conflict with the LockModeType argument. Resolution:  the LockModeType 
argument takes a higher precedent than the *LockLevel values specified in the 
Map argument.

If application specifies "version" or "pessimistic" as the openjpa.LockManager, 
the old behavior is honored.
However if new lock mode is requested using the new EntityManager Interface, 
the following behavior will be implemented:
"version" lock manager
All "Pessimistic_*" mode will be down grade to "Optimistic_Force_Increment" 
(Write) and a warning message is logged.
"pessimistic" lock manager
All lock type uses the same existing pessimistic semantics.

Albert Lee.

> JPA2 LockTypeMode Support, part 1 - optimistic lock types
> ---------------------------------------------------------
>
>                 Key: OPENJPA-891
>                 URL: https://issues.apache.org/jira/browse/OPENJPA-891
>             Project: OpenJPA
>          Issue Type: Sub-task
>          Components: jpa
>    Affects Versions: 2.0.0
>            Reporter: Albert Lee
>            Assignee: Albert Lee
>             Fix For: 2.0.0
>
>


-- 
This message is automatically generated by JIRA.
-
You can reply to this email to add a comment to the issue online.

Reply via email to