我是Java和JSF的新手,不知道如何构建事务。我有一个很好的理解,但似乎我不能让我的webApp运行一个请求不止一次没有错误:
WARNING: javax.el.ELException: /accounts.xhtml @38,125 value="#{login.accounts}": Error reading 'accounts' on type edu.pcc.cis234j.assign08.LoginBean
Caused by: javax.el.ELException: Error reading 'accounts' on type edu.pcc.cis234j.assign08.LoginBean
SEVERE: org.apache.tomcat.dbcp.dbcp.SQLNestedException: Cannot get a connection, pool error Timeout waiting for idle object
Caused by: java.util.NoSuchElementException: Timeout waiting for idle object
所以当我第一次登录时,一切都显示良好,但似乎如果我执行LoginBean.getAccounts
不止一次(单击accounts.xhtml中的Transfer按钮),这个错误抛出。当我尝试运行TransferBean.execute
函数时,就会出现这个问题,所以我为这个方法写了一个简单的测试,直到我能弄清楚这里发生了什么。我使用的是Eclipse、Apache Tomcat 7、带数据源连接的MySQL和JSF2。我的最后一步是让我的管理bean设置为@SessionScoped
,因为传输功能将结果在多个HTTP请求,我想保留登录信息,我认为这会解决我的问题,但现在我卡住了!有些事我不明白。提前感谢您的帮助:-).
LoginBean.Java:
@ManagedBean(name = "login")
@SessionScoped
public class LoginBean implements Serializable {
private static final long serialVersionUID = 1L;
private String userName = null;
private String pwd = null;
private int uId = 0;
private List<Account> accounts;
private DatabaseManager manager;
public LoginBean() {
manager = new DatabaseManager();
}
public String getUsername() {
return userName;
}
public String getPassword() {
return pwd;
}
public void setUsername(String userName) {
this.userName = userName;
}
public void setPassword(String pwd) {
this.pwd = pwd;
}
public void setUserId(int uId) {
this.uId = uId;
}
public List<Account> getAccounts() throws Exception {
accounts = new ArrayList<Account>();
String sql = "SELECT * FROM accounts WHERE UserId IN ( SELECT UserId FROM users WHERE UserName = '" + userName + "' )";
ResultSet rs = manager.executeQuery(sql);
while(rs.next()) {
accounts.add( new Account(rs.getInt("AccountId"), rs.getString("AccountName"), rs.getFloat("LastDeposit"), rs.getFloat("Balance")) );
}
return accounts;
}
}
TransferBean.java
@ManagedBean(name = "transfer")
@SessionScoped
public class TransferBean implements Serializable {
private static final long serialVersionUID = 1L;
private int sourceId;
private int destinationId;
private float amount;
public TransferBean() {}
public int getSourceId() {
return sourceId;
}
public int getDestinationId() {
return destinationId;
}
public float getAmount() {
return amount;
}
public void setSourceId(int sourceId) {
this.sourceId = sourceId;
}
public void setDestinationId(int destinationId) {
this.destinationId = destinationId;
}
public void setAmount(float amount) {
this.amount = amount;
}
// TEST METHOD - will envoke DatabaseManager.executeTransaction
public void execute() throws Exception {
System.out.print("execute method rannsourceId: " +sourceId + "ndestinationId: " + destinationId + "namount: " + amount + "n");
}
}
DatabaseManager.java:
public class DatabaseManager {
private int rowsAffected;
public DatabaseManager() {}
public ResultSet executeQuery(String sql) throws Exception {
ResultSet result = null;
CachedRowSetImpl cachedResult = null;
try {
result = getConnection().createStatement().executeQuery(sql);
cachedResult = new CachedRowSetImpl();
cachedResult.populate(result);
} catch (SQLException e) {
System.err.println("ERROR: " + e.getMessage());
e.printStackTrace();
} finally {
closeConnection(getConnection());
}
return cachedResult;
}
public int executeUpdate(String sql) throws Exception {
try {
rowsAffected = getConnection().createStatement().executeUpdate(sql);
} catch (SQLException e) {
System.err.println("ERROR: " + e.getMessage());
e.printStackTrace();
} finally {
closeConnection(getConnection());
}
return rowsAffected;
}
public Connection getConnection() throws Exception {
Context context = new InitialContext();
DataSource source = (DataSource)context.lookup("java:comp/env/jdbc/cis234j");
return source.getConnection();
}
private void closeConnection(Connection connection) {
if (connection != null) {
try {
connection.close();
} catch (SQLException e) {}
}
}
public int executeTransaction(String[] sqls) throws Exception {
int rowCount = 0;
Connection con = getConnection();
try {
con.setAutoCommit(false);
for(String sql : sqls) {
con.createStatement().executeUpdate(sql);
}
con.commit();
} catch (Exception e) {
con.rollback();
}
return rowCount;
}
}
java:会
public class Account {
private int id;
private String name;
private float lastDeposit;
private float balance;
private DatabaseManager manager;
public Account(int id, String name, float lastDeposit, float balance) {
this.id = id;
this.name = name;
this.lastDeposit = lastDeposit;
this.balance = balance;
manager = new DatabaseManager();
}
public int getId() {
return id;
}
public String getName() {
return name;
}
public float getLastDeposit() {
return lastDeposit;
}
public float getBalance() {
return balance;
}
public void setId(int id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
}
public void setLastDeposit(float lastDeposit) {
this.lastDeposit = lastDeposit;
}
public void setBalance(float balance) {
this.balance = balance;
}
public void deposit(float amount) throws Exception {
manager.executeUpdate("UPDATE accounts"
+ " SET LastDeposit = " + amount + ","
+ " Balance = " + (getBalance() + amount)
+ " WHERE AccountId = " + id);
}
public void withdraw(float amount) throws Exception {
deposit(-amount);
}
}
login.xhtml:
<div class="wrapper">
<h:form>
<div class="login">
<h:outputLabel class="label" value="User Name" />
<h:inputText id="user_id" title="User Name" value="#{login.username}" />
<h:outputText value="<br/>" escape="false" />
<h:outputText value="<br/>" escape="false" />
<h:outputLabel class="label" value="Password" />
<h:inputSecret id="password" title="Password" value="#{login.password}" />
<h:outputText value="<br/>" escape="false" />
<h:outputText value="<br/>" escape="false" />
<h:commandButton id="login" value="Login" action="accounts" />
</div>
</h:form>
</div><!-- .wrapper -->
accounts.xhtml:
<div class="wrapper">
<h2><h:outputText value="Hello, " />#{login.username}!</h2>
<h3><h:outputText value="Here is your PCC bank account information:" /></h3>
<h3>TEST - Here is your set Password: #{login.password}</h3>
<h:dataTable class="account-table" value="#{login.accounts}" var="act" border="3" width="100%">
<h:column>
<f:facet name="header">Account ID</f:facet>
#{act.id}
</h:column>
<h:column>
<f:facet name="header">Account Name</f:facet>
#{act.name}
</h:column>
<h:column>
<f:facet name="header">Last Deposit</f:facet>
$#{act.lastDeposit}
</h:column>
<h:column>
<f:facet name="header">Current Balance</f:facet>
$#{act.balance}
</h:column>
</h:dataTable>
<br />
<h:form class="trans-form">
<h3>Transfer Money:</h3>
<div class="trans-cont">
<h:outputLabel class="label" value="From:" />
<h:selectOneMenu class="select-menu float-r" value="#{transfer.sourceId}" converter="omnifaces.SelectItemsConverter">
<f:selectItem itemLabel="Choose Account" noSelectionOption="false" />
<f:selectItems value="#{login.accounts}" var="act" itemLabel="#{act.id} - #{act.name}" itemValue="#{act.id}" />
</h:selectOneMenu>
</div>
<div class="trans-cont">
<h:outputLabel class="label" value="To:" />
<h:selectOneMenu class="select-menu float-r" value="#{transfer.destinationId}" converter="omnifaces.SelectItemsConverter">
<f:selectItem itemLabel="Choose Account" noSelectionOption="false" />
<f:selectItems value="#{login.accounts}" var="act" itemLabel="#{act.id} - #{act.name}" itemValue="#{act.id}" />
</h:selectOneMenu>
</div>
<div class="trans-cont">
<h:inputText id="user_id" title="User Name" value="#{transfer.amount}" />
</div>
<div class="trans-cont">
<h:commandButton class="btn pad10 float-r" value="Transfer Now" action="#{transfer.execute}">
<f:ajax execute="@form" />
</h:commandButton>
</div>
</h:form>
<br /><br />
<div class="btn-container">
<h:outputLink value="login.xhtml">
<input class="btn float-r" type="button" value="Logout" />
</h:outputLink>
</div>
</div><!-- .wrapper -->
web . xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://java.sun.com/xml/ns/javaee" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" id="WebApp_ID" version="3.0">
<display-name>assign08</display-name>
<welcome-file-list>
<welcome-file>login.jsf</welcome-file>
</welcome-file-list>
<servlet>
<servlet-name>Faces Servlet</servlet-name>
<servlet-class>javax.faces.webapp.FacesServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Faces Servlet</servlet-name>
<!-- tomcat -->
<!-- <url-pattern>*.jsf</url-pattern> -->
<!-- eclipse -->
<url-pattern>*.xhtml</url-pattern>
</servlet-mapping>
<context-param>
<param-name>javax.faces.PROJECT_STAGE</param-name>
<param-value>Development</param-value>
</context-param>
<context-param>
<description>State saving method: 'client' or 'server' (=default). See JSF Specification 2.5.2</description>
<param-name>javax.faces.STATE_SAVING_METHOD</param-name>
<param-value>client</param-value>
</context-param>
<context-param>
<param-name>javax.servlet.jsp.jstl.fmt.localizationContext</param-name>
<param-value>resources.application</param-value>
</context-param>
<!-- Resource Reference -->
<resource-ref>
<res-ref-name>jdbc/cis234j</res-ref-name>
<res-type>javax.sql.DataSource</res-type>
<res-auth>Container</res-auth>
</resource-ref>
<listener>
<listener-class>com.sun.faces.config.ConfigureListener</listener-class>
</listener>
</web-app>
context.xml
<context>
<WatchedResource>WEB-INF/web.xml</WatchedResource>
<Resource name="jdbc/cis234j"
auth="Container"
type="javax.sql.DataSource"
maxActive="8"
maxIdle="3"
maxWait="1000"
username="student"
password="pencil1"
driverClassName="com.mysql.jdbc.Driver"
url="jdbc:mysql://localhost/cis234j" />
</context>
这个bug是在finally
块@ close(getConnection())
的DatabaseManager.java
中。
DatabaseManager.java
public ResultSet executeQuery(String sql) throws Exception {
ResultSet result = null;
CachedRowSetImpl cachedResult = null;
Connection con = getConnection();
try {
result = con.createStatement().executeQuery(sql);
cachedResult = new CachedRowSetImpl();
cachedResult.populate(result);
return cachedResult;
} catch (SQLException e) {
System.err.println("ERROR: " + e.getMessage());
e.printStackTrace();
} finally {
closeConnection(con);
}
return cachedResult;
}