【Redis缓存机制】详解Java连接Redis_Jedis_事务_java

Jedis事务

我们使用JDBC连接Mysql的时候,每次执行sql语句之前,都需要开启事务;在MyBatis中,也需要使用openSession()来获取session事务对象,来进行sql执行、查询等操作。当我们对数据库的操作结束的时候,是事务对象负责关闭数据库连接。

事务对象用于管理、执行各种数据库操作的动作。它能够开启和关闭数据库连接,执行sql语句,回滚错误的操作。

我们的Redis也有事务管理对象,其位于redis.clients.jedis.Transaction下。

Jedis事务的相关代码:

package cn.com.redis; 

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction; 

public class Test7 {
  public static void main(String[] args) {
    Jedis jedis = new Jedis("192.168.248.129",6379); 

    Transaction transaction=jedis.multi();//返回一个事务控制对象 

    //预先在事务对象中装入要执行的操作
    transaction.set("k4", "v4");
    transaction.set("k5", "v5"); 

    transaction.exec();//执行
  }
}

我们查看一下redis:

发现数据已经加入进去

我们把k4的value和k5的value改为“v44”和“v55”,然后在transaction.exec()语句后加入transaction.discard()语句:

package cn.com.redis; 

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction; 

public class Test7 {
  public static void main(String[] args) {
    Jedis jedis = new Jedis("192.168.248.129",6379); 

    Transaction transaction=jedis.multi();//返回一个事务控制对象 

    //预先在事务对象中装入要执行的操作
    transaction.set("k4", "v44");
    transaction.set("k5", "v55"); 

    transaction.discard();//回滚
  }
}

会发现数据插入操作被回滚,redis中那两个值未被改变:

我们模拟一个刷一次信用卡的交易,使用redis的事务来处理一些逻辑:

package cn.com.redis; 

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction; 

public class TestTransaction {
  //模拟信用卡消费和还款
  public static void main(String[] args) {
    TestTransaction t = new TestTransaction();
    boolean retValue = t.transMethod(100);
    if(retValue){
      System.out.println("使用信用卡消费成功!");
    }else{
      System.out.println("使用信用卡消费失败!");
    } 

  } 

  /**
   * 通俗点讲,watch命令就是标记一个键,如果标记了一个键,
   * 在提交事务前如果该键被别人修改过,那事务就会失败,这种情况通常可以在程序中
   * 重新再尝试一次。
   *
   * 首先标记了balance,然后检查余额是否足够,不足就取消标记,并不做扣减;
   * 足够的话,就启动事务进行更新操作。
   * 如果在此期间键balance被其他人修改,拿在提交事务(执行exec)时就会报错,
   * 程序中通常可以捕获这类错误再重新执行一次,直到成功。
   * */
  private boolean transMethod(int amount) { 

    System.out.println("您使用信用卡预付款"+amount+"元"); 

    Jedis jedis = new Jedis("192.168.248.129",6379); 

    int balance = 1000;//可用余额
    int debt;//欠额
    int amtToSubtract = amount;//实刷额度 

    jedis.set("balance", String.valueOf(balance));
    jedis.watch("balance");
    //jedis.set("balance", "1100");//此句不该出现,为了模拟其他程序已经修改了该条目
    balance = Integer.parseInt(jedis.get("balance"));
    if(balance < amtToSubtract){//可用余额小于实刷金额,拒绝交易
      jedis.unwatch();
      System.out.println("可用余额不足!");
      return false;
    }else{//可用余额够用的时候再去执行扣费操作
      System.out.println("扣费transaction事务开始执行...");
      Transaction transaction = jedis.multi();
      transaction.decrBy("balance",amtToSubtract);//余额减去amtToSubtract的钱数
      transaction.incrBy("debt", amtToSubtract);//信用卡欠款增加amtToSubtract的钱数
      transaction.exec();//执行事务
      balance = Integer.parseInt(jedis.get("balance"));
      debt = Integer.parseInt(jedis.get("debt"));
      System.out.println("扣费transaction事务执行结束..."); 

      System.out.println("您的可用余额:"+balance);
      System.out.println("您目前欠款:"+debt);
      return true;
    }
  } 

}

此代码就是模拟用户使用信用卡刷了100元的东西,此时应该减去信用卡的可用余额100元,增加100元的欠款。

运行结果:

redis的结果:

证明我们的操作是成功的。

