您的位置 首页 java

超详细讲解Redis基础类型String增删改查(带Java库源码)

做项目时用到Redis中的String类型,

东一下,西一下,虽然关于String类型数据的操作基本都涉及了,

但是不够系统,非常散,

为帮助开始学习Redis的开发者系统学习Redis String类型操作,

以及备忘,特汇总整理成文,

分享如下。

当然为了丰富文章内容,贴了一些源码的片段。

(1)文末附全部测试代码;

(2)本篇文章将学习使用如下函数(方法):

序号

操作

method

1

新增

set,mset,setnx,msetnx,setex

2

删除

del

3

修改

set,mset,setex,incr,incrBy,decr,decrBy

4

查询

get,mget

2 String测试

Redis基础数据类型String代表存储的值(value)为字符串类型。

本文采用连接池的方式直连Redis,连接池配置如下:

  • 依赖
 <!--  -->
<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
    <version>3.5.1</version>
</dependency>  
  • Redis连接池配置(可以根据需要设置为共用)
 private static JedisPool getJedisPool() {
 
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // Jedis池:最大连接数
        jedisPoolConfig.setMaxTotal(1);
        // Jedis池:最大空闲连接数
        jedisPoolConfig.setMaxIdle(10);
        // Jedis池:等待时间
        jedisPoolConfig.setMaxWaitMillis(3000);
        // Jedis池:连接Redis超时时间
        int connectTimeout = 2000;
        String redisHost = "127.0.0.1";
        int redisPort = 6379;
        String redisPassword = "123456";
        int redisDb = 0;
        // 创建连接池
        return new JedisPool(jedisPoolConfig, redisHost, redisPort, connectTimeout, redisPassword, redisDb);
    }  

2.1 新增数据

Redis新增String数据考量如下方面:

  • 单条插入:直接插入(数据键不存在则新增值,数据键存在则覆盖旧值),检查插入(只新增不存在键的键值对);
  • 批量插入:直接插入(数据键不存在则新增值,数据键存在则覆盖旧值),检查插入(只新增不存在键的键值对);
  • 是否为新增的数据添加过期时间;

2.1.1 直接单条新增数据:set

测试代码段如下,直接在Redis数据库中新增数据,不检查数据库是否存在。

 /**
     * 单条插入数据:不检查数据库是否存在数据
     */    @Test
    public void insertData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            String insertRes1 = jedis.set("xiaohua", "123456");
            String insertRes2 = jedis.set("xiaolan", "123456");
            String insertRes3 = jedis.set("xiaoxiao", "123456");
            logger.info(">>>>>>>Insert response:{},{},{}", insertRes1, insertRes2, insertRes3);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis插入String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }  

新增数据成功返回的响应码为:OK,测试结果如下图所示。

  • set方法原码如下图所示,由注释可知,字符串最大长度为1GB,时间复杂度为O(1)。
  • 位置:redis.clients.jedis.Jedis#set(java.lang.String, java.lang.String)

2.1.2 检查单条插入数据:setnx

测试代码段如下,只新增不存在的数据键,若数据键已存在则不会新增或覆盖。

 /**
     * 单条插数据:检查数据库是否存在数据
     * 存在则不插入数据
     */    @Test
    public void insertIfNotExistData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            Long insertNumber = jedis.setnx("se2", "123456");
            logger.info(">>>>>>>>>Insert if not exist response:{}", insertNumber);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis插入String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }  

数据如果存在,则不会新建数据或者覆盖数据,原值保持不变。如果数据不存在,则新增数据。

  • setnx源码如下图所示,由注释可知,setnx只会添加不同key的数据。
  • 位置:redis.clients.jedis.Jedis#setnx

2.1.3 批量插入数据:mset

批量插入数据代码段如下,同样不检查数据库是否存在数据。

 /**
     * 批量插入数据:不检查数据库是否存在数据
     */    @Test
    public void insertBatchData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            String multipleRes = jedis.mset("s1", "v1", "s2", "v2");
            logger.info(">>>>>>>>>Multiple insert response:{}", multipleRes);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis插入String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }  

