本文Liferay适用版本:v6.2.ce-ga6版

Liferay的插件体系是:模型-视图-控制器的portlet MVC框架。
MVC是一个伟大的用于Web应用程序的设计模式,在实际应用中还应处理持久化,它可以用于检索、处理或显示。为此你需要添加更多的层:一个持久层和服务层。
持久层负责保存和检索模型数据。服务层就像你的应用程序和持久层之间的缓冲区:在将来,它会给你自由的自由,即在不修改任何内容的情况下,哪怕是就算将来有一天你替换掉整个持久层。松耦合是良好的应用程序设计,并在Liferay框架中支持。它通过Service Builder(服务生成器)生成。

Service Builder的特性:

  • 自动生成模型,持久性和服务层
  • 自动生成本地和远程服务
  • 自动生成Hibernate和Spring的配置
  • 根据帐户的权限生成查找方法的实体和查找方法
  • 内置实体缓存(entity caching)支持
  • 自定义的SQL查询和动态查询的支持
  • 节省开发时间

你只需要生成一个 service.xml 文件,再运行 Service Builder ,这将生成一个新的 service .jar 文件,新文件包括模型层、持久层、服务层等文件。

通过服务生成器生成的远程服务包括SOAP或JSON访问。

另一种节省开发时间的方式是通过Spring和Hibernate的配置你的项目。业务生成器使用Spring的依赖注入使服务实现类可在运行时使用Spring AOP的事务管理。

生成方法。点击当前Liferay项目》 New → Liferay Service Builder

服务生成器的界面

配置文件的格式:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.2.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_2_0.dtd">
<service-builder package-path="com.liferay.docs.guestbook">
<author>wangxin</author>
<namespace>GB</namespace> <entity name="Guestbook" local-service="true"> <!-- PK fields --> <column name="guestbookId" type="long" primary="true"></column> <!-- Group instance --> <column name="groupId" type="long"></column> <!-- Audit fields --> <column name="companyId" type="long"></column>
<column name="userId" type="long"></column>
<column name="userName" type="String"></column>
<column name="createDate" type="Date"></column>
<column name="modifiedDate" type="Date"></column>
<column name="name" type="String"></column>
</entity>
<entity name="Entry"> <!-- PK fields --> <column name="entryId" type="long" primary="true"></column> <!-- Group instance --> <column name="groupId" type="long"></column> <!-- Audit fields --> <column name="companyId" type="long"></column>
<column name="userId" type="long"></column>
<column name="userName" type="String"></column>
<column name="createDate" type="Date"></column>
<column name="modifiedDate" type="Date"></column>
<column name="name" type="String"></column>
<column name="email" type="String"></column>
<column name="message" type="String"></column>
<column filter-primary="false" name="guestbookId" primary="false" type="long"></column>
</entity>
<exceptions>
<exception>GuestbookName</exception>
<exception>EntryName</exception>
<exception>EntryMessage</exception>
<exception>EntryEmail</exception>
</exceptions>
</service-builder>

当你写一个系统应用,例如,如果你为两个不同的站点添加一个Wiki,每个wiki的数据是根据每个站点而不同。换句话说,一个网站没有访问另一个网站的数据,但应用程序都相同。这是数据范围的概念。
在Liferay Portal的数据范围有一个层次,在下图描述。

在这里,你可以看到2个门户实例。每一个都是完全独立的门户,不同的用户、网站。在左边的门户实例中描述了2个用户:一个用户是一个独立站点的成员,而另一个用户是一个组织的成员,它本身有一个站点。

默认字段 Company ID,Group ID,User ID 就是提供了数据范围的支持。

然后创建Finders

