er实体建模和维度建模_用于扩展乐观锁定的实体建模策略

er实体建模和维度建模

介绍

应用程序级可重复读取适用于防止 Web对话中的更新丢失 。 启用实体级别的乐观锁定非常容易。 您只需要用JPA @Version注释标记一个逻辑时钟属性(通常是一个整数计数器) ,然后Hibernate会处理其余的事情

抓住

乐观锁定将丢弃所有相对于较旧实体版本的传入更改。 但是,一切都是有代价的,乐观的锁定没有任何区别。

乐观并发控制机制甚至对不重叠的更改也采用了全有或全无的方法。 如果两个并发事务正在更改不同的实体属性子集,那么就不会丢失更新。

从同一实体版本开始的两个并发更新总是会发生冲突。 这只是第一个成功的更新,第二个更新失败并带有乐观锁定异常。 这项严格的政策的作用就好像所有变更都是重叠的。 对于高度并发的写入方案,此单版本检查策略可能导致大量回滚更新。

测试时间

假设我们有以下产品实体:

乐观锁定一个生产实体版本

该实体由三个用户(例如Alice,Bob和Vlad)更新,每个用户更新一个不同的属性子集。 下图描述了它们的动作:

乐观锁定一个rootentityoneversion1

SQL DML语句序列如下所示:

#create tables
Query:{[create table product (id bigint not null, description varchar(255) not null, likes integer not null, name varchar(255) not null, price numeric(19,2) not null, quantity bigint not null, version integer not null, primary key (id))][]} 
Query:{[alter table product add constraint UK_jmivyxk9rmgysrmsqw15lqr5b  unique (name)][]} 

#insert product
Query:{[insert into product (description, likes, name, price, quantity, version, id) values (?, ?, ?, ?, ?, ?, ?)][Plasma TV,0,TV,199.99,7,0,1]} 

#Alice selects the product
Query:{[select optimistic0_.id as id1_0_0_, optimistic0_.description as descript2_0_0_, optimistic0_.likes as likes3_0_0_, optimistic0_.name as name4_0_0_, optimistic0_.price as price5_0_0_, optimistic0_.quantity as quantity6_0_0_, optimistic0_.version as version7_0_0_ from product optimistic0_ where optimistic0_.id=?][1]} 
#Bob selects the product
Query:{[select optimistic0_.id as id1_0_0_, optimistic0_.description as descript2_0_0_, optimistic0_.likes as likes3_0_0_, optimistic0_.name as name4_0_0_, optimistic0_.price as price5_0_0_, optimistic0_.quantity as quantity6_0_0_, optimistic0_.version as version7_0_0_ from product optimistic0_ where optimistic0_.id=?][1]} 
#Vlad selects the product
Query:{[select optimistic0_.id as id1_0_0_, optimistic0_.description as descript2_0_0_, optimistic0_.likes as likes3_0_0_, optimistic0_.name as name4_0_0_, optimistic0_.price as price5_0_0_, optimistic0_.quantity as quantity6_0_0_, optimistic0_.version as version7_0_0_ from product optimistic0_ where optimistic0_.id=?][1]} 

#Alice updates the product
Query:{[update product set description=?, likes=?, name=?, price=?, quantity=?, version=? where id=? and version=?][Plasma TV,0,TV,199.99,6,1,1,0]} 

#Bob updates the product
Query:{[update product set description=?, likes=?, name=?, price=?, quantity=?, version=? where id=? and version=?][Plasma TV,1,TV,199.99,7,1,1,0]} 
c.v.h.m.l.c.OptimisticLockingOneRootOneVersionTest - Bob: Optimistic locking failure
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.vladmihalcea.hibernate.masterclass.laboratory.concurrency.OptimisticLockingOneRootOneVersionTest$Product#1]

