ofbiz学习之部署learning组件
一、部署前准备
1、环境及工具
eclipse2017;jdk1.8;Mysql5。
2、连接Mysql数据库
3、导入eclipse
二、创建组件
右键点击ofbiz根目录下的build.xml——Run as(运行方式)——Ant构建(第二个)——在“选择要执行的目标”的框中——取消start——找到create component打钩——点击运行——在出现的输入框中都输入组建的名字learning——最后一步选择Y——完成——刷新左边的资源管理视图——hot-deploy下出现learning组件目录
三、初始部署
1、修改learning/ofbiz-component.xml文件中的webapp标签:
base-permission代表登陆权限,app-bar-display="true"表示该应用会在后台应用主菜单中出现。
2、初次体验。启动ofbiz,浏览器进入后台目录管理界面,点击左上角的应用菜单,再点击learning,进入应用主页面。如果页面中显示“不允许你浏览这个页面。”,不必担心,这只是你未做任何添加前的自动生成页面。
3、创建controller。在learning/webapp/learning/WEB-INF/controller.xml中,添加一个请求映射:
根据请求映射,再添加一个响应的view视图映射:
4、创建视图。在learning/widget/learningScreens.xml中,添加一个name=FindProductPlan的screen视图装饰器:
关于ofbiz装饰器的配置规则,请参考Apache+OFBiz+开发初学者指南.chm(关于此份文档如何获取,请浏览文章结尾)。而在该装饰器中,外围嵌套了名为main-decorator(在CommonScreens.xml中)的主装饰器,负责页面的主体装饰。内层嵌套了名为FindScreenDecorator的装饰器,该装饰器主要分为两部分:search-options(搜索选项)和search-results(搜索结果)。在这两部分中分别布置一个搜索表单和列表表单。
5、创建表单。在learning/widget/learningForms.xml中,添加name= FindProductPlan和name=ListProductPlan的表单:
关于表单的配置规则一样可以参考上述那个文档。同时,也要注意几个关键点:target属性相当于一个请求,当表单进行提交的时候,会定向到该请求上,如【1】target="FindProductPlan"对应请求转到learning/control/FindProductPlan;【2】标签相当于一个超链接;【3】在ListProductPlan表单的预处理动作中,调用了ofbiz自带的查找服务performFind,使用该功能时一定要指明entity实体引擎即数据表的名字,同时使用该功能的
6、创建菜单。在learning/widget/learningMenus.xml文件的名为MainAppBar
四、部署实体
1、在learning/entitydef/entitymodel.xml中,添加entity实体(即数据库表):
实体配置规则同样参照上述文档,此处不再复述。
2、利用相应的操作mysql数据库工具,对ProductPlan数据表新增一些数据。
五、小有成就
启动ofbiz,进入learning应用,点击菜单中的生产计划,即可看到FindProductPlan界面:
在搜索选项中输入数据项,点击查找,便可出现相应的搜索结果,查找功能暂且完成。但这仅仅是个小小的成果,还有View展示详细数据、Create新建数据、Remove删除数据、Edit修改数据等功能需要我们去实现。路漫漫其修远兮,吾将上下而求索···
六、实现View功能
1、由于ProductPlan数据表有很多字段,所以我们需要一个ViewProductPlan页面显示某一个数据项的详细信息。展现一个页面的基本步骤,可以参照本文第三部分:【1】配置control.xml文件,将请求映射到视图,再由视图映射到具体的装饰器(screen);【2】在装饰器xml配置文件中,通过该装饰器对视图进行布置,同时还要调用表单;【3】在表单xml配置文件中,完成被调用表单的配置。
(PS:如果数据库表字段不多,仅通过上述FindProductPlan界面就能完全显示,那可以不需要View功能。)
2、代码如下:
(1)control.xml:
(2)learningScreens.xml:
表单的actions预处理动作,保证了显示表单中的数据项是传进来的ProductPlanId的数据项(虽然笔者也不知为何?)。
(3)learningForms.xml:
在该表中,有几个要点:【1】的属性position="2"是表示该数据项的左右位置(默认是1),但相邻的数据项之间不能都等于2(此处玄学,笔者不甚了解);【2】 表示该数据项只显示日期;【3】 是数据项的一种可以提供便捷查找功能的格式,其属性target对应一个请求(control/LookupProductPlanItem),说明我们还要创建一个请求以及响应视图才能使用这个功能。
(4)创建Lookup请求,在controller.xml中添加:
(5)创建Lookup视图,在LearningScreens.xml中添加:
中的第三个标签是指定具体数据库表;第四个是用来,在调用该Lookup功能的数据项旁,显示value中字段数据的。
(6)创建Form表单,在LearningForms.xml中添加:
(7)完工的ViewProductPlan视图
七、实现Create以及Update功能
1、在我们完成的第一张视图中,有一个“新增”按钮,它对应第三—4步的ProductPlan装饰器中的:
同时在完成的第二张视图中,有一个“修改”按钮,它对应第六—3步中的ViewProductPlan表单中的:
这两个按钮都指向EditProductPlan请求(表单ViewProductPlan的target属性为EditProductPlan),因此,我们又得如出一辙地部署一个EditProductPlan请求和响应视图,重复性工作,直接上代码:
(1)controller.xml:
(2)learningScreens.xml:
(3)learningForms.xml:
2、当EditProductPlan表单完成后,会提交到CreateProductPlan或UpdateProductPlan请求,而这取决于表单一开始是否为空,例如新建时表单为空提交给CreateProductPlan。由于此处涉及到对数据库的增加和更新,则需要调用services服务,笔者在此使用java实现该服务。
(1)调用服务的第一步是要在learning/servicedef/services.xml文件中声明该服务:
learning Services
1.0
Create a ProductPlan
Update a ProductPlan
标签中,name是该服务的名字,engine是服务的实现方式,location指向java类的位置,invoke指向该java类中的一个方法(函数)。标签中,name是传进该方法中的数据项的名字,type是该数据项的类型,mode="IN"表示传进,"OUT"表示传出。
(2)实现该java类及方法。在learning/src下,创建org/ofbiz/learning/目录,并在该目录下创建一个名为ProductPlanServices的java类:
package org.ofbiz.learning;
import java.sql.Timestamp;
import java.util.Locale;
import java.util.Map;
import org.ofbiz.base.util.Debug;
import org.ofbiz.base.util.UtilMisc;
import org.ofbiz.base.util.UtilProperties;
import org.ofbiz.base.util.UtilValidate;
import org.ofbiz.entity.Delegator;
import org.ofbiz.entity.GenericEntityException;
import org.ofbiz.entity.GenericValue;
import org.ofbiz.service.DispatchContext;
import org.ofbiz.service.ServiceUtil;
public class ProductPlanServices {
public static final String module = ProductPlanServices.class.getName();
public static final String resource = "ProductPlanUiLabels";
/**
* create xpp Learn
* @param dctx
* @param context
* @return
*/
public static Map createProductPlan(DispatchContext dctx, Map context)
{
//获取用户信息
Locale locale = (Locale) context.get("locale");
//获取返回的Map类
Map result = ServiceUtil.returnSuccess();
//获取数据库操作类
Delegator delegator = dctx.getDelegator();
//获取传进来的数据项
String productPlanId = (String) context.get("productPlanId");
String itemId = (String) context.get("itemId");
String principleId = (String) context.get("principleId");
String preparationId = (String) context.get("preparationId");
String useProject = (String) context.get("useProject");
Timestamp startTime = (Timestamp) context.get("startTime");
Timestamp finishTime = (Timestamp) context.get("finishTime");
//将这些数据项放进一个Map类对象中
Map ProductPlanMap = UtilMisc.toMap("productPlanId", productPlanId,"itemId",itemId,"principleId",principleId);
ProductPlanMap.put("preparationId", preparationId);
ProductPlanMap.put("useProject", useProject);
ProductPlanMap.put("startTime", startTime);
ProductPlanMap.put("finishTime", finishTime);
//将Map类放入一个名为ProductPlan的数据表中,GenericValue类的对象相当于数据表中的一条记录
GenericValue GV = delegator.makeValue("ProductPlan", ProductPlanMap);
try {
//创建这条记录
GV.create(); }
catch (GenericEntityException e) {
Debug.logError(e, e.getMessage(), module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource,
"Learn.create.error", new Object[] { e.getMessage() }, locale));
}
return result;
}
/**
* update Learn xpp
* @param dctx
* @param context
* @return
*/
public static Map updateProductPlan(DispatchContext dctx,Map context)
{
Locale locale = (Locale) context.get("locale");
Map result = ServiceUtil.returnSuccess();
Delegator delegator = dctx.getDelegator();
//获取传进的数据项
String productPlanId = (String) context.get("productPlanId");
String itemId = (String) context.get("itemId");
String principleId = (String) context.get("principleId");
String preparationId = (String) context.get("preparationId");
String useProject = (String) context.get("useProject");
Timestamp startTime = (Timestamp) context.get("startTime");
Timestamp finishTime = (Timestamp) context.get("finishTime");
//将id放进一个Map中
Map ProductPlanMap = UtilMisc.toMap("productPlanId", productPlanId);
try {
//通过该Map对象即ProductPlanId,寻找与该id相同的数据表中的记录
GenericValue GV = delegator.findOne("ProductPlan", ProductPlanMap , false);
//如果找不到这条记录就报错
if(UtilValidate.isEmpty(GV))
{
return ServiceUtil.returnError(UtilProperties.getMessage(resource,"ProductPlan.update.error.entity.notFound", locale));
}
//如果找到了,就把传进来的数据放到该记录中
GV.put("itemId", itemId);
GV.put("principleId", principleId);
GV.put("preparationId", preparationId);
GV.put("useProject", useProject);
GV.put("startTime", startTime);
GV.put("finishTime", finishTime);
//更新该记录
GV.store();
}catch (GenericEntityException e) {
Debug.logError(e, e.getMessage(), module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource,
"Learn.update.error", new Object[] { e.getMessage() }, locale));
}
return result;
}
}
以上只是一个简单的操作数据库的java类,部分内容的讲解已经在该类的注释中,如果想更好地了解service服务,可以参考Apache+OFBiz+开发初学者指南.chm和ofbiz菜鸟笔记.doc。
(3)编写完类和方法后,接下来的工作非常重要:
【1】检查learning/ofbiz-component.xml中有没有以下代码:
【2】检查controller.xml中有没有以下代码:
【3】当然,如果你是用笔者第二步提供的方法来创建learning组件,那么你可以不必检查。
(4)重中之重,右键learning/src—选择构建路径—选择做源文件夹。
(5)泰山之重,右键learning/build.xml文件—选择运行方式(Run as)—选择第二个Ant 构建—进入编辑配置界面—在目标界面选择jar(一般默认就已经选择)—点击运行,运行完成后检查learning/build下是不是有东西了(文件夹不算)。
3、在controller.xml添加:
注意请求映射中多了一个event映射,表明该请求会调用该服务(invoke对应services.xml中声明的服务),通过该服务后才到达视图部分,而映射的视图此前已经创建完成了。
4、到此,Create以及Update功能已经完成,但增删改查还差了删除功能,不过这已经是手到擒来的了。
八、Remove功能
1、通过第七步,我们可以知道,涉及数据库操作功能时,我们不需要新建视图,只需要让该请求通过event进入服务,然后返回到已有视图即可,所以在controller.xml中,我们只需要添加:
2、然后在services.xml中添加:
Remove a ProductPlan
由于删除的时候只需要知道id号,因此传进id号即可。
3、在ProductPlanServices类中添加一个删除方法:
public static Map removeProductPlan(DispatchContext dctx,
Map context) {
Locale locale = (Locale) context.get("locale");
Map result = ServiceUtil.returnSuccess();
Delegator delegator = dctx.getDelegator();
String productPlanId = (String) context.get("productPlanId");
try {
GenericValue gv = delegator.findOne("ProductPlan", UtilMisc.toMap("productPlanId", productPlanId),false);
if(UtilValidate.isEmpty(gv)){
return ServiceUtil.returnError(UtilProperties.getMessage(resource,"Learn.delete.error.entity.notFound", locale));
}
gv.remove();
}catch (GenericEntityException e) {
Debug.logError(e, e.getMessage(), module);
return ServiceUtil.returnError(UtilProperties.getMessage(resource,
"Learn.delete.error", new Object[] { e.getMessage() }, locale));
}
return result;
}
4、最后,不要忘记通过build.xml对该类进行编译。另外,当对services.xml文件或java类进行修改后,要停止ofbiz再启动,否则修改无效。那如何停止ofbiz呢?右键ofbiz根目录下的build.xml,第二个Ant构建再选择stop,运行后ofbiz即停止。如果你能找到Eclipse的Ant视图,那就更方便了,但使用方法就要靠自己领悟了。
结语(本文结束的几个月后所添加的)
1.此处推荐一位ofbiz大神博客:上海香飘飘,博客中不仅有关于ofbiz的知识,有很多资源链接,读者可以下载阅读。其中就包括入门级指南Apache+OFBiz+开发初学者指南[Ob4.0-EN+Ob9.0-CN].chm。这份指南很全,若能认真反复阅读,必能获益匪浅。
2.随着笔者对ofbiz的研究时间日益长久,又有一番心得,包括如下几点:
- 一定要学习ofbiz中自创的mililang语言————类似一种有逻辑的标签语言。该语言在ofbiz中用处很大,可以很快速地编写一个服务,且不需要编译;可以在“表单”form以及“屏幕”screen的动作action中使用。至于怎么学习,依旧参考这位大神的博客
- 一定要时常回去看看Apache+OFBiz+开发初学者指南[Ob4.0-EN+Ob9.0-CN].chm,包含了很多知识点。
- 服务service和事件event的区别在于:服务用来对数据库进行操作,而事件用于实现逻辑跳转、数据处理之类的功能。