创建后的代码:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 6.2.0//EN" "http://www.liferay.com/dtd/liferay-service-builder_6_2_0.dtd">
<service-builder package-path="com.liferay.docs.guestbook">
<author>wangxin</author>
<namespace>GB</namespace> <entity name="Guestbook" local-service="true"> <!-- PK fields --> <column name="guestbookId" type="long" primary="true"></column> <!-- Group instance --> <column name="groupId" type="long"></column> <!-- Audit fields --> <column name="companyId" type="long"></column>
<column name="userId" type="long"></column>
<column name="userName" type="String"></column>
<column name="createDate" type="Date"></column>
<column name="modifiedDate" type="Date"></column>
<column name="name" type="String"></column>
<finder name="GroupId" return-type="Collection">
<finder-column name="groupId"></finder-column>
</finder>
</entity>
<entity name="Entry"> <!-- PK fields --> <column name="entryId" type="long" primary="true"></column> <!-- Group instance --> <column name="groupId" type="long"></column> <!-- Audit fields --> <column name="companyId" type="long"></column>
<column name="userId" type="long"></column>
<column name="userName" type="String"></column>
<column name="createDate" type="Date"></column>
<column name="modifiedDate" type="Date"></column>
<column name="name" type="String"></column>
<column name="email" type="String"></column>
<column name="message" type="String"></column>
<column filter-primary="false" name="guestbookId" primary="false" type="long"></column>
<finder name="G_G" return-type="Collection">
<finder-column name="groupId"></finder-column>
<finder-column name="guestbookId"></finder-column>
</finder>
</entity>
<exceptions>
<exception>GuestbookName</exception>
<exception>EntryName</exception>
<exception>EntryMessage</exception>
<exception>EntryEmail</exception>
</exceptions>
</service-builder>

并且编译:

Liferay的代码生成器生成了持久化层代码,但控制器、服务层、视图层的具体实现需要开发人员自己做,这样保证了极大的灵活性。

ServiceContext 是Liferay提供上下文访问的核心类,真的是非常强大:

视图层的一些代码,AUI是YUI的扩展。

<%@include file = "/html/init.jsp" %>

<portlet:renderURL var="viewURL">
<portlet:param name="mvcPath" value="/html/guestbook/view.jsp"></portlet:param>
</portlet:renderURL> <portlet:actionURL name="addGuestbook" var="addGuestbookURL"></portlet:actionURL> <aui:form action="<%= addGuestbookURL %>" name="<portlet:namespace />fm"> <aui:fieldset> <aui:input name="name" /> </aui:fieldset> <aui:button-row> <aui:button type="submit"></aui:button>
<aui:button type="cancel" onClick="<%= viewURL %>"></aui:button> </aui:button-row>
</aui:form>

前端界面

控制器代码

package com.liferay.docs.guestbook.portlet;

