OFBiz的实体配置

 
实体定义文件一般存放位置是在对应模块的entity文件夹下面,在该模块对应的ofbiz-component.xml配置文件中加入一行,用来声明实体定义文件路径:
 
<entity-resource type="model" reader-name="main" loader="main" location="entitydef/entitymodel.xml"/>
我们这里先建立一个普通实体,声明数据库中该表中的所有字段信息。
 

实体定义的命名规则

 
实体名称(entity-name)首字母大写,如果实体名称由多个关键字组成,那么关键字首字母大写,例如entity-name="TenantDataSource",ofbiz 会在创建数据库表的时候根据entity-name 实体名称除首字母之外的大写字母前加“_”,所以entity-name="TenantDataSource"生成的数据库表名为 “Tenant_Data_Source”,所以要控制entity-name 实体名称不要超过25个字母。 
 
Field 表字段,命名规则与实体名称差不多,唯一不同的是首字母小写。 
 
其中的fieldtype,可以通过文件:
 
%ofbiz_home%\framework\entity\fieldtype\fieldtypeXXX.xml 其中XXX为你使用的数据库名称
进行查找,就可以找到field同具体type之间的对应关系。
 
定义第一个实体:
 
<entity entity-name="DsConsultant" package-name="com.xxx.crm.drivingschool">
<field name="consultantId" type="id-ne"/>
<field name="consultantName" type="name"/>
<field name="consultantCode" type="name"/>
<field name="gender" type="indicator"/>
<field name="city" type="name"/>
<field name="email" type="name"/> <prim-key field="consultantId"/>
</entity>
实体中定义了属性,注意这些属性中field name会通过Java以及SQL的命名规范进行映射,例如声明的consultantId使用了Java Camel风格命名方式,但映射到数据库中字段为:consultant_id。
在OFBiz中的实体分为多种类型,我们这里介绍本人使用最多的两种类型:
 
<!--?xml version="1.0" encoding="UTF-8" standalone="no"?-->

普通实体

 
普通实体和数据库中的表是一一对应的。程序会根据实体定义在数据库中创建表,索引,外键约束等。 
 
 
<entity entity-name="TenantDataSource" package-name="org.ofbiz.entity.tenant">
<description>
There should be one record for each tenant and each group-map for the active delegator.
The jdbc fields will override the datasource -> inline-jdbc values for the per-tenant delegator.
</description>
<field name="tenantId" type="id-ne"/>
<field name="entityGroupName" type="name"/>
<field name="jdbcUri" type="long-varchar"/>
<field name="jdbcUsername" type="long-varchar"/>
<field name="jdbcPassword" type="long-varchar"></field>
<prim-key field="tenantId"/>
<prim-key field="entityGroupName"/>
<relation type="one" fk-name="TNTDTSRC_TNT" rel-entity-name="Tenant">
<key-map field-name="tenantId"/>
</relation>
</entity>
 

视图实体

 
View entity 一般用做多表连接复杂查询,view entity 不会在数据库中反映出来。 
 
<view-entity entity-name="WorkEffortAssocView"
package-name="org.ofbiz.workeffort.workeffort"
title="Work Effort Association Entity with Name">
<member-entity entity-alias="WA" entity-name="WorkEffortAssoc"/>
<member-entity entity-alias="WETO" entity-name="WorkEffort"/>
<alias-all entity-alias="WA"/>
<alias entity-alias="WETO" name="workEffortToName" field="workEffortName"/>
<alias entity-alias="WETO" name="workEffortToSetup" field="estimatedSetupMillis"/>
<alias entity-alias="WETO" name="workEffortToRun" field="estimatedMilliSeconds"/>
<alias entity-alias="WETO" name="workEffortToParentId" field="workEffortParentId"/>
<alias entity-alias="WETO" name="workEffortToCurrentStatusId" field="currentStatusId"/>
<alias entity-alias="WETO" name="workEffortToWorkEffortPurposeTypeId" field="workEffortPurposeTypeId"/>
<alias entity-alias="WETO" name="workEffortToEstimatedStartDate" field="estimatedStartDate"/>
<alias entity-alias="WETO" name="workEffortToEstimatedCompletionDate" field="estimatedCompletionDate"/>
<alias entity-alias="WETO" name="workEffortToActualStartDate" field="actualStartDate"/>
<alias entity-alias="WETO" name="workEffortToActualCompletionDate" field="actualCompletionDate"/>
<view-link entity-alias="WA" rel-entity-alias="WETO">
<key-map field-name="workEffortIdTo" rel-field-name="workEffortId"/>
</view-link>
<relation type="one-nofk" fk-name="WK_EFFRTASSV_FWE" title="From" rel-entity-name="WorkEffort">
<key-map field-name="workEffortIdFrom" rel-field-name="workEffortId"/>
</relation>
</view-entity>
 
