Concurrency 播放2.0控制器并发访问并删除同一记录

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

在Play2.x应用程序中,我有一个post请求,用于从父表中删除子成员。如果有多个请求具有相同的请求参数,我如何要求Play锁定子列表以避免并发访问和重复删除同一记录?如果发送重复请求的距离非常近,将引发异常,如下所示:

javax.persistence.OptimisticLockException:数据已更改。更新 [0]行sql[从通道_详细信息中删除,其中id=?和 成员id=?和通道信息id=?]绑定[null]

@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不太熟悉,因此我完全不确定它如何处理事务管理)

  • 捕获
    OptimisticLockException
    ,然后在几秒钟后再次尝试删除,如果再次以相同方式失败,则重复3次

@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;
}