import com.liferay.docs.guestbook.model.Entry;
import com.liferay.docs.guestbook.model.Guestbook;
import com.liferay.docs.guestbook.service.EntryLocalServiceUtil;
import com.liferay.docs.guestbook.service.GuestbookLocalServiceUtil;
import com.liferay.portal.kernel.exception.PortalException;
import com.liferay.portal.kernel.exception.SystemException;
import com.liferay.portal.kernel.servlet.SessionErrors;
import com.liferay.portal.kernel.servlet.SessionMessages;
import com.liferay.portal.kernel.util.ParamUtil;
import com.liferay.portal.service.ServiceContext;
import com.liferay.portal.service.ServiceContextFactory;
import com.liferay.portal.util.PortalUtil;
import com.liferay.util.bridges.mvc.MVCPortlet; import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import javax.portlet.ActionRequest;
import javax.portlet.ActionResponse;
import javax.portlet.PortletException;
import javax.portlet.PortletPreferences;
import javax.portlet.ReadOnlyException;
import javax.portlet.RenderRequest;
import javax.portlet.RenderResponse;
import javax.portlet.ValidatorException; import java.util.logging.Level;
import java.util.logging.Logger; /**
* Portlet implementation class GuestbookPortlet
*/
public class GuestbookPortlet extends MVCPortlet { public void addGuestbook(ActionRequest request, ActionResponse response)
throws PortalException, SystemException { ServiceContext serviceContext = ServiceContextFactory.getInstance(
Guestbook.class.getName(), request); String name = ParamUtil.getString(request, "name"); try {
GuestbookLocalServiceUtil.addGuestbook(serviceContext.getUserId(),
name, serviceContext); SessionMessages.add(request, "guestbookAdded"); } catch (Exception e) {
SessionErrors.add(request, e.getClass().getName()); response.setRenderParameter("mvcPath",
"/html/guestbook/edit_guestbook.jsp");
} } public void addEntry(ActionRequest request, ActionResponse response)
throws PortalException, SystemException { ServiceContext serviceContext = ServiceContextFactory.getInstance(
Entry.class.getName(), request); String userName = ParamUtil.getString(request, "name");
String email = ParamUtil.getString(request, "email");
String message = ParamUtil.getString(request, "message");
long guestbookId = ParamUtil.getLong(request, "guestbookId"); try {
EntryLocalServiceUtil.addEntry(serviceContext.getUserId(),
guestbookId, userName, email, message, serviceContext); SessionMessages.add(request, "entryAdded"); response.setRenderParameter("guestbookId",
Long.toString(guestbookId)); } catch (Exception e) {
SessionErrors.add(request, e.getClass().getName()); PortalUtil.copyRequestParameters(request, response); response.setRenderParameter("mvcPath",
"/html/guestbook/edit_entry.jsp");
} } @Override
public void render(RenderRequest renderRequest,
RenderResponse renderResponse) throws PortletException, IOException { try {
ServiceContext serviceContext = ServiceContextFactory.getInstance(
Guestbook.class.getName(), renderRequest); long groupId = serviceContext.getScopeGroupId(); long guestbookId = ParamUtil.getLong(renderRequest, "guestbookId"); List<Guestbook> guestbooks = GuestbookLocalServiceUtil
.getGuestbooks(groupId); if (guestbooks.size() == 0) {
Guestbook guestbook = GuestbookLocalServiceUtil.addGuestbook(
serviceContext.getUserId(), "Main", serviceContext); guestbookId = guestbook.getGuestbookId(); } if (!(guestbookId > 0)) {
guestbookId = guestbooks.get(0).getGuestbookId();
} renderRequest.setAttribute("guestbookId", guestbookId); } catch (Exception e) { throw new PortletException(e);
} super.render(renderRequest, renderResponse); } }

完整代码下载 Source Code