新增数据成功返回的响应码为:OK,测试结果如下图所示。

  • mset方法源码如下图所示,由注释可知,mset直接插入多个键值对,如果存在旧值,直接覆盖。msetnx添加数据时会先检查数据库是否存在数据键,不存在时才会新增数据。
  • 位置:redis.clients.jedis.Jedis#mset

2.1.4 批量插入,检查是否存在:msetnx

 /**
     * 批量插入数据:检查数据库是否存在数据
     */    @Test
    public void insertBatchIfNotExistData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            Long multipleRes = jedis.msetnx("s1", "v1", "s2", "v2");
            logger.info(">>>>>>>>>Multiple insert response:{}", multipleRes);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis插入String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }  

  • msetnx源码如下,只新增不存在键的键值对。
  • 位置:redis.clients.jedis.Jedis#msetnx

2.1.5 添加带过期时间的数据:setex

单条插入带过期时间的数据测试代码段如下。

 /**
     * 单条插入带过期时间的数据:不检查是否存在数据
     */    @Test
    public void insertExpireData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            String insertRes1 = jedis.setex("se1", 5, "123456");
            String insertRes2 = jedis.setex("se2", 10, "123456");
            String insertRes3 = jedis.setex("se3", 15, "123456");
            logger.info(">>>>>>>>Insert data with expire response:{}, {}, {}", insertRes1, insertRes2, insertRes3);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis插入String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }  

插入数据携带过期时间,测试结果如下,添加成功响应编码为OK。

  • setex源码如下图所示,由注释可知,该方法具备两个功能:set和expire,其中,过期时间单位为:秒。
  • 位置:redis.clients.jedis.Jedis#setex

2.2 删除数据:del

删除String类型数据有两种方式:单条删除和批量删除。

 /**
     * 删除数据
     */    @Test
    public void deleteData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            // 单条删除
            Long delRes1 = jedis.del("xiaohua");
            // 批量删除
            Long delRes2 = jedis.del("xiaolan", "xiaoxiao");
            logger.info(">>>>>>>Delete response:{}, {}", delRes1, delRes2);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis删除String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }  

删除数据成功后,会返回成功删除数据的条数,结果如下图所示。

  • del源码如下图所示,由源码可知删除方法可以实现单条和多条删除,del有两个重写,分别应对删除多条和删除单条数据,如果待删除的键不存在,则不会做任何操作。
  • 位置:redis.clients.jedis.Jedis#del(java.lang.String…)

2.3 修改数据

修改数据使用直接新增数据的方法(set和mset),即可覆盖旧数据。

同样,修改数据可以单条修改,也可以批量修改。

Redis对于String类型的数据,有一个比较有意思的操作:纯数字的String,可以加减。

加1:incr,减1:decr。

2.3.1 单条修改:set

修改单条数据使用set即可,测试代码段如下。

 /**
     * 单条修改数据
     */    @Test
    public void editData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            String res = jedis.set("s1", "1");
            logger.info(">>>>>>>>Edit response:{}", res);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis修改String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }  

测试结果如下:

2.3.2 批量修改:mset

修改多条数据使用mset即可,测试代码段如下。

 /**
     * 批量修改数据
     */    @Test
    public void editBatchData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            String res = jedis.mset("s1", "11", "s2", "22");
            logger.info(">>>>>>>Edit batch response:{}", res);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis修改String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }  

测试结果如下图所示。

2.3.3 值加一:incr

这是比较有意思的功能,虽然Redis存储的数据类型是String,

但是,实际存储的String为数字时,可以使用简单的加减进行计算,

如加一、减一等。

