Java程序读写性能提升和数据一致性的总结

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: Java程序读写性能提升和数据一致性的总结

读数据

Openresty(lua+nginx)-Guava-Redis做多级缓存

  1. 开辟一块内存空间
    在Nginx配置文件conf/nginx.conf中开启了一个内存大小为128M的内存空间,用来存储缓存数据;
  2. 定义内存字典业务实现lua脚本memory_shared_dic_java.lua(添加缓存,获取缓存)
---
--- 基于lua+Openresty实现内存字典,直接访问redis缓存
--- 引入Redis lua库文件
local red = require "resty.redis"
--- 调用new方法,获取redis对象
local redis = red:new()
--
local password = "123456";
-- 设置一下redis密码
redis:auth(password)
redis:select(0)
--- 1、实现缓存的添加操作
function set_to_cache(key,value,expr)
    if not expr then
        expr = 0
    end
    --- 获取本地内存字典对象
    local ngx_cache = ngx.shared.ngx_cache
    --- 向内存字典中添加缓存数据
    local succ,err,forcible = ngx_cache:set(key,value,expr)
    return succ
end
--- 2、从内存字典中获取缓存数据
function get_from_cache(key)
    --- 获取本地内存字典对象
    local ngx_cache = ngx.shared.ngx_cache
    --- 从内存字典中获取数据缓存
    local res = ngx_cache:get(key)
    -- 如果内存字典缓存不存在
    if not res then
        -- 查询redis缓存数据
        local rev,err = get_from_redis(key)
        if not rev then
            ngx.say("redis cache not exists",err)
            return
        end
        -- 添加到本地内存字典
        set_to_cache(key,rev,60)
    end
    return res
end
--- 向redis添加缓存数据
function set_to_redis(key,value)
    -- 设置连接Redis的超时时间
    redis:set_timeout(10000)
    -- 连接redis服务器
    local ok,err = redis:connect("172.17.61.90",6379)
    -- 判断连接redis服务是否成功
    if not ok then
        ngx.say("failed to connect:",err)
        return
    end
    -- 如果连接成功,向redis添加缓存数据
    local succ,err = redis:set(key,value)
    if not succ then
        ngx.say("failed set redis:",err)
        return
    end
    return succ
end
-- 从Redis中获取缓存数据
function get_from_redis(key)
    -- 设置连接Redis的超时时间
    redis:set_timeout(10000)
    -- 连接redis服务器
    local ok,err = redis:connect("172.17.61.90",6379)
    -- 判断连接redis服务是否成功
    if not ok then
        ngx.say("failed to connect:",err)
        return
    end
    -- 从Redis中获取缓存数据
    local succ,err = redis:get(key)
    if not succ then
        ngx.say("failed get redis:",err)
        return
    end
    ngx.say("get cache from redis......")
    return succ
end
--- 3、内存字典缓存响应业务实现
--- 获取请求参数
local params = ngx.req.get_uri_args()
--- 获取参数属性值
local  id = params.id
--- 从内存字典中获取数据缓存
local goods = get_from_cache("seckill_goods_"..id)
--- 判断内存字典中是否存在缓存数据,如果不存在,将会去查询后端服务数据
if goods == nil then
    -- 从后端服务查询数据
    local  result = ngx.location.capture("/seckill/goods/detail/"..id)
    goods = result.body
    -- 向内存字典中添加缓存数据
    set_to_cache("seckill_goods_"..id,goods,60)
end
-- 输出结果
ngx.say(goods)

这个lua脚本写好后,上传到Nginx/conf同级目录lua目录下,并且在nginx.conf文件中配置,然后sbin/nginx -s reload生效:

3. java中代码

添加guava依赖

<dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>18.0</version>
        </dependency>
