赞
踩
实体引擎的一个主要目标是尽可能提供一种通用的代码结构,来消除各模块中事务处理过程中的相近或相似的代码。这种抽象在于数据库数据操作逻辑的提取。它可以大大减少应用系统的开发费用和潜在的事务处理bug。
在Ofbiz的实体引擎中,将存储实体属性值的类结构设计成通用的数据结构,使用一个MAP对象来作为数据载体来存取实体的所有属性值。实体引擎和应用系统之间的接口体现在实体结构定义文件和SQL字段类型与Java数据类型映射关系定义中(以XML文件方式进行描述)。基于这个抽象层,使用实体引擎提供的API来处理实体持久化操作。
取代编写特定的代码,实体的定义是在entitymodel*.XML文件中完成的,并由实体引擎强制规定应用系统和数据源之间的访问规则。在实体文件entitymodel*.XML中定义了实体和实体的关系,包含一个相关联的数据库表和关系类型(one or many),以及关系的关键字值对。关系可以定义一个标识来区别实体关系中的其他关系。
实体引擎中的一个重要组成部分就是SQL语句的执行引擎。在Ofbiz中,SQL语句执行引擎的中心是SQLProcessor类,每一个GenericDAO执行一个Add/Update/Delete操作时,都会创建一个SQLProcessor执行器,SQLProcessor执行器会从ConnectionFactory中取得一个数据库连接。
在上图中,SqlJDBCUtil描述了数据库类型与Java类型间的映射关系,以及建立表间关系或View的方法。TransactionUtil则提供了一系列事务管理的静态方法。
数据库连接是由一个数据库连接池来进行管理,如下图:
我的理解,Ofbiz中的数据模型应是充血模型,在Domain Object中包含了业务逻辑和持久化,GenericDAO仅作为基础设施层,实现对业务对象的持久化的作用。
数据模型定义如下:
GenericPK类是模型的描述,它联系entitymodel.xml文件中所描述的模型转为应用中的类型定义。entitymodel.xml中的模型描述信息装载在ModelEntity类中。GenericValue则是模型对象的一个实例。
GenericHelperDAO是GenericHelper接口的一个实现,实现了DAO层功能。具体应用GenericDAO类来执行SQL。在GenericDAO类中引用了一个SQLPrococessor类来完成与DB的交互。
Delegator则是一个服务层代表。
实体引擎的过滤功能实现:由三个部分组成:EntityConditionValue、EntityOperator、EntityCondition。这些过滤类将在GenericDAO中被转化为相应的SQL语句执行。
主键是通过SequenceUtil来生成的,它仅在GenericDelegator中被使用。实现机制是在内存中保存表的当前序列号,当创建新的ID时,依据当前序列通过计算得到下一条序列。当出现错误时,强制重新从表中查询出当前序列号保存在内存中,作为下一次计算的依据。
算法如下:
private synchronized Long getNextSeqId(long staggerMax) {
long stagger = 1;
if (staggerMax > 1) {
stagger = Math.round(Math.random() * staggerMax);
if (stagger == 0) stagger = 1;
}
if ((curSeqId + stagger) <= maxSeqId) {
Long retSeqId = Long.valueOf(curSeqId);
curSeqId += stagger;
return retSeqId;
} else {
fillBank(stagger);
if ((curSeqId + stagger) <= maxSeqId) {
Long retSeqId = Long.valueOf(curSeqId);
curSeqId += stagger;
return retSeqId;
} else {
Debug.logError("[SequenceUtil.SequenceBank.getNextSeqId] Fill bank failed, returning null", module);
return null;
}
}
}
实体引擎动作示意图:
每个特定的GenericHelper的配置信息都在entityengine.xml文件的datsource元素指定。
GenericDelegator是实体引擎服务中最主要的访问接入点。每个服务请求被分派到和实体对应的帮助类,服务被请求用来和这个实体的实体组通信。OFBiz实体模型默认的实体组XML文件可以在ofbiz/mmonapp/entitydef/entitygroup.xml.中找到。
GenericDelegator通过一个使用一个包含delegator名称的String类型的参数的工厂类方法产生(DelegatorFactory)。delegator名称用来在entityengine.xml文件中查找delegator标签,其中包含相应的数据源信息。每个delegator使用一个实体模型和一个实体模型加载器(entitymodelreader)和一个实体组加载器(entitygroupreader),来加载相关的实体信息。
最有用处的事务工厂类是org.ofbiz.core.entity.transaction.JNDIFactory类。这个类使用entityengine.xml中附加元素来定位JNDI中的UserTransaction和TransactionManager对象。
user-transaction-jndi和transaction-manager-jndi标签用来指定对象在JNDI中的名称以及要使用JNDI服务的名称。两个标签都有两个必须的属性:jndi-server-name和jndi-name。一个例子:UserTransaction对象的jndi-name值为java:comp/UserTransaction,TransactionManager对象的jndi-name值为java:comp/TransactionManager。
另一个事务工厂类是GeronimoTransactionFactory,它使用apache.geronimo的事务管理来实现。同时提供了一个TransactionFactory的工厂类来得到已存在的事务管理器和数据库连接。
事务的应用是放在TransactionUtil中来应用的(TransactionUtil中提供一系统的静态方法来实现事务管理)。在实体引擎中,TransactionUtil会被GenericDelegator中应用,它同时会被其他地方显示使用。
在TransactionUtil中使用ThreadLocal实现线程安全,并通过编程方式实现了事务管理能力。主要代码结构如下:
public static synchronized boolean begin(int timeout) throws GenericTransactionException { //事务开始
UserTransaction ut = TransactionFactory.getUserTransaction(); //根据不同的实现得到不同的UserTransaction接口,这里有两个实现:GeronimoTransactionFactory和JNDIFactory两个。
if (ut != null) {
try {
//检查当前线程是否绑定了事务
int currentStatus = ut.getStatus();
if (currentStatus == Status.STATUS_ACTIVE) {
Debug.logVerbose("[TransactionUtil.begin] active transaction in place, so no transaction begun", module);
return false;
} else if (currentStatus == Status.STATUS_MARKED_ROLLBACK) {
Exception e = getTransactionBeginStack();
if (e != null) {
Debug.logWarning(e, "[TransactionUtil.begin] active transaction marked for rollback in place, so no transaction begun; this stack trace shows when the exception began: ", module);
} else {
Debug.logWarning("[TransactionUtil.begin] active transaction marked for rollback in place, so no transaction begun", module);
}
RollbackOnlyCause roc = getSetRollbackOnlyCause();
// do we have a cause? if so, throw special exception
if (roc != null && !roc.isEmpty()) {
throw new GenericTransactionException("The current transaction is marked for rollback, not beginning a new transaction and aborting current operation; the rollbackOnly was caused by: " + roc.getCauseMessage(), roc.getCauseThrowable());
} else {
return false;
}
}
// set the timeout for THIS transaction
if (timeout > 0) {
ut.setTransactionTimeout(timeout);
Debug.logVerbose("[TransactionUtil.begin] set transaction timeout to : " + timeout + " seconds", module);
}
// begin the transaction开始事务
ut.begin();
// reset the timeout to the default
if (timeout > 0) {
ut.setTransactionTimeout(0);
}
…..
return true;
} catch (NotSupportedException e) {
throw new GenericTransactionException("Not Supported error, could not begin transaction (probably a nesting problem)", e);
} catch (SystemException e) {
throw new GenericTransactionException("System error, could not begin transaction", e);
}
} else {
return false;
}
}
public static void commit() throws GenericTransactionException { //事务提交
UserTransaction ut = TransactionFactory.getUserTransaction();
if (ut != null) {
try {
int status = ut.getStatus();
if (status != STATUS_NO_TRANSACTION && status != STATUS_COMMITTING && status != STATUS_COMMITTED && status != STATUS_ROLLING_BACK && status != STATUS_ROLLEDBACK) {
ut.commit();
……
} else {
Debug.logWarning("[TransactionUtil.commit] Not committing transaction, status is " + getStatusString(), module);
}
} catch (RollbackException e) {
….
} catch (IllegalStateException e) {
….
} catch (HeuristicMixedException e) {
….
} catch (HeuristicRollbackException e) {
….
} catch (SystemException e) {
….
}
} else {
Debug.logInfo("[TransactionUtil.commit] UserTransaction is null, not commiting", module);
}
}
public static void rollback(Throwable causeThrowable) throws GenericTransactionException { //事务回滚
UserTransaction ut = TransactionFactory.getUserTransaction();
if (ut != null) {
try {
int status = ut.getStatus();
if (status != STATUS_NO_TRANSACTION) { //这是唯一的一个方法确定上下文中没有事务。
if (causeThrowable == null && Debug.infoOn()) {
Exception newE = new Exception("Stack Trace");
}
……….
ut.rollback();
} else {
}
} catch (IllegalStateException e) {
……….
} catch (SystemException e) {
……….
}
} else {
Debug.logInfo("[TransactionUtil.rollback] No UserTransaction, transaction not rolled back", module);
}
}
public static Transaction suspend() throws GenericTransactionException { //事务挂起
try {
if (TransactionUtil.getStatus() != STATUS_NO_TRANSACTION) {
TransactionManager txMgr = TransactionFactory.getTransactionManager();
if (txMgr != null) {
pushTransactionBeginStackSave(clearTransactionBeginStack());
pushSetRollbackOnlyCauseSave(clearSetRollbackOnlyCause());
Transaction trans = txMgr.suspend();
pushSuspendedTransaction(trans);
return trans;
} else {
return null;
}
} else {
return null;
}
} catch (SystemException e) {
throw new GenericTransactionException("System error, could not suspend transaction", e);
}
}
public static void resume(Transaction parentTx) throws GenericTransactionException { //事务恢复
if (parentTx == null) return;
try {
TransactionManager txMgr = TransactionFactory.getTransactionManager();
if (txMgr != null) {
setTransactionBeginStack(popTransactionBeginStackSave());
setSetRollbackOnlyCause(popSetRollbackOnlyCauseSave());
txMgr.resume(parentTx);
removeSuspendedTransaction(parentTx);
}
} catch (InvalidTransactionException e) {
/* NOTE: uncomment this for Weblogic Application Server
// this is a work-around for non-standard Weblogic functionality; for more information see: http://www.onjava.com/pub/a/onjava/2005/07/20/transactions.html?page=3
if (parentTx instanceof weblogic.transaction.ClientTransactionManager) {
// WebLogic 8 and above
((weblogic.transaction.ClientTransactionManager) parentTx).forceResume(transaction);
} else if (parentTx instanceof weblogic.transaction.TransactionManager) {
// WebLogic 7
((weblogic.transaction.TransactionManager) parentTx).forceResume(transaction);
} else {
throw new GenericTransactionException("System error, could not resume transaction", e);
}
*/
throw new GenericTransactionException("System error, could not resume transaction", e);
} catch (SystemException e) {
throw new GenericTransactionException("System error, could not resume transaction", e);
}
}
[注] Mysql事务隔离级别:
• 未提交读(Read Uncommitted):允许脏读,也就是可能读取到其他会话中未提交事务修改的数据
• 提交读(Read Committed):只能读取到已经提交的数据。Oracle等多数数据库默认都是该级别
• 可重复读(Repeated Read):可重复读。在同一个事务内的查询都是事务开始时刻一致的,InnoDB默认级别。在SQL标准中,该隔离级别消除了不可重复读,但是还存在幻象读
• 串行读(Serializable):完全串行化的读,每次读都需要获得表级共享锁,读写相互都会阻塞
事务隔离定义可参见:java.sql.Connection中的定义
事务界定选择的考虑:
1. 使用 JDBC API 进行事务界定:
> 事务界定代码应嵌入在 DAO 类中
> DAO类调用者不能界定事务。
> 事务范围局限于单个 JDBC 连接。
2. 使用JTA进行事务界定
> 事务用 JTA 界定,事务界定代码从 DAO 中分离出来。
> DAO类调用者负责界定事务。
> DAO 加入一个全局事务。
JDBC 事务并不总是适合复杂的企业应用程序,如果事务需要跨越多个 DAO 或者多个数据库,那么应该选择使用JTA界定。
ofbiz中对实体的访问(数据库)是通过delegate对象来进行的,而delegate对象是GenericDelegator类的一个实例,包含有关实体操作的方法和属性。
• 在JSP中使用
<jsp:useBeanid="delegator"type="org.ofbiz.core.entity.GenericDelegator"scope="request"/>
• 在severlet或event中使用
GenericDelegator delegator = (GenericDelegator) request.getAttribute("delegator");
• 通过一个已知的数值对象获取delegator,方法为
GenericDelegator delegator = entity.getDelegator();
• 手工建立
GenericDelegator delegator = GenericDelegator.getGenericDelegator("default")
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。