这里加一使用:incr方法,测试代码段如下:

 /**
     * 值加:1
     */    @Test
    public void increaseData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            logger.info(">>>>>>>>Before increase s1={}", jedis.get("s1"));
            Long res = jedis.incr("s1");
            logger.info(">>>>>>>>Increase response:{}, after increase s1={}", res, jedis.get("s1"));
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis String数据加1异常:", ex);
            throw new RuntimeException(ex);
        }
    }  

测试结果如下图所示,修改前s1值为11,加一后值为12。

  • incr源码如下图所示,由注释可知,incr可以为数字型的String加1,如果键不存在,会将该键对应的值置为0,然后加一,如果键存储值的类型不是纯数字,抛出异常。incr操作的数值上限为有符号64为整型数据。虽然存储的是String类型,但是,可以先解析为有符号的整型,然后转为String类型。
  • 位置:redis.clients.jedis.Jedis#incr

2.3.4 值加n:incrBy

当然,有的应用场景不是只加一就能满足的,如果需要加n,只使用incr就要操作多次,

幸好,Jedis提供了按照指定步长增加数据:incrBy,

测试代码段如下:

 /**
     * 值加:n
     */    @Test
    public void increaseByData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            logger.info(">>>>>>>>Before increase s1={}", jedis.get("s1"));
            Long res = jedis.incrBy("s1", 4);
            logger.info(">>>>>>>>Increase response:{}, after increase s1={}", res, jedis.get("s1"));
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis String数据加1异常:", ex);
            throw new RuntimeException(ex);
        }
    }  

测试结果如下图所示,增加前s1=12,增加步长4后,s1=16。

  • incrBy源码如下图所示,由注释可知,这是对incr的扩展。
  • 位置:redis.clients.jedis.Jedis#incrBy

2.3.5 值减1:decr

有了加1功能,自然会想到减1,Redis同样提供了减一方法:decr,

测试代码段如下:

 /**
     * 值减:1
     */    @Test
    public void decreaseData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            logger.info(">>>>>>>>Before decrease s1={}", jedis.get("s1"));
            Long res = jedis.decr("s1");
            logger.info(">>>>>>>>Decrease response:{}, after decrease s1={}", res, jedis.get("s1"));
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis String数据减1异常:", ex);
            throw new RuntimeException(ex);
        }
    }  

测试结果如下图所示,减1前,s1=16,减一后,s1=15。

  • decr源码如下图所示,与incr类似,只是减1。
  • 位置:redis.clients.jedis.Jedis#decr

2.3.6 值减n:decrBy

与加n相呼应,Redis同样提供了按照步长减数据的功能:decrBy

测试代码段如下:

 /**
     * 值减:n
     */    @Test
    public void decreaseByData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            logger.info(">>>>>>>>Before decrease s1={}", jedis.get("s1"));
            Long res = jedis.decrBy("s1", 4);
            logger.info(">>>>>>>>Decrease response:{}, after decrease s1={}", res, jedis.get("s1"));
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis String数据减n异常:", ex);
            throw new RuntimeException(ex);
        }
    }  

测试结果如下图所示,减n前,s1=15,减4后,s1=11。

  • decrBy源码如下图所示。
  • 位置:redis.clients.jedis.Jedis#decrBy

2.4 查询数据

查询数据同样分为单条查询(get)和批量查询(mget)。

2.4.1 单条查询:get

单条数据查询测试代码段如下:

 /**
     * 查询单条数据
     */    @Test
    public void queryData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            String v = jedis.get("xiaohua");
            logger.info(">>>>>>>>Query response:{}", v);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis查询String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }  

查询指定键的数据,测试结果如下图所示。

  • get源码如下图所示,由注释可知,get只能返回String类型的数据,如果存储的数据类型不是String,则抛出异常。
  • 位置:redis.clients.jedis.Jedis#get

2.4.2 批量查询:mget

批量查询测试代码段如下:

 /**
     * 批量查询数据
     */    @Test
    public void queryBatchData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            List<String> vs = jedis.mget("xiaolan", "xiaoxiao");
            logger.info(">>>>>>>>Query batch response:{}", vs);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis查询String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }  
  • mget源码如下图所示,由注释可知,如果批量查询的值不存在,返回null,而不是抛出异常。
  • 位置:redis.clients.jedis.Jedis#mget