@Autowired
      private SeckillGoodsMapper seckillGoodsMapper;
      //注入redistemplate对象
      @Autowired
      private RedisTemplate redisTemplate;
      //注入guva缓存对象
      @Autowired
      private Cache<String,Object> guavaCahce; 
  public TbSeckillGoods findOneByCache(Integer id){
            //1、先从jvm堆缓存中读取数据,使用guva缓存
            TbSeckillGoods seckillGoods = (TbSeckillGoods) guavaCahce.getIfPresent("seckill_goods_"+id);
            //判断jvm堆内缓存是否存在
            if(seckillGoods == null){
                  //2、从分布式缓存中查询
                  seckillGoods = (TbSeckillGoods) redisTemplate.opsForValue().get("seckill_goods_"+id);
                  //判断
                  if(seckillGoods == null){
                        //3、直接从数据库查询
                        seckillGoods = seckillGoodsMapper.selectByPrimaryKey(id);
                        if(seckillGoods != null && seckillGoods.getStatus() == 1){
                              //添加缓存
                              redisTemplate.opsForValue().set("seckill_goods_"+id,seckillGoods,1,TimeUnit.HOURS);
                        }
                  }
                  //添加guava缓存
                  guavaCahce.put("seckill_goods_"+id,seckillGoods);
           }
            //如果缓存存在,返回Redis缓存
            return seckillGoods;
            
      }

写数据

完整代码地址:https://gitee.com/zhushangling/miaosha

controller

@Controller("order")
@RequestMapping("/order")
@CrossOrigin(origins = {"*"},allowCredentials = "true")
public class OrderController extends BaseController {
    @Autowired
    private OrderService orderService;
    @Autowired
    private HttpServletRequest httpServletRequest;
    @Autowired
    private RedisTemplate redisTemplate;
    @Autowired
    private MqProducer mqProducer;
    @Autowired
    private ItemService itemService;
    @Autowired
    private PromoService promoService;
    private ExecutorService executorService;
    private RateLimiter orderCreateRateLimiter;
    @PostConstruct
    public void init(){
//        executorService = Executors.newFixedThreadPool(20);
        executorService = new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),
                100, 100,TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(2000), new ThreadFactory() {
            @Override
            public Thread newThread(Runnable r) {
                Thread thread = new Thread(r);
                thread.setName("client-transaction-msg-check-thread");
                return thread;
            }
        });
        orderCreateRateLimiter = RateLimiter.create(300.0);
    }
    //生成验证码
    @RequestMapping(value = "/generateverifycode",method = {RequestMethod.GET,RequestMethod.POST})
    @ResponseBody
    public void generateverifycode(HttpServletResponse response) throws BusinessException, IOException {
        String token = httpServletRequest.getParameterMap().get("token")[0];
        if(StringUtils.isEmpty(token)){
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登陆,不能生成验证码");
        }
        UserModel userModel = (UserModel) redisTemplate.opsForValue().get(token);
        if(userModel == null){
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登陆,不能生成验证码");
        }
        Map<String,Object> map = CodeUtil.generateCodeAndPic();
        redisTemplate.opsForValue().set("verify_code_"+userModel.getId(),map.get("code"));
        redisTemplate.expire("verify_code_"+userModel.getId(),10,TimeUnit.MINUTES);
        ImageIO.write((RenderedImage) map.get("codePic"), "jpeg", response.getOutputStream());
    }
    //生成秒杀令牌
    @RequestMapping(value = "/generatetoken",method = {RequestMethod.POST},consumes={CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType generatetoken(@RequestParam(name="itemId")Integer itemId,
                                        @RequestParam(name="promoId")Integer promoId,
                                          @RequestParam(name="verifyCode")String verifyCode) throws BusinessException {
        //根据token获取用户信息
        String token = httpServletRequest.getParameterMap().get("token")[0];
        if(StringUtils.isEmpty(token)){
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登陆,不能下单");
        }
        //获取用户的登陆信息
        UserModel userModel = (UserModel) redisTemplate.opsForValue().get(token);
        if(userModel == null){
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登陆,不能下单");
        }
        //通过verifycode验证验证码的有效性
        String redisVerifyCode = (String) redisTemplate.opsForValue().get("verify_code_"+userModel.getId());
        if(StringUtils.isEmpty(redisVerifyCode)){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"请求非法");
        }
        if(!redisVerifyCode.equalsIgnoreCase(verifyCode)){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"请求非法,验证码错误");
        }
        //获取秒杀访问令牌
        String promoToken = promoService.generateSecondKillToken(promoId,itemId,userModel.getId());
        if(promoToken == null){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"生成令牌失败");
        }
        //返回对应的结果
        return CommonReturnType.create(promoToken);
    }
        //封装下单请求
    @RequestMapping(value = "/createorder",method = {RequestMethod.POST},consumes={CONTENT_TYPE_FORMED})
    @ResponseBody
    public CommonReturnType createOrder(@RequestParam(name="itemId")Integer itemId,
                                        @RequestParam(name="amount")Integer amount,
                                        @RequestParam(name="promoId",required = false)Integer promoId,
                                        @RequestParam(name="promoToken",required = false)String promoToken) throws BusinessException {
        // 限流
        boolean tryAcquire = orderCreateRateLimiter.tryAcquire();
        if(!tryAcquire){
            throw new BusinessException(EmBusinessError.RATELIMIT);
        }
        String token = httpServletRequest.getParameterMap().get("token")[0];
        Map map = httpServletRequest.getParameterMap();
        if(StringUtils.isEmpty(token)){
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登陆,不能下单");
        }
        //获取用户的登陆信息
        UserModel userModel = (UserModel) redisTemplate.opsForValue().get(token);
        if(userModel == null){
            throw new BusinessException(EmBusinessError.USER_NOT_LOGIN,"用户还未登陆,不能下单");
        }
        //校验秒杀令牌是否正确
        if(promoId != null){
            String inRedisPromoToken = (String) redisTemplate.opsForValue().get("promo_token_"+promoId+"_userid_"+userModel.getId()+"_itemid_"+itemId);
            if(inRedisPromoToken == null){
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"秒杀令牌校验失败");
            }
            if(!org.apache.commons.lang3.StringUtils.equals(promoToken,inRedisPromoToken)){
                throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"秒杀令牌校验失败");
            }
        }
        //同步调用线程池的submit方法
        //拥塞窗口为20的等待队列,用来队列化泄洪
        Future<Object> future = executorService.submit(new Callable<Object>() {
            @Override
            public Object call() throws Exception {
                //每次秒杀,生成一条流水,加入库存流水init状态,这是为了MqProducer类中checkLocalTransaction方法 查看扣减库存成功与否
                String stockLogId = itemService.initStockLog(itemId,amount);
                //再去完成对应的下单事务型消息机制
                if(!mqProducer.transactionAsyncReduceStock(userModel.getId(),itemId,promoId,amount,stockLogId)){
                    throw new BusinessException(EmBusinessError.UNKNOWN_ERROR,"下单失败");
                }
                return null;
            }
        });
        try {
            future.get();
        } catch (InterruptedException e) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR);
        } catch (ExecutionException e) {
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR);
        }
        return CommonReturnType.create(null);
    }
}

