2016-06-13 14 views
1

ExecutorServicesによって呼び出されるいくつかのスレッドがMySQL DBに書き込むことを許可するDAOクラスを作成しました。JDBCとc3p0を使用したMySQL DBのマルチスレッド書き込み

EDIT:私はc3p0を使用してJDBC ConnectionPoolを作成しています。だから、すべての新しいスレッドが

DataBaseManager.getInstance().getConnection() 

を呼び出して新しいJDBC Connectionを取得します例えば、実行中にランダム同時実行の問題があるようです:

java.sql.SQLException: No value specified for parameter 1 
at com.eanurag.dao.DataBaseManager.writeData(DataBaseManager.java:102) 

私はコードですべての問題を理解することはできませんよ。私はちょうどwriteData()全体を同期させるべきですか?

public class DataBaseManager { 

    private final static Logger logger = Logger.getLogger(DataBaseManager.class); 

    private static volatile DataBaseManager dbInstance = null; 

    private DataBaseManager() { 
     cpds = new ComboPooledDataSource(); 
     try { 
      cpds.setDriverClass("com.mysql.jdbc.Driver"); 
     } catch (PropertyVetoException e) { 
      logger.error("Error in Initializing DB Driver class", e); 
     } 
     cpds.setJdbcUrl("jdbc:mysql://" + DB_HOST + "/" + DB_NAME); 
     cpds.setUser(DB_USER); 
     cpds.setPassword(DB_PASS); 

     cpds.setMinPoolSize(MINIMUM_POOL_SIZE); 
     cpds.setAcquireIncrement(INCREMENT_SIZE); 
     cpds.setMaxPoolSize(MAXIMUM_POOL_SIZE); 
     cpds.setMaxStatements(MAX_STATEMENTS); 
    } 

    public static DataBaseManager getInstance() { 
     if (dbInstance == null) { 
      synchronized (WorkerManager.class) { 
       if (dbInstance == null) { 
        dbInstance = new DataBaseManager(); 
       } 
      } 
     } 

     return dbInstance; 
    } 

    private ComboPooledDataSource cpds; 

    private static final Integer MINIMUM_POOL_SIZE = 10; 
    private static final Integer MAXIMUM_POOL_SIZE = 1000; 
    private static final Integer INCREMENT_SIZE = 5; 
    private static final Integer MAX_STATEMENTS = 200; 

    private volatile Connection connection = null; 
    private volatile Statement statement = null; 
    private volatile PreparedStatement preparedStatement = null; 

    private static final String DB_HOST = "localhost"; 
    private static final String DB_PORT = "3306"; 
    private static final String DB_USER = "root"; 
    private static final String DB_PASS = ""; 
    private static final String DB_NAME = "crawly"; 
    private static final String URL_TABLE = "url"; 


    public Connection getConnection() throws SQLException { 
     logger.info("Creating connection to DB!"); 
     return this.cpds.getConnection(); 
    } 

    public Boolean writeData(URL url) { 
     StringBuffer writeDBStatement = new StringBuffer(); 
     writeDBStatement.append("insert into"); 
     writeDBStatement.append(" "); 
     writeDBStatement.append(DB_NAME); 
     writeDBStatement.append("."); 
     writeDBStatement.append(URL_TABLE); 
     writeDBStatement.append(" "); 
     writeDBStatement.append("values (?,?,default)"); 

     Boolean dbWriteResult = false; 

     try { 
      connection = DataBaseManager.getInstance().getConnection(); 

       preparedStatement = connection.prepareStatement(writeDBStatement.toString()); 
       preparedStatement.setString(1, url.getURL()); 
       preparedStatement.setString(2, String.valueOf(url.hashCode())); 
       dbWriteResult = (preparedStatement.executeUpdate() == 1) ? true : false; 


      if(dbWriteResult){ 
       logger.info("Successfully written to DB!"); 
      } 
     } catch (SQLException e) { 
      logger.error("Error in writing to DB", e); 
     } finally { 
      try { 
       preparedStatement.close(); 
       connection.close(); 
      } catch (SQLException e) { 
       e.printStackTrace(); 
      } 
     } 
     return dbWriteResult; 
    } 


} 
+2

異なるスレッドからメンバ変数(Connection、PreparedStatement)にアクセスします。 writeData()のメソッド変数を作成するか、各スレッドがクラスのインスタンスを所有していることを確認してください。 – Michal

+1

私は 'volatile'はあなたがそれがそこにあると思う利益を与えることになると思いません。なぜ、preparedstatementはクラスレベルの変数ですか? – kolossus

+0

@Michal(Connection、PreparedStatement)メソッドの変数writeData()は、各スレッドにクラスの独自のインスタンスを渡すので、ConnectionPoolの現在のロジックでは機能しません。ありがとう! – Anurag

答えて

1

ここで何が起こっているか

public Connection getConnection() throws SQLException { 
    logger.info("Creating connection to DB!"); 
    return this.cpds.getConnection(); 
} 

つまり、cpds.getConnection()は何をしますか?お問い合わせの際:

connection = DataBaseManager.getInstance().getConnection(); 

あなたの接続オブジェクトは、ここではシングルトンクラスが、書き込みデータへのすべての呼び出し()することになっているもののメンバーで新しいのgetConnection()の呼び出しでそれを上書きします。 getConnection()コールスレッドも安全ではありませんか?

また、接続オブジェクトがクラスメンバーとして宣言され、writeData()が呼び出されるたびに上書きされるのはなぜですか?マルチスレッド環境では、writeData()へのアクセスがロックされていないため、prepareStatement()が呼び出される直前に別のgetConnection()コールによって接続オブジェクトを上書きすることができます。 preparedStatementと同じです。それらをwriteData()メソッドに移動します。

+0

だから 'cpds​​'は' ComboPooledDataSource'です。これは、c3p0を使用してJDBC接続プールを作成する方法です。したがって、ここに: 'connection = DataBaseManager.getInstance()。getConnection();'は常にデータベースへの新しい接続を返します。 – Anurag

+0

なぜ接続オブジェクトはクラスメンバーとして宣言され、writeData()が呼び出されるたびに上書きされますか?マルチスレッド環境では、writeData()へのアクセスがロックされていないため、prepareStatement()が呼び出される直前に別のgetConnection()コールによって接続オブジェクトを上書きすることができます。 preparedStatementと同じです。 – AWT

+0

もう少し説明していただけますか?どのようにConnectionオブジェクトをここで問題を解決する変数にするのですか?複数のスレッドが同時にアクセスします。 – Anurag

2

connectionpreparedStatementの変数は、インスタンスメンバーではなくローカルである必要があります。

同期は必要ありません。

関連する問題