项目实际开发中遇到的事务问题

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/System_out_print_Boy/article/details/79160236

废话不多说 看功能:
最近做了一个app的记步功能,要求是app一打开就开始进行步数的计算,然后一分钟向后台传送一次数据保存到数据库中,此时保存的是用户的走的步数和有效步数(有效步数是在一分钟内步频大于90的算是真正的走路)和有效时间,然后当你打开记步页面的时候,又一个请求一分钟一次的向后天传数据,此时保存的数据是用户走的实际的里程和用户从打开手机到现在累计消耗的能量,先看实体类的设计

package com.kd.food.model;

import com.kd.framework.data.orm.BaseModel;

/**
 * @author 用户记步实体类
 *
 */
public class RecordStep extends BaseModel {

    private static final long serialVersionUID = -5858855168283954031L;
    /**
     * 用户ID
     */
    private String userId;
    private int count;
    /**
     * 今日有效步数
     */
    private int effectiveSteps;
    /**
     * 今日实际步数
     */
    private Integer actualStep;
    /**
     * 今日目标步数
     */
    private Integer targetStep;
    /**
     * 今日实际运动时间
     */
    private Integer actualTime;
    /**
     * 今日目标运动时间
     */
    private Integer targetTime;
    /**
     * 今日实际运动里程
     */
    private String actualMileage;
    /**
     * 今日目标运动里程
     */
    private String targetMileage;
    /**
     * 今日消实际耗能量
     */
    private Integer actualEnergy;
    /**
     * 今日目标能量
     */
    private Integer targetEnergy;

    /**
     * 删除
     */
    private String deleteFlag;


    public String getUserId() {
        return userId;
    }
    public void setUserId(String userId) {
        this.userId = userId;
    }
    public Integer getActualStep() {
        return actualStep;
    }
    public void setActualStep(Integer actualStep) {
        this.actualStep = actualStep;
    }
    public Integer getTargetStep() {
        return targetStep;
    }
    public void setTargetStep(Integer targetStep) {
        this.targetStep = targetStep;
    }
    public Integer getActualTime() {
        return actualTime;
    }
    public void setActualTime(Integer actualTime) {
        this.actualTime = actualTime;
    }
    public Integer getTargetTime() {
        return targetTime;
    }
    public void setTargetTime(Integer targetTime) {
        this.targetTime = targetTime;
    }
    public String getActualMileage() {
        return actualMileage;
    }
    public void setActualMileage(String actualMileage) {
        this.actualMileage = actualMileage;
    }
    public String getTargetMileage() {
        return targetMileage;
    }
    public void setTargetMileage(String targetMileage) {
        this.targetMileage = targetMileage;
    }
    public Integer getActualEnergy() {
        return actualEnergy;
    }
    public void setActualEnergy(Integer actualEnergy) {
        this.actualEnergy = actualEnergy;
    }
    public Integer getTargetEnergy() {
        return targetEnergy;
    }
    public void setTargetEnergy(Integer targetEnergy) {
        this.targetEnergy = targetEnergy;
    }

    public static long getSerialversionuid() {
        return serialVersionUID;
    }
    public String getDeleteFlag() {
        return deleteFlag;
    }
    public void setDeleteFlag(String deleteFlag) {
        this.deleteFlag = deleteFlag;
    }
    public int getCount() {
        return count;
    }
    public void setCount(int count) {
        this.count = count;
    }
    public int getEffectiveSteps() {
        return effectiveSteps;
    }
    public void setEffectiveSteps(int effectiveSteps) {
        this.effectiveSteps = effectiveSteps;
    }


}

controller层 这是app一打开就开始的请求,现在需要保存的数据是实际的步数,有效时间,有效步数:


    /**
     * 记步请求 一分钟请求一次 
     * @return
     */
    @RequestMapping("/saveStep")
    @ResponseBody
    public String saveStep(HttpServletRequest request,HttpServletResponse response){
        try {
            String userId = request.getParameter("id");//用户ID
            String step = request.getParameter("step");//实际的步数
            String time = request.getParameter("effectiveTime");//有效运动时间
            String effectiveSteps = request.getParameter("effectiveSteps");//有效步数

            RecordStep recordStep=new RecordStep();
            recordStep.setUserId(userId);
            if (StringUtil.isEmpty(step)) {
                step="0";
            }
            recordStep.setActualStep(DataConvert.getInt(step));
            if(StringUtil.isEmpty(effectiveSteps)){
                effectiveSteps="0";
            }
            recordStep.setEffectiveSteps(DataConvert.getInt(effectiveSteps));
            if (StringUtil.isEmpty(time)) {
                time="0";
            }
            recordStep.setActualTime(DataConvert.getInt(time));
            recordStep.setCreateBy(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));

            recordStepService.saverecordStep(recordStep);
            JSONObject json=new JSONObject();
            json.put("success", true);
            return json.toJSONString();
        } catch (Exception e) {
            e.printStackTrace();
        }
        return "ok";
    }

