我编写了下面的代码 - 我希望DataSource是一个单例,并使用枚举习惯用于单例。经过一段时间后,我得到了很多Data source rejected establishment of connection, message from server: "Too many connections"
- 我对Singleton模式的实现是错误的还是原因在其他地方?
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.sql.DataSource;
class DBConnectionPool {
private DataSource ds = null;
private DBConnectionPool() {
try {
Context context = new InitialContext();
Context envctx = (Context) context.lookup("java:comp/env");
ds = (DataSource) envctx.lookup("jdbc/TestDB");
} catch (Exception e) {
e.printStackTrace();
}
}
private static enum PoolSingleton {
POOL_INSTANCE;
private static final DBConnectionPool singleton = new DBConnectionPool();
private DBConnectionPool getSingleton() {
return singleton;
}
}
private static DBConnectionPool getDBConnectionPoolInstance() {
return PoolSingleton.POOL_INSTANCE.getSingleton();
}
static Connection getConnection() {
try {
return getDBConnectionPoolInstance().ds.getConnection();
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
}
为了完整起见,这里是context.xml的内容:
<?xml version="1.0" encoding="UTF-8"?>
<Context antiJARLocking="true" path="/myapp">
<Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"
maxActive="100" maxIdle="30" maxWait="10000" username="root"
password="root" factory="org.apache.tomcat.jdbc.pool.DataSourceFactory"
driverClassName="com.mysql.jdbc.Driver" url="jdbc:mysql://localhost:3306/mydb"
removeAbandoned="true" removeAbandonedTimeout="60" />
</Context>
NB:我没有实现连接池!我用的是tomcat工厂。我所做的是将池包装在一个类中。该池是我认为仅实例化一次的ds
对象。我关闭了finally块中的连接+集合+语句。使用示例:
public User findByUsername(String username) throws DBExFailure {
Connection conn = DBConnectionPool.getConnection();
PreparedStatement statement = null;
ResultSet set = null;
final String query = "SELECT * FROM users WHERE username=?";
if (conn != null) {
try {
statement = conn.prepareStatement(query);
statement.setString(1, username);
set = statement.executeQuery();
while (set.next()) {
User user = new User();
// user.setId(set.getInt("ID"));
user.setUsername(set.getString("username"));
user.setName(set.getString("name"));
user.setSurname(set.getString("surname"));
user.setPassword(set.getString("password"));
user.setEmail(set.getString("email"));
user.setRole(RolesENUM.values()[set.getInt("role")]);
return user;
}
} catch (SQLException ex) {
ex.printStackTrace();
throw new DBExFailure();
} finally {
DBConnectionPool.closeResources(set, statement, conn);
}
}
return null;
}
其中:
static void closeResources(ResultSet set, Statement statement,
Connection conn) {
if (set != null) {
try {
set.close();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
if (statement != null) {
try {
statement.close();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
if (conn != null) {
try {
conn.close();
} catch (SQLException ex) {
ex.printStackTrace();
}
}
}
答案 0 :(得分:5)
我不得不说写一个的正确方法是不写一个:使用现有的库,如C3P0
这也不是一个“聪明的裤子”的答案。它只是一个“不要重新发明轮子”,特别是一个线程安全的池实现,这很难做到正确,通常创建一个与线程相关,微妙和难以修复的错误的雷区。
答案 1 :(得分:2)
你的游泳池非常浅 - 里面只有一个连接!这个规模怎么样?
您已经硬连接了JNDI名称。为什么不把它传入?
这对于多线程应用程序来说不是很好。连接不是线程安全的。
你得到的最好建议是“不要这样做”。使用现有池。或者,更好的是,内置于Java EE应用服务器中的那个。