在并发数据库操作领域,处理数据完整性至关重要。 Spring Data 与 JPA(Java Persistence API)集成,提供乐观和悲观锁定机制。
创新互联建站主要从事网站建设、成都网站制作、网页设计、企业做网站、公司建网站等业务。立足成都服务乳源,十多年网站建设经验,价格优惠、服务专业,欢迎来电咨询建站服务:028-86922220
乐观锁: 乐观锁的基本思想是,认为在大多数情况下,数据访问不会导致冲突。因此,乐观锁允许多个事务同时读取和修改相同的数据,而不进行显式的锁定。在提交事务之前,会检查是
否有其他事务对该数据进行了修改。如果没有冲突,则提交成功;如果发现冲突,就需要回滚并重新尝试。
乐观锁通常使用版本号或时间戳来实现。每个数据项都会包含一个表示当前版本的标识符。在读取数据时,会将版本标识符保存下来。在提交更新时,会检查数据的当前版本是否与保存的版本匹配。如果匹配,则更新成功;否则,表示数据已被其他事务修改,需要处理冲突。
乐观锁适用于读操作频率较高、写操作冲突较少的场景。它减少了锁的使用,提高了并发性能,但需要处理冲突和重试的情况。
悲观锁: 悲观锁的基本思想是,在数据访问期间假设会发生冲突,因此在访问数据之前就会对其进行锁定,阻止其他事务对该数据进行修改。
悲观锁使用排他锁(Exclusive Lock)来实现。当一个事务对数据进行修改时,它会请求排他锁,并且其他事务无法获取相同的锁直到该事务释放锁。这样可以确保在任何时候只有一个事务能够修改数据,避免了冲突。
悲观锁适用于写操作频率较高、写操作冲突较多的场景。它确保了数据的一致性和完整性,但可能降低并发性能,因为其他事务需要等待锁的释放。
选择乐观锁还是悲观锁取决于具体的应用场景和并发控制需求。乐观锁适合读多写少、冲突较少的情况,而悲观锁适合写多读少、冲突较多的情况。
@Data
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Version
private int version;
private String name;
private double price;
}
public interface ProductRepository extends JpaRepository {}
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
public void updatePrice(Long id, double newPrice) {
Product product = productRepository.findById(id).orElseThrow();
product.setPrice(newPrice);
productRepository.save(product);
}
}
在上面的示例中,当两个线程同时尝试更新同一产品的价格时,第一个线程将成功更新该产品。但第二个线程将失败,因为版本不匹配,抛出ObjectOptimisticLockingFailureException。
updatePrice方法生成的 SQL如下:
SELECT id, name, price, version FROM product WHERE id = ?
UPDATE product SET name = ?, price = ?, version = ? WHERE id = ? AND version = ?
原理如下:
如果你想确保读取操作是最新的或在读取时阻止其他操作,则需要采用悲观锁定策略,例如 PESSIMISTIC_READ 或 PESSIMISTIC_WRITE。
@Data
@Entity
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
private double price;
}
public interface ProductRepository extends JpaRepository {
@Lock(LockModeType.PESSIMISTIC_WRITE)
Optional findByIdLocked(Long id);
}
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
@Transactional
public void updatePrice(Long id, double newPrice) {
Product product = productRepository
.findByIdLocked(id)
.orElseThrow(EntityNotFoundException::new);
product.setPrice(newPrice);
}
}
@Lock(LockModeType.PESSIMISTIC_WRITE) 注解确保在调用 findByIdLocked 时获得写锁。
此处,@Transactional 注释在调用 updatePrice 时启动新事务。如果该方法成功完成,则事务提交,如果抛出异常,则回滚。
用锁获取:
SELECT id, name, price FROM product WHERE id = ? FOR UPDATE
updatePrice SQL如下:
UPDATE product SET name = ?, price = ? WHERE id = ?
悲观锁提供了一种通过在事务的整个持续时间内获取锁定来防止并发数据访问冲突的方法。此方法在高争用场景中特别有用。然而,必须意识到死锁的可能性以及对系统吞吐量的影响。正确的事务管理(如 @Transactional 所示)可确保操作的原子性。
在 Spring Data JPA 的事务管理和数据一致性方面,我们有两种主要的锁定策略可供使用:
根据特定的用例和性能要求,开发人员可以在悲观锁定和乐观锁定之间进行选择。每种方法都有其独特的优点和挑战。该决定取决于并发数据访问的预期频率以及管理数据一致性所需的严格程度。
本文名称:如何使用SpringDataJPA优雅地实现乐观锁和悲观锁
本文链接:http://www.csdahua.cn/qtweb/news21/517671.html
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网