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;
}
}
다른 스레드에서 구성원 변수 (Connection, PreparedStatement)에 액세스합니다. writeData()의 메서드 변수를 만들거나 각 스레드가 자신의 클래스 인스턴스를 갖고 있는지 확인하십시오. – Michal
나는 '휘발성'이 당신이 생각하는 이점을 줄 것이라고 생각하지 않는다. (어쨌든'쓰기 '에 대한 동기화가 없다.) preparedstatement가 클래스 수준의 변수 인 이유는 무엇입니까? – kolossus
@Michal (Connection, PreparedStatement) 메소드의 writeData() 메소드 변수는 클래스의 자신의 인스턴스를 각 스레드에 전달할 때 ConnectionPool의 현재 로직과 작동하지 않습니다. 감사! – Anurag