如何在使用Spring JdbcTemplate发出REST更新请求时验证记录是否存在?

4

我有一个简单的数据库表users,它包含3个列:

| id | username | nationality | 
|  1 |   John   | American    | 
|  2 |   Doe    | English     |

我想通过POST请求向http://mysite/users/2/nationality发布更新。

我的初始方法是执行单个查询 UPDATE users SET nationality="French" WHERE id=2;,然后查询更新的对象 SELECT * FROM users WHERE id=2;,最后在响应中返回更新后的对象。

问题在于请求中传递的id可能不存在于我的数据库中。 我应该如何验证用户是否存在于数据库中?

  1. 我只需检查查询是否返回一个对象吗?
  2. 我应该先验证受影响的行数(如果未对要更新的数据进行更改,则受影响的行数将为零,因此在这种情况下无法抛出UserNotFoundException)?
  3. 在更新之前是否最好先发出查询以检查行是否存在,然后更新,最后查询更新后的行?
  public void updateRecord(Long id, String username) {  
 String updateSql = "UPDATE users SET username = ? WHERE id = ?";  
 JdbcTemplate template = new JdbcTemplate(dataSource);  
   Object[] params = { username, id};  
    int[] types = {Types.VARCHAR, Types.BIGINT};  
    int rows = template.update(updateSql, params, types);  
    System.out.println(rows + " row(s) updated.");  
    }
7个回答

2
  1. 如果您始终需要更新返回响应中的更新对象,那么选项1似乎是一种合理的方式来检查更新是否匹配现有用户。尽管如果您没有使用事务,则应该意识到在更新时可能不存在该用户,但是在选择之前,另一个连接可能会插入该用户。

    话虽如此,如果没有使用事务,则始终存在选择将会从刚刚执行的更新中返回不同状态的对象的可能性。在这种情况下略微更糟,因为从技术上讲,更新应该失败。

  2. 如果您不需要更新将更新后的对象返回给响应,则选项2似乎是更好的解决方案。要使其正常工作,您需要更新返回匹配行数而不是更改行数(因此,如果更新与现有用户匹配,但要更新的字段未更改,则仍会得到非零结果)。

    通常,您需要设置连接属性才能使MySQL正常工作(例如,在PHP的PDO驱动程序中,有MYSQL_ATTR_FOUND_ROWS属性)。但是,我的理解是JDBC中已启用此选项,因此executeUpdate应返回匹配行数。我目前无法确认,但您可以很容易地进行测试。


2

在您的情况下,最好的方法是根据给定的ID运行一个选择查询,以验证数据库中是否存在相应的记录。如果记录存在,则可以继续成功流程并运行上述更新和选择查询。否则,如果记录不存在,则可以继续失败流程(引发异常等)。


2

我建议选择选项3,因为考虑到可重用性和代码的健壮性,除非你担心出于(非常严格且不明显的)性能原因多执行一些查询。

由于你可能会在其他地方重复使用检索用户的代码,所以我建议先检索用户,如果找不到则返回404。然后调用更新方法并检查行数是否正确。最后,调用检索方法获取用户并将其转换为响应体。这种方法简单易懂,可预测,易于测试,而且很可能足够快。同时你还可以重复使用检索方法。


1
我认为最安全的方法是:


SELECT EXISTS(
             SELECT *
             FROM users
             WHERE id =  3 ) as columnCount;

这将返回id为3的行数。然后您可以返回此值并检查columnCount是否为1,如果是,则执行更新语句,否则执行其他操作。

1
我遇到了类似的问题。这是我解决问题的方法:
  1. 每当有新用户时,将id标记为默认数字(例如51002122),其中51002122不是数据库中的id。所以页面显示“/51002122/user”。每当用户的id是51002122时,我会在数据库中插入一个记录。插入后,我使用从数据库中获取的id渲染页面。例如,在插入后,页面将变成“/27/user”。

  2. 对于除51002122之外的所有其他id(例如“/12/user”或“/129/user”),我会在数据库中进行更新,因为我知道该用户存在于数据库中。

不确定这是否是正确的方法,但它起作用了。有人能告诉我更好或正确的方法吗?

1

在找到解决方案之前,需要考虑以下几点:

  1. 这是一个REST API调用,从使用的角度来看需要简单易懂
  2. 服务器端代码还应考虑所选实现方式的性能影响。
  3. API应该健壮。也就是说,无论发生什么情况,请求始终应按设计中构思的流程(正常/异常)进行。

基于这些考虑,我建议采取以下方法。

  1. 在DAO中定义两个不同的方法,分别为updateRecord(Long id, String username)getRecord(Long id)
  2. 将事务属性(@Transaction)标记到这些方法上,如下所示:
    • updateRecord的事务属性标记为REQUIRED
    • getRecord的事务属性标记为NOT_REQUIRED,因为这只是一个纯读取调用。
  3. 请注意,在所有情况下,至少需要进行一次DB调用。
  4. 从控制器中首先调用updateRecord方法。该方法将返回一个整数。
  5. 如果返回值为非零,则调用getRecord从数据库中检索更新后的记录。
  6. 如果返回值为零,则表示用户不存在,无需调用getRecord。应返回适当的错误响应(404 Not Found)给调用客户端。
  7. 使用这种方法,当用户不存在时,您将节省一次数据库调用。

总的来说,这种方法很整洁,不那么混乱,最重要的是简单高效(我们仅在更新调用时限制事务边界)。此外,getRecord 可以独立使用作为另一个 API 来检索记录(无需事务)。


0
我曾经遇到过类似的问题,需要在更新表之前检查id是否存在。我使用了openjpa并编写了verifyUser(id)方法,其中id是我需要检查的内容。OpenJpa findById返回记录。在更新时,它会返回完整的记录,而在添加时,它会返回新的主键,通过该主键添加记录。我不确定hibernate如何工作,但是jpa和hibernate有许多相似之处。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接