视图实体用于做多表连接的复杂查询,并不会在数据库端被反映出来,在更新的情况下也会有一定的困难。
 
<!--?xml version="1.0" encoding="UTF-8" standalone="no"?-->

 
首先,视图实体中的所有关联的数据表都需要在ofbiz的管理范围内:
 
<member-entity entity-alias="SR" entity-name="DsStudentReservation"/>
用来指定关联的实体及其别名,
 
<alias name="id" entity-alias="SR" field="id"/>
用来引用关联实体中的属性,属性可以重新命名(如果实体中属性冲突或更改显示名称时使用)。
 
<view-link entity-alias="SR" rel-entity-alias="SS">
<key-map field-name="studentId" rel-field-name="id"/>
</view-link>
使用关联的方式将其属性关联在一起。
 
如果optional=“false”, 将进行强制内关联。
 
在用groovy查询及在Forms.xml中使用时,与其它方式是相同的。
 
<alias-all entity-alias="ODD" prefix="orderDate" group-by="true">
<exclude field="dimensionId"/>
</alias-all>
alias-all 将某个实体的全部字段定义进来。Prefix定义以规定字段字符开头的字段。 
exclude 将实体中某些字段剔除出去。
 
视图实体中relation 只能用来做关系查询, 而view-link 用来做 join 关联查询。
在entityengine.xml中<datasource ..>元素当中的join-style属性当中设置你的数据库join语法;rel-optional:关联类型,默认是内连接,如果将此属性值设为true ,则为外连接。
 
假如需要做一些比较复杂的关联查询,使用下面的方式:
 
<alias entity-alias="OI" name="quantityOrdered" 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(OI.CANCEL_QUANTITY, 0)))
一个缺省值是一个良好的习惯,否则当他们之中有一个为空就会导致结果为空 
这个操作可以支持你使用数据库的所有函数例如  +, -, * 和 /,字符串连接符||。 
 
还可以添加一个 function="" 实现min, max, sum, avg, count, count-distinct, upper 和 lower 在 complex-alias-field中,例如:
 
<alias entity-alias="OI" >
<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>
 
翻译成SQL为:
 
SELECT (SUM(COALESCE(OI.QUANTITY,'0')) - SUM(COALESCE(OI.CANCEL_QUANTITY,'0')))

Delegator

 
Delegator是OFBiz中根据定义的实体模型来查询数据库的主要方式,可以在服务中通过dispatchContext.getDelegator()获取,也可以在groovy文件中直接使用。
使用Delegator的create方法来创建单个实体:
 
Delegator delegator = ctx.getDelegator();
String id = delegator.getNextSeqId("ServiceCar");
GenericValue dsServiceCar = delegator.makeValue("ServiceCar", UtilMisc.toMap(
"id", id,
"carType", context.get("carType")
"carNumber", context.get("carNumber"),
"coachId", context.get("coachId"),
"phone", context.get("phone")
));
delegator.create(dsServiceCar);
需要创建GenericValue,可以通过可变参数的方式来将该实体创建出来。值得注意的是,实体中的主键需要通过getNextSeqId来拿到,在获取下一个sequenceId的过程中,可能出现错误:
 
<!--?xml version="1.0" encoding="UTF-8" standalone="no"?-->

delegator.getNextSeqId(“数据表”), 但是居然出现了下面的问题:
 