MqProducer

@Component
public class MqProducer {
    private DefaultMQProducer producer; // 默认生产者
    private TransactionMQProducer transactionMQProducer; // 事务型生产者
    @Value("${mq.nameserver.addr}")
    private String nameAddr;
    @Value("${mq.topicname}")
    private String topicName;
    @Autowired
    private OrderService orderService;
    @Autowired
    private StockLogDOMapper stockLogDOMapper;
    @PostConstruct
    public void init() throws MQClientException {
        //做mq producer的初始化
        producer = new DefaultMQProducer("producer_group");
        producer.setNamesrvAddr(nameAddr);
        producer.start();
        transactionMQProducer = new TransactionMQProducer("transaction_producer_group");
        transactionMQProducer.setNamesrvAddr(nameAddr);
        transactionMQProducer.start();
        transactionMQProducer.setTransactionListener(new TransactionListener() {
            //  第2步执行
            @Override
            public LocalTransactionState executeLocalTransaction(Message msg, Object arg) {
                //真正要做的事  创建订单
                //arg的值来自 transactionAsyncReduceStock方法中的argsMap
                Integer itemId = (Integer) ((Map)arg).get("itemId");
                Integer promoId = (Integer) ((Map)arg).get("promoId");
                Integer userId = (Integer) ((Map)arg).get("userId");
                Integer amount = (Integer) ((Map)arg).get("amount");
                String stockLogId = (String) ((Map)arg).get("stockLogId");
                try {
                    orderService.createOrder(userId,itemId,promoId,amount,stockLogId);
                } catch (Exception e) {
                    e.printStackTrace();
                    //设置对应的stockLog为回滚状态3,表示下单失败
                    StockLogDO stockLogDO = stockLogDOMapper.selectByPrimaryKey(stockLogId);
                    stockLogDO.setStatus(3);
                    stockLogDOMapper.updateByPrimaryKeySelective(stockLogDO);
                    return LocalTransactionState.ROLLBACK_MESSAGE;
                }
                return LocalTransactionState.COMMIT_MESSAGE;
            }
            //  第2步执行出错后,没有明确返回COMMIT_MESSAGE或ROLLBACK_MESSAGE的时候 执行
            @Override
            public LocalTransactionState checkLocalTransaction(MessageExt msg) {
                //根据是否扣减库存成功,来判断要返回COMMIT,ROLLBACK还是继续UNKNOWN
                String jsonString  = new String(msg.getBody());
                Map<String,Object>map = JSON.parseObject(jsonString, Map.class);
                String stockLogId = (String) map.get("stockLogId");
                // 查询操作流水,看这笔交易是否成功
                StockLogDO stockLogDO = stockLogDOMapper.selectByPrimaryKey(stockLogId);
                if(stockLogDO == null){
                    return LocalTransactionState.UNKNOW;
                }
                if(stockLogDO.getStatus().intValue() == 2){
                    return LocalTransactionState.COMMIT_MESSAGE;
                }else if(stockLogDO.getStatus().intValue() == 1){
                    return LocalTransactionState.UNKNOW;
                }
                return LocalTransactionState.ROLLBACK_MESSAGE;
            }
        });
    }
    //事务型同步库存扣减消息 第1步执行
    public boolean transactionAsyncReduceStock(Integer userId,Integer itemId,Integer promoId,Integer amount,String stockLogId){
        Map<String,Object> bodyMap = new HashMap<>();
        bodyMap.put("itemId",itemId);
        bodyMap.put("amount",amount);
        bodyMap.put("stockLogId",stockLogId);
        Map<String,Object> argsMap = new HashMap<>();
        argsMap.put("itemId",itemId);
        argsMap.put("amount",amount);
        argsMap.put("userId",userId);
        argsMap.put("promoId",promoId);
        argsMap.put("stockLogId",stockLogId);
        Message message = new Message(topicName,"increase",
                JSON.toJSON(bodyMap).toString().getBytes(Charset.forName("UTF-8")));
        TransactionSendResult sendResult = null;
        try {
            //这里的artsMap,会传到 executeLocalTransaction(Message msg, Object arg)中的arg
            sendResult = transactionMQProducer.sendMessageInTransaction(message,argsMap);
        } catch (MQClientException e) {
            e.printStackTrace();
            return false;
        }
        if(sendResult.getLocalTransactionState() == LocalTransactionState.ROLLBACK_MESSAGE){
            return false;
        }else if(sendResult.getLocalTransactionState() == LocalTransactionState.COMMIT_MESSAGE){
            return true;
        }else{
            return false;
        }
    }
}

