採用經典分層:Model (Book) → DAO (BookDao) → Action (BookAction) → JSP (List/Edit)。為聚焦 Struts2 機制,DAO 使用 ConcurrentHashMap 模擬資料庫。
public class Book {
private String isbn; // 主鍵
private String title;
private double price;
// 構造器、getter/setter 省略
}
public class BookDao {
private static final BookDao INSTANCE = new BookDao();
private final ConcurrentMap data = new ConcurrentHashMap<>();
static { /* 初始化 5 本書 */ }
private BookDao() {}
public static BookDao getInstance() { return INSTANCE; }
public Collection getBooks() { return data.values(); }
public Book getBook(String isbn) { return data.get(isbn); }
public void storeBook(Book book) { data.put(book.getIsbn(), book); }
public void removeBook(String isbn) { data.remove(isbn); }
public void removeBooks(String[] isbns) { for (String id : isbns) data.remove(id); }
}
@Controller("bookAction")
@Scope("prototype")
public class BookAction extends ActionSupport implements ModelDriven {
private Book book = new Book();
private String isbn;
private String[] isbns;
private Collection books;
private BookDao dao = BookDao.getInstance();
// 分頁參數
private int page = 1, rows = 5;
private Map pageMap = new HashMap<>();
@Override public Book getModel() { return book; }
// getter/setter 省略
public String list() {
books = dao.getBooks();
return SUCCESS;
}
public String load() { // 編輯回顯
book = dao.getBook(isbn);
return SUCCESS;
}
public String store() { // 新增/更新
dao.storeBook(book);
return SUCCESS;
}
public String remove() { // 刪除(單/批)
if (isbn != null) dao.removeBook(isbn);
else dao.removeBooks(isbns);
return SUCCESS;
}
// AJAX 分頁查詢 (EasyUI DataGrid 專用)
public String query() {
// 實際專案中由 Service 處理分頁 HQL/SQL
pageMap.put("rows", books); // 簡化演示
pageMap.put("total", books.size());
return "jsonMap";
}
}
<package name="book" namespace="/Book" extends="struts-default">
<action name="List" class="bookAction" method="list">
<result>/Book/List.jsp</result>
</action>
<action name="Edit" class="bookAction" method="load">
<result>/Book/Edit.jsp</result>
</action>
<action name="Store" class="bookAction" method="store">
<result type="redirectAction">List</result>
</action>
<action name="Remove" class="bookAction" method="remove">
<result type="redirectAction">List</result>
</action>
<action name="Query" class="bookAction" method="query">
<result type="json">
<param name="root">pageMap</param>
</result>
</action>
</package>
<%@ taglib prefix="s" uri="/struts-tags" %>
<table id="dg"></table>
<script>
$(function(){
$('#dg').datagrid({
url: 'Query.action',
fitColumns: true, pagination: true, pageSize: 5,
toolbar: [{
iconCls: 'icon-add', text: '新增',
handler: function(){ openDialog('Add'); }
}, '-', {
iconCls: 'icon-edit', text: '編輯',
handler: function(){ openDialog('Edit'); }
}, '-', {
iconCls: 'icon-remove', text: '刪除',
handler: removeSelected
}],
columns: [[
{field:'isbn', title:'ISBN', width:100},
{field:'title', title:'書名', width:150},
{field:'price', title:'價格', width:80, formatter: function(v){ return '$'+v; }}
]]
});
});
function openDialog(mode) {
// 彈窗載入 Edit.jsp,mode 區分新增/編輯
}
function removeSelected() {
var rows = $('#dg').datagrid('getSelections');
if (!rows.length) return $.messager.alert('提示','請選中行');
var ids = rows.map(r => r.isbn).join(',');
$.post('Remove.action', {isbns: ids}, function(){
$('#dg').datagrid('reload');
});
}
</script>
<s:form action="Store">
<s:hidden name="book.isbn" />
<s:textfield name="book.title" label="書名" />
<s:textfield name="book.price" label="價格" />
<s:submit />
</s:form>
book 對象,name="book.title" 對應 book.setTitle()。type="json" + root 參數,自動序列化指定對象為 JSON 響應。storeBook 統一處理。本例為教學簡化:生產環境請補全 — 輸入驗證、事務管理、Service 層解耦、分頁查詢下推到 SQL、國際化、權限控制。