OFBiz实体引擎开发烹调书

来源:互联网 发布:美国电视直播软件 编辑:程序博客网 时间:2024/04/30 15:43
* 保持实体名称少于25个字符
这个限制主要是为了Oracle只支持30字符以内的数据库对象名称,再加上OFBiz会自动在单词之间加上"_",所以就得出了这么个限制.

* 关联的工作方式

它们定义于entitymodel.xml文件中的<entity>段,示例如下:

      <relation type="one" fk-name="PROD_CTGRY_PARENT" title="PrimaryParent" rel-entity-name="ProductCategory">        <key-map field-name="primaryParentCategoryId" rel-field-name="productCategoryId"/>      </relation>      <relation type="many" title="PrimaryChild" rel-entity-name="ProductCategory">        <key-map field-name="productCategoryId" rel-field-name="primaryParentCategoryId"/>      </relation>

    type这个属性标签定义关联类型: "one"表示一对一,"many"表示从此实体引出的一对多关系
    fk-name的属性值是数据库外键名.为自己的外键命名是一个好的习惯,虽然如果你不设置此属性,OFiz也会自己建外建.
    rel-entity-name的属性值指向关联的实体名称
    title用来区分两个实体之间的多重关系
    <key-map>节点定义关联中使用到的字段.field-name指向本实体内的引用字段,rel-field-name定义关联的实体字段,你可以通过多个字段组合关联
    当你访问一个关联,你可以使用title+entityName作为参数调用.getRelated("")或.getRelatedOne("")方法.在关联为"many"时使用.getRelated("")是恰当的,因为它返回一个List,同样在关联为"one"时通过.getRelatedOne("")方法获得一个值.

* view-entities相关内容
view-entities的功能非常强大,它允许你可以创建一个join-like查询,即使你的数据库不支持join.
关于你数据库的join语法存放在entityengine.xml的datasource节点下的join-style属性中.
当你通过<view-link...>节点将两上实体连接起来时,记住:
1. 实体名称顺序是重要的
2. 默认的连接方式是inner join(即同样的值存在于两个实体类中),外连接需要使用rel-optional="true"
如果多个实体中拥有相同的字段名称,比如statusId,结果集中的statusId使用第一个实体中的该列,其它实体中的同名列将被丢弃.如果你想要同时获得这些列,你需要通过在其之前加入<alias-all>节点,一个方式是使用<alias ..>节点来为不同实体的同名字段起别名,示例:

<alias entity="EntityOne" name="entityOneStatusId" field="statusId"/><alias entity="EntityTwo" name="entityTwoStatusId" field="statusId"/>

另一种方法是在<alias-all>节点中使用<exclude field="">,如下:

<alias-all entity-alias="EN">  <exclude field="fieldNameToExclude1"/>  <exclude field="fieldNameToExclude2"/></alias-all>

这样也可以排除掉很多不打算使用到的信息,特别是在一个非常大的表中查询时.
如果你打算执行类似于以下的查询语句时:

SELECT count(visitId) FROM  GROUP BY trackingCodeId WHERE fromDate > '2005-01-01'

需要包含字段visitId以及function="count" 标签,trackingCodeId需加上group-by="true"标签,fromDate需要加上group-by="false"标签

在你进行查询时,有一件非常重要的事情需要注意,比如说delegator.findByCondition方法,你必须指定检出的字段列表,并且你不能指定fromDate字段,否则你将得到一个错误.这就是为webtools不能够使用view-entities来查看的原因.

你可以查看applications/marketing/entitydef/entitymodel.xml的底部内容学习,及通过applications/marketing/webapp/marketing/WEB-INF/actions/reports学习beanshell脚本的调用.

* 我可以在entitymodel.xml文件中定义自己的view-entities吗?

不能, 你可以动态定义它们.你可以查看org.ofbiz.party.party.PartyServices中的findParty方法学习它的使用

* 如果为有效期间创建条件?

我们提供了一组非常有用的方法EntityUtil.getFilterByDateExpr ,它能返回一个EntityConditionList根据有效期间来筛选一个结果集. 

* 如何在大数据结果集下工作

如果你检出一个大的数据结果集,你应当使用EntityListIterator通过迭代方式读取数据,而非List.
示例,如果你使用:

List products = delegator.findAll("Product");

