私はこのクラスを持っていますが、ログインしているユーザーが3人だけで、複数のSQLクエリを使用しているにもかかわらず、このエラーが発生するため、接続を適切に閉じる方法がわかりません。
> com.mysql.jdbc.exceptions.jdbc4.MySQLNonTransientConnectionException: > Data source rejected establishment of connection, message from > server: "Too many connections"
import Java.io.File;
import Java.io.IOException;
import Java.sql.PreparedStatement;
import Java.sql.ResultSet;
import Java.sql.SQLException;
import Java.text.DateFormat;
import Java.text.SimpleDateFormat;
import Java.util.ArrayList;
import Java.util.Date;
import Java.util.List;
import javax.sql.DataSource;
public class UserDaoImpl implements UserDao
{
DataSource dataSource;
public DataSource getDataSource()
{
return this.dataSource;
}
public void setDataSource(DataSource dataSource)
{
this.dataSource = dataSource;
}
public boolean isValidUser(String username, String password) throws SQLException
{
PreparedStatement pstmt = null;
ResultSet resultSet = null;
boolean rt = false;
try{
PasswordEncryptor pws = new PasswordEncryptor();
String encryptedPass = pws.encrypt(password);
String query = "Select count(1) from userdetails where username = ? and password = ?";
pstmt = dataSource.getConnection().prepareStatement(query);
pstmt.setString(1, username);
pstmt.setString(2, encryptedPass);
resultSet = pstmt.executeQuery();
if (resultSet.next()){
rt = (resultSet.getInt(1) > 0);
}
else{
rt = false;
}
}
catch(Exception e){
e.printStackTrace();
}
finally{
resultSet.close();
pstmt.close();
dataSource.getConnection().close();
}
return rt;
}
}
SpringConfiguration.xml
<bean name="userDao" class="com.spring.acadconnect.services.UserDaoImpl">
<property name="dataSource" ref="dataSource"></property>
</bean>
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/acadconnect" />
<property name="username" value="root" />
<property name="password" value="" />
</bean>
.getConnection()
を複数回呼び出していることに注意してください。ドキュメントはこの面でより明確になる可能性がありますが DataSource.getConnection()
実際には新しい接続を開きます(既存の接続を返すのではなく)したがってそのメソッドから返された各インスタンスを閉じる必要があります。
.getConnection()
は、呼び出されるたびに新しいインスタンスを作成するため、返される接続を閉じていないため、この行は接続リークです。
_pstmt = dataSource.getConnection().prepareStatement(query);
_
そして、この行は無駄に新しい接続を開き、すぐに閉じます。
_dataSource.getConnection().close();
_
isValidUser()
の呼び出しごとに個別の接続を開いたり閉じたりしようとしているようです(そのメソッド呼び出しの最後に接続を閉じているため)。上記のリークを修正したとしても、それは接続の使用目的ではありません。代わりに、アプリケーション全体で1つの接続(または少数の接続)を共有する必要があります。したがって、プログラムが起動すると、そのような接続が開かれ、一度プログラム全体が接続を必要としなくなります(多くの場合、終了する直前に)。
この種の動作は通常、 依存性注入 によって実装されます。ここで、接続やその他のリソースを構築し、それらを必要なオブジェクトに渡します。これにより、リソース管理がそれらのリソースを使用するコードから切り離されます。単純な例として:
_public static void main(String[] args) {
DataSource dataSource = createDataSource();
try (Connection connection = dataSource.getConnection()) {
runProgram(connection);
}
}
/**
* this method doesn't need to worry about closing the Connection,
* it trusts that its caller will be responsible for that.
*/
private static void runProgram(Connection connection) {
// ...
}
_
経験則として、オブジェクトは、構築したオブジェクトを閉じることのみを担当し、渡されたオブジェクトを閉じることは避けてください。現在のコードでは、UserDaoImpl
が接続を開いているので、接続を閉じる責任がありますが、代わりにConnection
を渡すことをお勧めします。
dataSource.getConnection()
は常に新しい接続を返すため、考えている接続を閉じていません。 DataSourceUtils.getConnection()
を使用して現在のスレッドのアクティブな接続を取得するか、conn = dataSource.getConnection()のように返された参照を保存してconn.close()
を呼び出す必要があります。
代わりにこれを試してください。
conn = dataSource.getConnection();
pstmt = conn.prepareStatement(query);
後でデータベースに接続する必要がなくなったときは、
conn.close();
更新:
Connection conn = dataSource.getConnection();
PreparedStatement ps = pstmt = conn.prepareStatement(query);
try {
// ur code
}
catch (SQLException ex)
{
// Exception handling
}
finally
{
if (rs != null)
{
if (ps != null) {
try {
ps.close();
} catch (SQLException e) { out.print(e)} // This will print exception to help better
}
if (conn != null) {
try {
conn.close();
} catch (SQLException e) { out.print(e)}
}
}