#Vlad updates the product
Query:{[update product set description=?, likes=?, name=?, price=?, quantity=?, version=? where id=? and version=?][Plasma HDTV,0,TV,199.99,7,1,1,0]} 
c.v.h.m.l.c.OptimisticLockingOneRootOneVersionTest - Vlad: Optimistic locking failure
org.hibernate.StaleObjectStateException: Row was updated or deleted by another transaction (or unsaved-value mapping was incorrect) : [com.vladmihalcea.hibernate.masterclass.laboratory.concurrency.OptimisticLockingOneRootOneVersionTest$Product#1]

因为只有一个实体版本,所以这只是成功的第一笔交易。 由于第二和第三次更新引用了较旧的实体版本,因此将其丢弃。

鸿沟

如果书写模式不止一种,我们可以将原始实体分为几个子实体。 现在,每个子实体都有一个不同的计数器,而不仅仅是一个乐观的锁定计数器。 在我们的例子中, 数量可以移动到ProductStock和喜欢 ProductLiking。

乐观锁定一个生产力多个版本

每当我们更改产品数量时,都只会检查ProductStock版本,因此可以防止其他竞争数量更新。 但是现在,我们可以同时更新主要实体(例如Product)和每个单独的子实体(例如ProductStock和ProductLiking):

乐观锁定一个根实体多个版本

运行先前的测试用例将产生以下输出:

#create tables
Query:{[create table product (id bigint not null, description varchar(255) not null, name varchar(255) not null, price numeric(19,2) not null, version integer not null, primary key (id))][]} 
Query:{[create table product_liking (likes integer not null, product_id bigint not null, primary key (product_id))][]} 
Query:{[create table product_stock (quantity bigint not null, product_id bigint not null, primary key (product_id))][]} 
Query:{[alter table product add constraint UK_jmivyxk9rmgysrmsqw15lqr5b  unique (name)][]} 
Query:{[alter table product_liking add constraint FK_4oiot8iambqw53dwcldltqkco foreign key (product_id) references product][]} 
Query:{[alter table product_stock add constraint FK_hj4kvinsv4h5gi8xi09xbdl46 foreign key (product_id) references product][]} 

#insert product
Query:{[insert into product (description, name, price, version, id) values (?, ?, ?, ?, ?)][Plasma TV,TV,199.99,0,1]} 
Query:{[insert into product_liking (likes, product_id) values (?, ?)][0,1]} 
Query:{[insert into product_stock (quantity, product_id) values (?, ?)][7,1]} 

#Alice selects the product
Query:{[select optimistic0_.id as id1_0_0_, optimistic0_.description as descript2_0_0_, optimistic0_.name as name3_0_0_, optimistic0_.price as price4_0_0_, optimistic0_.version as version5_0_0_ from product optimistic0_ where optimistic0_.id=?][1]} 
Query:{[select optimistic0_.product_id as product_2_1_0_, optimistic0_.likes as likes1_1_0_ from product_liking optimistic0_ where optimistic0_.product_id=?][1]} 
Query:{[select optimistic0_.product_id as product_2_2_0_, optimistic0_.quantity as quantity1_2_0_ from product_stock optimistic0_ where optimistic0_.product_id=?][1]} 

#Bob selects the product
Query:{[select optimistic0_.id as id1_0_0_, optimistic0_.description as descript2_0_0_, optimistic0_.name as name3_0_0_, optimistic0_.price as price4_0_0_, optimistic0_.version as version5_0_0_ from product optimistic0_ where optimistic0_.id=?][1]} 
Query:{[select optimistic0_.product_id as product_2_1_0_, optimistic0_.likes as likes1_1_0_ from product_liking optimistic0_ where optimistic0_.product_id=?][1]} 
Query:{[select optimistic0_.product_id as product_2_2_0_, optimistic0_.quantity as quantity1_2_0_ from product_stock optimistic0_ where optimistic0_.product_id=?][1]} 

#Vlad selects the product
Query:{[select optimistic0_.id as id1_0_0_, optimistic0_.description as descript2_0_0_, optimistic0_.name as name3_0_0_, optimistic0_.price as price4_0_0_, optimistic0_.version as version5_0_0_ from product optimistic0_ where optimistic0_.id=?][1]} 
Query:{[select optimistic0_.product_id as product_2_1_0_, optimistic0_.likes as likes1_1_0_ from product_liking optimistic0_ where optimistic0_.product_id=?][1]} 
Query:{[select optimistic0_.product_id as product_2_2_0_, optimistic0_.quantity as quantity1_2_0_ from product_stock optimistic0_ where optimistic0_.product_id=?][1]} 

#Alice updates the product
Query:{[update product_stock set quantity=? where product_id=?][6,1]} 

#Bob updates the product
Query:{[update product_liking set likes=? where product_id=?][1,1]} 

#Vlad updates the product
Query:{[update product set description=?, name=?, price=?, version=? where id=? and version=?][Plasma HDTV,TV,199.99,1,1,0]}

根据三个不同的写入职责 ,所有三个并发事务均成功,因为我们不再只有一个逻辑时钟版本,而是三个。

结论

在设计持久域模型时,必须同时考虑查询和编写责任模式。

将较大的实体分为几个子实体可以帮助您扩展更新,同时减少乐观锁定失败的机会。 如果您警惕可能的性能问题(由于实体状态碎片),那么您应该知道Hibernate提供了几种优化技术来克服分散的实体信息副作用。

如果您需要所有与实体相关的数据,则始终可以在单个SQL查询中加入所有子实体

二级缓存也是在不访问数据库的情况下获取子实体的良好解决方案。 由于我们将根实体分为几个实体,因此可以更好地利用缓存。 库存更新只会使相关的ProductStock缓存条目无效,而不会干扰Product和ProductLiking缓存区域。

翻译自: https://www.javacodegeeks.com/2014/11/an-entity-modelling-strategy-for-scaling-optimistic-locking.html

er实体建模和维度建模

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值