基於 Eclipse WTP、Tomcat 5、MySQL 4 建立的 DBTest Web 應用存在兩大問題:
引入 **Struts** 解決導航耦合,**Hibernate** 解決持久層耦合。
Customer (id, firstName, lastName, address) 1 —< * Order (id, custId, datePlaced, amount)
對應資料表 CUSTOMERS、ORDERS。
struts.jar, commons-lang.jar, commons-collections.jar, commons-beanutils.jar,
commons-validator.jar, commons-logging.jar, commons-digester.jar, commons-fileupload.jar
struts-config.xml, struts-bean.tld, struts-html.tld, struts-logic.tld,
struts-nested.tld, struts-template.tld, struts-tiles.tld
<servlet>
<servlet-name>action</servlet-name>
<servlet-class>org.apache.struts.action.ActionServlet</servlet-class>
<init-param>
<param-name>config</param-name>
<param-value>/WEB-INF/struts-config.xml</param-value>
</init-param>
<init-param><param-name>debug</param-name><param-value>2</param-value></init-param>
<init-param><param-name>detail</param-name><param-value>2</param-value></init-param>
<init-param><param-name>validate</param-name><param-value>true</param-value></init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>action</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
public abstract class AbstractAction extends Action {
public ActionForward execute(ActionMapping m, ActionForm f,
HttpServletRequest req, HttpServletResponse res) throws Exception {
ActionErrors errors = new ActionErrors();
ActionForward forward = new ActionForward();
try {
forward = performAction(m, f, req, res);
} catch (Exception e) {
errors.add("name", new ActionError("id"));
}
if (!errors.isEmpty()) {
saveErrors(req, errors);
return mapping.findForward("failure");
}
return forward != null ? forward : mapping.findForward("success");
}
protected abstract ActionForward performAction(ActionMapping m, ActionForm f,
HttpServletRequest req, HttpServletResponse res) throws Exception;
}
public class CreateCustomerAction extends AbstractAction {
protected ActionForward performAction(ActionMapping m, ActionForm f,
HttpServletRequest req, HttpServletResponse res) {
String first = req.getParameter("first_name");
String last = req.getParameter("last_name");
String addr = req.getParameter("address");
int id = Math.abs((int) System.currentTimeMillis());
Customer c = new Customer();
c.setId(id); c.setFirstName(first); c.setLastName(last); c.setAddress(addr);
DatabaseCommand cmd = new CreateCustomer(c);
CommandExecutor.getInstance().executeDatabaseCommand(cmd);
return mapping.findForward("customer_created");
}
}
<action path="/CreateCustomer" type="actions.CreateCustomerAction">
<forward name="customer_created" path="/customer_created.jsp"/>
<forward name="failure" path="/failure.jsp"/>
</action>
JSP 名稱不再硬編碼於 Java 程式碼,改由設定檔解析。
hibernate3.jar, dom4j.jar
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD//EN"
"https://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="hibernate.connection.datasource">java:comp/env/jdbc/TestDB</property>
<property name="show_sql">true</property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<mapping resource="hibernate.mapping.xml"/>
</session-factory>
</hibernate-configuration>
<?xml version="1.0"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD//EN"
"https://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
<class name="domain.Customer" table="CUSTOMER" lazy="false">
<id name="id" column="ID"/>
<property name="firstName" column="FIRST_NAME"/>
<property name="lastName" column="LAST_NAME"/>
<property name="address" column="ADDRESS"/>
</class>
<class name="domain.Order" table="ORDERS" lazy="false">
<id name="id" column="ID"/>
<property name="custId" column="CUST_ID"/>
<property name="datePlaced" column="DATE_PLACED"/>
<property name="orderAmount" column="AMOUNT"/>
</class>
</hibernate-mapping>
lazy="false" 避免 Session 關閉後存取延遲屬性拋出異常(本範例資料量小、無後續操作)。
private SessionFactory sessionFactory = null;
public SessionFactory getSessionFactory() {
if (sessionFactory == null) {
sessionFactory = new Configuration().configure().buildSessionFactory();
}
return sessionFactory;
}
public Object executeHibernateCommand(DatabaseCommand cmd) throws Exception {
Session session = null;
try {
session = getSessionFactory().openSession();
return cmd.executeHibernateOperation(session);
} catch (SQLException e) { throw e; }
finally {
if (session != null) { session.flush(); session.close(); }
}
}
public Object executeHibernateOperation(Session session) throws SQLException;
// CreateCustomer
public Object executeHibernateOperation(Session session) {
session.save(cust); session.flush(); return 1;
}
// CreateOrder (類似)
// ListCustomers
public Object executeHibernateOperation(Session session) {
Query q = session.createQuery("from Customer");
Iterator it = q.iterate();
List list = new ArrayList();
while (it.hasNext()) list.add(it.next());
return list;
}
// ListCustomerOrders
public Object executeHibernateOperation(Session session) {
Query q = session.createQuery("from Order where custId = '" + this.cust_id + "'");
Iterator it = q.iterate();
List list = new ArrayList();
while (it.hasNext()) list.add(it.next());
return list;
}
HQL 查詢物件而非資料表,無硬編碼 SQL、欄位名,切換資料庫只需改 dialect 與 datasource。
將 CommandExecutor.getInstance().executeDatabaseCommand(cmd) 改為 executeHibernateCommand(cmd)。
File → Export → WAR file 產出 DBTestStruts.war。Struts 分離 Model/View/Controller,Hibernate 消除硬編碼 SQL 與 JDBC 樣板碼,兩者結合大幅提升專案可維護性、程式碼重用性與可讀性。