Concurrency 播放2.0控制器并发访问并删除同一记录
在Play2.x应用程序中,我有一个post请求,用于从父表中删除子成员。如果有多个请求具有相同的请求参数,我如何要求Play锁定子列表以避免并发访问和重复删除同一记录?如果发送重复请求的距离非常近,将引发异常,如下所示: javax.persistence.OptimisticLockException:数据已更改。更新 [0]行sql[从通道_详细信息中删除,其中id=?和 成员id=?和通道信息id=?]绑定[null]Concurrency 播放2.0控制器并发访问并删除同一记录,concurrency,playframework,playframework-2.0,Concurrency,Playframework,Playframework 2.0,在Play2.x应用程序中,我有一个post请求,用于从父表中删除子成员。如果有多个请求具有相同的请求参数,我如何要求Play锁定子列表以避免并发访问和重复删除同一记录?如果发送重复请求的距离非常近,将引发异常,如下所示: javax.persistence.OptimisticLockException:数据已更改。更新 [0]行sql[从通道_详细信息中删除,其中id=?和 成员id=?和通道信息id=?]绑定[null] @BodyParser.Of(BodyParser.FormUrlE
@BodyParser.Of(BodyParser.FormUrlEncoded.class)
公共静态结果removeMemberFromChannel(){
RequestBody body=request().body();
Map dict=body.asFormUrlEncoded();
最后一个字符串memberId=dict.get(“memberId”)!=null?dict.get(Config.MEMBER_ID_PARAM)[0]:null;
ChannelInfo ChannelInfo=ChannelInfo.getChannelForName(channelName);//已删除方法以节省空间
如果(channelInfo!=null){
channelInfo.removeMemberId(memberId);
channelInfo.save();
}
}
@实体
@表(name=“channel”)
公共类ChannelInfo扩展模型{
@身份证
私人长id;
@约束条件。必需
私有字符串名;
@OneToMany(cascade=CascadeType.ALL,mappedBy=“channelInfo”)
私有集合成员;
私有int membersCount=0;
公共无效removeMemberId(字符串memberId){
迭代器iter=this.getMembers().Iterator();
while(iter.hasNext()){
ChannelDetailMember dMember=iter.next();
if(dMember.getMemberId().equals(memberId)){
dMember.delete();
成员国——;
打破
}
}
}
}
@实体
@表(name=“频道详细信息”)
公共类ChannelDetailMember扩展模型{
@身份证
私人长id;
@约束条件。必需
私有字符串成员ID;
@约束条件。必需
@manytone(cascade=CascadeType.PERSIST)
ChannelInfo ChannelInfo;
}
我不确定Play本身是否有防止类似情况发生的机制,抛出的异常已经是锁定策略的一种形式。我认为主要的问题是,在获取数据和删除条目之间,数据有时间进行更改,因此它会执行以下操作之一:
- 以不同的方式实现删除方法,以便查找和删除成员一次完成(免责声明:我对EBean不太熟悉,因此我完全不确定它如何处理事务管理)
- 捕获
,然后在几秒钟后再次尝试删除,如果再次以相同方式失败,则重复3次OptimisticLockException
@BodyParser.Of(BodyParser.FormUrlEncoded.class)
public static Result removeMemberFromChannel() {
RequestBody body = request().body();
Map<String, String[]> dict = body.asFormUrlEncoded();
final String memberId = dict.get("memberId") != null ? dict.get(Config.MEMBER_ID_PARAM)[0] : null;
ChannelInfo channelInfo = ChannelInfo.getChannelForName(channelName); //method was removed to save space
if (channelInfo != null) {
channelInfo.removeMemberId(memberId);
channelInfo.save();
}
}
@Entity
@Table(name="channel")
public class ChannelInfo extends Model {
@Id
private Long id;
@Constraints.Required
private String channelName;
@OneToMany(cascade=CascadeType.ALL, mappedBy="channelInfo")
private Set<ChannelDetailMember> members;
private int membersCount = 0;
public void removeMemberId(String memberId) {
Iterator<ChannelDetailMember> iter = this.getMembers().iterator();
while (iter.hasNext()) {
ChannelDetailMember dMember = iter.next();
if (dMember.getMemberId().equals(memberId)) {
dMember.delete();
membersCount--;
break;
}
}
}
}
@Entity
@Table(name="channel_detail")
public class ChannelDetailMember extends Model {
@Id
private Long id;
@Constraints.Required
private String memberId;
@Constraints.Required
@ManyToOne(cascade=CascadeType.PERSIST)
ChannelInfo channelInfo;
}