就像以前强调过的,在finally
子句中关闭连接是很重要的,这样每个连接都会被正确的返回连接池。
为使你的应用程序更加健壮(robust),你可以实现org.zkoss.zk.ui.event.EventThreadCleanup
接口来关闭任何处在等待之际的连接和语句(any pending connections and statements),以防你的一些应用程序代码忘记了在finally
子句中关闭它们。
但是,关闭处在等待之际的连接和语句实际上依赖于你使用的服务器。你需要参考服务器的文档来得知如何编写关闭代码。
[提示]: 在许多情况下,并不需要(且并不容易)提供这样的方法,因为多数连接池的实现可以循环一个连接若调用了连接池的finalized
方法(because most implementation of connection pooling be recycled a connection if its finalized method is called)。
除了在事件监听器中访问数据库,使用EL表达式访问数据库填充一个属性也是很常见的。在下面的例子中,我们从数据库取出数据,并使用EL表达式将它们用listbox
展示出来。
<zscript> import my.CustomerManager; customers = new CustomerManager().findAll(); //load from database </zscript> <listbox id="personList" width="800px" rows="5"> <listhead> <listheader label="Name"/> <listheader label="Surname"/> <listheader label="Due Amount"/> </listhead> <listitem value="${each.id}" forEach="${customers}"> <listcell label="${each.name}"/> <listcell label="${each.surname}"/> <listcell label="${each.due}"/> </listitem> </listbox>
有几种方式来实现findAll
方法。
最简单的方式是在findAll
方法内获取所有的数据,将它们拷贝到一个列表,然后关闭连接。
public class CustomerManager { public List findAll() throws Exception { DataSource ds = (DataSource)new InitialContext() .lookup("java:comp/env/jdbc/MyDB"); Connection conn = null; Statement stmt = null; ResultSet rs = null; List results = new LinkedList(); try { conn = ds.getConnection(); stmt = conn.createStatement(); rs = stmt.executeQuery("SELECT id, name, surname FROM customers"); while (rs.next()) { long id = rs.getInt("id"); String name = rs.getString("name"); String surname = rs.getString("surname"); results.add(new Customer(id, name, surname)); } return results; } finally { if (rs != null) try { rs.close(); } catch (SQLException ex) [} if (stmt != null) try { stmt.close(); } catch (SQLException ex) [} if (conn != null) try { conn.close(); } catch (SQLException ex) [} } } }
你可以使用init
指令来加载数据,以代替在试图中混合使用Java代码。
<?init class="my.AllCustomerFinder" arg0="customers"?> <listbox id="personList" width="800px" rows="5"> <listhead> <listheader label="Name"/> <listheader label="Surname"/> <listheader label="Due Amount"/> </listhead> <listitem value="${each.id}" forEach="${customers}"> <listcell label="${each.name}"/> <listcell label="${each.surname}"/> <listcell label="${each.due}"/> </listitem> </listbox>
然后,使用org.zkoss.zk.ui.util.Initiator
接口实现my.CustomerFindAll
类。
import org.zkoss.zk.ui.Page; import org.zkoss.zk.ui.util.Initiator; public class AllCustomerFinder implements Initiator { public void doInit(Page page, Object[] args) { try { page.setVariable((String)args[0], new CustomerManager().findAll()); //Use setVariable to pass the result back to the page } catch (Exception ex) { throw UiException.Aide.wrap(ex); } } public void doCatch(Throwable ex) { //ignore } public void doFinally() { //ignore } }
度与复杂的应用程序(例如分布式事务处理),你或许需要明确的控制一个事务处理的活动周期。若所有的数据库访问均是在事件监听器中被处理的,那么在ZK中为使其工作并不需要改变什么。你开始,提交或回滚(start, commit or rollback)一个事务处理,与你的J2EE/Web服务器文档建议的一样。
但是,若你想将整个ZUML页面(组件创建阶段)的赋值(evaluation)在相同的事务处理里被执行,如上一章节描述的那样,你可以实现org.zkoss.zk.util.Initiator
接口控制给定页面的事务处理活动周期。
实现的框架如下所示。
import org.zkoss.zk.ui.Page; import org.zkoss.zk.ui.util.Initiator; public class TransInitiator implements Initiator { private boolean _err; public void doInit(Page page, Object[] args) { startTrans(); //depending the container, see below } public void doCatch(Throwable ex) { _err = true; rollbackTrans(); //depending the container, see below } public void doFinally() { if (!_err) commitTrans(); //depending the container, see below } }
如上所示,事务处理在doInit
方法内开始,且在org.zkoss.zk.util.Initiator
接口的doFinally
方法内结束。
如何开始,提交和回滚(start, commit and rollback)一个事务处理取决于你使用的容器。
若你使用的为一个J2EE容器,你可以找到事务管理器(transaction manager)(javax.transaction.TransactionManager
),然后调用它的 begin
方法开始一个事务处理。调用rollback
方法回滚。调用commit
方法提交。
若你使用的为一个没有事务管理器的Web容器,可以通过构造(constructing)一个数据库连接来开始事务处理。然后,据此调用commit
和rollback
方法。
import java.sql.*; import javax.sql.DataSource; import javax.naming.InitContext; import org.zkoss.util.logging.Log; import org.zkoss.zk.ui.Page; import org.zkoss.zk.ui.util.Initiator; public class TransInitiator implements Initiator { private static final Log log = Log.lookup(TransInitiator.class); private Connection _conn; private boolean _err; public void doInit(Page page, Object[] args) { try { DataSource ds = (DataSource)new InitialContext() .lookup("java:comp/env/jdbc/MyDB"); _conn = ds.getConnection(); } catch (Throwable ex) { throw UiException.Aide.wrap(ex); } } public void doCatch(Throwable t) { if (_conn != null) { try { _err = true; _conn.rollback(); } catch (SQLException ex) { log.warning("Unable to roll back", ex); } } } public void doFinally() { if (_conn != null) { try { if (!_err) _conn.commit(); } catch (SQLException ex) { log.warning("Failed to commit", ex); } finally { try { _conn.close(); } catch (SQLException ex) { log.warning("Unable to close transaction", ex); } } } } }