【发布时间】:2026-01-11 12:35:02
【问题描述】:
我正在编写 Java API,并且正在使用 MySQL。线程安全读写数据库的最佳实践是什么?
例如,采用以下createUser 方法:
// Create a user
// If the account does not have any users, make this
// user the primary account user
public void createUser(int accountId) {
String sql =
"INSERT INTO users " +
"(user_id, is_primary) " +
"VALUES " +
"(0, ?) ";
try (
Connection conn = JDBCUtility.getConnection();
PreparedStatement ps = conn.prepareStatement(sql)) {
int numberOfAccountsUsers = getNumberOfAccountsUsers(conn, accountId);
if (numberOfAccountsUsers > 0) {
ps.setString(1, null);
} else {
ps.setString(1, "y");
}
ps.executeUpdate();
} catch (SQLException e) {
e.printStackTrace();
}
}
// Get the number of users in the specified account
public int getNumberOfAccountsUsers(Connection conn, int accountId) throws SQLException {
String sql =
"SELECT COUNT(*) AS count " +
"FROM users ";
try (
PreparedStatement ps = conn.prepareStatement(sql);
ResultSet rs = ps.executeQuery()) {
return rs.getInt("count");
}
}
假设源 A 调用 createUser,并且刚刚读取到帐户 ID 100 有 0 个用户。然后,源 B 调用createUser,在源 A 执行更新之前,源 B 也读取到帐户 ID 有 0 个用户。因此,来源 A 和来源 B 都创建了主要用户。
这种情况如何安全实施?
【问题讨论】:
-
所以这个问题其实和线程安全无关。这是你的实际问题还是你只是用它来说明你的问题?另外,您使用的是什么数据库系统(MySQL、SQL Server 等)?
-
他/她提到了 MySQL
-
顺便问一下,您在第二种方法中如何使用
accountId? -
为什么不将用户的
is_primary字段设为可派生。例如,对于特定帐户具有最早创建时间的任何用户都可以是主要用户。否则你很可能需要有表级锁。 -
您的代码允许使用
user_id1 和is_primary标志NULL创建许多用户。您是要插入numberOfAccountsUsers + 1作为user_id吗?我认为这不会是基础表上典型的唯一键约束和连接上合适的事务设置的问题。
标签: java mysql multithreading jdbc