java.lang.Exception: Failure in create operation for entity [DsSignupStudent]: org.ofbiz.entity.GenericEntityException:
Error while inserting: [GenericEntity:DsSignupStudent][carType,DRIVING_TYPE_C1(java.lang.String)][comment,null()][commissionFee,0(java.lang.Long)][consultantId,10020(java.lang.String)][course1Time,0(java.lang.Long)][course2Time,0(java.lang.Long)][course3Time,0(java.lang.Long)][createdStamp,2015-10-10 09:44:05.147(java.sql.Timestamp)][createdTxStamp,2015-10-10 09:44:05.146(java.sql.Timestamp)][drivingSchoolId,2(java.lang.String)][handleBy,null()][id,10122(java.lang.String)][idCard,2123123(java.lang.String)][infoChannel,INFO_CHANNEL_NET(java.lang.String)][isBodyCheckTable,null()][isCommissionPayed,null()][isContractTable,null()][isFinger,null()][isGivenMaterial,null()][isIdCardTable,null()][isPersonalPhoto,null()][isReceipt,null()][isSignupTable,null()][isUrgent,null()][lastUpdatedStamp,2015-10-10 09:44:05.147(java.sql.Timestamp)][lastUpdatedTxStamp,2015-10-10 09:44:05.146(java.sql.Timestamp)][localTrain,LOCAL_TRAIN_BD(java.lang.String)][name,a1(java.lang.String)][nextVisitDate,2015-10-11(java.sql.Date)][payForm,PAY_FORM_CARD(java.lang.String)][payMethod,PAY_METHOD_ALL(java.lang.String)][payedFee,111(java.lang.Long)][phone,122(java.lang.String)][referrerId,null()][serviceCityId,2(java.lang.String)][signupDate,2015-10-10(java.sql.Date)][signupFee,111(java.lang.Long)][signupStore,SIGNUP_STORE_FHD(java.lang.String)][studentStatus,STUDENT_STATUS_Y(java.lang.String)][studyProgress,STUDY_PROGRESS_LR(java.lang.String)][trainType,TRAIN_TYPE_ZX(java.lang.String)][trainingSiteId,10000(java.lang.String)] (SQL Exception while executing the following:INSERT INTO DS_SIGNUP_STUDENT (ID, SERVICE_CITY_ID, SIGNUP_STORE, NAME, ID_CARD, PHONE, CAR_TYPE, SIGNUP_FEE, PAY_METHOD, PAY_FORM, PAYED_FEE, SIGNUP_DATE, STUDY_PROGRESS, DRIVING_SCHOOL_ID, LOCAL_TRAIN, TRAIN_TYPE, INFO_CHANNEL, TRAINING_SITE_ID, CONSULTANT_ID, REFERRER_ID, NEXT_VISIT_DATE, STUDENT_STATUS, COMMENT, COURSE1_TIME, COURSE2_TIME, COURSE3_TIME, IS_ID_CARD_TABLE, IS_BODY_CHECK_TABLE, IS_SIGNUP_TABLE, IS_PERSONAL_PHOTO, IS_CONTRACT_TABLE, IS_RECEIPT, IS_FINGER, IS_GIVEN_MATERIAL, IS_URGENT, HANDLE_BY, COMMISSION_FEE, IS_COMMISSION_PAYED, IS_EXAM_FEE_PAYED, LAST_UPDATED_STAMP, LAST_UPDATED_TX_STAMP, CREATED_STAMP, CREATED_TX_STAMP) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?) (Duplicate entry '10122' for key 'PRIMARY')).
Rolling back transaction.
at
org.ofbiz.entity.transaction.TransactionUtil.setRollbackOnly(TransactionUtil.java:378) [ofbiz-entity-test.jar:?]
 
为什么系统会自动填充10122这个主键?我们发现OFBiz中存在一张表:
 
select * from sequence_value_item;
 
用于记录每个实体的下一个生成主键,如果这个主键已经被使用了,就会发生上面的错误:


 
在delegator中使用各种find方法来进行查找,最主要的查询方法是findList:
 
condition = EntityCondition.makeCondition(EntityJoinOperator.AND,EntityCondition.makeCondition("isConfirmed", EntityOperator.EQUALS, 'Y'),cityCondition,deleteCondition);
context.allRecords = delegator.findList("DsStudentPayRecordView", condition, null, ["-recordDate"], null, false);
可以根据需要选择的实体,选择的条件,选出的字段(如果为null则选择所有的字段),排序方式(在groovy中通过减号-来进行倒序),是否缓存(cache)来进行全面的查询。
此外,OFBiz中是可以跳过内置的实体模型,来直接查询数据库的。

