点赞多大胆,就有多大产!路漫漫其修远兮,吾将上下而求索,献给每一位技术使用者和爱好者!
干货满满,摆好姿势,点赞发车
前言
有哪些姿势
Java操作Redis有很多种方式,主要有Jedis
,redisson
,lettuce
等还有Spring家族的spring-data-redis
Jedis
- 是Redis的Java实现客户端,提供了比较全面的Redis命令的支持
Redisson
- 实现了分布式和可扩展的Java数据结构,提供很多分布式相关操作服务,例如,分布式锁,分布式集合,延迟队列
Lettuce
- 用于线程安全同步,异步和响应使用,支持集群,Sentinel,管道和编码器,主要在一些分布式缓存框架上使用比较多
spring-data-redis
- Spring-Data-Redis项目(简称SDR)对Redis的Key-Value数据存储操作提供了更高层次的抽象
- 类似于Spring Framework对JDBC支持一样
- 项目主页: http://projects.spring.io/spring-data-redis/
- 项目文档: http://docs.spring.io/spring-data/redis/docs
- Spring Data Redis使得在Spring应用中读写Redis数据库更加容易。
- Spring Data Redis提供了四种Redis服务的Java客户端包的集成,分别是 Jedis ,JRedis , SRP and Lettuce
核心对象
RedisConnectionFactory
redis的连接工厂类,根据Redis配置连接上Redis服务
RedisTemplate
熟悉Spring操作JDBC都知道JdbcTemplate作用,这个类也一样,spring 封装了 RedisTemplate 对象来进行对redis的各种操作,它支持所有的 redis 原生的 api
Operation
针对Jedis客户端中大量api进行了归类封装,将同一类型操作封装为operation接口
ValueOperations
:String类型数据操作HashOperations
:针对map类型的数据操作ListOperations
:针对list类型的数据操作SetOperations
:set类型数据操作ZSetOperations
:zset类型数据操作
BoundKeyOperations
提供了对key的bound
(绑定)便捷化操作API,可以通过bound封装指定的key,然后进行一系列的操作而无须“显式”的再次指定Key
BoundValueOperations
:Redis String/Value key 约束BoundHashOperations
:Redis Hash key 约束BoundListOperations
:Redis List key 约束BoundSetOperations
:Redis Set key 约束BoundZSetOperations
:Redis Sort Set key 约束
RedisTemplate序列化问题
介绍
当数据存储到Redis时,键(key)和值(value)都是通过Spring提供的Serializer序列化到数据库的。RedisTemplate默认使用的是JdkSerializationRedisSerializer,StringRedisTemplate默认使用的是StringRedisSerializer
问题
spring-data-redis中redisTemplate默认使用JDK的序列化策略,会出现两个问题
- 使用redis-cli或者可视化工具查看数据时,不管是key还是value头部都会携带很多特殊字符,不易查看,Java代码中获取数据时不受影响,但是对运维来说就不友好了
- JDK Serializer太费资源
解决
- 使用Jackson serializer替代JDK Serializer
- StringRedisSerializer结合GenericJackson2JsonRedisSerializer
spring-data-redis支持的序列化策略
spring-data-redis默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略
GenericToStringSerializer
:可以将任何对象泛化为字符串并序列化Jackson2JsonRedisSerializer
:跟JacksonJsonRedisSerializer实际上是一样的JacksonJsonRedisSerializer
:序列化object对象为json字符串JdkSerializationRedisSerializer
:序列化java对象(被序列化的对象必须实现Serializable接口)StringRedisSerializer
:简单的字符串序列化GenericToStringSerializer
:类似StringRedisSerializer的字符串序列化GenericJackson2JsonRedisSerializer
:类似Jackson2JsonRedisSerializer,但使用时构造函数不用特定的类
key和hashKey
:推荐使用 StringRedisSerializer: 简单的字符串序列化
value和hashValue
:推荐使用Jackson2JsonRedisSerializer
RedisTemplate和StringRedisTemplate的区别
- 两者的关系是StringRedisTemplate继承RedisTemplate。
- 两者的数据是不共通的;也就是说StringRedisTemplate只能管理StringRedisTemplate里面的数据,RedisTemplate只能管理RedisTemplate中的数据
- SDR默认采用的序列化策略有两种,一种是String的序列化策略,一种是JDK的序列化策略
- StringRedisTemplate默认采用的是String的序列化策略,保存的key和value都是采用此策略序列化保存
- RedisTemplate默认采用的是JDK的序列化策略,保存的key和value都是采用此策略序列化保存
当你的redis数据库里面本来存的是字符串数据或者你要存取的数据就是字符串类型数据的时候,那么你就使用StringRedisTemplate即可,但是如果你的数据是复杂的对象类型,而取出的时候又不想做任何的数据转换,直接从Redis里面取出一个对象,那么使用RedisTemplate是更好的选择
开搞
开发环境
SpringBoot2.1.5、Redis5.0.5、Maven3.5.0、mybatis-Plus3.2.0
pom依赖
<!--web启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--redis启动器-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.7</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
application.yml文件
spring:
#Redis配置
redis:
host: test201
port: 6379
password:
#这些都有默认配置,如果没有特殊需求可不写
#连接超时时间(2.0中该参数的类型为Duration,这里在配置的时候需要指明单位
timeout: 2000ms
#连接池最大连接数(使用负值表示没有限制)
max-active: 8
#连接池最大阻塞等待时间(使用负值表示没有限制)
max-wait: -1ms
#连接池中的最大空闲连接
max-idle: 8
#连接池中的最小空闲连接
min-idle: 0
RedisConfig配置类
上边咱们说默认的RedisTemplate使用的序列化方式是JDK的,会存在特殊字符的问题而且性能损耗较大,我们创建Java Config修改默认实现的RedisTemplate序列化方式
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
/**
* Redis的配置类
* SDR项目操作Redis的话需要使用RedisTemplate对象
* 但是该对象默认使用的数据序列化方法是JDK的,可能会存在特殊字符
*
* key和hashKey 我们推荐使用String序列化
* value 我们推荐是JSON存储,使用Json序列化
*/
//添加Configuration注解,否则该类不会被加载
@Configuration
public class RedisConfig {
/**
* @Bean:创建对象放到IOC容器中
* RedisConnectionFactory:Redis的连接工厂,根据application.yml文件中的Redis的配置做Redis连接和连接池的管理
该对象在项目初始化时被创建,有一个实现类是 LettuceConnectionFactory
*/
@Bean
public RedisTemplate<String,Object> redisTemplate(RedisConnectionFactory connectionFactory){
//创建原生的RedisTemplate对象
RedisTemplate<String,Object> redisTemplate = new RedisTemplate<String,Object>();
//设置连接工厂
redisTemplate.setConnectionFactory(connectionFactory);
// 使用Jackson2JsonRedisSerialize替换 默认的JDK序列化
Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);
//创建JSON序列化的对象,value使用JSON序列化方式
ObjectMapper om = new ObjectMapper();
//设置序列化的策略
om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
//启用默认的类型
om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//引用JSON序列化
jackson2JsonRedisSerializer.setObjectMapper(om);
//创建String序列化,我们的key使用String序列化方式
StringRedisSerializer stringRedisSerializer = new StringRedisSerializer();
// key采用String的序列化方式
redisTemplate.setKeySerializer(stringRedisSerializer);
// hash的key也采用String的序列化方式
redisTemplate.setHashKeySerializer(stringRedisSerializer);
// value序列化方式采用JSON
redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
// hash的value序列化方式采用JSON
redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);
//使用该配置
redisTemplate.afterPropertiesSet();
//返回修改后的RedisTemplate对象
return redisTemplate;
}
}
测试类
package com.zhiyou100.redis;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.*;
import org.springframework.test.context.junit4.SpringRunner;
import java.util.*;
import java.util.concurrent.TimeUnit;
@RunWith(SpringRunner.class)
@SpringBootTest
public class Day0512RedisApplicationTests {
@Test
public void contextLoads() {
}
//注入RedisTemplate
@Autowired
private RedisTemplate redisTemplate;
/**
* 操作String
*/
@Test
public void test1(){
//获取操作String'类型的对象
ValueOperations valueOperations = redisTemplate.opsForValue();
//存储数据
try {
valueOperations.set("user1","添哥");
valueOperations.set("user2","小添哥哥");
System.out.println("存储数据成功");
//获取数据
String user1 = (String)valueOperations.get("user1");
String user2 = (String)valueOperations.get("user2");
System.out.println(user1 + "==>" + user2);
}catch (Exception e){
e.printStackTrace();
}
}
/**
* 操作list
*/
@Test
public void test2(){
ListOperations operations = redisTemplate.opsForList();
Long num = operations.leftPushAll("l1", "l11", "l22", "l33", "l44");
System.out.println("插入数据个数===》" + num);
//取值
List list = operations.range("l1", 0, -1);
for (Object o : list) {
System.out.println("list---》"+o);
}
}
/**
* 操作set
*/
@Test
public void test3(){
SetOperations operations = redisTemplate.opsForSet();
Long num = operations.add("s1", "s11", "s22", "s33", "s44", "s11", "s22", "s55");
System.out.println(num);
//获取
Set set = operations.members("s1");
for (Object o : set) {
System.out.println(o);
}
}
/**
* 操作zset
*/
@Test
public void test4(){
ZSetOperations operations = redisTemplate.opsForZSet();
operations.add("z1","z10",100.00);
operations.add("z1","z20",200.00);
operations.add("z1","z5",50.00);
//获取值
Set set = operations.range("z1", 0, -1);
for (Object o : set) {
System.out.println(o);
}
}
/**
* 操作Hash
*/
@Test
public void test5(){
HashOperations operations = redisTemplate.opsForHash();
//存储学生1
operations.put("stu:1:info","name","小添");
operations.put("stu:1:info","classRoom","快刀班");
//存储学生2
HashMap<String, Object> map = new HashMap<>();
//添加学生信息
map.put("name","晓添");
map.put("classRoom","吃瓜班");
operations.putAll("stu:2:info",map);
//获取数据1
Object name = operations.get("stu:1:info","name");
Object classRoom = operations.get("stu:1:info","classRoom");
System.out.println("学生1==》"+name+"==>"+classRoom);
//获取数据2
Map entries = operations.entries("stu:2:info");
//遍历map
Set set = entries.keySet();
for (Object o : set) {
System.out.println(o + "==>" + entries.get(o));
}
}
/**
* 对key的操作
*/
@Test
public void test6(){
//判断key是否存在
Boolean hasKey = redisTemplate.hasKey("stu:2:info");
System.out.println("stu:2:info===>"+hasKey);
//设置过期时间,k3键10秒过期
redisTemplate.expire("k3",10, TimeUnit.SECONDS);
//设置l1键在2020 5 13 7点过期
redisTemplate.expireAt("l1",new Date(2020,5,13,7,00,00));
}
}
//下方两个测试是使用RedisUtil中封装使用boundHashOps方式操作
@Test
public void t1(){
Student student = new Student();
student.setId("100");
student.setName("石靓仔");
student.setSex("男");
student.setBirth("1995-06-26");
redisUtil.putSome(Student.class,"student",student.getId(),student);
}
@Test
public void t2(){
List<Student> studentList = new ArrayList<>();
Cursor<Map.Entry<String, Object>> student = redisUtil.getSome("student", Student.class);
while (student.hasNext()){
System.out.println(student.getCursorId());
Map.Entry<String, Object> next = student.next();
Student student1 = (Student) next.getValue();
studentList.add(student1);
}
for (Student student1 : studentList) {
System.out.println(student1);
}
}
RedisUtil类
封装一些常用的操作,大家可以根据自己的需求进行封装,这个作为参考吧
package com.stt.springbootredis.util;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.CollectionUtils;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;
/**
* ClassName: RedisUtils
* Description:
* date: 2019/11/11 0011 下午 19:59
*
* @author stt
* @since JDK 1.8
*/
@Component
public class RedisUtil {
@Autowired
public RedisTemplate redisTemplate;
// =============================common============================
/**
26
* 指定缓存失效时间
27
* @param key 键
28
* @param time 时间(秒)
29
* @return
30
*/
public boolean expire(String key, long time) {
try {
if (time > 0) {
redisTemplate.expire(key, time, TimeUnit.SECONDS);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据key 获取过期时间
* @param key 键 不能为null
* @return 时间(秒) 返回0代表为永久有效
*/
public long getExpire(String key) {
return redisTemplate.getExpire(key, TimeUnit.SECONDS);
}
/**
* 判断key是否存在
* @param key 键
* @return true 存在 false不存在
*/
public boolean hasKey(String key) {
try {
return redisTemplate.hasKey(key);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除缓存
* @param key 可以传一个值 或多个
*/
@SuppressWarnings("unchecked")
public boolean del(String... key) {
if (key != null && key.length > 0) {
if (key.length == 1) {
return redisTemplate.delete(key[0]);
}
return redisTemplate.delete(CollectionUtils.arrayToList(key)) > 0 ? true : false;
}
return false;
}
/**
* 匹配所有的key
* @param pettern
* @return
*/
public Set<String> keys(String pettern){
if(pettern.trim() != "" && pettern != null){
return redisTemplate.keys(pettern);
}
return null;
}
// ============================String=============================
/**
* 普通缓存获取
* @param key 键
* @return 值
*/
public Object get(String key) {
return key == null ? null : redisTemplate.opsForValue().get(key);
}
/**
* 普通缓存放入
* @param key 键
* @param value 值
* @return true成功 false失败
*/
public boolean set(String key, Object value) {
try {
redisTemplate.opsForValue().set(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 普通缓存放入并设置时间
* @param key 键
* @param value 值
* @param time 时间(秒) time要大于0 如果time小于等于0 将设置无限期
* @return true成功 false 失败
*/
public boolean set(String key, Object value, long time) {
try {
if (time > 0) {
redisTemplate.opsForValue().set(key, value, time, TimeUnit.SECONDS);
} else {
set(key, value);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 递增
* @param key 键
* @param delta 要增加几(大于0)
* @return
*/
public long incr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递增因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, delta);
}
/**
* 递减
* @param key 键
* @param delta 要减少几(小于0)
* @return
*/
public long decr(String key, long delta) {
if (delta < 0) {
throw new RuntimeException("递减因子必须大于0");
}
return redisTemplate.opsForValue().increment(key, -delta);
}
// ================================Map=================================
/**
* HashGet
* @param key 键 不能为null
* @param item 项 不能为null
* @return 值
*/
public Object hget(String key, String item) {
return redisTemplate.opsForHash().get(key, item);
}
/**
* 获取hashKey对应的所有键值
* @param key 键
* @return 对应的多个键值
*/
public List<Object> hmget(String key) {
return redisTemplate.opsForHash().values(key);
}
/**
* HashSet
* @param key 键
* @param map 对应多个键值
* @return true 成功 false 失败
*/
public boolean hmset(String key, Map<String, Object> map) {
try {
redisTemplate.opsForHash().putAll(key, map);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* HashSet 并设置时间
* @param key 键
* @param map 对应多个键值
* @param time 时间(秒)
* @return true成功 false失败
*/
public boolean hmset(String key, Map<String, Object> map, long time) {
try {
redisTemplate.opsForHash().putAll(key, map);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value) {
try {
redisTemplate.opsForHash().put(key, item, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 向一张hash表中放入数据,如果不存在将创建
* @param key 键
* @param item 项
* @param value 值
* @param time 时间(秒) 注意:如果已存在的hash表有时间,这里将会替换原有的时间
* @return true 成功 false失败
*/
public boolean hset(String key, String item, Object value, long time) {
try {
redisTemplate.opsForHash().put(key, item, value);
if (time > 0) {
expire(key, time);
}
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 删除hash表中的值
* @param key 键 不能为null
* @param item 项 可以使多个 不能为null
*/
public void hdel(String key, Object... item) {
redisTemplate.opsForHash().delete(key, item);
}
/**
* 判断hash表中是否有该项的值
* @param key 键 不能为null
* @param item 项 不能为null
* @return true 存在 false不存在
*/
public boolean hHasKey(String key, String item) {
return redisTemplate.opsForHash().hasKey(key, item);
}
/**
* hash递增 如果不存在,就会创建一个 并把新增后的值返回
* @param key 键
* @param item 项
* @param by 要增加几(大于0)
* @return
*/
public double hincr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, by);
}
/**
* hash递减
* @param key 键
* @param item 项
* @param by 要减少记(小于0)
* @return
*/
public double hdecr(String key, String item, double by) {
return redisTemplate.opsForHash().increment(key, item, -by);
}
/**
* 存储对象
* @param key
* @param field
* @param value
*/
public void putSome(Class cls,String key,String field,Object value){
redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(cls));
redisTemplate.boundHashOps(key).put(field,value);
}
/**
* 获取对象
* @param key
* @param cls
* @return
*/
public Cursor<Map.Entry<String,Object>> getSome(String key,Class cls){
redisTemplate.setHashValueSerializer(new Jackson2JsonRedisSerializer<>(cls));
Cursor<Map.Entry<String, Object>> scan = redisTemplate.boundHashOps(key).scan(ScanOptions.scanOptions().build());
return scan;
}
// ============================set=============================
/**
* 根据key获取Set中的所有值
* @param key 键
* @return
*/
public Set<Object> sGet(String key) {
try {
return redisTemplate.opsForSet().members(key);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 根据value从一个set中查询,是否存在
* @param key 键
* @param value 值
* @return true 存在 false不存在
*/
public boolean sHasKey(String key, Object value) {
try {
return redisTemplate.opsForSet().isMember(key, value);
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将数据放入set缓存
* @param key 键
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSet(String key, Object... values) {
try {
return redisTemplate.opsForSet().add(key, values);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 将set数据放入缓存
* @param key 键
* @param time 时间(秒)
* @param values 值 可以是多个
* @return 成功个数
*/
public long sSetAndTime(String key, long time, Object... values) {
try {
Long count = redisTemplate.opsForSet().add(key, values);
if (time > 0)
expire(key, time);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 获取set缓存的长度
* @param key 键
* @return
*/
public long sGetSetSize(String key) {
try {
return redisTemplate.opsForSet().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 移除值为value的
* @param key 键
* @param values 值 可以是多个
* @return 移除的个数
*/
public long setRemove(String key, Object... values) {
try {
Long count = redisTemplate.opsForSet().remove(key, values);
return count;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
// ===============================list=================================
/**
* 获取list缓存的内容
* @param key 键
* @param start 开始
* @param end 结束 0 到 -1代表所有值
* @return
*/
public List<Object> lGet(String key, long start, long end) {
try {
return redisTemplate.opsForList().range(key, start, end);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 获取list缓存的长度
* @param key 键
* @return
*/
public long lGetListSize(String key) {
try {
return redisTemplate.opsForList().size(key);
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
/**
* 通过索引 获取list中的值
* @param key 键
* @param index 索引 index>=0时, 0 表头,1 第二个元素,依次类推;index<0时,-1,表尾,-2倒数第二个元素,依次类推
* @return
*/
public Object lGetIndex(String key, long index) {
try {
return redisTemplate.opsForList().index(key, index);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, Object value) {
try {
redisTemplate.opsForList().rightPush(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, Object value, long time) {
try {
redisTemplate.opsForList().rightPush(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
* @param key 键
* @param value 值
* @return
*/
public boolean lSet(String key, List<Object> value) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 将list放入缓存
*
* @param key 键
* @param value 值
* @param time 时间(秒)
* @return
*/
public boolean lSet(String key, List<Object> value, long time) {
try {
redisTemplate.opsForList().rightPushAll(key, value);
if (time > 0)
expire(key, time);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 根据索引修改list中的某条数据
* @param key 键
* @param index 索引
* @param value 值
* @return
*/
public boolean lUpdateIndex(String key, long index, Object value) {
try {
redisTemplate.opsForList().set(key, index, value);
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
/**
* 移除N个值为value
* @param key 键
* @param count 移除多少个
* @param value 值
* @return 移除的个数
*/
public long lRemove(String key, long count, Object value) {
try {
Long remove = redisTemplate.opsForList().remove(key, count, value);
return remove;
} catch (Exception e) {
e.printStackTrace();
return 0;
}
}
}