现在的位置: 首页 > 综合 > 正文

EBS OAF开发中的Java 实体对象(Entity Object)<一>

2014年04月04日 ⁄ 综合 ⁄ 共 10126字 ⁄ 字号 评论关闭

EBS OAF开发中的Java 实体对象(Entity Object)<一>

(版权声明,本人原创或者翻译的文章如需转载,如转载用于个人学习,请注明出处;否则请与本人联系,违者必究)

概览

l  关于实体对象

l  创建

l  更新/校验

l  删除

l  加锁

l  提交

l  回滚

l  事务撤回

l  对象版本号列

l  标准WHO列

l  错误处理

l  实体专家,验证视图对象和验证应用模块

l  调用PL/SQL方法和程序

l  多语言表(TL)的实体对象

l  标准验证模式/示例

关于实体对象

实体对象封装了业务逻辑和应用表的DML操作.

对象模型和关键的类

oracle.apps.fnd.framework.server.OAEntityCache:这个缓存用于存储一个特定实体查询出的行.多个映射到同一个实体的视图对象共享同一个的实体缓存。

<YourEntityName>EOImpl继承oracle.apps.fnd.framework.server.OAEntityImpl:这是视图对象本身.当实例化时,它代表了一行数据。

oracle.apps.fnd.framework.server.OAEntityDefImpl:代表了描述实体对象的元数据,包括特征(attribute),事件,验证,联系和属性.当实例化时,它描述了实体对象类的所有实例。实体定义类是一个单例类。

<YourEntityName>Expert继承oracle.apps.fnd.framework.server.OAEntityExpert: 这是一个特殊的单例辅助类,其注册于一个实体对象。

oracle.jbo.Key:这是一个不可改变的主键,外键或者组合行标示器。

创建

要创建一个实体对象,你必须像下面演示的调用相应视图对象的createRow方法然后调用insertRow方法.

// In the application module; this example from the OA Framework

// ToolBox Tutorial will instantiate a SupplierEOImpl.

 

public void create()

{

  OAViewObject vo = getSuppliersVO();

  vo.insertRow(vo.createRow());

 

  // Always call this after you perform a row insert. See the Entity Object

  // New / Initial section below for additional information.

  vo.setNewRowState(Row.STATUS_INITIALIZED);

}

视图对象的createRow()方法调用了潜在实体对象上的create()方法。在实体对象上的create()方法里添加所有设置默认值/初始化代码,就像ToolBox Tutorial 中的oracle.apps.fnd.framework.toolbox.tutorial.server.SupplierEOImpl类。

警告:不要在实体对象的构造器上放置任何初始化逻辑;一般应该添加在create()方法中调用super.create(attributeList)方法之后。

小建议:如果默认值在设计时就可以确定,并且对一个特定的UI合适,你也可以通过在OA扩展设置Initial Value属性来设置默认值。这些值可以被客户个性化;他们不必继承你的实体对象并重载create()方法来设置默认值。更多信息可以参考属性视图中的Defaulting主题。

/**

 * In the SupplierEOImpl class; initialize a new supplier.

 */

Public void create(AttributeList attributeList)

{

  super.create(attributeList); 

  OADBTransaction transaction = getOADBTransaction();

   

  // Supplier id is obtained from the table's sequence

  Number supplierId = transaction.getSequenceValue("FWK_TBX_SUPPLIERS_S");

  setSupplierId(supplierId);

  

  // Start date should be set to sysdate

  setStartDate(transaction.getCurrentDBDate());

} // end create()

 

小建议:当在你的实体对象中设置值的时候,一般调用set<AttributeName>(val)替代setAttribute("<AttributeName>",val),这样性能好点因为省略了查找步骤。要跳过任何代码中的属性验证,但还要做相应属性定义上声明式的验证,可以直接调用setAttributeInternal()。更多信息,参考实体和视图对象的属性设置器。

组合实体关系

在组合关系中,BC4J自动在子实体对象上设置父主键属性值。父主键值被传递给子的create()方法attributeList参数,并在调用super.create(attributeList)的时候设置值。

不要尝试自己填充父主键的值。

实体对象初始化/新建状态