MqConsumer

@Component
public class MqConsumer {
    private DefaultMQPushConsumer consumer;
    @Value("${mq.nameserver.addr}")
    private String nameAddr;
    @Value("${mq.topicname}")
    private String topicName;
    @Autowired
    private ItemStockDOMapper itemStockDOMapper;
    @PostConstruct
    public void init() throws MQClientException {
        consumer = new DefaultMQPushConsumer("stock_consumer_group");
        consumer.setNamesrvAddr(nameAddr);
        consumer.subscribe(topicName,"*");
        consumer.registerMessageListener(new MessageListenerConcurrently() {
            @Override
            public ConsumeConcurrentlyStatus consumeMessage(List<MessageExt> msgs, ConsumeConcurrentlyContext context) {
                //实现库存真正到数据库内扣减的逻辑
                Message msg = msgs.get(0);
                String jsonString  = new String(msg.getBody());
                Map<String,Object>map = JSON.parseObject(jsonString, Map.class);
                Integer itemId = (Integer) map.get("itemId");
                Integer amount = (Integer) map.get("amount");
                itemStockDOMapper.decreaseStock(itemId,amount);
                return ConsumeConcurrentlyStatus.CONSUME_SUCCESS;
            }
        });
        consumer.start();
    }
}

OrderServiceImpl