在配置文件entityengine.xml中,需要使用GenericHelperDAO:

<datasource name="localmysqlolap"
helper-class="org.ofbiz.entity.datasource.GenericHelperDAO"
field-type-name="mysql"
check-on-start="true"
add-missing-on-start="true"
check-pks-on-start="false"
use-foreign-keys="true"
join-style="ansi-no-parenthesis"
alias-view-columns="false"
drop-fk-use-foreign-key-keyword="true"
table-type="InnoDB"
character-set="utf8"
collate="utf8_general_ci">
<read-data reader-name="tenant"/>
<read-data reader-name="seed"/>
<read-data reader-name="seed-initial"/>
<read-data reader-name="demo"/>
<read-data reader-name="ext"/>
<read-data reader-name="ext-test"/>
<read-data reader-name="ext-demo"/>
<inline-jdbc
jdbc-driver="com.mysql.jdbc.Driver"
jdbc-uri="jdbc:mysql://xxx.com:3306/xxx?useUnicode=true&amp;characterEncoding=utf8&amp;autoReconnect=true&amp;failOverReadOnly=false"
jdbc-username="xxx"
jdbc-password="xxxx"
isolation-level="ReadCommitted"
pool-minsize="2"
pool-maxsize="250"
time-between-eviction-runs-millis="600000"/><!-- Please note that at least one person has experienced a problem with this value with MySQL
and had to set it to -1 in order to avoid this issue.
For more look at http://markmail.org/thread/5sivpykv7xkl66px and http://commons.apache.org/dbcp/configuration.html-->
<!-- <jndi-jdbc jndi-server-name="localjndi" jndi-name="java:/MySqlDataSource" isolation-level="Serializable"/> -->
</datasource>

找到其中的group-name,在本文件声明中的group-name为:

<delegator name="test" entity-model-reader="main" entity-group-reader="main" entity-eca-reader="main">
<group-map group-name="org.ofbiz" datasource-name="localmysql"/>
<group-map group-name="org.ofbiz.olap" datasource-name="localmysqlolap"/>
<group-map group-name="org.ofbiz.tenant" datasource-name="localmysqltenant"/> </delegator>

可以看出,对应的group-name为org.ofbiz.tenant,使用这个group-name来构建SQLProcessor,就可以进行数据库SQL查询。

在Groovy中的写法为:

sqlProcessor = new SQLProcessor(delegator.getGroupHelperInfo("org.ofbiz.tenant"));
result = sqlProcessor.executeQuery("select * from xxx")
while (result.next()){
println result.getString("id")
}

可以通过调用ResultSet的方式进行数据库的查询操作,此时SQL就需要为可以在数据库中直接查询的SQL了。

 
总之,OFBiz中的实体模型还是比较强大的而且易于扩展,虽然相比Hibernate肯定有所不足,但足以应付其中的所有应用场景,以一种非常快捷易用的方式来进行业务场景逻辑的组合工作。
 