默认,实体创建时的行状态是STATUS_NEW,BC4J会把它们添加到验证和提交监听列表。在这情况下,任何触发验证或数据库提交顺序的事件就会包含这些实体对象。

就像OA Framework Model Coding Standards描述的,在你插入新创建的行之后,应立刻明确调用ViewRowImpl的方法setNewRowState(STATUS_INITIALIZED)。参考上面的代码示例。这会设置所有关联的实体对象的状态为STATUS_INISTIALIZAED.

当你这么做的时候,BC4J会从事务和验证监听列表中移除相应实体对象,这样它们就不会被验证或者提交到数据库中。只要用户做了修改(一个属性的设置器被调用了),那么实体对象的状态就改变成了STATUS_NEW,并且BC4J就会把它返回添加到验证/提交列表。你也可以在任意时刻调用ViewRowImpl上的setNewRowState(STATUS_NEW)来修改状态。

特殊的”创建”情景

单行中“扁平的”主记录/明细

在OAF框架中的ToolBox Tutorial中,里面有显示给用户看到的单个,”扁平”行的,组合的主记录/明细实体。一个采购订单可能包含很多行,而且行相应的包含很多发运,但是在UI中我们显示行和发运为1:1的关系。

尽管BC4J可能很容易为一个单个视图行创建几个不同实体对象,这里这几个实体对象是无关的或者是同等的—如果这些对象之中一个是另一个的子的话,你就需要避免这个。在这种情况下,你必须添加下面自定义的create()方法到你的视图对象行实现来保证在低级别的实体上设置了正确的父键。

// The following is required to support the creating the master/detail line

// and shipment entities that have been "flattened" into a single row in

// POLineShipFullVO with a 1:1 join.

//

// If you don't do this, BC4J will automatically treat them like peers and

// try to set the po header id as the parent key for both entities.

   

protected void create(oracle.jbo.AttributeList nvp) 

{ 

  PurchaseOrderLineEOImpl lineEO = (PurchaseOrderLineEOImpl)getEntity(0); 

  PurchaseOrderShipmentEOImpl shipmentEO = (PurchaseOrderShipmentEOImpl)getEntity(1);    

   

  try 

  {

    // Create Lines EO 

    lineEO.create(nvp); 

   

    // Create Shipments EO 

    shipmentEO.create(lineEO); 

 

    // Calling this ensures that any personalization default values are

    // properly set since the OAF normally sets this in the super.create(), but

    // since this is not called in this workaround, we need another method

    // to ensure customer defaults are applied.

    setDefaultValue();

  } 

  catch (Exception ex) 

  { 

    lineEO.revert(); 

    shipmentEO.revert();

  

    if (ex instanceof oracle.jbo.JboException) 

    { 

      oracle.jbo.JboException jboEx = (oracle.jbo.JboException)ex; 

      // Developers have to do the mapping on their own because of the override. 

      jboEx.doEntityToVOMapping(getApplicationModule(), new oracle.jbo.ViewObject[]{getViewObject()});    

      throw jboEx; 

    } 

    throw OAException.wrapperException(ex); 

  }

} // end create() 

实体对象缓存

实体对象一旦创建,BC4J就会存储视图对象到一个特殊的事务缓存,有很多原因我们需要这么做,这在JDeveloper BC4J文档中有全面的描述。两个重要的好处是:

l  同一个根应用模块中的多个视图对象可以共享相同的潜在的实体对象。那意味着对一个视图对象上做的修改就可以立即对其它引用修改后实体对象的视图对象可见。

l  未保存的修改保存在缓存中,即使视图的行被刷新了。比如,在主/明细关系中,明细视图对象上实体继承的属性值保存在缓存里,即使用户从主记录行导航到明细行。所有你的未保存的修改在事务的生命周期保持不变。

理解了缓存存在很重要,因为你必须明确和它交互来做特定验证,比如,当做一个唯一性验证,你必须即啊要在实体缓存和也要在数据库中查找重复数据。

这里有三种主要方式来即在缓存也在数据库里检查数据。

1.      调用findByPrimaryKey()方法

2.      手工遍历缓存.

3.      使用关联遍历缓存

findByPrimaryKey()方法