@Service
public class OrderServiceImpl implements OrderService {
    @Autowired
    private SequenceDOMapper sequenceDOMapper;
    @Autowired
    private ItemService itemService;
    @Autowired
    private UserService userService;
    @Autowired
    private OrderDOMapper orderDOMapper;
    @Autowired
    private StockLogDOMapper stockLogDOMapper;
    @Override
    @Transactional
    public OrderModel createOrder(Integer userId, Integer itemId, Integer promoId, Integer amount,String stockLogId) throws Exception {
        //1.校验下单状态,下单的商品是否存在,用户是否合法,购买数量是否正确
        //ItemModel itemModel = itemService.getItemById(itemId);
        ItemModel itemModel = itemService.getItemByIdInCache(itemId);
        if(itemModel == null){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"商品信息不存在");
        }
        if(amount <= 0 || amount > 99){
            throw new BusinessException(EmBusinessError.PARAMETER_VALIDATION_ERROR,"数量信息不正确");
        }
        //2.落单减库存
        boolean result = itemService.decreaseStock(itemId,amount);
        if(!result){
            throw new BusinessException(EmBusinessError.STOCK_NOT_ENOUGH);
        }
        //3.订单入库
        OrderModel orderModel = new OrderModel();
        orderModel.setUserId(userId);
        orderModel.setItemId(itemId);
        orderModel.setAmount(amount);
        if(promoId != null){
            orderModel.setItemPrice(itemModel.getPromoModel().getPromoItemPrice());
        }else{
            orderModel.setItemPrice(itemModel.getPrice());
        }
        orderModel.setPromoId(promoId);
        orderModel.setOrderPrice(orderModel.getItemPrice().multiply(new BigDecimal(amount)));
        //生成交易流水号,订单号
        orderModel.setId(generateOrderNo());
        OrderDO orderDO = convertFromOrderModel(orderModel);
        orderDOMapper.insertSelective(orderDO);
        //加上商品的销量
        itemService.increaseSales(itemId,amount);
        //设置库存流水状态为成功
        StockLogDO stockLogDO = stockLogDOMapper.selectByPrimaryKey(stockLogId);
        if(stockLogDO == null){
            throw new BusinessException(EmBusinessError.UNKNOWN_ERROR);
        }
        stockLogDO.setStatus(2);
        stockLogDOMapper.updateByPrimaryKeySelective(stockLogDO);
//        int i = 10/0;
        //4.返回前端
        return orderModel;
    }
//    @Transactional(propagation = Propagation.REQUIRES_NEW)
    private String generateOrderNo(){
        //订单号有16位
        StringBuilder stringBuilder = new StringBuilder();
        //前8位为时间信息,年月日
        LocalDateTime now = LocalDateTime.now();
        String nowDate = now.format(DateTimeFormatter.ISO_DATE).replace("-","");
        stringBuilder.append(nowDate);
        //中间6位为自增序列
        //获取当前sequence
        int sequence = 0;
        SequenceDO sequenceDO =  sequenceDOMapper.getSequenceByName("order_info");
        sequence = sequenceDO.getCurrentValue();
        sequenceDO.setCurrentValue(sequenceDO.getCurrentValue() + sequenceDO.getStep());
        sequenceDOMapper.updateByPrimaryKeySelective(sequenceDO);
        String sequenceStr = String.valueOf(sequence);
        for(int i = 0; i < 6-sequenceStr.length();i++){
            stringBuilder.append(0);
        }
        stringBuilder.append(sequenceStr);
        //最后2位为分库分表位,暂时写死
        stringBuilder.append("00");
        return stringBuilder.toString();
    }
    private OrderDO convertFromOrderModel(OrderModel orderModel){
        if(orderModel == null){
            return null;
        }
        OrderDO orderDO = new OrderDO();
        BeanUtils.copyProperties(orderModel,orderDO);
        orderDO.setItemPrice(orderModel.getItemPrice().doubleValue());
        orderDO.setOrderPrice(orderModel.getOrderPrice().doubleValue());
        return orderDO;
    }
}

StockLogDOMapper