Liferay开发实战(2):Service Builder生成持久化层,及开发服务层的更多相关文章

  1. JavaScript使用DeviceOne开发实战(二) 生成调试安装包

    生成调试安装包 首先需要说明的是,这个步骤并不是每次调试App都必须的,大部分情况生成一次调试安装包,安装到手机上之后就可以忽略整个这个步骤.因为调试安装包包含了很多原生组件,都是可以定制勾选的,如果 ...

  2. SDL 开发实战(一):SDL介绍及开发环境配置

    一.什么是SDL? SDL是 “Simple DirectMedia Layer”的缩写,SDL是一个开源的跨平台的多媒体库,封装了复杂的音视频底层操作,简化了音视频处理的难度. SDL使用C语言写成 ...

  3. [原创].NET 分布式架构开发实战之三 数据访问深入一点的思考

    原文:[原创].NET 分布式架构开发实战之三 数据访问深入一点的思考 .NET 分布式架构开发实战之三 数据访问深入一点的思考 前言:首先,感谢园子里的朋友对文章的支持,感谢大家,希望本系列的文章能 ...

  4. [原创].NET 分布式架构开发实战之二 草稿设计

    原文:[原创].NET 分布式架构开发实战之二 草稿设计 .NET 分布式架构开发实战之二 草稿设计 前言:本篇之所以称为草稿设计,是因为设计的都是在纸上完成的.反映了一个思考的过程. 本篇的议题如下 ...

  5. 《iOS 7 应用开发实战详解》

    <iOS 7 应用开发实战详解> 基本信息 作者: 朱元波    管蕾 出版社:人民邮电出版社 ISBN:9787115343697 上架时间:2014-4-25 出版日期:2014 年5 ...

  6. [原创].NET 业务框架开发实战之十 第一阶段总结,深入浅出,水到渠成(后篇)

    原文:[原创].NET 业务框架开发实战之十 第一阶段总结,深入浅出,水到渠成(后篇) .NET 业务框架开发实战之十 第一阶段总结,深入浅出,水到渠成(后篇) 前言:接着上篇来. 系列文章链接: [ ...

  7. [原创].NET 业务框架开发实战之八 业务层Mapping的选择策略

    原文:[原创].NET 业务框架开发实战之八 业务层Mapping的选择策略 .NET 业务框架开发实战之八 业务层Mapping的选择策略 前言:在上一篇文章中提到了mapping,感觉很像在重新实 ...

  8. [原创].NET 业务框架开发实战之七 业务层初步构想

    原文:[原创].NET 业务框架开发实战之七 业务层初步构想 .NET 业务框架开发实战之七 业务层初步构想 前言:本篇主要讲述如何把DAL和BLL衔接起来. 本篇议题如下: 1.       DAL ...

  9. [原创].NET 业务框架开发实战之六 DAL的重构

    原文:[原创].NET 业务框架开发实战之六 DAL的重构 .NET 业务框架开发实战之六 DAL的重构 前言:其实这个系列还是之前的".NET 分布式架构开发实战 ",之所以改了 ...

随机推荐

  1. 核心一:IoC

    IoC 1.中文名称:控制反转 2.英文名称:(Inversion of Control) 3.IoC是什么? 3.1 IoC 完成的事情原先由程序员主动通过new 实例化对象事情,转交给Spring ...

  2. fastq-to-fasta转换及fasta拆分、合并

    格式转换: use awk :awk 'BEGIN{P=1}{if(P==1||P==2){gsub(/^[@]/,">");print}; if(P==4)P=0; P++ ...

  3. 2019.02.09 bzoj2440: [中山市选2011]完全平方数(二分答案+容斥原理)

    传送门 题意简述:qqq次询问(q≤500)(q\le500)(q≤500),每次问第kkk个不被除111以外的完全平方数整除的数是多少(k≤1e9)(k\le1e9)(k≤1e9). 思路:考虑二分 ...

  4. 2019.01.21 洛谷P3919 【模板】可持久化数组(主席树)

    传送门 题意简述:支持在某个历史版本上修改某一个位置上的值,访问某个历史版本上的某一位置的值. 思路: 用主席树直接维护历史版本即可. 代码: #include<bits/stdc++.h> ...

  5. 2018.11.07 hdu1465不容易系列之一(二项式反演)

    传送门 其实标签只是搞笑的. 没那么难. 二项式反演只是杀鸡用牛刀而已. 这道题也只是让你n≤20n\le20n≤20的错排数而已. 还记得那个O(n)O(n)O(n)的递推式吗? 没错那个方法比我今 ...

  6. base64编码理解

    原文地址:http://www.ruanyifeng.com/blog/2008/06/base64.html 所谓Base64,就是说选出64个字符----小写字母a-z.大写字母A-Z.数字0-9 ...

  7. 变动事件_DOM2级的变动事件(mutation)

    DOM2级定义了如下变动事件: DOMSubtreeModified:在DOM结构中发生任何变化时触发.这个事件在其他任何事件触发后都会触发. DOMNodeInserted:在一个节点作为子节点被插 ...

  8. IDEA插件开发总结

    一:前置步骤 1.添加开发插件所需的SDK: 1.1先添加JDK 1.2打开Project Structure-Platform Settings-SDKs 1.3添加IntelliJ Platfor ...

  9. 20155205 2016-2017-2 《Java程序设计》第3周学习总结

    20155205 2016-2017-2 <Java程序设计>第3周学习总结 教材学习内容总结 第四章 定义类的两种方法(new和this的用法) 只要有一个类定义,编译程序就会产生一个. ...

  10. error C2143: syntax error : missing ';' before 'type'

    许久之前,就想看看C和C++,看看这两种神奇的编程语言,但是一直被这样或者那样的琐事给耽搁了(狂喷自己的拖延症). 今天翻开自己的移动硬盘找到一本古老的书籍:<The C Programming ...