findByPrimaryKey()方法保证了先从缓存中查找匹配给定的键值和实体对象类型,然后才查找数据库。这是一个非常有用的方法,但是如果你想使用的轻量级的方法,那它就不是你要找的的了,因为它会实例化在数据库中找到的所有匹配的实体对象。它也会把整个实体对象拉入到内存中,而不仅仅是键值。尽管如此,这个方法可以—而且应该—用于你不期望找到匹配结果的情形,比如,当验证序列号生成的主键时。当你需要调用一个方法来访问中间层来查找匹配的时候使用这个方法也是合适的。

oracle.apps.fnd.framework.toolbox.schema.sever.SupplierEOImpl中下面的代码演示了如何使用这个方法:

public void setSupplierId(Number value)

{ 

  if (value != null)

  {

    // Supplier id must be unique. To verify this, you must check both the

    // entity cache and the database. In this case, it's appropriate

    // to use findByPrimaryKey( ) because you're unlikely to get a match, and

    // and are therefore unlikely to pull a bunch of large objects into memory.

 

    // Note that findByPrimaryKey() is guaranteed to check all suppliers. 

    // First it checks the entity cache, then it checks the database.

 

    OADBTransaction transaction = getOADBTransaction();

    Object[] supplierKey = {value};

    EntityDefImpl supplierDefinition = SupplierEOImpl.getDefinitionObject();

    SupplierEOImpl supplier = 

      (SupplierEOImpl)supplierDefinition.findByPrimaryKey(transaction, new Key(supplierKey));

    if (supplier != null)

    {

      throw new OAAttrValException(OAException.TYP_ENTITY_OBJECT,

             getEntityDef().getFullName(), // EO name

             getPrimaryKey(), // EO PK

             "SupplierId", // Attribute Name

             value, // Bad attribute value

             "ICX", // Message application short name

             "FWK_TBX_T_SUP_ID_UNIQUE"); // Message name 

    }

  }

  

  setAttributeInternal(SUPPLIERID, value);

} // end setSupplierId()

手工迭代缓存

你可以手工检查实体对象缓存来做可以做findByPrimaryKey()的同样的检查。然后你可以选择在一个单独的步骤对数据库做同样的检查。这个方法的好吃就是你可以避免不必要的对象实例化。

下面的示例也是来自于ToolBox Tutorial SupplierEOImpl类:

public void setName(String value)

{

  // Since this value is marked as "mandatory," the BC4J Framework will take

  // care of ensuring that it's a non-null value. However, if it is null, we

  // don't want to proceed with any validation that could result in a NPE.

   

  if ((value != null) || (!("".equals(value.trim()))))

  {

    // Verify that the name is unique. To do this, we must check both the entity

    // cache and the database. We begin with the entity cache.

    com.sun.java.util.collections.Iterator supplierIterator = 

      getEntityDef().getAllEntityInstancesIterator(getDBTransaction());

   

    Number currentId = getSupplierId();

   

    while ( supplierIterator.hasNext() )

    {

      SupplierEOImpl cachedSupplier = (SupplierEOImpl)supplierIterator.next();

 

      String cachedName = cachedSupplier.getName();

      Number cachedId = cachedSupplier.getSupplierId();

      

      // We found a match for the name we're trying to set, so throw an

      // exception. Note that we need to exclude this EO from our test.

 

      If (cachedName != null && value.equalsIgnoreCase(cachedName) &&    

        cachedId.compareTo(currentId) != 0 )

      {

        throw new OAAttrValException(OAException.TYP_ENTITY_OBJECT,

                               getEntityDef().getFullName(), // EO name

                               getPrimaryKey(), // EO PK

                               "Name", // Attribute Name

                               value, // Attribute value

                               "ICX", // Message product short name

                               "FWK_TBX_T_SUP_DUP_NAME"); // Message name 

      }

    }

     

    // Now we want to check the database for any occurrences of the supplier

    // name. The most efficient way to check this is with a validation view

    // object which we add to a special "Validation" application module.

    OADBTransaction transaction = getOADBTransaction();

    OAApplicationModule vam;

    // Look to see if the VAM has already been created in this transaction. If not,

    // create it.

    vam = (OAApplicationModule)transaction.findApplicationModule("supplierVAM");

    if (vam == null)

    { 

      vam = 

      (OAApplicationModule)transaction.createApplicationModule("supplierVAM",

         "oracle.apps.fnd.framework.toolbox.schema.server.SupplierVAM");

    }

   

    // Now, we use a lightweight "validation" view object to see if a supplier exists

    // with the given name.

    SupplierNameVVOImpl valNameVo = (SupplierNameVVOImpl)vam.findViewObject("SupplierNameVVO");

    valNameVo.initQuery(value);

    if (valNameVo.hasNext())

    {

      throw new OAAttrValException(OAException.TYP_ENTITY_OBJECT,

            getEntityDef().getFullName(), // EO name

            getPrimaryKey(), // EO PK

            "Name", // Attribute Name

            value, // Attribute value

            "ICX", // Message application short name

            "FWK_TBX_T_SUP_DUP_NAME"); // Message name

    }

  }

   

  setAttributeInternal(NAME, value);

} // end setName()