public interface StockLogDOMapper {
    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table stock_log
     *
     * @mbg.generated Mon Feb 25 23:42:11 CST 2019
     */
    int deleteByPrimaryKey(String stockLogId);
    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table stock_log
     *
     * @mbg.generated Mon Feb 25 23:42:11 CST 2019
     */
    int insert(StockLogDO record);
    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table stock_log
     *
     * @mbg.generated Mon Feb 25 23:42:11 CST 2019
     */
    int insertSelective(StockLogDO record);
    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table stock_log
     *
     * @mbg.generated Mon Feb 25 23:42:11 CST 2019
     */
    StockLogDO selectByPrimaryKey(String stockLogId);
    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table stock_log
     *
     * @mbg.generated Mon Feb 25 23:42:11 CST 2019
     */
    int updateByPrimaryKeySelective(StockLogDO record);
    /**
     * This method was generated by MyBatis Generator.
     * This method corresponds to the database table stock_log
     *
     * @mbg.generated Mon Feb 25 23:42:11 CST 2019
     */
    int updateByPrimaryKey(StockLogDO record);
}

xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.imooc.miaoshaproject.dao.StockLogDOMapper">
  <resultMap id="BaseResultMap" type="com.imooc.miaoshaproject.dataobject.StockLogDO">
    <id column="stock_log_id" jdbcType="VARCHAR" property="stockLogId" />
    <result column="item_id" jdbcType="INTEGER" property="itemId" />
    <result column="amount" jdbcType="INTEGER" property="amount" />
    <result column="status" jdbcType="INTEGER" property="status" />
  </resultMap>
  <sql id="Base_Column_List">
    stock_log_id, item_id, amount, status
  </sql>
  <select id="selectByPrimaryKey" parameterType="java.lang.String" resultMap="BaseResultMap">
    select 
    <include refid="Base_Column_List" />
    from stock_log
    where stock_log_id = #{stockLogId,jdbcType=VARCHAR}
  </select>
  <delete id="deleteByPrimaryKey" parameterType="java.lang.String">
    delete from stock_log
    where stock_log_id = #{stockLogId,jdbcType=VARCHAR}
  </delete>
  <insert id="insert" parameterType="com.imooc.miaoshaproject.dataobject.StockLogDO">
    insert into stock_log (stock_log_id, item_id, amount, 
      status)
    values (#{stockLogId,jdbcType=VARCHAR}, #{itemId,jdbcType=INTEGER}, #{amount,jdbcType=INTEGER}, 
      #{status,jdbcType=INTEGER})
  </insert>
  <insert id="insertSelective" parameterType="com.imooc.miaoshaproject.dataobject.StockLogDO">
    insert into stock_log
    <trim prefix="(" suffix=")" suffixOverrides=",">
      <if test="stockLogId != null">
        stock_log_id,
      </if>
      <if test="itemId != null">
        item_id,
      </if>
      <if test="amount != null">
        amount,
      </if>
      <if test="status != null">
        status,
      </if>
    </trim>
    <trim prefix="values (" suffix=")" suffixOverrides=",">
      <if test="stockLogId != null">
        #{stockLogId,jdbcType=VARCHAR},
      </if>
      <if test="itemId != null">
        #{itemId,jdbcType=INTEGER},
      </if>
      <if test="amount != null">
        #{amount,jdbcType=INTEGER},
      </if>
      <if test="status != null">
        #{status,jdbcType=INTEGER},
      </if>
    </trim>
  </insert>
  <update id="updateByPrimaryKeySelective" parameterType="com.imooc.miaoshaproject.dataobject.StockLogDO">
    update stock_log
    <set>
      <if test="itemId != null">
        item_id = #{itemId,jdbcType=INTEGER},
      </if>
      <if test="amount != null">
        amount = #{amount,jdbcType=INTEGER},
      </if>
      <if test="status != null">
        status = #{status,jdbcType=INTEGER},
      </if>
    </set>
    where stock_log_id = #{stockLogId,jdbcType=VARCHAR}
  </update>
  <update id="updateByPrimaryKey" parameterType="com.imooc.miaoshaproject.dataobject.StockLogDO">
    update stock_log
    set item_id = #{itemId,jdbcType=INTEGER},
      amount = #{amount,jdbcType=INTEGER},
      status = #{status,jdbcType=INTEGER}
    where stock_log_id = #{stockLogId,jdbcType=VARCHAR}
  </update>
</mapper>
目录
相关文章
|
2月前
|
机器学习/深度学习 Java 编译器
解锁硬件潜能:Java向量化计算,性能飙升W倍!
编译优化中的机器相关优化主要包括指令选择、寄存器分配、窥孔优化等,发生在编译后端,需考虑目标平台的指令集、寄存器、SIMD支持等硬件特性。向量化计算利用SIMD技术,实现数据级并行,大幅提升性能,尤其适用于图像处理、机器学习等领域。Java通过自动向量化和显式向量API(JDK 22标准)支持该技术。
88 4
|
2月前
|
Cloud Native 前端开发 Java
WebAssembly 与 Java 结合的跨语言协作方案及性能提升策略研究
本文深入探讨了WebAssembly与Java的结合方式,介绍了编译Java为Wasm模块、在Java中运行Wasm、云原生集成等技术方案,并通过金融分析系统的应用实例展示了其高性能、低延迟、跨平台等优势。结合TeaVM、JWebAssembly、GraalVM、Wasmer Java等工具,帮助开发者提升应用性能与开发效率,适用于Web前端、服务器端及边缘计算等场景。
97 0
|
5月前
|
负载均衡 算法 关系型数据库
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
本文聚焦 MySQL 集群架构中的负载均衡算法,阐述其重要性。详细介绍轮询、加权轮询、最少连接、加权最少连接、随机、源地址哈希等常用算法,分析各自优缺点及适用场景。并提供 Java 语言代码实现示例,助力直观理解。文章结构清晰,语言通俗易懂,对理解和应用负载均衡算法具有实用价值和参考价值。
大数据大厂之MySQL数据库课程设计:揭秘MySQL集群架构负载均衡核心算法:从理论到Java代码实战,让你的数据库性能飙升!
|
16天前
|
Java Spring
如何优化Java异步任务的性能?
本文介绍了Java中四种异步任务实现方式:基础Thread、线程池、CompletableFuture及虚拟线程。涵盖多场景代码示例,展示从简单异步到复杂流程编排的演进,适用于不同版本与业务需求,助你掌握高效并发编程实践。(239字)
126 6
|
1月前
|
缓存 Java 开发者
Java 开发者必看!ArrayList 和 LinkedList 的性能厮杀:选错一次,代码慢成蜗牛
本文深入解析了 Java 中 ArrayList 和 LinkedList 的性能差异,揭示了它们在不同操作下的表现。通过对比随机访问、插入、删除等操作的效率,指出 ArrayList 在多数场景下更高效,而 LinkedList 仅在特定情况下表现优异。文章强调选择合适容器对程序性能的重要性,并提供了实用的选择法则。
108 3
|
4月前
|
存储 Java 大数据
Java代码优化:for、foreach、stream使用法则与性能比较
总结起来,for、foreach和stream各自都有其适用性和优势,在面对不同的情况时,有意识的选择更合适的工具,能帮助我们更好的解决问题。记住,没有哪个方法在所有情况下都是最优的,关键在于理解它们各自的特性和适用场景。
414 23
|
7月前
|
Java C语言
课时8:Java程序基本概念(标识符与关键字)
课时8介绍Java程序中的标识符与关键字。标识符由字母、数字、下划线和美元符号组成,不能以数字开头且不能使用Java保留字。建议使用有意义的命名,如student_name、age。关键字是特殊标记,如蓝色字体所示。未使用的关键字有goto、const;特殊单词null、true、false不算关键字。JDK1.4后新增assert,JDK1.5后新增enum。
115 4
|
7月前
|
Java 编译器
课时7:Java程序基本概念(注释)
课时7介绍了Java程序中的注释。编程语言有其语法和语义,注释有助于理解代码需求,防止断档。Java支持三类注释:单行(//)、多行(/* */)和文档注释(/** */)。注释不会被编译器编译。范例中展示了如何在代码中使用注释,并强调了注释对项目文档管理的重要性。
114 3
|
7月前
|
存储 Java 数据库连接
【YashanDB知识库】Java程序调用存储过程,在提取clob时报YAS-00004
【YashanDB知识库】Java程序调用存储过程,在提取clob时报YAS-00004
|
7月前
|
搜索推荐 Java Android开发
课时146:使用JDT开发Java程序
在 Eclipse 之中提供有 JDT环境可以实现java 程序的开发,下面就通过一些功能进行演示。 项目开发流程
259 0

热门文章

最新文章