我寫了一個DAO類,它允許ExecutorServices
調用的幾個線程寫入MySQL DB。使用JDBC和c3p0在MySQL DB上多線程寫入
編輯:我正在使用c3p0來創建一個JDBC連接池。因此,每一個新的線程將通過調用
DataBaseManager.getInstance().getConnection()
得到一個新的JDBC Connection
似乎是隨機的併發性問題,同時執行,e.g:
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
我不認爲'volatile'會給你帶來的好處,你認爲它會在那裏(無論如何不是沒有'write'上的同步)。爲什麼預備陳述是一個類級變量? – kolossus
@ writeData()的Micrill(Connection,PreparedStatement)方法變量將類的自身實例傳遞給每個線程將無法使用ConnectionPool的當前邏輯。謝謝! – Anurag