关联迭代

这意义而言就和findByPrimaryKey()类似,它保证了会即检查实体缓存和数据库。它也会加载它在数据库中找到的实体对象到内存中,如果你想在实体对象上调用方法的话,它就比较有用。不像findByPrimaryKey()它可以使用任意键来查找任意类型的实体对象,这个方法仅用于取出以关联关系关联当前对象的实体对象。

下面的示例表明了如何使用根组合实体对象的关联来查找它所有的子对象。

private void checkLineExists()

{ 

  // A purchase order header must have at least 1 associated line.

  // To check this, we first do a manual check of the entity cache

  // If we find a line for this header, we're done (note that the entity cache won't

  // include EOs that are DELETED or DEAD).

 

  com.sun.java.util.collections.Iterator fastIterator = 

    PurchaseOrderLineEOImpl.getDefinitionObject().getAllEntityInstancesIterator(getDBTransaction());

  Number currentHeaderId = getHeaderId();

  while ( fastIterator.hasNext() )

  {

    PurchaseOrderLineEOImpl cachedLine = (PurchaseOrderLineEOImpl)fastIterator.next();

 

    Number cachedHeaderId = cachedLine.getHeaderId();

 

    // If we find a match, we're done. Don't forget to look ONLY for lines

    // for this header... The entity cache can include lines for other headers

    // also.

   

    If ((cachedHeaderId != null) && (cachedHeaderId.compareTo(currentHeaderId) == 0 ))

    {

      return;

    } 

  }

 

  // We haven't found any matches in the cache yet, so now we need to check

  // the database...    

  

  // In this example, we're illustrating the use of the association between the

  // header and its lines to iterate through all the shipments.  This will

  // check both the cache and the database, and will bring all the rows

  // into the middle tier.
  // Note that this looks only at lines for this

  // header so we don't need to filter our results, so it is convenient.

  RowIterator linesIterator = getPurchaseOrderLineEO();

   

  if (!(linesIterator.hasNext()))

  {

    throw new OARowValException(OARowValException.TYP_ENTITY_OBJECT,

                getEntityDef().getFullName(),

                getPrimaryKey(),

                "ICX", // Message product short name

                "FWK_TBX_T_PO_NO_LINES"); // Message name

  }

} // end checkLineExists()

实体状态

每一个实体对象都有一个关联的”实体状态”,它描述了关联了潜在的数据库数据和事务的状态。你可以在你的代码通过调用getEntityState()来测试这些状态。

小建议:BC4J可以自动从实体缓存移除实体对象,如果它的状态是STATUS_DEAD,因此你不需要在你的代码里担心手工排除对象如果你在查找”好”的对象。

l  STATUS_NEW –在当前事务中实体对象是新建的。

l  STATUS_DELETED –来自于数据库中的实体对象,并且已在当前事务中被删除了。

l  STATUS_MODIFIED-来自于数据库中实体对象,并且在当前事务中已被修改了。

l  STATUS_UNMODIFIED-来资源数据库中的实体对象,并且没有被修改过,或者被修改过并且这些修改已被提交。

l  STATUS_DEAD-实体对象在当前事务是新建的,并且已经被删除了。

l  STATUS_INITIALIZED-实体对象是”临时”状态并且不会被提交或者验证。

抱歉!评论已关闭.