超详细讲解Redis基础类型String增删改查(带Java库源码)

核心:

(1)增加数据:

  • 单条新增:直接新增:set,key不存在则新增,key存在则覆盖旧值;
  • 单条新增:条件新增:setnx,key不存在则新增,key存在不操作;
  • 批量新增:mset,msetnx;
  • 添加过期时间:setex,时间单位:秒,只能单条新增;

(2)删除数据:可以单条也可批量删除;

(3)修改:修改数据使用set或者mset,直接覆盖旧值。如果需要增加或减少数据,可以使用纯数字的数据,加一或减一,指定步长加或减;

(4)查询数据:单条查询(get),返回单条数据;多条查询(mget),返回列表数据。

序号

操作

method

1

新增

set,mset,setnx,msetnx,setex

2

删除

del

3

修改

set,mset,setex,incr,incrBy,decr,decrBy

4

查询

get,mget

 package database_test.redis_test;

import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.JedisPoolConfig;

import java.util.List;

/**
 * String类型增删查改.
 *
 * @author xindaqi
 * @since 2022-06-26 10:40
 */public class StringTest {
 

    private static final Logger logger = LoggerFactory.getLogger(StringTest.class);

    private static JedisPool getJedisPool() {
 
        JedisPoolConfig jedisPoolConfig = new JedisPoolConfig();
        // Jedis池:最大连接数
        jedisPoolConfig.setMaxTotal(1);
        // Jedis池:最大空闲连接数
        jedisPoolConfig.setMaxIdle(10);
        // Jedis池:等待时间
        jedisPoolConfig.setMaxWaitMillis(3000);
        // Jedis池:连接Redis超时时间
        int connectTimeout = 2000;
        String redisHost = "127.0.0.1";
        int redisPort = 6379;
        String redisPassword = "123456";
        int redisDb = 0;
        // 创建连接池
        return new JedisPool(jedisPoolConfig, redisHost, redisPort, connectTimeout, redisPassword, redisDb);
    }

    /**
     * 单条插入数据:不检查数据库是否存在数据
     */    @Test
    public void insertData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            String insertRes1 = jedis.set("xiaohua", "123456");
            String insertRes2 = jedis.set("xiaolan", "123456");
            String insertRes3 = jedis.set("xiaoxiao", "123456");
            logger.info(">>>>>>>Insert response:{},{},{}", insertRes1, insertRes2, insertRes3);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis插入String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 批量插入数据:不检查数据库是否存在数据
     */    @Test
    public void insertBatchData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            String multipleRes = jedis.mset("s1", "v1", "s2", "v2");
            logger.info(">>>>>>>>>Multiple insert response:{}", multipleRes);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis插入String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 单条插入带过期时间的数据:不检查是否存在数据
     */    @Test
    public void insertExpireData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            String insertRes1 = jedis.setex("se1", 5, "123456");
            String insertRes2 = jedis.setex("se2", 10, "123456");
            String insertRes3 = jedis.setex("se3", 15, "123456");
            logger.info(">>>>>>>>Insert data with expire response:{}, {}, {}", insertRes1, insertRes2, insertRes3);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis插入String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 单条插数据:检查数据库是否存在数据
     * 存在则不插入数据
     */    @Test
    public void insertIfNotExistData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            Long insertNumber = jedis.setnx("s1", "123456");
            logger.info(">>>>>>>>>Insert if not exist response:{}", insertNumber);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis插入String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 批量添加数据:数据库存在相同键则不添加
     */    @Test
    public void insertIfNotExistBatchData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            Long batchInsertNumber = jedis.msetnx("se1", "123456", "se2", "123456");
            logger.info(">>>>>>>>Insert data batch response:{}", batchInsertNumber);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis插入String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }


    /**
     * 删除数据
     */    @Test
    public void deleteData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            // 单条删除
            Long delRes1 = jedis.del("xiaohua");
            // 批量删除
            Long delRes2 = jedis.del("xiaolan", "xiaoxiao");
            logger.info(">>>>>>>Delete response:{}, {}", delRes1, delRes2);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis删除String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 单条修改数据
     */    @Test
    public void editData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            String res = jedis.set("s1", "1");
            logger.info(">>>>>>>>Edit response:{}", res);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis修改String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 批量修改数据
     */    @Test
    public void editBatchData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            String res = jedis.mset("s1", "11", "s2", "22");
            logger.info(">>>>>>>Edit batch response:{}", res);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis修改String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 值加:1
     */    @Test
    public void increaseData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            logger.info(">>>>>>>>Before increase s1={}", jedis.get("s1"));
            Long res = jedis.incr("s1");
            logger.info(">>>>>>>>Increase response:{}, after increase s1={}", res, jedis.get("s1"));
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis String数据加1异常:", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 值加:n
     */    @Test
    public void increaseByData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            logger.info(">>>>>>>>Before increase s1={}", jedis.get("s1"));
            Long res = jedis.incrBy("s1", 4);
            logger.info(">>>>>>>>Increase response:{}, after increase s1={}", res, jedis.get("s1"));
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis String数据加n异常:", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 值减:1
     */    @Test
    public void decreaseData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            logger.info(">>>>>>>>Before decrease s1={}", jedis.get("s1"));
            Long res = jedis.decr("s1");
            logger.info(">>>>>>>>Decrease response:{}, after decrease s1={}", res, jedis.get("s1"));
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis String数据减1异常:", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 值减:n
     */    @Test
    public void decreaseByData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            logger.info(">>>>>>>>Before decrease s1={}", jedis.get("s1"));
            Long res = jedis.decrBy("s1", 4);
            logger.info(">>>>>>>>Decrease response:{}, after decrease s1={}", res, jedis.get("s1"));
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis String数据减n异常:", ex);
            throw new RuntimeException(ex);
        }
    }


    /**
     * 查询单条数据
     */    @Test
    public void queryData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            String v = jedis.get("xiaohua");
            logger.info(">>>>>>>>Query response:{}", v);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis查询String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }

    /**
     * 批量查询数据
     */    @Test
    public void queryBatchData() {
 
        try (Jedis jedis = getJedisPool().getResource()) {
 
            List<String> vs = jedis.mget("xiaolan", "xiaoxiao");
            logger.info(">>>>>>>>Query batch response:{}", vs);
        } catch(Exception ex) {
 
            logger.error(">>>>>>>>Redis查询String数据异常:", ex);
            throw new RuntimeException(ex);
        }
    }
}  

文章来源:智云一二三科技

文章标题:超详细讲解Redis基础类型String增删改查(带Java库源码)

文章地址:https://www.zhihuclub.com/186681.shtml

关于作者: 智云科技

热门文章

评论已关闭

4条评论

  1. Breast cancer treatment generally uses a multi modal treatment approach utilising combinations of surgery, chemotherapy, endocrine therapy and radiotherapy, dependant on disease stage and sub type classification 2

  2. Management Consider avoiding concurrent administration of high dose proton pump inhibitors PPIs with sustained release mesalamine products These inconclusive findings suggest that considering the original values of the concentrations of TAM and its metabolites might not accurately reveal the effects of CYP2D6 polymorphisms because of the effects of several TAM metabolizing enzymes involved in END transformation including the two subpathways for END formation through NDMT and 4OHT

  3. J Clin Invest 54 913 918, 1974 Further analysis of the unique role of NHEJ loss in endocrine treatment response is warranted

  4. After chemotherapy, the liver metastasis and axillary lymph node metastases had disappeared on imaging findings, showing a complete response CR, but the primary breast tumor remained, showing a partial response PR cialis buy vasotec baikal pharmacy

网站地图