这是记步页面打开的时候开始的请求 ,此时需要保存的是用户走的里程和实际消耗的能量

/**
     * 打开记步页面 存储里程和卡路里 (一分钟存储一次实际里程和消耗能量)
     * (上传实时运动数据 里程和能量的消耗)
     * @return
     */
    @RequestMapping("/updateStep")
    @ResponseBody
    public String querySteps(HttpServletRequest request,HttpServletResponse response){
        JSONObject json=new JSONObject();
        try {
            String userId = request.getParameter("id");//用户ID
            String licheng = request.getParameter("sumJourney");//实际里程
            String enenrgy = request.getParameter("sumKcal");//消耗能量
            RecordStep recordStep=new RecordStep();
            recordStep.setUserId(userId);
            //如果传来NaN
            if("NaN".equals(licheng)){
                return json.toJSONString();
            }
            //实际里程为0 则没走 也没消耗能量 直接返回呗
            if (StringUtil.isEmpty(licheng)||"0".equals(licheng)) {
                return json.toJSONString();
            }
            recordStep.setActualMileage(licheng);
            if (StringUtil.isEmpty(enenrgy)) {
                enenrgy="0";
            }
            recordStep.setActualEnergy(DataConvert.getInt(enenrgy));
            recordStep.setCreateBy(new SimpleDateFormat("yyyy-MM-dd").format(new Date()));
            boolean flag = recordStepService.saverecordStep(recordStep);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return json.toJSONString();
    }

这是你会看到 ,两个请求访问的是同一个service,因为表的设计是每天保存用户一一条数据,这一条数据就是他这一天的步数里程等等,所以当你app打开的时候判断当前用户今天有没有走路,有了就插入,没了就更新数据,实时保存嘛
service实现层:

    /**
     * 保存步数(更新and插入操作)
     */
    public boolean saverecordStep(RecordStep recordStep) {

        try {
            baseDao.update("RecordStep.insertStep", recordStep);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

这是mapper文件:有则更新,没有则插入

<!--  如果存在就更新 不存在则插入-->
    <insert id="insertStep" useGeneratedKeys="true" keyProperty="id" parameterType="RecordStep">
        <selectKey keyProperty="count" resultType="int" order="BEFORE">
            select count(*)  as count from tj_record_step where  CREATEBY=#{createBy} and USER_ID=#{userId}
      </selectKey>
      <!--如果不存在创建  -->
          <if test="count==0">
            INSERT INTO 
            tj_record_step
        (
            <include refid="SRecordColumns" />
        )VALUES(
            #{userId},
            #{actualTime},
            #{actualEnergy},
            #{actualMileage},
            #{actualStep},
            #{effectiveSteps},
            NOW(),
            NOW()
        )
        </if>
        <!--如果存在,更新时间  -->
        <if test="count > 0">
            UPDATE tj_record_step SET UPDATEBY=NOW() 
            <if test="actualTime !=null and actualTime !=''">
                ,ACTUAL_TIME=#{actualTime}
            </if>
            <if test="actualEnergy !=null and actualEnergy !=''">
                ,ACTUAL_ENERGY=#{actualEnergy}
            </if>
            <if test="actualMileage !=null and actualMileage !=''">
                ,ACTUAL_MILEAGE=#{actualMileage}
            </if>
            <if test="actualStep !=null and actualStep !=''">
                ,ACTUAL_STEP=#{actualStep}
            </if>
            <if test="effectiveSteps !=null and effectiveSteps !=''">
                ,EFFECTIVE_STEPS=#{effectiveSteps}
            </if>
            WHERE  USER_ID=#{userId} and CREATEBY=#{createBy}
        </if>
    </insert>

当时测试的时候是在浏览器上,没有考虑到并发情况,测试的时候都是单线程情况,没一点问题,当撞到手机上之后,我曹,简直惊呆了。,因为要把用户的每天走路的步数用图表展示起来,然后用户昨天的数据以及前天的数据全部被更新了,然后我赶紧查看数据库,我曹,竟然全部被更新了,而且,同时插入了两条,一脸蒙蔽啊,百思不得其解,,,,然后呢,才想到,他妈并发的情况所以呢想到了用事务,

/**
     * 保存步数(更新and插入操作)
     * Isolation.READ_COMMITTED 隔离级别 防止脏读
     */
    @Transactional(isolation=Isolation.READ_COMMITTED)
    public boolean saverecordStep(RecordStep recordStep) {

        try {
            baseDao.update("RecordStep.insertStep", recordStep);
            return true;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return false;
    }

ok 解决了

猜你喜欢

转载自blog.csdn.net/System_out_print_Boy/article/details/79160236