本文記錄一個在 Liferay(Ext 環境)中開發「用戶列表顯示」 Portlet 的完整流程,涵蓋 Service Builder 自動生成持久層、Hibernate 映射調整、Service 層業務邏輯、Struts Action 處理、Portlet 類編寫、JSP 視圖、以及各類配置檔修改。適合需要快速上手 Liferay MVC 架構的開發者參考。
在資料庫建立 users 表:
CREATE TABLE users (
userid VARCHAR(64) PRIMARY KEY,
username VARCHAR(100),
password VARCHAR(100)
);
INSERT INTO users VALUES ('1', 'alice', 'pwd1'), ('2', 'bob', 'pwd2');
<?xml version="1.0"?>
<!DOCTYPE service-builder PUBLIC "-//Liferay//DTD Service Builder 5.1.0//EN"
"https://www.liferay.com/dtd/liferay-service-builder_5_1_0.dtd">
<service-builder package-path="com.ext.portlet.users">
<namespace>Users</namespace>
<entity name="UsersEntry" local-service="false" remote-service="true"
persistence-class="com.ext.portlet.users.service.persistence.UsersEntryPersistenceImpl">
<column name="userid" type="String" primary="true" />
<column name="username" type="String" />
<column name="password" type="String" />
<order by="asc">
<order-column name="username" case-sensitive="false" />
</order>
</entity>
<exceptions>
<exception>UsersEntry</exception>
</exceptions>
</service-builder>
在 ext-impl/build-parent.xml 的 build-service 目標下添加:
<target name="build-service-portlet-users">
<antcall target="build-service">
<param name="service.file" value="src/com/ext/portlet/users/service.xml" />
</antcall>
</target>
執行後自動生成 Model、Persistence、Service、Util 等類別至 ext-impl 與 ext-service。
生成的 ext-hbm.xml 預設表名為實體名 UsersEntry,需手動改為實際資料表 Users:
<class name="com.ext.portlet.users.model.impl.UsersEntryImpl" table="Users">
// ext-service/src/com/ext/portlet/users/service/UsersEntryService.java
public List getAllUsers() throws SystemException;
// ext-impl/src/com/ext/portlet/users/service/impl/UsersEntryServiceImpl.java
public List getAllUsers() throws SystemException {
return UsersEntryUtil.findAll();
}
// UsersEntryServiceUtil.java
public static List getAllUsers() throws SystemException {
return _service.getAllUsers();
}
// ViewUsersAction.java
public class ViewUsersAction extends PortletAction {
public ActionForward render(ActionMapping mapping, ActionForm form,
PortletConfig config, RenderRequest req, RenderResponse res)
throws Exception {
if (req.getWindowState().equals(WindowState.NORMAL)) {
return mapping.findForward("portlet.ext.users.view");
} else {
List users = UsersEntryServiceUtil.getAllUsers();
List usernames = new ArrayList();
for (Object u : users) {
usernames.add(((UsersEntryModel) u).getUsername());
}
req.setAttribute("users", usernames);
req.setAttribute("count", String.valueOf(users.size()));
return mapping.findForward("portlet.ext.users.view_users");
}
}
}
// UsersPortlet.java
public class UsersPortlet extends StrutsPortlet {
public void doView(RenderRequest renderRequest, RenderResponse renderResponse)
throws IOException, PortletException {
PortletPreferences prefs = renderRequest.getPreferences();
System.out.println(prefs.getValue("test", ""));
super.doView(renderRequest, renderResponse);
}
}
<%@ include file="/html/common/init.jsp" %>
<portlet:defineObjects />
<% PortletPreferences prefs = renderRequest.getPreferences(); %>
<%@ include file="/html/portlet/ext/users/init.jsp" %>
<a href="<portlet:renderURL windowState="<%= WindowState.MAXIMIZED.toString() %>" />">
MyUser <%= prefs.getValue("user", "") %>
</a>
<%@ include file="/html/portlet/ext/users/init.jsp" %>
<% List users = (List) request.getAttribute("users"); %>
<% String count = (String) request.getAttribute("count"); %>
size: <%= count %>.<br/>
<% for (int i = 0; i < users.size(); i++) { %>
<%= users.get(i) %><br>
<% } %>
| 檔案 | 關鍵配置 |
|---|---|
| portlet-ext.xml | 定義 portlet-name=EXT_Users、portlet-class、init-param view-action、preferences、security-role-ref |
| liferay-portlet-ext.xml | struts-path=ext/users、use-default-template=false |
| liferay-display.xml | <portlet id="EXT_Users" /> 加入 category.test |
| struts-config.xml | action path=/ext/users/view_users 對應 ViewUsersAction、兩個 forward |
| tiles-defs.xml | definition name 對應 forward path,指向 view.jsp / view_users.jsp |
| Language-ext.properties | javax.portlet.title.EXT_Users=Users、view-users=View Users |
password_,資料表實為 password。修改 ext-hbm.xml 的 <property name="password" column="password"/>。userid 為 VARCHAR,service.xml 需設 type="String" primary="true"。build-service-portlet-user vs build-service-portlet-users,區分單複數。Liferay Service Builder 大幅減少樣板碼,但仍需注意:生成後的 Hibernate 映射表名、欄位名需與資料庫一致;Struts/Tiles 配置層層對應,命名衝突易導致 404;Portlet 偏好設定需在 portlet-ext.xml 預先聲明。掌握此流程,可快速構建符合 Liferay 規範的企業級 Portlet。