❤ 作者主页:李奕赫揍小邰的博客
❀ 个人介绍:大家好,我是李奕赫!( ̄▽ ̄)~*
记得点赞、收藏、评论⭐️⭐️⭐️
认真学习!!!
初始代码就是根据用户id和接口id进行判断是否为空,之后对总调用次数+1,剩余调用次数-1更新。
public boolean invokeCount(long interfaceInfoId, long userId) {
if (interfaceInfoId <= 0 || userId <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 使用 UpdateWrapper 对象来构建更新条件
UpdateWrapper<UserInterfaceInfo> updateWrapper = new UpdateWrapper<>();
updateWrapper.eq("interfaceInfoId", interfaceInfoId);
updateWrapper.eq("userId", userId);
// leftNum=leftNum-1和totalNum=totalNum+1。意思是将leftNum字段减一,totalNum字段加一。
updateWrapper.setSql("leftNum = leftNum - 1, totalNum = totalNum + 1");
// 最后,调用update方法执行更新操作,并返回更新是否成功的结果
return this.update(updateWrapper);
}
要确保在更新用户接口信息时的原子性、数据一致性和安全性,可以使用数据库事务和行级锁来实现。
@Override
@Transactional // 添加事务注解
public boolean invokeCount(long interfaceInfoId, long userId) {
if (interfaceInfoId <= 0 || userId <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 开启数据库事务
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
TransactionStatus status = transactionManager.getTransaction(def);
try {
// 使用悲观锁来锁定要更新的记录,确保其他事务无法同时修改
UserInterfaceInfo userInterfaceInfo = this.selectForUpdate(interfaceInfoId, userId);
// 判断记录是否存在
if (userInterfaceInfo == null) {
throw new BusinessException(ErrorCode.RECORD_NOT_FOUND);
}
// 更新操作
userInterfaceInfo.setLeftNum(userInterfaceInfo.getLeftNum() - 1);
userInterfaceInfo.setTotalNum(userInterfaceInfo.getTotalNum() + 1);
boolean updateResult = this.updateById(userInterfaceInfo);
// 提交事务
transactionManager.commit(status);
return updateResult;
} catch (Exception e) {
// 发生异常时回滚事务
transactionManager.rollback(status);
throw e;
}
}
private UserInterfaceInfo selectForUpdate(long interfaceInfoId, long userId) {
// 使用forUpdate方法在查询时添加行级锁
QueryWrapper<UserInterfaceInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("interfaceInfoId", interfaceInfoId);
queryWrapper.eq("userId", userId);
queryWrapper.forUpdate(); // 添加forUpdate方法
return this.getOne(queryWrapper);
}
上述修改的关键点在于:
1.添加 @Transactional 注解:在方法上添加 @Transactional 注解,以确保方法的执行在一个数据库事务中。
2.使用事务管理器:根据具体的实际情况,注入适当的事务管理器,并使用它来开启、提交或回滚事务。在示例中,使用了 transactionManager。
3.添加行级锁:通过在查询时使用 forUpdate 方法,可以在查询过程中锁定要更新的记录,确保其他事务无法同时修改。
4.异常处理和事务回滚:在发生异常时,通过捕获异常并调用 transactionManager.rollback(status) 来回滚事务,以确保数据的一致性。
请注意,上述修改仅提供了一种实现思路,具体的实现方式可能因您的业务需求和数据库配置而有所不同。建议在实际应用中进行充分的测试和验证,以确保事务和锁的正确使用,以及数据的一致性和安全性。
在分布式项目中,为了保证数据的一致性和安全性,可以使用分布式锁来控制对用户接口信息的更新操作。
@Override
public boolean invokeCount(long interfaceInfoId, long userId) {
if (interfaceInfoId <= 0 || userId <= 0) {
throw new BusinessException(ErrorCode.PARAMS_ERROR);
}
// 获取分布式锁
boolean lockAcquired = acquireDistributedLock(interfaceInfoId, userId);
if (!lockAcquired) {
throw new BusinessException(ErrorCode.LOCK_ACQUISITION_FAILED);
}
try {
// 使用悲观锁来锁定要更新的记录,确保其他事务无法同时修改
UserInterfaceInfo userInterfaceInfo = this.selectForUpdate(interfaceInfoId, userId);
// 判断记录是否存在
if (userInterfaceInfo == null) {
throw new BusinessException(ErrorCode.RECORD_NOT_FOUND);
}
// 更新操作
userInterfaceInfo.setLeftNum(userInterfaceInfo.getLeftNum() - 1);
userInterfaceInfo.setTotalNum(userInterfaceInfo.getTotalNum() + 1);
boolean updateResult = this.updateById(userInterfaceInfo);
return updateResult;
} finally {
// 释放分布式锁
releaseDistributedLock(interfaceInfoId, userId);
}
}
private boolean acquireDistributedLock(long interfaceInfoId, long userId) {
// 在这里实现获取分布式锁的逻辑,可以使用分布式锁框架(例如Redisson、ZooKeeper等)来实现。
// 获取成功返回true,获取失败返回false。
// 根据实际情况进行实现。
// 示例:
// return distributedLock.acquireLock(interfaceInfoId + "_" + userId);
// 其中,distributedLock是分布式锁框架的实例。
}
private void releaseDistributedLock(long interfaceInfoId, long userId) {
// 在这里实现释放分布式锁的逻辑,与acquireDistributedLock方法对应。
// 根据实际情况进行实现。
// 示例:
// distributedLock.releaseLock(interfaceInfoId + "_" + userId);
// 其中,distributedLock是分布式锁框架的实例。
}
private UserInterfaceInfo selectForUpdate(long interfaceInfoId, long userId) {
// 使用forUpdate方法在查询时添加行级锁
QueryWrapper<UserInterfaceInfo> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("interfaceInfoId", interfaceInfoId);
queryWrapper.eq("userId", userId);
queryWrapper.forUpdate(); // 添加forUpdate方法
return this.getOne(queryWrapper);
}
上述修改的关键点在于:
1.获取分布式锁:在 invokeCount 方法中,通过调用 acquireDistributedLock 方法获取分布式锁,确保只有一个线程能够执行更新操作。
2.释放分布式锁:在 finally 块中,通过调用 releaseDistributedLock 方法释放分布式锁,以便其他线程可以获取锁并执行更新操作。
2.acquireDistributedLock 和 releaseDistributedLock 方法的实现:根据您使用的分布式锁框架(例如Redisson、ZooKeeper等),实现获取和释放分布式锁的具体逻辑。
3.上述修改同样仅是提供了一种实现思路,具体的实现方式可能因使用的分布式锁框架和环境配置而有所不同。建议根据实际情况进行适当的调整和测试,以确保分布式锁的正确使用,以及数据的一致性和安全性。
我们不仅要熟悉锁,事务的理论知识,更要能够把这些理论知识实现到实际开发当中,这样我们才能更加熟练的掌握这个知识。