本文地址:https://flexibility.github.io/MD/SAP/Java/01-Enqueue-Lock-in-NW-as-JAVA
[TOC]
參考
SAP Help Portal : Developing Java EE 5 Applications/Task/Developing Persistence/Locks
概念
鎖和事務(wù)隔離
鎖的概念和 事務(wù)隔離( transaction isolation )相關(guān)荞膘。
事務(wù)隔離 是為了規(guī)避多個并發(fā)(simultaneously running)的事務(wù)不被相互影響疟暖。隔離的級別通過對數(shù)據(jù)庫表的行的讀和改的鎖定來實現(xiàn)
鎖定機制
有以下兩種鎖定機制:
- Database locks。通過數(shù)據(jù)庫提供的鎖機制來實現(xiàn)钠右,缺點是不同的數(shù)據(jù)庫對鎖的語義(semantics)沒有統(tǒng)一標(biāo)準(zhǔn)舌界。
- Logical locks掘譬。鎖定機制由NW as Java提供和集中管理( Enqueue Server 通過集中的lock table進(jìn)行管理)。
J2EE應(yīng)用使用由 Locking Adapter Service 提供的LogicalLocking
和TableLocking
接口來訪問 Locking Manager 來和 Enqueue Server 進(jìn)行通信
Enqueue Server
Enqueue Server 不和持久層存儲介質(zhì)( persistent storage )交互呻拌,如DB葱轩,文件系統(tǒng)等;只和主存中的 central lock table 進(jìn)行通信
每一次鎖定請求就有一次應(yīng)用和 Enqueue Server 之間的通信。合適的 鎖定粒度(granularity)可以減少網(wǎng)絡(luò)通信靴拱,比如使用通配符(wildcard character)來鎖定范圍數(shù)據(jù)垃喊。
read/write/shared/exclusive locks
使用邏輯鎖可以鎖定單行數(shù)據(jù)或者區(qū)域數(shù)據(jù)。分為讀鎖(write locks)和 寫鎖(write locks)袜炕。
對于一個對象本谜,最多可以存在一個寫鎖,讀鎖 可以存在多個偎窘。寫鎖 和 讀鎖 是互斥(mutually exclusive)的乌助。
因此,讀鎖 也叫 共享鎖(shared locks), 寫鎖 也叫 排他鎖(exclusive locks)
Enqueue Locks Guidelines
Enqueue Locks Guidelines
- Enqueue locking applies both to the SAP NetWeaver AS for Java system database and external databases supported by SAP NetWeaver AS for Java.
- All application components must rely on enqueue locking. Locking works if all components rely on the same locking principle.
- All application components must commit to apply the same locking protocol.
- With enqueue locking, JDBC data sources must operate on the lowest, READ_UNCOMMITTED transaction isolation level.
- Enqueue locking works with Open SQL, Native SQL, and Vendor SQL.
- To obtain an enqueue lock, you must have an active JTA or database transaction
- We recommend that you choose and use one persistence API consistently:
With ORM technologies such as EJB CMP and JDO, you do not need to set locks explicitly. Both frameworks implicitly lock persistent objects by default.
In all other cases, you set and release enqueue locks using the interfaces of the Application Locking Service:
- To lock specific database items you use the TableLocking API.
- In all other cases, you use the LogicalLocking API.
- We recommend that you set locks only when necessary and for as short a time as possible. To release locks you use one of the following approaches:
- To release locks in the application, you use the dedicated methods of the TableLocking and LogicalLocking APIs.
- Locks exist within the scope of a transaction. When a transaction ends, the AS Java automatically releases the locks set during the transaction.
- To manage, test, and view statistics about enqueue locks, you use the Application Locking Service.
More information: Managing Locks
使用
準(zhǔn)備
- Design time:需要訪問以下庫 applocking.jar陌知,frame.jar他托,exception.jar(根本沒用,后面會有說明)
然而實際使用的時候發(fā)現(xiàn)未找到frame.jar仆葡,applocking.jar之間報過時赏参,exception.jar增加了命名空間,變成了com.sap.exception沿盅。
代碼根本編譯不了登刺,然后看了下700~750的文檔,發(fā)現(xiàn)這塊的文檔基本都沒變過嗡呼,還是這幾個lib,估計基本都放棄更新文檔了皇耗,畢竟南窗。。郎楼。
經(jīng)過不停的嘗試万伤,最終發(fā)現(xiàn) tc/je/locking/api 和 tc/bl/exception/lib 這兩個service對dc可以。
DC依賴
jar包
API
APIs
TableLocking API
public void lock(
byte lifetime,
Connection connection,
String tableName,
Map primaryKeys,
char mode
)
throws LockException,
TechnicalLockException,
IllegalArgumentException;
- lifetime : 生命周期
- connection : 數(shù)據(jù)庫連接
- tableName : 表名
- primaryKeys : 主鍵(可用通配符)
- mode : 鎖模式
LogicalLocking API
public void lock(
byte lifetime,
java.lang.String name,
java.lang.String argument,
char mode,
int timeout
)
throws com.sap.engine.frame.core.locking.LockException,
com.sap.engine.frame.core.locking.TechnicalLockException,
java.lang.IllegalArgumentException;
- lifetime : 生命周期
- name : 鎖名( 必須以所屬的namespace開頭 )
- argument : 鎖參數(shù)
- mode : 鎖模式
- timeout : 加鎖沖突等待超時時間(ms)
參數(shù)
-
LIFETIME | 生命周期
-
TableLocking.LIFETIME_USERSESSION
: 基于用戶session呜袁,若不顯式unlock會一直持續(xù)到session結(jié)束 -
TableLocking.LIFETIME_TRANSACTION
: 推薦方式敌买。依賴事務(wù),必須激活JTA或者數(shù)據(jù)庫事務(wù)阶界,若不顯式unlock會在事務(wù)結(jié)束(commit|rollback)后自動釋放
-
-
MODE | 鎖模式
-
TableLocking.MODE_SHARED
: 共享鎖(S Lock) -
TableLocking.MODE_EXCLUSIVE_CUMULATIVE
: 累積互斥鎖(E Lock)虹钮,同一個transaction中可以累積加鎖。 -
TableLocking.MODE_EXCLUSIVE_NONCUMULATIVE
: 非累積互斥鎖(X Lock)膘融,任何時候只能鎖定一次 -
*TableLocking.MODE_OPTIMISTIC*
: 樂觀鎖(O Lock)
與S lock類似芙粱,可以與S lock/O lock共存,與E lock/X lock互斥氧映。
與S lock最大區(qū)別:O lock 可以升級到R lock春畔,當(dāng)一個O lock升到R lock,其他O lock會失效 -
*TableLocking.MODE_OPTIMISTIC_TO_EXCLUSIVE*
: 樂觀鎖升級互斥鎖(R Lock)。
必須保證存在樂觀鎖律姨,否則會報錯 "the lock propagation failed, because the optimistic lock is gone"
-
*TableLocking.MODE_CHECK_SHARED*
: 只check不占用 -
*TableLocking.MODE_CHECK_EXCLUSIVE_CUMULATIVE*
: 只check不占用 -
*TableLocking.MODE_CHECK_EXCLUSIVE_NONCUMULATIVE*
: 只check不占用
-
-
通配符
-
TableLocking.WILDCARD_CHARACTER_SINGLE
: 代表1個字符 -
TableLocking.WILDCARD_CHARACTER_MULTI
: 代表多個字符
-
DEMO(LogicalLock)
package flex.test.rest;
import java.sql.Connection;
import java.util.Map;
import javax.naming.InitialContext;
import javax.transaction.UserTransaction;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import afce.base.ejb.common.type.FlexMap;
import afce.base.ejb.util.DBUtil;
import com.ibm.basis.sqldao.executor.DBConnection;
import com.sap.engine.frame.core.locking.LockException;
import com.sap.engine.services.applocking.LogicalLocking;
import com.sap.engine.services.applocking.LogicalLockingFactory;
@Path("/query")
public class TestRestCFG {
@GET
@Path("/lock")
@Produces("application/json;charset=utf-8")
public Map<String,Object> test(
@DefaultValue("test01") @QueryParam("name") String p_lock_name,
@DefaultValue("E") @QueryParam("mode") String p_lock_mode,
@DefaultValue("1000") @QueryParam("timeout") int p_lock_timeout,
@DefaultValue("30") @QueryParam("during") int p_lock_during
){
char c_lock_mode = (p_lock_mode==null||p_lock_mode.length()<1)?'E':p_lock_mode.charAt(0);
// E - LogicalLocking.MODE_EXCLUSIVE_CUMULATIVE
// X - LogicalLocking.MODE_EXCLUSIVE_NONCUMULATIVE
// S - LogicalLocking.MODE_SHARED
// O - LogicalLocking.MODE_OPTIMISTIC
// R - LogicalLocking.MODE_OPTIMISTIC_TO_EXCLUSIVE
FlexMap<String, Object> oret = FlexMap.SO();
String lock_NS = "Flex.lock";
String lock_name = lock_NS + "." + p_lock_name;
String lock_para = "flex01";
UserTransaction ut = null;
Connection conn = null;
try {
conn = DBConnection.getConnection();
ut = DBUtil.getUT(conn);
ut.begin();
InitialContext ctx = new InitialContext();
LogicalLockingFactory lf = (LogicalLockingFactory) ctx.lookup(LogicalLockingFactory.JNDI_NAME);
LogicalLocking ll = lf.createLogicalLocking(lock_NS, "Flex Lock NS");
oret.put("before lock", "------"+new java.util.Date().toString());
oret.put("LOCK_NAME", lock_name)
.put("LOCK_PARA", lock_para)
.put("LOCK_MODE", p_lock_mode)
.put("LOCK_TIMEOUT", p_lock_timeout)
.put("LOCK_DURING", p_lock_during)
;
ll.lock(
LogicalLocking.LIFETIME_TRANSACTION,
lock_name, lock_para,
// LogicalLocking.MODE_EXCLUSIVE_CUMULATIVE
c_lock_mode
,p_lock_timeout
);
oret.put("after lock", "------"+new java.util.Date().toString());
try {java.util.concurrent.TimeUnit.SECONDS.sleep(p_lock_during);}
catch (InterruptedException e) {e.printStackTrace();}
oret.put("last", "------"+new java.util.Date().toString());
}
catch (LockException e) {
oret.put("exception",
"請求的鎖對象<"+lock_name+">"+"正在被【"+
e.getCollisionUserName()+
"】鎖定!"+
"("+lock_para+")"
);
oret.put("exception-msg",e.getMessage());
}
catch (Exception e) {
oret.put("exception", e.getMessage()+new java.util.Date().toString());
}
finally{DBConnection.freeConnection(conn);}
return oret.o();
}
}
提供了rest服務(wù)進(jìn)行鎖定振峻,如果鎖定成功,將會占用30秒择份,如果未占用成功則直接返回報錯信息.
*https://IP端口/應(yīng)用/rest/query/lock?name=flex&mode=E&timeout=0&during=30
測試的時候扣孟,如果在一個session的瀏覽器環(huán)境需要加參數(shù)來區(qū)分連接,不清楚原因缓淹,否則將會出現(xiàn)串行處理請求的情況哈打,無法模擬并發(fā)鎖的場景
可以增加參數(shù)來區(qū)分連接:
- 服務(wù)路徑/rest/query/lock?name=flex&mode=E&timeout=0&during=30
- 服務(wù)路徑/rest/query/lock?name=flex&mode=E&timeout=0&during=30&_r=2
測試截圖:
打開2個互斥鎖
打開3個共享鎖后打開1個互斥鎖