One Transaction, One Invocation — 每個業務用例對應一次遠程調用、一個事務邊界。此思想貫穿五層架構:
封裝 JDBC 操作,隔離 Domain Layer 與數據庫細節。
// Database.java — 連接池獲取
public class Database {
public static Connection getConnection() throws Exception {
Context ctx = new InitialContext();
DataSource ds = (DataSource) ctx.lookup("jdbc/template");
return ds.getConnection();
}
}
// DataAccessBean.java — 統一 SQL 執行模板
public class DataAccessBean {
private Connection con;
private PreparedStatement pstt;
private String sql;
public void setSql(String sql) throws NotConnectionException { ... }
public void setString(int col, String val) { ... }
public void setInt(int col, int val) { ... }
// ... 其他 setXxx 方法
public ResultSet executeQuery() throws SQLException { ... }
public int executeUpdate() throws SQLException { ... }
public boolean execute() throws SQLException { ... }
public void close() throws NotCloseException { ... }
}
異常類:NotConnectionException、NotCloseException。
WSAD 配置連接池: Server Configuration → Data Source → JDBC Driver (Oracle Thin) → Data Source (JNDI: jdbc/template) → Resource Property URL=jdbc:oracle:thin:@host:1521:sid。
命名規範: com.模塊.domain.實體名
通用屬性修改模式 — Generic Attribute Method:
// 遠程接口
public interface TDomain extends EJBObject {
void changeValue(HashMap attri) throws RemoteException;
}
// Bean 實現
public void changeValue(HashMap attri) {
if (attri.containsKey("name")) setName((String)attri.get("name"));
if (attri.containsKey("sex")) setSex(((String)attri.get("sex")).charAt(0));
if (attri.containsKey("birth")) setBirth((String)attri.get("birth"));
}
優勢:單次遠程調用完成多字段更新,減少網絡開銷;擴展字段無需修改接口簽名。
BMP 關鍵回調實現: ejbLoad/Store/Passivate/Activate/Remove/Create/PostCreate/FindByPrimaryKey 統一使用 DataAccessBean 操作數據庫,isModified 標記優化 ejbStore。
CMP 差異: 字段聲明為 public,Finder 方法在 Home 接口聲明,SQL 在 EJB Extension Editor 配置 (EJB QL / Where Clause),無需 Bean 類編寫持久化代碼。
// Tserver.java (Remote)
public interface Tserver extends EJBObject {
RowSet readData(String birth1, String birth2) throws RemoteException;
void create(String id, String name, String birth, String sex) throws Exception, RemoteException;
}
// TserverBean.java
public RowSet readData(String birth1, String birth2) {
// JDBC for Reading 模式:直接用連接池 + CachedRowSet,繞過 Entity Bean 開銷
Connection con = Database.getConnection();
PreparedStatement ps = con.prepareStatement("select * from template1 where birth>=? and birth<=?");
ps.setString(1, birth1); ps.setString(2, birth2);
CachedRowSet rs = new CachedRowSet();
rs.populate(ps.executeQuery());
rs.setTableName("template1");
return rs; // 可序列化傳遞給客戶端
}
public void create(String id, String name, String birth, String sex) throws Exception {
TDomainHome home = getTDomainHome(); // JNDI 查找
try {
TDomain domain = home.findByPrimaryKey(new TDomainKey(id));
HashMap attri = new HashMap();
attri.put("name", name); attri.put("birth", birth); attri.put("sex", sex);
domain.changeValue(attri); // 更新
} catch (FinderException fe) {
home.create(id, name, birth, sex.charAt(0)); // 新建
}
}
數據傳輸模式選擇: HashMap (少量字段)、DTO (多行/頁面綁定)、RowSet (查詢結果集)、XML (異構集成)。
// Tbusiness.java (Business Delegate)
public class Tbusiness {
public Tserver getTserver() throws Exception {
HomeFactory factory = HomeFactory.builder();
TserverHome home = (TserverHome) factory.findBy(TserverHome.class);
return home.create();
}
public void updateAndCreate(String id, String name, String birth, String sex) throws Exception {
// 參數校驗
if (sex.length()!=1 || birth.length()!=8) throw new Exception("error data formation");
getTserver().create(id, name, birth, sex);
}
public RowSet showData(String birth1, String birth2) throws Exception {
if (birth1.length()!=8 || birth2.length()!=8) throw new Exception("error data formation");
RowSet rs = getTserver().readData(birth1, birth2);
if (rs==null) throw new Exception("return value is null");
return rs;
}
}
// HomeFactory.java — 單例 + 享元模式緩存 Home 接口
public class HomeFactory {
private static HomeFactory factory;
private static HashMap map = new HashMap();
private static final String[] JNDI_NAMES = {"ejb/Tserver"};
private static final Class[] CLASSES = {TserverHome.class};
private HomeFactory() throws HomeFactoryCreateException {
Context ctx = new InitialContext();
for (int i=0; i
作用:隔離 JNDI 名稱變更、緩存 Home 接口減少查找開銷、統一異常轉換、參數校驗前置。
JSP 使用 JSTL/EL 顯示 RowSet,Servlet 委派給 Tbusiness,保持控制器輕薄。
ejb/TDomain)。該框架通過五層分離、經典模式組合 (Session Facade + Business Delegate + DAO Command Bean + Generic Attribute Method + JDBC for Reading + Home Factory),實現了高性能、可復用、可擴展的 J2EE 應用架構。核心代碼已在 WSAD 環境驗證,可直接遷移至其他 J2EE 容器。