你可能获得一个"java.lang.OutOfMemoryError".  这是由于你通过findAll, findByAnd, findByCondition等方法来获得一个大的内存数据结果集导致内存溢出.  在这种情况下, 应该使用EntityListIterator迭代方式来读取你的数据. 这个示例应改写成:

productsELI = delegator.findListIteratorByCondition("Product", new EntityExpr("productId", EntityOperator.NOT_EQUAL, null), UtilMisc.toList("productId"), null);

注意获得EntityListIterator的方法只用通过条件, 所以你需要将你的条件重写为EntityExpr (在此次情况下,productId是主键字段不可能为空的, 所以将返回所有Proudct实例,)或 EntityConditionList.

此方法参数中包含检出的字段(这里为productId)以及排序字段(这里不需要,所以赋了null)

你可以传递一个null作为EntityCondition参数来获得所有结果.然后这不一定在所有数据库下都能正常工作!  在maxdb及其它不常用的数据库下时你要小心使用这些高级功能.

* 如何使用EntityListIterator

当我们通过EntityListIterator迭代访问数据时, 通常是这样:

while ((nextProduct = productsELI.next()) != null) {.    // operations on nextProduct}

在EntityListIterator 中使用 .hasNext()方法是一种不经济的做法.

在你完成你的操作后,要记得关闭此迭代

productsELI.close();

* 如何查询无重结果集

当前只能通过list iterator方法并指定EntityFindOptions参数,示例如下:

       listIt = delegator.findListIteratorByCondition(entityName, findConditions,             null, // EntityConditions参数            fieldsToSelectList,             fieldsToOrderByList,                          //关键部分.  第一个true表示"specifyTypeAndConcur"            // 第二个true指完是一个滤重查询.  显然在实体引擎中只能通过这个方法来进行滤重查询            new EntityFindOptions(true, EntityFindOptions.TYPE_SCROLL_INSENSITIVE, EntityFindOptions.CONCUR_READ_ONLY, true));

在minilang, 它会更简单:

  <entity-condition entity-name="${entityName}" list-name="${resultList}" distinct="true">     <select field="${fieldName}"/>     .

* 如何进行一个大小写不敏感的查询(即不分大小写)

你需要查询条件表达式两边均转为大写,示例:

andExprs.add(new EntityExpr("lastName", true, EntityOperator.LIKE, "%"+lastName+"%", true));

    (来源org.ofbiz.party.party.PartyServices)


* 如何将EntityListIterator转换成List

  使用EntityListIterator.getCompleteList() 及getPartialList 方法


* 如何自动获得下一个ID值

  在minilang 中使用 <sequence-id-to-env ...> 或在Java中通过delegator.getNextSeqId(...) 获得 .  id序列存放于SequenceValueItem中.


* 关于ID值的一些警告

  不要在种子/演示数据中使用10000做为数据的ID,当系统尝试自动创建数据时,它们都将尝试10000,这将导致一个键值冲突错误.

* 如何从一个明细项中获得序列ID
  
  有些实体,比如拥有itemSeqId 的InvoiceItem(发票明细项) and OrderItem(订单明细项).此项通常在你处一次为item生成GenericValue 时自动生成ID,之后向delegator要求生成项目的seq Id:

  GenericValue orderItem = delegator.makeValue("OrderItem", orderItemValues);  delegator.setNextSubSeqId(orderItem, "orderItemSeqId", ORDER_ITEM_PADDING, 1);

* 如何为SELECT SUM(QUANTITY - CANCEL_QUANTITY) AS QUANTITY之类的语句设置别名
  <alias entity-alias="OI" name="quantity" function="sum">      <complex-alias operator="-">          <complex-alias-field entity-alias="OI" field="quantity" default-value="0"/>          <complex-alias-field entity-alias="OI" field="cancelQuantity" default-value="0"/>      </complex-alias>  </alias> 

  SELECT SUM(COALESCE(OI.QUANTITY,'0') - COALESCE(0I.CANCEL_QUANTITY)) AS QUANTITY在结果集包含默认值是一个好的习惯,否则如果有一项为null,那么最终相减的结果就也为null了.
  操作符可以为任何你当前使用数据库所支持的SQL操作符,比如算术运算符+, -, * 和/ 或者字符串连接符 ||.

  你可以增加function=""标签来完成在complex-alias-field中的min, max, sum, avg, count, count-distinct, upper 及lower集合运算.  示例, 以上的定义可以用另一种方法表示为:

  <alias entity-alias="OI" name="quantity">      <complex-alias operator="-">          <complex-alias-field entity-alias="OI" field="quantity" default-value="0" function="sum"/>          <complex-alias-field entity-alias="OI" field="cancelQuantity" default-value="0" function="sum"/>      </complex-alias>  </alias>

   即为SELECT (SUM(COALESCE(OI.QUANTITY,'0')) - SUM(COALESCE(OI.CANCEL_QUANTITY,'0'))) AS QUANTITY查询结果集

* 我讨厌OFBiz的实体引擎,我要自己的JDBC连接!
  好的,以下是你获得JDBC连接的方法:

import org.ofbiz.entity.jdbc.ConnectionFactory;String helperName = delegator.getGroupHelperName("org.ofbiz");    // gets the helper (localderby, localmysql, localpostgres, etc.) for your entity group org.ofbizConnection conn = ConnectionFactory.getConnection(helperName); Statement statement = conn.createStatement();statement.execute("SELECT * FROM PARTY");ResultSet results = statement.getResultSet();//  通过普通JDBC 的结果集来操作//Alternatively, you can use the SQLProcessor like this:SQLProcessor sqlproc = new SQLProcessor(helperName);sqlproc.prepareStatement("SELECT * FROM PARTY");ResultSet rs1 = sqlproc.executeQuery();ResultSet rs2 = sqlproc.executeQuery("SELECT * FROM PRODUCT");

 你可以查看framework/webtools/webapp/webtools/WEB-INF/actions/entity/EntitySQLProcessor.bsh了解它的使用

在以下网址你可以获得相关JavaDoc的内容:
http://www.opentaps.org/javadocs/version-1.0/framework/api/org/ofbiz/entity/jdbc/SQLProcessor.html
http://www.opentaps.org/javadocs/version-1.0/framework/api/org/ofbiz/entity/jdbc/ConnectionFactory.html

  *** 请先考虑以下内容: 你放弃数据库的无关性意味着你在某些方法将无法与框架或其它程序集成.你确定你要这么做吗?

获得更好的做法,请访问 http://www.opentaps.org/docs/index.php/Using_the_Query_Tool


* 关于时间比较方法的一些警告

在你用 GREATER_THAN比较一个 Timestamp类型数据时, 你有可能获得相同的时间数据:

delegator.findByAnd("XXX", UtilMisc.toList(new EntityExpr("fromDate", EntityOperator.GREATER_THAN, "2007-12-31 23:59:59.998")));

 有可能包含fromDate=2007-12-31 23:59:59.998的数据.  (此种情况发生于PostgreSQL 8.1并且GenericDAO 类生成的SQL代码是'FROM_DATE > ' so 所以我也不明白发生这个问题的原因.) 所以确保安全的方法是, 增加1秒到需要比较的时间中然后使用 GREATER_THAN_EQUAL_TO方法 
delegator.findByAnd("XXX", UtilMisc.toList(new EntityExpr("fromDate", EntityOperator.GREATER_THAN_EQUAL_TO, "2008-01-01 00:00:00.998")));

* 警告: 在空集合中使用EntityOperator.IN

请小心如果使用EntityOperator.IN去判断一个非空集合在一个空集合中的包含项,你有可能获得一个语法错误: 在Derby或者其它一些不为人知的数据库中可能会出错.
所以建议你能在使用EntityOperator.IN之间,通常执行UtilValidate.isNotEmpty方法来判断一下结果集是否为空

* 警告: delegator.getNextSubSeqId 不能确保唯一性

很多实体有很多合成的主键.示例OrderItem's 主键是orderId + orderItemSeqId.  InventoryItemDetail's 主键是inventoryItemId +inventoryItemSeqId.  通常, delegator.getNextSubSeqId 通常是获得一个序列值,但是在多线程的访问下有可能无法确保生成的值的唯一性.  此段内容在http://issues.apache.org/jira/browse/OFBIZ-1636 中有相关文档记录.

当前, 如果有可能多个线程尝试同时写入实体组合键时,可以使用delegator.getNextSeqId来替代getNextSubSeqId.  (此问题不会发生于OrderItem, 因为它只使用单线程写入, 但有可能发生于 InventoryItemDetail, 它使用多线程来创建库存记录项.)

转载:http://www.blogjava.net/hispark/archive/2008/10/24/236404.html

原创粉丝点击