Java 使用ApacheTomcatJDBC的JDBC池的单例数据库类
我正在尝试配置一个单例数据库类,以允许使用JDK8和Tomcat7对Oracle XE 18c数据库进行连接池 由于以下错误,我无法编译源代码: 未报告异常SQLException;必须被抓住或宣布为 扔 类源代码:Java 使用ApacheTomcatJDBC的JDBC池的单例数据库类,java,oracle,tomcat,jdbc,java-8,Java,Oracle,Tomcat,Jdbc,Java 8,我正在尝试配置一个单例数据库类,以允许使用JDK8和Tomcat7对Oracle XE 18c数据库进行连接池 由于以下错误,我无法编译源代码: 未报告异常SQLException;必须被抓住或宣布为 扔 类源代码: package com.example.webapp.db; import java.sql.Connection; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statemen
package com.example.webapp.db;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
public class DatabaseManager {
private static final DatabaseManager SINGLE_INSTANCE = new DatabaseManager();
private DatabaseManager() throws SQLException {
PoolProperties p = new PoolProperties();
p.setUrl("jdbc:oracle:thin:@localhost:1521:xe");
p.setDriverClassName("oracle.jdbc.driver.OracleDriver");
p.setUsername("scott");
p.setPassword("tiger");
p.setJmxEnabled(true);
p.setTestWhileIdle(false);
p.setTestOnBorrow(true);
p.setValidationQuery("SELECT 1");
p.setTestOnReturn(false);
p.setValidationInterval(30000);
p.setTimeBetweenEvictionRunsMillis(30000);
p.setMaxActive(100);
p.setInitialSize(10);
p.setMaxWait(10000);
p.setRemoveAbandonedTimeout(60);
p.setMinEvictableIdleTimeMillis(30000);
p.setMinIdle(10);
p.setLogAbandoned(true);
p.setRemoveAbandoned(true);
p.setJdbcInterceptors("org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"+
"org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
DataSource datasource = new DataSource();
datasource.setPoolProperties(p);
Connection con = null;
try {
con = datasource.getConnection();
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select * from user");
int cnt = 1;
while (rs.next()) {
System.out.println((cnt++)+". Host:" +rs.getString("Host")+
" User:"+rs.getString("User")+" Password:"+rs.getString("Password"));
}
rs.close();
st.close();
} finally {
if (con!=null) try {con.close();}catch (Exception ignore) {}
}
}
public static DatabaseManager getInstance() {
return SINGLE_INSTANCE;
}
}
如下图所示更改代码,您必须捕获异常
private static DatabaseManager SINGLE_INSTANCE = null;
static {
try {
SINGLE_INSTANCE = new DatabaseManager();
}
catch(Exception e) {
e.printStackTrace();
}
}
如下图所示更改代码,您必须捕获异常
private static DatabaseManager SINGLE_INSTANCE = null;
static {
try {
SINGLE_INSTANCE = new DatabaseManager();
}
catch(Exception e) {
e.printStackTrace();
}
}
我认为您应该在
getInstance()
中抛出或处理SQLException
,并使用try with resource(自动关闭资源),而不是finally
块。您应该not使用引发异常
进行初始化的构造函数将实例
设置为常量
package com.example.webapp.db;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
public class DatabaseManager {
// not final anymore and null as default
private static DatabaseManager instance = null;
private DatabaseManager() {
PoolProperties p = new PoolProperties();
p.setUrl("jdbc:oracle:thin:@localhost:1521:xe");
p.setDriverClassName("oracle.jdbc.driver.OracleDriver");
p.setUsername("scott");
p.setPassword("tiger");
p.setJmxEnabled(true);
p.setTestWhileIdle(false);
p.setTestOnBorrow(true);
p.setValidationQuery("SELECT 1");
p.setTestOnReturn(false);
p.setValidationInterval(30000);
p.setTimeBetweenEvictionRunsMillis(30000);
p.setMaxActive(100);
p.setInitialSize(10);
p.setMaxWait(10000);
p.setRemoveAbandonedTimeout(60);
p.setMinEvictableIdleTimeMillis(30000);
p.setMinIdle(10);
p.setLogAbandoned(true);
p.setRemoveAbandoned(true);
p.setJdbcInterceptors("org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"
+ "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
javax.sql.DataSource datasource = new DataSource();
datasource.setPoolProperties(p);
// use a try-with resource to get rid of the finally block...
try (Connection con = datasource.getConnection()) {
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select * from user");
int cnt = 1;
while (rs.next()) {
System.out.println((cnt++) + ". Host:" + rs.getString("Host")
+ " User:" + rs.getString("User")
+ " Password:" + rs.getString("Password"));
}
rs.close();
st.close();
// ... and handle the exception
} catch (SQLException e) {
System.err.println("SQLException while constructing the instance of DatabaseManager");
e.printStackTrace();
}
}
public static DatabaseManager getInstance() {
// check for null here:
if (instance == null) {
instance = new DatabaseManager();
}
return instance;
}
}
也许,创建数据库连接的初始化方法比初始化构造函数中的所有内容更好,但这是一种基于观点的方法。我认为您应该在
getInstance()
中抛出或处理SQLException
,并使用try with资源(自动关闭资源)而不是最终
块。您应该不使用引发异常
进行初始化的构造函数将实例
设置为常量
package com.example.webapp.db;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.apache.tomcat.jdbc.pool.PoolProperties;
public class DatabaseManager {
// not final anymore and null as default
private static DatabaseManager instance = null;
private DatabaseManager() {
PoolProperties p = new PoolProperties();
p.setUrl("jdbc:oracle:thin:@localhost:1521:xe");
p.setDriverClassName("oracle.jdbc.driver.OracleDriver");
p.setUsername("scott");
p.setPassword("tiger");
p.setJmxEnabled(true);
p.setTestWhileIdle(false);
p.setTestOnBorrow(true);
p.setValidationQuery("SELECT 1");
p.setTestOnReturn(false);
p.setValidationInterval(30000);
p.setTimeBetweenEvictionRunsMillis(30000);
p.setMaxActive(100);
p.setInitialSize(10);
p.setMaxWait(10000);
p.setRemoveAbandonedTimeout(60);
p.setMinEvictableIdleTimeMillis(30000);
p.setMinIdle(10);
p.setLogAbandoned(true);
p.setRemoveAbandoned(true);
p.setJdbcInterceptors("org.apache.tomcat.jdbc.pool.interceptor.ConnectionState;"
+ "org.apache.tomcat.jdbc.pool.interceptor.StatementFinalizer");
javax.sql.DataSource datasource = new DataSource();
datasource.setPoolProperties(p);
// use a try-with resource to get rid of the finally block...
try (Connection con = datasource.getConnection()) {
Statement st = con.createStatement();
ResultSet rs = st.executeQuery("select * from user");
int cnt = 1;
while (rs.next()) {
System.out.println((cnt++) + ". Host:" + rs.getString("Host")
+ " User:" + rs.getString("User")
+ " Password:" + rs.getString("Password"));
}
rs.close();
st.close();
// ... and handle the exception
} catch (SQLException e) {
System.err.println("SQLException while constructing the instance of DatabaseManager");
e.printStackTrace();
}
}
public static DatabaseManager getInstance() {
// check for null here:
if (instance == null) {
instance = new DatabaseManager();
}
return instance;
}
}
也许,为数据库连接创建一个初始化方法比初始化构造函数中的所有内容更好,但这是一种基于观点的方法。…关于该消息还有什么不清楚的地方?它是一个选中的异常,因此“必须捕获或声明要抛出”。由于无法在静态初始值设定项中声明它,因此需要捕获它。您让构造函数抛出一个
SQLException
,调用此构造函数时需要捕获该异常。这意味着包含new DatabaseManager()
的每一行都必须包装在try{…}catch(SQLException e)中
。在您的情况下,这将很困难,因为该行是一个私有静态最终数据库管理器
…因此可能需要在try{…}finally{…}中添加一个catch
子句
您在构造函数代码中添加了,并从构造函数签名中删除了抛出SQLException
。数据库管理器的初始化可能不会因为当前无法连接到DB而失败。可以说,它甚至不应该尝试在构造函数中执行类似操作。相关:另请参阅…和该消息还有什么不清楚的地方?它是一个已检查的异常,因此“必须捕获或声明要抛出”。由于无法在静态初始值设定项中声明它,因此需要捕获它。您让构造函数抛出一个SQLException
,调用此构造函数时需要捕获该异常。这意味着包含new DatabaseManager()
的每一行都必须包装在try{…}catch(SQLException e)中
。在您的情况下,这将很困难,因为该行是一个私有静态最终数据库管理器
…因此可能需要在try{…}finally{…}中添加一个catch
子句
您在构造函数代码中添加了一个属性,并从构造函数签名中删除了抛出SQLException
。数据库管理器的初始化可能不会因为当前无法连接到DB而失败。可以说,它甚至不应该尝试在构造函数中执行类似的操作。相关:另请参阅While this将使其可编译,这可能不是一个好主意,因为现在无论何时您想要访问SINGLE_实例
。是的。正如Hulk所说,需要空检查&修改方法public static DatabaseManager getInstance()抛出SQLException{if(SINGLE_INSTANCE==null){synchronized(DatabaseManager.class){if(SINGLE_INSTANCE==null){SINGLE_INSTANCE=new DatabaseManager();}}}}返回SINGLE_INSTANCE;}}
最好抛出ExceptionInInitializeError
,而不是忽略错误,假装什么也没发生。虽然这会使它编译,但这可能不是一个好主意,因为现在每当你想访问单个实例时都需要空检查。是的。正如Hulk所说,需要空检查并修改了methodpublicstaticdatabasemanager getInstance()抛出SQLException{if(SINGLE_INSTANCE==null){synchronized(DatabaseManager.class){if(SINGLE_INSTANCE==null){SINGLE_INSTANCE=newdatabasemanager();}}}}}返回SINGLE u INSTANCE;}}
最好抛出ExceptionInInitializeError
,而不是忽略错误,假装什么都没发生。@Hulk好的,那该怎么办?同步或使其易失性
?你的建议是什么?我不确定-我可能会将DB访问代码移出构造函数。或者不应该en实际上是一个单例。单例意味着它不能被构造超过一次,如果这真的是一个要求,那么在失败的情况下崩溃/终止应用程序是可以的(但我怀疑这是期望的行为)@Hulk是的,我知道。有时人们只是因为订单或关于单例的特殊任务而不得不使用它们。我们不知道这里是否是这种情况,所以我只是尝试简化提供的代码。如果它不是线程安全的,这不是一个好的答案。你可以让getInstance
-method同步>-这将影响性能,但保持简单和相对正确。但是,这将在每次调用时重试DB连接,直到成功,并且可能仍然返回null
(因为您吞并了异常)。对于getInstance()
,但引发异常是意外的