加watch命令是为了在事务执行的过程中,防止其它的操作打断事务,或者是影响事务的计算结果,导致“幻读”、“脏数据”等异常情况的发生。watch命令建立了一个键,一旦发现执行过程中该键被别人修改过,那事务就会失败,程序中通常可以捕获这类错误再重新执行一次,直到成功。所以watch命令可以保证数据的同步安全。

为了证明watch命令的用途,我们把上面代码里面的jedis.set("balance", "1100");注释释放,然后transMethod方法抛出打断异常:throws InterruptedException,main方法捕获打断异常,然后弹出相应警告框。

package cn.com.redis; 

import java.util.List; 

import redis.clients.jedis.Jedis;
import redis.clients.jedis.Transaction; 

public class TestTransaction {
  //模拟信用卡消费和还款
  public static void main(String[] args) {
    TestTransaction t = new TestTransaction();
    boolean retValue=false;
    boolean Interrupted = false; 

    try {
      retValue = t.transMethod(100);
    } catch (InterruptedException e) {
      Interrupted = true;
      System.out.println("事务被打断,请重新执行!");
    }finally{
      if(retValue){
        System.out.println("使用信用卡消费成功!");
      }else{
        if(!Interrupted){
          System.out.println("使用信用卡消费失败!余额不足!");
        }
      }
    }
  } 

  /**
   * 通俗点讲,watch命令就是标记一个键,如果标记了一个键,
   * 在提交事务前如果该键被别人修改过,那事务就会失败,这种情况通常可以在程序中
   * 重新再尝试一次。
   *
   * 首先标记了balance,然后检查余额是否足够,不足就取消标记,并不做扣减;
   * 足够的话,就启动事务进行更新操作。
   * 如果在此期间键balance被其他人修改,拿在提交事务(执行exec)时就会报错,
   * 程序中通常可以捕获这类错误再重新执行一次,直到成功。
   * */
  private boolean transMethod(int amount) throws InterruptedException{ 

    System.out.println("您使用信用卡预付款"+amount+"元"); 

    Jedis jedis = new Jedis("192.168.248.129",6379); 

    int balance = 1000;//可用余额
    int debt;//欠额
    int amtToSubtract = amount;//实刷额度 

    jedis.set("balance", String.valueOf(balance));
    jedis.watch("balance");
    jedis.set("balance", "1100");//此句不该出现,为了模拟其他程序已经修改了该条目
    balance = Integer.parseInt(jedis.get("balance"));
    if(balance < amtToSubtract){//可用余额小于实刷金额,拒绝交易
      jedis.unwatch();
      System.out.println("可用余额不足!");
      return false;
    }else{//可用余额够用的时候再去执行扣费操作
      System.out.println("扣费transaction事务开始执行...");
      Transaction transaction = jedis.multi();
      transaction.decrBy("balance",amtToSubtract);//余额减去amtToSubtract的钱数
      transaction.incrBy("debt", amtToSubtract);//信用卡欠款增加amtToSubtract的钱数
      List<Object> result = transaction.exec();//执行事务 

      if(result==null){//事务提交失败,说明在执行期间数据被修改过 

        System.out.println("扣费transaction事务执行中断...");
        throw new InterruptedException(); 

      }else{//事务提交成功
        balance = Integer.parseInt(jedis.get("balance"));
        debt = Integer.parseInt(jedis.get("debt"));
        System.out.println("扣费transaction事务执行结束..."); 

        System.out.println("您的可用余额:"+balance);
        System.out.println("您目前欠款:"+debt); 

        return true;
      }
    }
  } 

}

再运行一下,看一下效果:

这就说明了,如果在watch命令执行后和事务提交之前,如果数据发生了修改操作,事务执行就不会成功,此举保证了数据的安全性。

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。

以上是小编为您精心准备的的内容,在的博客、问答、公众号、人物、课程等栏目也有的相关内容,欢迎继续使用右上角搜索按钮进行搜索java
, redis
, 事务
, jedis
jedis操作redis
redis缓存原理详解、redis和jedis的关系、jedis和redis的区别、jedis 密码访问redis、redisson jedis 比较,以便于您获取更多的相关知识。

时间: 2024-07-29 14:17:52

【Redis缓存机制】详解Java连接Redis_Jedis_事务_java的相关文章

C基础 redis缓存访问详解_C 语言

引言 先说redis安装, 这里采用的环境是. Linux version 4.4.0-22-generic (buildd@lgw01-41) (gcc version 5.3.1 20160413 (Ubuntu 5.3.1-14ubuntu2) ) #40-Ubuntu SMP Thu May 12 22:03:46 UTC 2016 对于 ubuntu 安装 redis是非常简单的. 这里采用源码安装. 安装代码如下 wget http://download.redis.io/relea

