Liferay Service Build(II)

来源:互联网 发布:java回文数 编辑:程序博客网 时间:2024/05/11 14:35

上文中,大致地介绍了Liferay Service build的功能,也算是对Service Build有了一个感性的认识了。

从本文开始,将通过一个例子来讲解service build的使用方式,并对其中可能涉及到的一些liferay的代码进行讲解,来一步一步地研究Service buildLiferay。当然,本人此处所写的内容仍旧来自Liferay in Action一书中(仍建议阅读原文),如果阅读的过程中出现有什么疑问或者有什么想法,欢迎留下意见与建议。

简单来说,该例子就是一个在Liferay环境下的,具有增删改查功能的工程。但是与以往基于纯Tomcat环境下的工程不同,这里不仅会涉及到Liferay的特性(AUI标签),也会涉及到一些其他的内容(JSTL标签)。文中不会对其进行介绍,感兴趣的同学请自行Google.

下面,就逐步地来讲解。

1.    构建Service.xml

该文件的一般形式与作用在前一篇文章已经给出,此处不再赘述。该工程中

所涉及到三个数据表如下:

数据表1:f_fproduct

ColumnName

Datatype

PK

NN

FK

productId

INT(20)

*

*

 

productName

String(75)

 

 

 

serialNumber

String(75)

 

 

 

companyId

INT(20)

 

 

*

groupId

INT(20)

 

 

*

数据表2:f_fregistration

ColumnName

DataType

PK

NN

FK

registrationId

INT(20)

*

*

 

fUserId

INT(20)

 

 

 

datePurchased

DATETIME

 

 

 

serialNumber

VARCHAR(75)

 

 

 

productId

INT(20)

 

 

 

companyId

INT(20)

 

 

 

groupId

INT(20)

 

 

 

数据表3:f_fuser

ColumnName

DataType

PK

NULL

FK

fUserId

INT(20)

*

*

 

firstName

String(75)

 

 

 

Address1

String(75)

 

 

 

City

String(75)

 

 

 

State

String(75)

 

 

 

Email

String(75)

 

 

 

Country

String(75)

 

 

 

birthDate

DATETIME

 

 

 

Male

TINYINT

 

 

 

userId

INT(20)

 

 

*

companyId

INT(20)

 

 

*

groupId

INT(20)

 

 

*

 

而与之对应的Service.xml文件内容如下所示(注:这里最终的文件内容,不是初期就一次性写成的)

<?xmlversion="1.0"encoding="UTF-8"?>

<!DOCTYPEservice-builderPUBLIC"-//Liferay//DTD Service Builder 6.0.0//EN"

"http://www.liferay.com/dtd/liferay-service-builder_6_0_0.dtd">