ApacheOFBiz的相关介绍以及使用总结(二)的更多相关文章

  1. ApacheOFBiz的相关介绍以及使用总结(一)

    由于最近一段时间在给一个创业的公司做客户关系管理CRM系统,限于人力要求(其实是没有多少人力),只能看能否有稳定,开源的半成品进行改造,而且最好不需要前端(js)相关开发人员的支援就可以把事情做成,经 ...

  2. ApacheOFBiz的相关介绍以及使用总结(三)

    Ofbiz中还提供了一些基础性服务,可以直接用来使用,下面就简单介绍说明一下.   ofbiz邮件发送服务   ofbiz中提供发送邮件相关功能:sendMailFromScreen   contex ...

  3. 【STM32】使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍

    其他链接 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(一)-初步认识SD卡 [STM32]使用SDIO进行SD卡读写,包含文件管理FatFs(二)-了解SD总线,命令的相关介绍 ...

  4. Django day 33 vue中使用element-ui的使用,课程的相关介绍,vue绑定图片,课程列表接口,课程详情页面

    一:vue中使用element-ui的使用, 二:课程的相关介绍, 三:vue绑定图片, 四:课程列表接口, 五:课程详情页面

  5. Vue 封装axios(四种请求)及相关介绍(十三)

    Vue 封装axios(四种请求)及相关介绍 首先axios是基于promise的http库 promise是什么? 1.主要用于异步计算 2.可以将异步操作队列化,按照期望的顺序执行,返回符合预期的 ...

  6. ppDelegate的相关介绍

    //  AppDelegate的相关介绍//  IOS笔记 //@interface AppDelegate : UIResponder <UIApplicationDelegate>// ...

  7. 【个人笔记】002-PHP基础-01-PHP快速入门-02-PHP语言相关介绍输

    002-PHP基础-01-PHP快速入门 02-PHP语言相关介绍 1.PHP是什么 Hypertext Preprocessor超文本预处理器 是一种通用开源脚本语言 Personal Home P ...

  8. Android HttpClient HttpURLConnection相关介绍

    Android HttpClient HttpURLConnection相关介绍 遇到一个问题 在android studio上用HttpClient编写网络访问代码的时候,发现该类无法导入并使用.. ...

  9. java 并发多线程 锁的分类概念介绍 多线程下篇(二)

    接下来对锁的概念再次进行深入的介绍 之前反复的提到锁,通常的理解就是,锁---互斥---同步---阻塞 其实这是常用的独占锁(排它锁)的概念,也是一种简单粗暴的解决方案 抗战电影中,经常出现为了阻止日 ...

随机推荐

  1. 12.18 aop身份验证

    对所有卖家页面进行身份验证,采用aop编程 步骤:1.获得request 2.查询cookie 3.查询redis 4.查询不通过时,采用抛出异常,捕捉异常,再异常里加入跳转到登陆页面的方法 准备工作 ...

  2. Shell 从日志文件中选择时间段内的日志输出到另一个文件

    Shell 从日志文件中选择时间段内的日志输出到另一个文件 情况是这样的,某系统的日志全部写在一个日志文件内,所以这个文件非常大,非常长,每次查阅的时候非常的不方便.所以,相关人员希望能够查询某个时间 ...

  3. Unity 播放 带 alpha 通道的视频(Video Player组件)

    孙广东  2017.6.18 http://blog.csdn.NET/u010019717 通常是  .webm类型文件!!!!!  你可以下载这个文件到本地: Http://tsubakit1.s ...

  4. 学习三部曲:WHAT、HOW、WHY

    一个人学习的过程要经历以下三步,才可以说得上"学会"两字: 第一步:WHAT 所谓的"WHAT",就是搞清楚某个东东是什么?有什么用?有什么语法?有什么功能特性 ...

  5. HDU 2268

    http://acm.hdu.edu.cn/showproblem.php?pid=2268 小学四年级应用题,让我找回了儿时的快乐... #include <iostream> #inc ...

  6. 为虚拟机配置vhost-net网卡,方便调试

    很多时候为了方便自己手动编译和调试虚拟平台,我们需要自己编译qemu等组件并给虚拟机配置网卡等.其中稍微麻烦点的就是配置网卡这块,目前最方便的就是给虚拟机配置一个vhost-net网卡了. vhost ...

  7. ExpressCache

    ExpressCache 非联想.等品牌电脑使用的ExpressCache(带破解文件). (原始安装文件+patch+说明).注意,执行eccmd命令时,需要管理员权限.否则报错. http://a ...

  8. 让svn具有分布式的功能。

    最近开发遇到了个难事.公司的svn库不能随便提交,必须要经过验证.但是平时修改太多,如果不提交到svn说不定前面被删掉的代码后面又需要了.svn自带的relocate和switch都不能达到要求.找遍 ...

  9. BZOJ2753 SCOI2012 滑雪与时间胶囊 【最小生成树】*

    BZOJ2753 SCOI2012 滑雪与时间胶囊 Description a180285非常喜欢滑雪.他来到一座雪山,这里分布着M条供滑行的轨道和N个轨道之间的交点(同时也是景点),而且每个景点都有 ...

  10. BZOJ2199 奶牛议会 【2-sat】

    BZOJ2199 奶牛议会 Description 由于对Farmer John的领导感到极其不悦,奶牛们退出了农场,组建了奶牛议会.议会以"每头牛 都可以获得自己想要的"为原则, ...