我有一个spring应用程序,它从数据库中读取数据并将其发送到系统' X'。我正在使用任务执行器来启动线程,因此有5个线程同时读取数据库中的行。对于每个线程,我需要确保选择唯一的记录。 为实现这一目标,我使用JdbcTemplate和"选择更新"
我已编写代码但在日志中我能够看到2个线程拾取相同的行。我无法弄清楚这个问题的根本原因。 有没有人有建议
try {
List<Map<String, Object>> rows = getJdbcTemplate().queryForList(
SELECT_FOR_UPDATE,
new Object[] {a,b,c,d});
for (Map<String,Object> row : rows) {
Header a = new Header();
a.setMailID(((BigDecimal)row.get("mailID")).intValue());
a.setVersion(((BigDecimal)row.get("version")).intValue());
// some other parameters to get
getJdbcTemplate().update(UPDATE_MSG_STATE_VERSION_N_ORIG_MSG_STAT,
x,
a.getVersion()+1,
y),
a.getMailID(),
a.getVersion());
headers.add(a);
}
}
UPDATE_MSG_STATE_VERSION_N_ORIG_MSG_STAT = update MESSAGE set MSG_STAT_CD = ?, VERSION_NBR = ?, ORIG_MSG_STAT_CD=?, LAST_UPD_TS=SYSTIMESTAMP where MESSAGE.MAIL_ID = ? and VERSION_NBR = ?
String SELECT_FOR_UPDATE = "select m.MAIL_ID mailID, m.VERSION_NBR version, m.MSG_STAT_CD state,"
+ "from message m
+ "and m.MSG_STAT_CD in ('Nerwerw')"
+ " and m.create_ts > (sysdate - ?)"
+ " and mod(mail_id,?) = ?"
+ " and ROWNUM <= ?"
+ " order by mt.MSG_PRIORITY FOR UPDATE";
答案 0 :(得分:1)
您是否正确设置了交易控制?
如果没有,事务只会在更新语句的持续时间内发生,并且将自动提交(您使用的是Oracle,我相信,根据您的语法)。
这意味着,虽然您获得了这些记录的锁定,但它们会立即被释放。
答案 1 :(得分:1)
您需要使用@Repostitory标记和@Transactional标记为您的班级添加注释,以确保同一个调用中的所有操作都在一个事务中处理。
如果它们未在同一事务中处理,则每个 SELECT_FOR_UPDATE 将在不同的事务上发生,因此您的线程查询将不会同步,并且您的select_for_update无关紧要。
答案 2 :(得分:0)
您是否有权修改数据库?如果我正确理解你的问题,我最近遇到了类似的问题并实施了这样的计划:
在数据库中添加一个新列,如“thread_number”或类似的东西。将其设置为某个默认值,如0.给每个线程一个唯一的标识符。然后通过将“thread_number”更新为处理它的线程的标识符来“声明”数据库中的记录。然后其他线程在查询时是否在SQL中包含“where thread_number = 0”时将找不到它。
我知道它有点广泛,但我希望它有所帮助。