openjpa-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Albert Lee (JIRA)" <j...@apache.org>
Subject [jira] Commented: (OPENJPA-891) JPA2 LockTypeMode Support, part 1 - optimistic lock types
Date Fri, 20 Feb 2009 23:29:01 GMT

    [ 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.


Mime
View raw message