2016-06-13 1 views
1

ExecutorServices에 의해 호출 된 여러 스레드가 MySQL DB에 쓰도록 허용하는 DAO 클래스를 작성했습니다.JDBC와 c3p0을 사용하는 MySQL DB에서 다중 스레드 쓰기

EDIT : JDBC 연결 풀을 만들 때 c3p0을 사용하고 있습니다. , 예컨대을 실행하는 동안 그래서 모든 새로운 스레드가

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

나는 '휘발성'이 당신이 생각하는 이점을 줄 것이라고 생각하지 않는다. (어쨌든'쓰기 '에 대한 동기화가 없다.) 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(); 

에 대한 연결 개체는 여기에 싱글 클래스 만 writeData는 호출 할 때마다()해야하는데 무엇의 구성원 인 새의 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 변수는 인스턴스 구성원이 아니라 로컬이어야합니다.

동기화가 필요하지 않습니다.

관련 문제