使用JDBC和c3p0在MySQL DB上进行多线程写入

时间:2016-06-13 19:37:10

标签: java mysql multithreading jdbc

我编写了一个DAO类,它允许ExecutorServices调用的几个线程写入MySQL DB。

编辑:我正在使用c3p0创建JDBC ConnectionPool。因此,每个新线程都会通过调用

获得新的JDBC Connection
DataBaseManager.getInstance().getConnection()

执行时似乎存在随机并发问题,例如:

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 个答案:

答案 0 :(得分:3)

connectionpreparedStatement变量必须是本地变量,而不是实例成员。

无需同步。

答案 1 :(得分:2)

这里发生了什么?

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()时都会被覆盖?在多线程环境中,存在的代码允许在调用prepareStatement()之前立即由另一个getConnection()调用覆盖连接对象,因为未锁定对writeData()的访问。对于preparedStatement也是如此。将它们移到writeData()方法中。

相关问题