精华内容
下载资源
问答
  • 楼主使用的 sqlserver 数据库,不支持 insert or uodate,但是楼主需要完成批量的数据操作,从前端拿到数据集合,从数据库拿到已...这里说明一下,由于是因为使用了sqlerver 数据库,数据相同的情况下进行update 会...

    楼主使用的 sqlserver 数据库,不支持 insert or uodate,但是楼主需要完成批量的数据操作,从前端拿到数据集合,从数据库拿到已有的数据集合,进行比对,如果主键相同并且其他字段不同,则进行更新,如果所有字段都相同即忽略,其他情况就都是插入,以下的图是数据示例:

     

    【方法一】

    这里说明一下,由于是因为使用了sqlerver 数据库,数据相同的情况下进行update 会返回1 也就是数据库仍然会进行一次操作。在mysql里则会返回0,所以如果是使用的mysql,就不用考虑数据相同的情况了,直接update即可

    		for (Map data : incomeData) { //遍历从前端拿到的数据
    			boolean bo_insert = false;  //标记是否做插入操作
    			boolean bo_update = false; //标记是否需要更新
    			boolean bo_nothing = false;
    			String staff_id2 = (String)data.get("staff_id");
    			String yearmon2 = (String)data.get("yearmon");
    			BigDecimal ratio2 ;
    			if (data.get("ratio") instanceof Double) {
    				 ratio2 = new BigDecimal((Double)data.get("ratio"));
    			}else{
    				ratio2 = new BigDecimal((Integer)data.get("ratio"));
    			}
    			for (Map map : ratioList) { //遍历从数据库拿到的数据
    				String staff_id = (String)map.get("staff_id");
    				String yearmon = (String)map.get("yearmon");
    				BigDecimal ratio = (BigDecimal)map.get("ratio");
    				//主键相同且其他字段不相同,标记为 更新
    				if (staff_id.equals(staff_id2)&&yearmon.equals(yearmon2)&&ratio2.compareTo(ratio)!=0) {bo_update = true;}
    				//全部字段相同,标记为 忽略
    				if (staff_id.equals(staff_id2)&&yearmon.equals(yearmon2)&&ratio2.compareTo(ratio)==0) {bo_nothing = true;}
    			}
    			//不是更新操作 也不是忽略操作,标记为插入
    			if (!bo_update&&!bo_nothing) {bo_insert = true;}
    			
    			if (bo_insert) {//插入操作
    
    			} 
    			
    			if(bo_update){//修改操作
    
    			}
    		}

     

    【方法二(推荐)】

     

    原本的数据结构(示例):

    [{rdproj_id:1001,yearmon:201801,staff_id:2001,ratio:0.5},{rdproj_id:1001,yearmon:201802,staff_id:2001,ratio:0.8}]

    现在将其改造成以下结构

    {1001:{{201801:{2001:0.5}},{201802:{2001:0.8}}}}

    这样我们就可以通过一层一层的 map.get("")方法来进行比较,比如,我现在想要比较的数据是

    {rdproj_id:1002,yearmon:201801,staff_id:2001,ratio:0.5}

    那么,很明显在第一层 map.get("1002")时就会返回null,那么,这一组数据可以直接判定为insert操作。

    注意:在这个方法里,我没有再去忽略完全相同的记录。将其归为了update

    public class MemoryContainer {
    
            //准备一个内存容器
    	public static Map<String, Map<String, Map<String, Double>>> staffRatioMap = new HashMap<String, Map<String, Map<String, Double>>>();
    	//准备一个将list转为map
    	public static void putStaffRatioMap(List<Map> ratioList){
            for (Map map : ratioList) {
                String project_id = (String) map.get("rdproj_id");
                String ym = (String) map.get("yearmon");
                String staff_id = (String) map.get("staff_id");
                Double ratio ;
    			if (map.get("ratio") instanceof Double) {
    				ratio = (Double)map.get("ratio");
    			}else if (map.get("ratio") instanceof Integer) {
    				int r = (Integer)map.get("ratio");
    				if (r==0) {ratio = 0.0;}else{ratio = 1.0;}
    			}else {
    				BigDecimal r = (BigDecimal)map.get("ratio");
    				ratio = r.doubleValue();
    			}
    			
                Map<String, Map<String, Double>> ymmap;
    			
                Map<String, Double> smap;
    			
                if (CMYUtils.isEmpty(ymmap = MemoryContainer.staffRatioMap.get(project_id))){
                    smap = new HashMap<String, Double>();
                    smap.put(staff_id, ratio);
                    ymmap = new HashMap<String, Map<String, Double>>();
                    ymmap.put(ym, smap);
                    MemoryContainer.staffRatioMap.put(project_id, ymmap);
                } else {
                    if (CMYUtils.isEmpty(smap = ymmap.get(ym))) {
                    	smap = new HashMap<String, Double>();
                    	smap.put(staff_id, ratio);
                        ymmap.put(ym, smap);
                    } else {
                        if(CMYUtils.isEmpty(smap.get(staff_id))){
                            smap.put(staff_id,ratio);
                        }
                    }
                }
            }
    	}
    }
    
    		Map<String, Map<String, Map<String, Double>>> menoryRec = MemoryContainer.staffRatioMap;
    		if (menoryRec.size()==0) {//如果内存中没有,查数据库,并且存入内存
    				List<Map> ratioList = businessMapper.selectStaffRatio(inDto);
    				MemoryContainer.putStaffRatioMap(ratioList);
    				menoryRec = MemoryContainer.staffRatioMap;
    		}
    	   Map<String, Map<String, Double>> rec = menoryRec.get(incomeData.get(0).get("rdproj_id"));
    	   if (CMYUtils.isEmpty(rec)) { //如果第一层比对就为null,做插入操作
    		   MemoryContainer.putStaffRatioMap(incomeData);
    		   for (Map webData : incomeData) {
                         //insert方法
                       }
    	   }else{
    		   for (Map webData : incomeData) { //遍历 前台拿到的数据
    			   boolean bo_insert = true;
    			   String webStaff = (String)webData.get("staff_id");
    			   String webYearMon = (String)webData.get("yearmon");
    			   Double ratioDouble;
    			   if (webData.get("ratio") instanceof Double) {
    				   ratioDouble = (Double)webData.get("ratio");
    			   }else {
    				   if ((Integer)webData.get("ratio")==1) {
    					   ratioDouble = 1.0;
    				   }else{
    					   ratioDouble = 0.0;
    				   }
    			   }
    			   if (CMYUtils.isNotEmpty(rec.get(webYearMon))) {
    				   Map<String, Double> staff = (Map) rec.get(webYearMon);
    				   if (CMYUtils.isNotEmpty(staff.get(webStaff))) {bo_insert = false;}//标记为更新操作
    				   staff.put(webStaff, ratioDouble);//往内存存入数据,有则覆盖(修改),无则添加
    			   }else{
    				   Map<String,Double> sMap = new HashMap<String, Double>();
    				   sMap.put(webStaff, ratioDouble);
    				   rec.put(webYearMon, sMap);//往内存添加数据。
    			   }
    			   
    			   if (bo_insert) {
                                   //insert方法
    			   }else {
                                  //更新方法
    			   }
    		   }
    	   }

    我在做更新或者插入操作的同时,内存容器 staffRatioMap 也在做同样的动作,所以,在下一次调用这个方法时,staffRatioMap里的数据和数据库是同步的,在项目重启前完全不需要再访问数据库了。


     

    展开全文
  • 记一次java实现mysql的批量更新

    千次阅读 2018-05-25 00:24:44
    项目中开发一个任务执行模块以下简称执行模块,由别的模块调用执行模块计算任务的结果,计算完成后需要把结算结果、计算状态、计算耗时等信息更新到Mysql库里,执行模块是来一个任务执行一次更新一次,后来发现这样...

    背景

    项目中开发一个任务执行模块以下简称执行模块,由别的模块调用执行模块计算任务的结果,计算完成后需要把结算结果、计算状态、计算耗时等信息更新到Mysql库里,执行模块是来一个任务执行一次更新一次,后来发现这样需要频繁的与Mysql交互,且需要等待写库结果返回,效率堪忧拖慢其他模块。

    优化

    第一步:用线程池来更新,将更新代码提交到线程池中,由线程池调度入库
    缺点:没有解决与数据库频繁交互的问题。
    第二步:执行模块不管更新结果,只需将更新任务放入一个队列中然后直接返回;用Spring的定时任务注解@Scheduled,指定一个方法,隔一段时间调用一次入库方法;入库的逻辑是,获取队列当前任务个数cnt,循环poll任务然后添加到一个List中,poll够cnt个之后,通过批量更新方法将List更新到数据库。
    缺点:定时执行无法控制队列大小,可能一次会取出很多条任务,也可能会把队列撑得过大。
    第三步:使用阻塞队列放更新任务,用守护线程poll的队列中的任务,当条数等于5000条(此值根据实际情况),则批量更新一次,在poll时设置超时时间为30秒,当超过30秒还是没有取到任务,则也批量把已经渠道的任务更新一次。

    总结:没有三板斧都不好意思写代码!!!

    队列实现

    @Service
    public class JobExecutorDataServiceImpl {
        //定义一个容量为10000的阻塞队列,BlockingQueue线程安全可以多个生产者同时put
        private BlockingQueue<Job> dataQueue = new LinkedBlockingQueue<>(10000);
    
        //put任务的方法,供生产者调用
        public void recordJob(Job job) {
            try {
                dataQueue.put(job);
            } catch (InterruptedException e) {
                LOGGER.info("批量更新Job入队列异常");
                Thread.currentThread().interrupt();
            }
        }
    
        //初始化即调用
        @PostConstruct
        private void batchUpdate() {
            Thread thread = new Thread(() -> {
                LOGGER.info("启动批量更新守护线程,启动时间{}", new Date(System.currentTimeMillis()));
                while (Boolean.TRUE) {
                    Job poll = null;
                    boolean pollTimeOut = false;
                    long startTime;
                    long endTime;
                    try {
                        // poll时设置超时时间为30秒
                        poll = dataQueue.poll(30, TimeUnit.SECONDS);
                    } catch (InterruptedException e) {
                        LOGGER.info("批量更新Job异常");
                        Thread.currentThread().interrupt();
                    }
    
                    if (null != poll) {
                        // poll到任务添加到List中
                        jobs.add(poll);
                    } else {
                        // poll超时,设置超时标志位
                        pollTimeOut = true;
                    }
    
                    // 如果任务List等于5000或poll超时且List中还有任务就批量更新
                    if (jobs.size() == 5000 || (pollTimeOut && !CollectionUtils.isEmpty(jobs))) {
                        startTime = System.currentTimeMillis();
                        jobMapper.batchUpdateByPrimaryKeySelective(jobs);
                        endTime = System.currentTimeMillis();
                        LOGGER.info("Job任务批量更新{}条任务,耗时{}毫秒", jobs.size(), endTime-startTime);
                        jobs.clear();
                    }
                }
            });
    
            thread.setName("job-batchUpdate-deamon");
            // 设置启动的线程为守护线程
            thread.setDaemon(true);
            thread.start();
        }
    }

    生产者方法就不说了,主要说消费者方法:

    1. @PostConstruct作用是在Bean初始化之前执行消费者方法
    2. poll = dataQueue.poll(30, TimeUnit.SECONDS);
      使用阻塞队列的poll方法,设置poll超时时间,当超过时间返回一个null值
    3. jobs.size() == 5000,如果List中值等于5000了就执行批量更新
    4. pollTimeOut && !CollectionUtils.isEmpty(jobs),如果poll超时了,说明当前生产者暂时没有生产任务或不再生产任务,把List中剩余的任务批量更新
    5. thread.setDaemon(true),设置线程为守护线程,直到jvm停了才停止

      Mysql批量更新实现

      <update id="batchUpdateByPrimaryKeySelective" parameterType="java.util.List">
          update abacus_job
          <trim prefix="set" suffixOverrides=",">
              <trim prefix="status =case" suffix="end,">
                  <foreach collection="jobs" item="it" index="index">
                      when job_id=#{it.jobId,jdbcType=BIGINT} then #{it.status,jdbcType=TINYINT}
                  </foreach>
              </trim>
              <trim prefix="result =case" suffix="end,">
                  <foreach collection="jobs" item="it" index="index">
                      when job_id=#{it.jobId,jdbcType=BIGINT} then #{it.result,jdbcType=VARCHAR}
                  </foreach>
              </trim>
          </trim>
          where job_id in
          <foreach collection="jobs" index="index" item="it" separator="," open="(" close=")">
              #{it.jobId,jdbcType=BIGINT}
          </foreach>
      </update>

      实际生成出来的SQL语句像以下格式:

      update abacus_job
       set status =
       case when job_id=1 then 1
               when job_id=2 then 2
               ...
       end,
       set result =
       case when job_id=1 then 'true'
               when job_id=2 then false
               ...
       end
      where job_id in (1, 2...)

      其实mysql批量更新还有一种写法:

      <update id="batchUpdateByPrimaryKeySelective" parameterType="java.util.List">
      <foreach collection="jobs" item="item" index="index" open="" close="" separator=";">
        update abacus_job
        <set>
          <if test="item.status != null">
            status = #{item.status,jdbcType=TINYINT},
          </if>
          <if test="item.result != null">
            result = #{item.result,jdbcType=VARCHAR},
          </if>
        </set>
        where job_id = #{item.jobId,jdbcType=BIGINT}
      </foreach>
      </update>

      这样生成的SQL是这样:

      update abacus_job set status = 1, result = 'true' where job_id = 1;
      update abacus_job set status = 2, result = 'false' where job_id = 2;

      其实第二种方式的运行效率比第一种好,但是由于用了公司的数据连接中间件来做分表,用第二种方式更新的话,公司的中间件不支持,只好使用第一种,具体情况明天测试之后再看。


    最近工作起来很费劲,感觉脑子不好使了,啥都容易忘,也是自己一直没怎么用心学,hold不住了,生活不止眼前的苟且,还有左右和后边的苟且。

    特别鸣谢一直陪伴在身边的陈美美!!!

    展开全文
  • 工作中遇到一个问题:要更新一个数据表。...这时候应该利用update的方法。一次更新多条信息的思路如下: UPDATE table_name SET field_name = CASE other_field WHEN 1 THEN 'value' WHEN 2 THEN '
  • Java实现Mysql数据同步

    千次阅读 2018-11-09 17:01:27
    本篇博客介绍的是Java程序实现Mysql数据同步,要对抽象类有深刻的理解,不然会对代码逻辑很懵懂,不懂得同学可以看我这篇博客回忆一下Java基础知识: Java抽象类 编写同步数据逻辑抽象类代码: 根据主键id...

    应用场景:

    • 离线应用程序数据同步到服务器端
    • 服务器端数据同步到离线应用程序

    同步记录表设计:

    类型不是null主键备注
    idint主键id
    start_idint  被同步表数据,开始id
    end_idint  被同步表数据,结束id
    end_upate_timetimestamp  同步结束时的时间(被同步表最后一条同步数据创建时间)
    sync_typevarchar  同步类型
    create_timetimestamp 创建时间

     

    创建同步记录表sql文件:

    CREATE TABLE `sync_record` (
      `id` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键id',
      `start_id` int(11) DEFAULT NULL COMMENT '被同步表数据,开始id',
      `end_id` int(11) DEFAULT NULL COMMENT '被同步表数据,结束id',
      `end_upate_time` timestamp(4) NULL DEFAULT NULL COMMENT '同步结束时的时间(被同步表最后一条同步数据创建时间)',
      `sync_type` varchar(3) DEFAULT NULL COMMENT '同步类型',
      `create_time` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 COMMENT='同步记录表';

    本篇博客介绍的是Java程序实现Mysql数据同步,要对抽象类有深刻的理解,不然会对代码逻辑很懵懂,不懂得同学可以看我这篇博客回忆一下Java基础知识:

    Java抽象类

    编写同步数据逻辑抽象类代码:

    • 根据主键id同步

     AbstractSyncByIdService.java(抽象类)

    @Service
    public abstract class AbstractSyncByIdService {
    
        private static final Logger logger = LoggerFactory.getLogger(AbstractSyncByIdService.class);
    
        @Autowired
        private SyncDao syncDao;
    
    
        /**
         * 获取同步的上一个id
         *
         * @author HeLiu
         * @date 2018/7/18 11:20
         */
        public Integer queryPreviousId(String syncType) {
            return syncDao.queryPreviousId(syncType);
        }
    
        /**
         * 异常或者结束时,保存或者更新本次的同步记录
         *
         * @author HeLiu
         * @date 2018/7/18 11:39
         */
        protected void saveOrUpdateSyncRecord(Integer startId, Integer endId, String syncType) {
            boolean exsitFlag = syncDao.queryExsitBySyncType(syncType);
            //如果存在该同步类型的 同步记录,则只更新endId ; 不存在,则插入该类型的同步记录
            if (exsitFlag) {
                syncDao.updateEndIdBySyncType(syncType, endId);
            } else {
                syncDao.saveSyncRecord(syncType, startId, endId);
            }
        }
    
        /**
         * 执行同步,同步中的业务逻辑,数据之间的同步先后关系,都在这里编写
         *
         * @author HeLiu
         * @date 2018/7/18 11:36
         */
        public void excuteSync(String syncType, String pcId) {
    
            logger.info(".......start .excuteSync  ..syncType:{}..", EnumSyncType.enumOfByCode(syncType).desc);
    
            // 获取开始id
            Integer previousId = queryPreviousId(syncType);
    
            // 每次都会执行方法,判断是否有需要同步的数据
            Pair<Boolean, Object> resultPair = exsitNeedSync(previousId, pcId);
            while (resultPair.getLeft()) {
                // 设置最已同步的id 为前一个id
                Integer syncEndId = previousId;
                try {
                    // 同步数据,并返回结束时,本次同步数据的id,
                    // 没有异常,则表示同步成功
                    syncEndId = syncData(resultPair.getRight());
                    logger.info(".......同步数据id:{}.. .excuteSync  ..syncType:{}..", syncEndId, syncType);
                    // 同步成功 最新同步成功的id , 则变成上一个id
                    previousId = syncEndId;
    
                    resultPair = exsitNeedSync(previousId, pcId);
                } catch (Exception e) {
                    logger.info(".excuteSync..excetption..previousId:{}...syncType.:{}", previousId, syncType);
                    logger.error("excuteSync..excetption.", e);
    
                } finally {
                    // 保存同步记录,
                    // 每次同步成功一条数据,都需要更新最新已同步的id
                    logger.info("..saveOrUpdateSyncRecord...........");
                    saveOrUpdateSyncRecord(previousId, syncEndId, syncType);
                }
            }
            logger.info(".......end .excuteSync  ..syncType:{}..", EnumSyncType.enumOfByCode(syncType).desc);
    
    
        }
    
        /**
         * 根据同步开始id,同步数据, 返回结束时的id, 不同模块,实现不一样,这里抽象出来
         *
         * @author HeLiu
         * @date 2018/7/18 11:32
         */
        protected abstract Integer syncData(Object data) throws  Exception;
    
        /**
         * 根据同步id ,查询是否有需要同步的数据,true 表示有, false 表示没有
         *
         * @author HeLiu
         * @date 2018/7/18 16:21
         */
        public abstract Pair<Boolean, Object> exsitNeedSync(Integer previousId, String pcId);
    
    
    }
    •  根据创建时间同步

      AbstractSyncByTimeService.java(抽象类)

    @Service
    public abstract class AbstractSyncByTimeService {
    
        private static final Logger logger = LoggerFactory.getLogger(AbstractSyncByTimeService.class);
    
        @Autowired
        private SyncDao syncDao;
    
        /**
         * 获取最后一次的更新时间
         *
         * @author HeLiu
         * @date 2018/7/18 11:20
         */
        public String queryPreviousEndUpdateTime(String syncType) {
            return syncDao.queryPreviousEndUpdateTime(syncType);
        }
    
        /**
         * 异常或者结束时,保存或者更新本次的同步记录
         *
         * @author HeLiu
         * @date 2018/7/18 11:39
         */
        protected void saveOrUpdateSyncRecord(String endUpdateTime, String syncType) {
            boolean exsitFlag = syncDao.queryExsitBySyncType(syncType);
            // 如果存在该同步类型的 同步记录,则只更新同步数据的创建时间; 不存在,则插入该类型的同步记录
            if (exsitFlag) {
                syncDao.updateEndUpdateTimeBySyncType(syncType, endUpdateTime);
            } else {
                syncDao.saveEndUpdateTimeBySyncType(syncType, endUpdateTime);
            }
        }
    
        /**
         * 执行同步,同步中的业务逻辑,数据之间的同步先后关系,都在这里编写
         *
         * @author HeLiu
         * @date 2018/7/18 11:36
         */
        public void excuteSync(String syncType, String pcId) {
    
            logger.info(".......start .excuteSync  ..syncType:{}..", EnumSyncType.enumOfByCode(syncType).desc);
    
            // 获取开始同步时间
            String endUpdateTime = queryPreviousEndUpdateTime(syncType);
    
            // 每次都会执行方法,判断是否有需要同步的数据
            Pair<Boolean, Object> resultPair = exsitNeedSync(endUpdateTime, pcId);
            while (resultPair.getLeft()) {
                // 设置已同步的时间 为前一个时间
                String syncEndUpdateTime = endUpdateTime;
                try {
                    // 同步数据,并返回结束时,本次同步数据的创建时间,
                    // 没有异常,则表示同步成功
                    syncEndUpdateTime = syncData(resultPair.getRight());
                    logger.info(".......同步数据endUpdateTime:{}.. .excuteSync  ..syncType:{}..", syncEndUpdateTime, syncType);
                    // 同步成功 最新同步成功的创建时间 , 则变成上一个创建时间
                    endUpdateTime = syncEndUpdateTime;
    
                    resultPair = exsitNeedSync(endUpdateTime, pcId);
                } catch (Exception e) {
                    logger.info(".excuteSync..excetption..previousId:{}...syncType.:{}", endUpdateTime, EnumSyncType.enumOfByCode(syncType).desc);
                    logger.error("excuteSync..excetption.", e);
    
                } finally {
                    // 保存同步记录,
                    // 每次同步成功一条数据,都需要更新最新已同步的创建时间
                    saveOrUpdateSyncRecord(endUpdateTime, syncType);
                }
            }
            logger.info(".......end .excuteSync  ..syncType:{}..", EnumSyncType.enumOfByCode(syncType).desc);
    
    
        }
    
        /**
         * 根据同步开始时间,同步数据, 返回结束时的时间, 不同模块,实现不一样,这里抽象出来
         *
         * @author HeLiu
         * @date 2018/7/18 11:32
         */
        protected abstract String syncData(Object data) throws  Exception;
    
        /**
         * 根据同步开始时间 ,查询是否有需要同步的数据,true 表示有, false 表示没有
         *
         * @author HeLiu
         * @date 2018/7/18 16:21
         */
        public abstract Pair<Boolean, Object> exsitNeedSync(String endUpdateTime, String pcId);
    
    
    }

     注意:

    • 两者同步逻辑都是一样的一个根据主键id,前提是你的主键id是数字递增类型的不是UUID之类的,另一个根据数据的创建时间,利用时间有先后的原理。这二者同步的区别要区分好。
    • 根据你同步数据设置好区分的类别也就是syncType,例如:人员-'1';视频-'2'......,怎么开心怎么来。
    • 然后编写你自己的同步数据逻辑层一定要继承该类(AbstractSyncByIdService / AbstractSyncByTimeService,重写抽象类里面的方法,自定义你自己的业务代码,因为不同的同步数据,业务的代码不一样。
    • 这两个抽象类一定要仔细看,有详细的注解。
    • 两个抽象方法至关重要,一定要理解这两个方法的用处。

    代码补充: 

     SyncDao.java

    @Repository
    public class SyncDao {
    
        private static final String  name_space = "syncRecord" + SPOT;
    
        @Autowired
        private DaoClient daoClient;
    
    
        /**
         *  根据同步类型,查询出,原数据表中,开始同步的id
         * @date 2018/7/18 14:18
         */
        public Integer queryPreviousId(String syncType){
            String sqlId = name_space + "queryPreviousId";
            Map<String,Object> param = new HashMap<>();
            param.put("syncType", syncType);
            return daoClient.queryForObject(sqlId, param, Integer.class);
        }
    
        /**
         *  判断该种类型的同步信息是否存在
         * @author liuao
         * @date 2018/7/18 15:16
         */
        public boolean queryExsitBySyncType(String syncType){
            String sqlId = name_space + "queryExsitBySyncType";
            Map<String,Object> param = new HashMap<>();
            param.put("syncType", syncType);
            int count =  daoClient.queryForObject(sqlId, param, Integer.class);
            return count > 0 ? true : false ;
        }
    
        /**
         *  根据同步类型更新同步结束时的id
         * @author liuao
         * @date 2018/7/18 15:24
         */
        public int updateEndIdBySyncType(String syncType, Integer endId){
            String sqlId = name_space + "updateEndIdBySyncType";
            Map<String,Object> param = new HashMap<>();
            param.put("syncType", syncType);
            param.put("endId", endId);
            return daoClient.excute(sqlId, param);
        }
    
        /**
         *  根据同步类型更新同步结束时的id
         * @author liuao
         * @date 2018/7/18 15:24
         */
        public int updateEndUpdateTimeBySyncType(String syncType, String endUpdateTime){
            String sqlId = name_space + "updateEndUpdateTimeBySyncType";
            Map<String,Object> param = new HashMap<>();
            param.put("syncType", syncType);
            param.put("endUpdateTime", endUpdateTime);
            return daoClient.excute(sqlId, param);
        }
    
        /**
         *  根据同步类型保存同步结束时的更新时间
         * @author liuao
         * @date 2018/7/18 15:24
         */
        public int saveEndUpdateTimeBySyncType(String syncType, String endUpdateTime){
            String sqlId = name_space + "saveEndUpdateTimeBySyncType";
            Map<String,Object> param = new HashMap<>();
            param.put("syncType", syncType);
            param.put("endUpdateTime", endUpdateTime);
            return daoClient.insertAndGetId(sqlId, param);
        }
    
    
        /**
         *  保存同步记录
         * @date 2018/7/18 15:28
         */
        public int saveSyncRecord(String syncType, Integer startId ,Integer endId){
            String sqlId = name_space + "saveSyncRecord";
            Map<String,Object> param = new HashMap<>();
            param.put("syncType", syncType);
            param.put("startId", startId);
            param.put("endId", endId);
            return daoClient.excute(sqlId, param);
        }
    
        /**
         *  查询出最后一次的更新时间
         * @date 2018/8/2 19:48
         */
        public String queryPreviousEndUpdateTime(String syncType) {
            String sqlId = name_space + "queryPreviousEndUpdateTime";
            Map<String,Object> param = new HashMap<>();
            param.put("syncType", syncType);
            return daoClient.queryForObject(sqlId, param, String.class);
        }
    }

    sql语句:

    <sqltemplate id="queryPreviousId">
            <![CDATA[
                 SELECT
                    IFNULL (MAX(end_id),0) lastId
                FROM SYNC_RECORD
                WHERE SYNC_TYPE = :syncType
    		]]>
        </sqltemplate>
    
    
        <sqltemplate id="queryExsitBySyncType">
            <![CDATA[
                 SELECT
                    count(id)
                FROM SYNC_RECORD
                WHERE SYNC_TYPE = :syncType
    		]]>
        </sqltemplate>
    
        <sqltemplate id="updateEndIdBySyncType">
            <![CDATA[
                UPDATE SYNC_RECORD
                SET
                    END_ID = :endId
                WHERE SYNC_TYPE = :syncType
    		]]>
        </sqltemplate>
    
    
        <sqltemplate id="saveSyncRecord">
            <![CDATA[
                    INSERT INTO SYNC_RECORD
                    SET
                        START_ID = :startId ,
                        END_ID = :endId ,
                        SYNC_TYPE = :syncType
    
    		]]>
        </sqltemplate>
    
        <sqltemplate id="updateEndUpdateTimeBySyncType">
            <![CDATA[
                    update  SYNC_RECORD
                    SET
                        end_upate_time = :endUpdateTime
                        where SYNC_TYPE = :syncType
    
    		]]>
        </sqltemplate>
    
    
        <sqltemplate id="saveEndUpdateTimeBySyncType">
            <![CDATA[
                    INSERT INTO SYNC_RECORD
                    SET
                        END_UPATE_TIME = :endUpdateTime ,
                        SYNC_TYPE = :syncType
    		]]>
        </sqltemplate>
    
        <sqltemplate id="queryPreviousEndUpdateTime">
            <![CDATA[
                 SELECT
                    IFNULL (MAX(end_upate_time),'2018-01-01 00:00:00') lastId
                FROM SYNC_RECORD
                WHERE SYNC_TYPE = :syncType
    		]]>
        </sqltemplate>

    注意:代码是死的人是活的,灵活使用,不要被代码局限了,这个只是提供一下思路,具体怎么使用可以自己根据实际需求改和优化,深刻理解设计思路和对抽象类的一个灵活使用。

    这里的Dao层和sql的写法是jdbctemplate的封装,可以借鉴我的一篇博客——Java基于jdbctemplate数据持久层操作封装

    刚开始肯定有点难理解,耐下心仔细看,欢迎相互讨论——QQ:892715310,WX:Miss5202468。

    展开全文
  • MySQL的for updateJava中的应用

    千次阅读 2019-03-27 17:04:46
    前言 Java Web中经常出现多线程针对同一数据库进行操作的情况,如何避免多个线程同时操作同一... for updateJava代码中的应用。 下述代码中将使用一个简单SpringBoot应用,采用定时任务模拟同时有2个进程修改同一...

    前言

    Java Web中经常出现多线程针对同一数据库进行操作的情况,如何避免多个线程同时操作同一行数据是至关重要的。
    MySQL在这种情景下 可以考虑使用悲观锁,即当前只能有一个线程执行,结束了唤醒了其他线程进行处理。下述描述的是使用MySQL的select... for update在Java代码中的应用。
    下述代码中将使用一个简单SpringBoot应用,采用定时任务模拟同时有2个进程修改同一行数据的情景。

    环境准备

    目标数据库准备

    目标表test的SQL脚本test.sql

    -- 建表语句
    DROP TABLE IF EXISTS `test`;
    CREATE TABLE `test`  (
      `id` int(11) NOT NULL,
      `task_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      `editer` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      `msg` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
      PRIMARY KEY (`id`) USING BTREE
    ) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Compact;
    -- 数据插入
    INSERT INTO `test` VALUES (2, 'sss', NULL, NULL);
    INSERT INTO `test` VALUES (3, 'ddsdd', NULL, NULL);
    INSERT INTO `test` VALUES (4, 'dsdsad', NULL, NULL);
    INSERT INTO `test` VALUES (5, 'dsadsadasdw', NULL, NULL);
    INSERT INTO `test` VALUES (6, 'wdwdwq', NULL, NULL);
    INSERT INTO `test` VALUES (7, '2deqew', NULL, NULL);
    INSERT INTO `test` VALUES (8, '222dads', NULL, NULL);
    

    定时任务配置类QuartzConfig.java

    // 源码
    package com.example.demo;
    
    import org.quartz.*;
    import org.springframework.context.annotation.Bean;
    import org.springframework.context.annotation.Configuration;
    
    /**
     * @author painter
     * @version 1.0
     * @date 2019/3/27
     */
    @Configuration
    public class QuartzConfig {
        @Bean
        public JobDetail teatQuartzDetail(){
            return JobBuilder.newJob(TestQuartz.class).withIdentity("testQuartz").storeDurably().build();
        }
    
        @Bean
        public JobDetail taskQuartzDetail() {
            return JobBuilder.newJob(TaskQuartz.class).withIdentity("taskQuartz").storeDurably().build();
        }
    
        @Bean
        public Trigger testQuartzTrigger(){
            SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
                    //设置时间周期单位秒
                    .withIntervalInSeconds(5)
                    .repeatForever();
            return TriggerBuilder.newTrigger().forJob(teatQuartzDetail())
                    .withIdentity("testQuartz")
                    .withSchedule(scheduleBuilder)
                    .build();
        }
    
        @Bean
        public Trigger taskQuartzTrigger() {
            SimpleScheduleBuilder scheduleBuilder = SimpleScheduleBuilder.simpleSchedule()
                    //设置时间周期单位秒
                    .withIntervalInSeconds(5)
                    .repeatForever();
            return TriggerBuilder.newTrigger().forJob(taskQuartzDetail())
                    .withIdentity("taskQuartz")
                    .withSchedule(scheduleBuilder)
                    .build();
        }
    }
    

    上述代码创建了2个定时任务,一个为taskQuartz,另一个为testQuartz。当应用启动时,两个定时任务回同时触发,每五秒执行一次。

    定时任务类TaskQuartz.java

    // 源码
    package com.example.demo;
    
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.springframework.scheduling.quartz.QuartzJobBean;
    
    import java.sql.*;
    import java.util.Date;
    
    /**
     * @author painter
     * @version 1.0
     * @date 2019/3/27
     */
    public class TaskQuartz extends QuartzJobBean {
        @Override
        protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            String url = "jdbc:mysql://192.168.14.173:11011/ytest";
            String userName = "root";
            String password = "root";
            String sql = "select * from test where msg is null or msg = '' for update ";
            String data = "";
            try {
                // 注册驱动
                Class.forName("com.mysql.jdbc.Driver");
                // 创建连接
                Connection connection = DriverManager.getConnection(url, userName, password);
                // 预编译sql
                PreparedStatement ps = connection.prepareStatement(sql);
                // 关闭自动提交
                connection.setAutoCommit(false);
                ResultSet rs = ps.executeQuery();
                // 修改指定记录
                while (rs.next()) {
                    data = rs.getString("msg");
                    if (data == null || "".equals(data)) {
                        sql = "update test set editer = 'task1', msg = 'task1修改成功' where id = " + rs.getInt("id");
                        ps.addBatch(sql);
                        System.out.println("task1 修改完成:" + rs.getInt("id"));
                    }
                }
                ps.executeBatch();
                connection.commit();
                connection.setAutoCommit(true);
                rs.close();
                ps.close();
                connection.close();
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            }
    
            System.out.println("task1 run " + new Date());
        }
    }
    
    

    定时任务类TestQuartz.java

    // 源码
    package com.example.demo;
    
    import org.quartz.JobExecutionContext;
    import org.quartz.JobExecutionException;
    import org.springframework.scheduling.quartz.QuartzJobBean;
    
    import java.sql.*;
    import java.util.Date;
    
    /**
     * @author painter
     * @version 1.0
     * @date 2019/3/27
     */
    public class TestQuartz extends QuartzJobBean {
        @Override
        protected void executeInternal(JobExecutionContext jobExecutionContext) throws JobExecutionException {
            String url = "jdbc:mysql://192.168.14.173:11011/ytest";
            String userName = "root";
            String password = "root";
            String sql = "select * from test where msg is null or msg = '' for update ";
            String data = "";
            try {
                Class.forName("com.mysql.jdbc.Driver");
                Connection connection = DriverManager.getConnection(url, userName, password);
                PreparedStatement ps = connection.prepareStatement(sql);
                connection.setAutoCommit(false);
                ResultSet rs = ps.executeQuery();
                while (rs.next()) {
                    data = rs.getString("msg");
                    if (data == null || "".equals(data)) {
                        sql = "update test set editer = 'task2', msg = 'task2修改成功' where id = " + rs.getInt("id");
                        ps.addBatch(sql);
                        System.out.println("task2 修改完成:" + rs.getInt("id"));
                    }
                }
                ps.executeBatch();
                connection.commit();
                connection.setAutoCommit(true);
                System.out.println("task2执行查询");
                rs.close();
                ps.close();
                connection.close();
            } catch (ClassNotFoundException | SQLException e) {
                e.printStackTrace();
            }
            System.out.println("task2 run " + new Date());
        }
    }
    

    上述两个类执行了该操作:获取表中msg为空的记录,修改editer和msg列的记录。
    执行结果:
    在这里插入图片描述
    在这里插入图片描述
    当task1修改了查询记录时,test中的记录被row lock,task2需要等待task1的更新完成释放后,才能进行查询。此时数据已经不符合task2的更新条件,因此保证了一行数据不会同时被多个线程所修改。

    展开全文
  • 主要介绍了php+MySQL判断update语句是否执行成功的方法,可以通过mysql_affected_rows方法加以实现,需要的朋友可以参考下
  • java+mysql实现图书管理系统

    千次阅读 2021-04-13 19:27:24
    java图书管理系统
  • 一 开启mysql数据库bin_log 打开数据库my.ini配置文件添加以下代码重启mysql log_bin=mysql-bin binlog-format=ROW server-id=1 在https://github.com/下载Canla deployer部署包,canal是源码包 解压打开conf/...
  • psm = connection.prepareStatement("update account set balance = ? where stuname = ?"); psm.setDouble(1,5000); psm.setString(1,"张三丰"); psm.executeUpdate(); psm.setDouble(1,15000); psm.setString(2,...
  • mysql行级锁 java 实现

    千次阅读 2019-06-27 21:04:05
    mysql行级锁: 数据库引擎: InnerDB,其他的不支持行锁 所在方法要加上事务注解, 必须加上: @Transactional(isolation = Isolation.READ_COMMITTED) 查询sql举例: select * from global_lock where ...
  • MySQL update语句一个“经典”的坑

    千次阅读 多人点赞 2019-10-02 00:33:35
    技术文章第一时间送达! ...问题归纳起来就是:在MySQL里面update一条记录,语法都正确的,但记录并没有被更新… 结论 小结:在一条UPDATE语句中,如果要更新多个字段,字段间不能使用“...
  • Java+Mysql 实现简单的学生管理系统

    千次阅读 多人点赞 2020-03-28 17:43:47
    Java+Mysql 实现简单的学籍管理系统数据库准备Java准备运行调试 数据库准备 这里我使用的可视化数据库软件为 Wampserver64 如有需要可自行安装。 进入数据库新建一个status数据库,在该数据库下新建一个status_info...
  • JAVA+mySQL成绩管理系统

    2013-06-30 20:49:55
    java+mySQL做的成绩管理系统 本程序包括19个类文件,每个类文件只含有一个类,分别是包含main方法的主类 Main,实现登录功能的类 Login, 实现各种功能选择功能的类 Function, 实现数据库连接的类 DataBaseCreate, ...
  • MySQL_(Java)使用JDBC向数据库发起查询请求 传送门  MySQL_(Java)使用JDBC向数据库中插入(insert)数据 ... MySQL_(Java)使用JDBC向数据库中修改(update)数据 传送门  MySQL数据库中的数据,数据库名gar...
  • Java+mysql 汽车租赁系统设计与实现

    千次阅读 热门讨论 2020-12-19 11:30:07
    3.UpdateSupplier() 7)租车模块:调用Add.java的AddRent() 8)还车模块:调用Update.java的UpdateRent()方法 9)管理员操作: 1):按租金升降序显示可租用车辆:调用Show.java的showAdmin_car_can方法 2):按租金升降...
  • java实现mysql中数据导入elasticsearch

    千次阅读 2019-04-18 17:19:50
    import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.ResultSetMetaData; import java.sql.SQLException; import java......
  • MySQL+java: 实现学生成绩管理系统(1.0版本)

    千次阅读 多人点赞 2020-09-23 22:30:51
    3.Java 连接 MySQL 需要驱动包,使用以下方法让JavaMySQL连接起来。 让JavaMySQL连接起来 实现 使用MySQL生成表 包括 学号,姓名,成绩,班级四个字段。 USE students; DROP TABLE IF EXISTS stu_score; CREATE...
  • 本文将利用一个简单的登录评论功能,完整介绍mybatis框架的使用。 实现功能:对数据库的增删改查,多表查询,模糊查询,动态查询
  • Java如何实现Mysql数据库的行锁

    万次阅读 2017-08-24 20:15:39
    场景如下: ...这里如果发生并发问题,那么会造成用户余额和实际交易的不一致,这对公司和... 当需要变更余额时,通过代码在事务中对当前需要更新的记录设置for update行锁,然后开始正常的查询和更新操作  这样,
  • update user f set f.user_identify_label =2 where f.user_id in (122345),这样的语句执行了后,数据没有变化,这样写update语句是不规范的,需要按照规范来写,可以使用以下两种方式,不用别名跟用别名的情况。...
  • javamysql的简单操作——修改数据

    千次阅读 2020-12-31 00:11:20
    javamysql的简单操作——增加数据 javamysql的简单操作——删除数据 下面是数据修改片段的代码 在这里插入代码片 下面是修改数据的完整代码 (为了体现出修改数据的效果,在修改数据前后添加了查询数据的代码)...
  • 学生信息管理系统--(Java+MySQL实现

    万次阅读 多人点赞 2016-01-07 15:02:15
    基于Java swing+MySQL实现学生信息管理系统:主要实现JDBC对学生信息进行增删改查,应付一般课设足矣,分享给大家。(由于篇幅原因,代码未全部列出,如有需要留下邮箱) 1、 开发环境:jdk7+MySQL5+win7 ...
  • Java实现mysql存储过程调用

    万次阅读 2013-02-19 19:42:28
    系统管理员通过执行某一存储过程的权限进行限制,能够实现对相应的数据的访问权限的限制,避免了非授权用户对数据的访问,保证了数据的安全。 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>procedure>mysql 1、语法:...
  • Mysql load data的使用 数据库中,最常见的写入数据方式是通过SQL INSERT来写入,另外就是通过备份文件恢复数据库,这种备份文件在MySQL中是SQL脚本,实际上执行的还是在批量INSERT语句。 在实际中,常常...
  • 利用JAVA+MySQL实现学生选课系统

    万次阅读 多人点赞 2016-09-24 21:37:44
    String sql = " " + "UPDATE login SET stuPassword=" + "'" + newPassword + "' WHERE stuName=" + "'" + name + "'"; PreparedStatement ptmt; try { ptmt = conn.prepareStatement(sql); ptmt....
  • Java+MySQL实现学生信息管理系统

    万次阅读 多人点赞 2018-08-14 11:41:35
    基于Java swing+MySQL实现学生信息管理系统:主要实现JDBC对学生信息进行增删改查,应付一般课设足矣,分享给大家。 源码: https://github.com/ZhuangM/student.git 1、开发环境:jdk7+MySQL5+win7 代码结构...
  • java 操作mysqljava连接mysql数据库并查询数据

    万次阅读 多人点赞 2017-12-24 11:03:55
    java开发不可避免要处理数据库,所以这里写篇用jdbc来连接mysql的文章, 主要内容包括: 1、java连接mysql 2、java查询mysql数据。 3、java插入mysql数据。 4、java更新mysql数据。 5、java删除mysql数据。 6、...
  • MySQL——Java 访问 MySQL 数据库

    万次阅读 多人点赞 2018-06-09 22:24:53
     下载与安装 MySQL Connector/J MySOL 驱动 MySQL Connector/J 下载网站 https://dev.mysql.com/downloads/connector/j/5.1.html Source and Binaries(zip) 用于 Windows 操作系统,Source and Binaries(tar...
  • Java.mysql+jdbc基础实现图书管理系统

    千次阅读 2019-08-30 20:33:54
    准备材料 mysql数据库+接口的jar包(根据自己数据库版本去官网下载) Eclipse开发工具+基本编译运行环境 具体步骤 注册驱动 创建连接 准备sql语句 准备sql执行模板 ...
  • 文章目录前言一、学生管理系统最终目标二、功能实现分析1.账户库、注册账号与找回密码2.录入、更新信息成绩3.注销、修改密码4.其他功能三、具体实现四、源码总结欢迎使用Markdown编辑器新的改变功能快捷键合理的创建...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 221,257
精华内容 88,502
关键字:

java实现mysqlupdate

java 订阅
mysql 订阅