java - 如何在 Java 线程中将记录插入数据库?

标签 java multithreading hsqldb

我正在尝试处理 Java 中的多线程。

我已经阅读了很多文章和问题(在 StackOverflow 上),但没有找到任何明确的示例来说明如何使用它。

我在 HsqlDB 数据库中有 Unique_Numbers 表。有 2 列:NUMBER 和 QTY。 我的任务是检查号码是否存在,如果存在则增加号码的数量,如果不存在则插入此号码。

那么,我得到了什么。

这是我的数据库配置

private final ComboPooledDataSource dataSource;

public Database(String url, String userName, String password) throws PropertyVetoException {
    dataSource = new ComboPooledDataSource();
    dataSource.setDriverClass("org.hsqldb.jdbcDriver");
    dataSource.setJdbcUrl(url);
    dataSource.setUser(userName);
    dataSource.setPassword(password);
    dataSource.setMaxPoolSize(10);
    dataSource.setMaxStatements(180);
    dataSource.setMinPoolSize(5);
    dataSource.setAcquireIncrement(5);
}

这是我的逻辑:

public void insertRow(String number) throws SQLException {
    int cnt = getCount(number);
    if (cnt == 0) {
        insert(number);
    } else if (cnt > 0) {
        update(number);
    }
}

获取表格中的数字个数

private int getCount(String number) {
        int cnt = 0;
        String sql = "select count(number) as cnt from \"PUBLIC\".UNIQUE_NUMBER where number='" + number + "'";
        try {
            Statement sta;
            try (Connection connection = dataSource.getConnection()) {
                sta = connection.createStatement();
                ResultSet rs = sta.executeQuery(sql);
                if (rs.next()) {
                    cnt = rs.getInt("cnt");
                }
            }
            sta.close();
        } catch (Exception e) {
            LOGGER.error("error select cnt by number" + e.toString());
        }

        return cnt;
    }

插入和更新

private boolean insert(String number) throws SQLException {
    String sql = "insert into \"PUBLIC\".UNIQUE_NUMBER (number, qty) values(?, ?)";
    try (Connection connection = dataSource.getConnection()) {
        connection.setAutoCommit(false);
        try (PreparedStatement ps = connection.prepareStatement(sql)) {
            ps.setString(1, number);
            ps.setInt(2, 0);
            ps.addBatch();
            ps.executeBatch();
            try {
                connection.commit();
            } catch (Exception e) {
                connection.rollback();
                LOGGER.error(e.toString());
                return false;
            }
        }
    }
    return true;
}

private boolean update(String number) throws SQLException {
    String sql = "update \"PUBLIC\".UNIQUE_NUMBER set (qty) = (?) where number = ?";
    int qty = selectQtyByNumber(number) + 1;
    try (Connection connection = dataSource.getConnection()) {
        connection.setAutoCommit(false);
        try (PreparedStatement ps = connection.prepareStatement(sql)) {
            ps.setInt(1, qty);
            ps.setString(2, number);
            ps.executeUpdate();
            try {
                connection.commit();
            } catch (Exception e) {
                connection.rollback();
                LOGGER.error(e.toString());
                return false;
            }
        }
    }
    return true;
}

在我阅读时,我必须使用 Pool Connection。为每个线程提供一个连接很重要。 当我启动我的应用程序时,我遇到约束异常或回滚异常:序列化失败。

我做错了什么?

这是我的日志

[INFO] [generate38] ERROR se.homework.hwbs.tasks.un.server.threads.InsertRowThread - exception while inserting numberintegrity constraint violation: check constraint; SYS_CT_10114 table: UNIQUE_NUMBER

[INFO] [generate38] ERROR se.homework.hwbs.tasks.un.server.database.Database - error select cnt by number java.sql.SQLTransactionRollbackException: transaction rollback: serialization failure
[INFO] [generate38] ERROR se.homework.hwbs.tasks.un.server.threads.InsertRowThread - exception while inserting numbertransaction rollback: serialization failure

[INFO] [generate38] ERROR se.homework.hwbs.tasks.un.server.database.Database - error select cnt by number java.sql.SQLTransactionRollbackException: transactionrollback: serialization failure

最佳答案

非事务方式

先做增量

update UNIQUE_NUMBER set qty = qty + 1 where number = ?

检查它是否确实更新了任何行,如果没有则插入数字

int rowsMatched = ps.executeUpdate();
if(rowsMatched == 0) {
    try {
        insert into UNIQUE_NUMBER (number, qty) values(?, 0)
    } catch(Exception e) {
        // the insert will fail if another thread has already
        // inserted the same number. check if that's the case
        // and if so, increment instead.
        if(isCauseUniqueConstraint(e)) {
            update UNIQUE_NUMBER set qty = qty + 1 where number = ?
        } else {throw e;}
    }
}

不需要事务处理(setAutoCommit(false)commit()rollback())。

交易方式

如果您仍想以事务方式执行此操作,则需要在单个事务中执行所有步骤,如@EJP 建议的那样:

connection.setAutoCommit(false);
// check if number exists
// increment if it does
// insert if it doesn't
// commit, rollback & repeat in case of error
connection.setAutoCommit(true);

如果此代码与其他代码共享连接池(因为这是其他人期望连接所处的默认状态)或明确连接池中的连接将始终处于事务模式,则将自动提交设置回真。

在您的代码中,getCount 有时会在自动提交模式(首次使用)下获得连接,有时会在事务模式下获得连接(在 insert 和/或之后重新使用) update) - 这就是您在 getCount 中看到回滚异常的原因。

关于java - 如何在 Java 线程中将记录插入数据库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34149872/

相关文章:

c# - JoinableTaskFactory.RunAsync 的正确用法是什么?

Python:线程管理其他线程通知的事件

ios - 核心数据卡住所有应用程序

java - 由 : org. hsqldb.HsqlException 引起:无效的 statemnet - 导入 CSV 数据时需要文本表

spring - @Transactional on Spring shutdown 正确关闭 Hsqldb

java - Spring MVC中如何过滤请求参数?

java - 带变量但不带表达式的 If 语句

java - 输入、数组和 NextLine

java - 在 Spring Web 服务框架中流式传输 MTOM Web 服务

java - 在 MyBatis 中添加一个项目列表,在 SQL 列中共享一个值