<service-builderpackage-path="com.productregistration">

       <author>Feng</author>

       <namespace>F</namespace>

       <entityname="FProduct"local-service="true"remote-service="false"><!—A-->

             <!-- PK Fields -->

             <columnname="productId"type="long"primary="true"/>

             <!-- Other Fields -->

             <columnname="productName"type="String"/>

             <columnname="serialNumber"type="String"/>

             <!-- Foreign Keys -->

             <columnname="companyId"type="long"/>

             <columnname="groupId"type="long"/>

             <!-- Order -->

             <orderby="asc">

                    <order-columnname="productName"/>

             </order>

             <!-- Finder Methods -->

             <findername="G_PN"return-type="Collection">

                    <finder-columnname="groupId"/>

                    <finder-columnname="productName"/>

             </finder>

             <findername="GroupId"return-type="Collection">

                    <finder-columnname="groupId"/>

             </finder>

             <findername="CompanyId"return-type="Collection">

                    <finder-columnname="companyId"/>

             </finder>

       </entity>

       <entityname="FUser"local-service="true"remote-service="false">

             <!-- PK Fields -->

             <columnname="fUserId"type="long"primary="true"/>

             <!-- Other Fields -->

             <columnname="firstName"type="String"/>

             <columnname="address1"type="String"/>

             <columnname="city"type="String"/>

             <columnname="state"type="String"/>

             <columnname="email"type="String"/>

             <columnname="country"type="String"/>

             <columnname="birthDate"type="Date"/>

             <columnname="male"type="boolean"/>

             <!-- Foreign Keys -->

             <columnname="userId"type="long"/>

             <columnname="companyId"type="long"/>

             <columnname="groupId"type="long"/>

             <!-- Relationships -->

             <columnname="userRegistrations"type="Collection"entity="FRegistration"

                     mapping-key="fUserId"/>

             <!-- Order -->

             <orderby="asc">

                    <order-columnname="firstName"case-sensitive="false"/>

             </order>

             <!-- Finder Methods -->

             <findername="G_LN"return-type="Collection">

                    <finder-columnname="groupId"/>

                    <finder-columnname="firstName"/>

             </finder>

             <findername="G_E"return-type="Collection">

                    <finder-columnname="groupId"/>

                    <finder-columnname="email"/>

             </finder>

             <findername="G_U"return-type="Collection">

                    <finder-columnname="groupId"/>

                    <finder-columnname="userId"/>

             </finder>

       </entity>

       <entityname="FRegistration"local-service="true"

              remote-service="false">

             <!-- PK Fields -->

             <columnname="registrationId"type="long"primary="true"/>

             <!-- Other Fields -->

             <columnname="fUserId"type="long"/>

             <columnname="datePurchased"type="Date"/>

             <columnname="serialNumber"type="String"/>

             <columnname="productId"type="long"/>

             <columnname="companyId"type="long"/>

             <columnname="groupId"type="long"/>

             <!-- Finders -->

             <findername="GroupId"return-type="Collection">

                    <finder-columnname="groupId"/>

             </finder>

             <findername="G_RU"return-type="Collection">

                    <finder-columnname="groupId"/>

                    <finder-columnname="fUserId"/>

             </finder>

             <findername="G_DP"return-type="Collection">

                    <finder-columnname="groupId"/>

                    <finder-columnname="datePurchased"/>

             </finder>

             <findername="G_SN"return-type="Collection">

                    <finder-columnname="groupId"/>

                    <finder-columnname="serialNumber"/><!--password -->

             </finder>

       </entity>

</service-builder>

下面,我们来分析一下这个配置文件.

先从简单的开始,如下图。

1-3行的意义自不必说。

4行中,<service-builder/>元素签是整个配置文件的基础,所有对该文件的配置都是在<service-builder/>内部完成的。其中,package-path是指定所在包的路径。在Build后,就会以”com.productregistration”为统一的包前缀名来生成相应的层次包。但要注意的是,此处所指定的path至少应该包含在所创建的portlet的包中。

5行指定作者,以表明责任人。

6行指定命名空间,一是为了防止可能存在的命名重复,二是指定了所生成的数据表的前缀,利于区分,如以上三个表的名称格式,就是在Service Build后所形成的。

下面,我们开始分析第一个实体。内容如下:

在第7行中,指定实体名称,及将要生成的数据表的名称为FProduct,类型为本地服务。8-12行指定数据表内字段名称与类型,13-14行指定外键,17-19行指定以productName的正序来排列。21-24行,是指定了一个查找的方法,生成后的方法名称为findByG_PN并指定返回类型为Collection,即合集。而且在该方法中,需要程序员指定groupIdproductName这两个变量。系统在Service Build后,不会直接在接口和Util里生成find方法的,而是在在Persistence里来生成方法。如果我们

需要在接口和Util类中使用的话,需要在Util类中自写定义的方法,并重新Service Build,则会自动地加入到接口和Util类中(推荐阅读上传的《liferayservice.xml元素解释》)

在定义第一个实体后,就可以Service Build一下了。来生成对应的层次包了,如果没有错误,控制台将出现以下内容:

表明成功Build。同时也会生成如下的层次结构:

除此之外,还会在docroot/WEB-INF/src下生成新的META-INF文件夹,在WEB-INF下生成servicesql文件夹。此处不再一一展示其内容,请实际操作并查看。

这样,第一次的Service Build就完成了,下面开始进行代码的书写,逐步地实现功能。

 

2 添加代码

2.1纯粹的功能实现

观察所生成的层次结构,需要进行第一次改动的,就是在

com.productregistration.service.impl这个包中的FProductLocalServiceImpl.java文件中。