详解java动态代理模式_java

本文针对java动态代理进行知识点整理,具体内容如下 一. JAVA的动态代理(比较官方说法) 代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处 理消息.过滤消息.把消息转发给委托类,以及事后处理消息等. 代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的 对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提 供特定的服务. 按照代理的创建时期,代理类可以分为两种. 静态代理:由程序员创建或特定工

Smarty模板引擎缓存机制详解_php实例

本文实例讲述了Smarty模板引擎缓存机制.分享给大家供大家参考,具体如下: 首先说下smarty缓存和编译,这是两个不同的概念,编译默认情况下是启动的,而缓存机制需要人为开启,smarty编译过的文件还是php文件,所以执行的时候还是编译的,如果涉及到数据库,还是要访问数据库的所以开销也不小啦,所以需要smarty缓存来解决! 1.开启全局缓存 $smarty->cache_dir = "/caches/"; //缓存目录 $smarty->caching = true;

Smarty模板引擎缓存机制详解

本文实例讲述了Smarty模板引擎缓存机制.分享给大家供大家参考,具体如下: 首先说下smarty缓存和编译,这是两个不同的概念,编译默认情况下是启动的,而缓存机制需要人为开启,smarty编译过的文件还是php文件,所以执行的时候还是编译的,如果涉及到数据库,还是要访问数据库的所以开销也不小啦,所以需要smarty缓存来解决! 1.开启全局缓存 $smarty->cache_dir = "/caches/"; //缓存目录 $smarty->caching = true;

详解Java中native关键字_java

 一. 什么是Native Method   简单地讲,一个Native Method就是一个java调用非java代码的接口.一个Native Method是这样一个java的方法:该方法的实现由非java语言实现,比如C.这个特征并非java所特有,很多其它的编程语言都有这一机制,比如在C++中,你可以用extern "C"告知C++编译器去调用一个C的函数.    "A native method is a Java method whose implementatio

Java 反射机制详解及实例代码_java

Java反射详解 本篇文章依旧采用小例子来说明,因为我始终觉的,案例驱动是最好的,要不然只看理论的话,看了也不懂,不过建议大家在看完文章之后,在回过头去看看理论,会有更好的理解. 下面开始正文. [案例1]通过一个对象获得完整的包名和类名 package Reflect; /** * 通过一个对象获得完整的包名和类名 * */ class Demo{ //other codes... } class hello{ public static void main(String[] args) {

图文详解java内存回收机制_java

在Java中,它的内存管理包括两方面:内存分配(创建Java对象的时候)和内存回收,这两方面工作都是由JVM自动完成的,降低了Java程序员的学习难度,避免了像C/C++直接操作内存的危险.但是,也正因为内存管理完全由JVM负责,所以也使Java很多程序员不再关心内存分配,导致很多程序低效,耗内存.因此就有了Java程序员到最后应该去了解JVM,才能写出更高效,充分利用有限的内存的程序.  1.Java在内存中的状态  首先我们先写一个代码为例子: Person.java package tes

Java反射机制详解_java

本文较为详细的分析了Java反射机制.分享给大家供大家参考,具体如下: 一.预先需要掌握的知识(java虚拟机) java虚拟机的方法区: java虚拟机有一个运行时数据区,这个数据区又被分为方法区,堆区和栈区,我们这里需要了解的主要是方法区.方法区的主要作用是存储被装载的类 的类型信息,当java虚拟机装载某个类型的时候,需要类装载器定位相应的class文件,然后将其读入到java虚拟机中,紧接着虚拟机提取class 中的类型信息,将这些信息存储到方法区中.这些信息主要包括: 1.这个类型的全

详解Java动态代理的实现机制_java

一.概述 代理是一种设计模式,其目的是为其他对象提供一个代理以控制对某个对象的访问,代理类负责为委托类预处理消息,过滤消息并转发消息以及进行消息被委托类执行后的后续处理.为了保持行为的一致性,代理类和委托类通常会实现相同的接口. 按照代理的创建时期,代理类可分为两种: 静态代理:由程序员创建代理类或特定工具自动生成源代码再对其编译,也就是说在程序运行前代理类的.class文件就已经存在.动态代理:在程序运行时运用反射机制动态创建生成. 下面在将动态代理的实现机制之前先简单介绍一下静态代理. 二.