其实,Service Build所产生的结构层次,所运用就是面向接口编程的思想,(关于该思想的好处以及具体内容,此处只作提及,并不予以说明。请自行查找资料),我们所需要实现的方法,基本上就是在XXX.service.impl下的相应文件来实现的,之后,通过一层层的调用来最终实现功能,减低代码间的耦合,方便源码管理。

我们来看FProductLocalServiceImpl.java中的内容:

应当注意注释里的话,它说明了一切。

来看具体的代码,这里重点区解释一些变量,因为这些变量的具有以下特点:系统生成,数量众多,功能强大。但往往我们却不知道它们有哪些,有什么用以及在哪。

变量fProductPersistence的声明在FProductLocalServiceBaseImp.javal中,这是用系统生成的持久类变量,通过对该文件的观察,你会发现很多有用的东西。后面的create(long Id)方法位于FProductPersistence.java中,作用是通过主键在数据库来创建一个新的product,并通知适当的层次监听器(create a new f product with the primary key from the database.Also notifies the appropriate model listerners)。

变量counterLocalService同样位于FProductLocalServiceBaseImpl.java中,所在的类为CounterLocalService,increment()方法的作用是初始化一个新的以FProduct.class.getName()为名字的计数器,该方法较为常用。该处语句的意义就是在添加数据实体的时候非主键会自动递增。在我所看到的而资料中,说明liferay中并不是使用hibernate的自动递增,而是使用CounterLocalService这样能够更好的屏蔽数据库的差异性(求大神解释)。54行中的变量resourceLocalService同样位于FProductLocalServiceBaseImpl中,该变量通过addResources()方法来保存增加的数据(我没有找到addResources()方法的具体说明以及其所在类的类说明)。

以上的代码的作用就是实现添加产品的功能。下面来总结一下:

第一步,就是创建一个新的Fproduct对象。之后用fPersonPersistence变量通过Spring将注入相应的类中。

第二步,就是调用resourceLocalService来保存实体并创建相应的权限。

最后,就是通过set方法和update方法来完成添加功能。

2.2 结合Liferay的MVC

但是要注意到,该代码是在service.impl中实现的,而工程是基于Portlet的,所以我们就要在对应的portlet类里来再次写一个”add”方法,通过对service.impl中的add方法的调用,并结合一些界面信息的显示功能,才算是完成了一个Portlet下的基于MVC模式的添加功能。代码如下图:

在这段代码中涉及到了很多内容,ThemeDisplayLiferay的内置对象,ActionUtilProdRegValidator都是自定义的方法集合,这两个方法的具体内容将以附录的形式给出。在此处,主要是添加了一些检验和错误提示的功能。

首先,productFromRequest()的功能就是得到用户所提交表单的FProduct对象,之后,通过ValidateProduct()来验证对象的有效性,并进行下一步的操作。43行就是调用了上面所写的addProduct()方法。

2.3 JSP页面代码

在这一小节中,主要涉及到一些Liferay的界面编程内容,Liferay中,推荐使用AlloyUI(AUI)标签元素来进行界面编程,在这里不做过多的介绍,通过下面的截图,相信就可以对它有一个初步的了解了。当然,并不是在Liferay环境下就必须使用AUI来进行界面的编写工作。

好,下面来看看和上文代码对应的portlet的view.jsp文件内容,如下图:

31-36行的代码,就是我们根据不同需要而在页面上显示的校验信息,其中的key值是定义在一个属性文件中,在该属性文件中,定义了不同操作所对应的不同提示。37行定义了一个名为addProduct的actionURL并在下面的form中调用该action,注意,该出的name的名称要与代码中的名字一致。

这样,在liferay下就完成一个简单的添加的功能。

以上简单地介绍了Service Build的配置文件的内容意义以及在Liferay下借助Service Build功能如何进行基本的编程。介绍的还是比较简单,省去了一些对技术原理和一些技术内容的介绍。本文的目的是尽可能的为大家扼要的介绍Liferay下的编程技术,如果想获取更多的内容,还请大家能够尽量阅读原文,

如有错误,敬请指正。

Edited by FengJunlong

 

 

原创粉丝点击