Redis缓存系列--(四)Redis基础数据类型在Java中的使⽤
Redis在Java中的基础使⽤
Redis作为缓存主要使⽤在Java应⽤或者服务中读多写少的场景,从⽽来提⾼⽤户请求服务器数据的速度。⽽且Redis服务器⾯对Java的⾼并发请求时,不会出现并发问题,因为Redis服务器在执⾏命令的时候,是原⼦性的操作。Redis在Java中的使⽤⽅式
以下⽰例项⽬采⽤SpringMvc+JdbcTemplate的框架,同时使⽤Druid作为数据库连接池,⽰例代码只展⽰了核⼼的代码,有关SpringMvc配置⽂件以及相关实体类、控制器类以及⽇志配置在这⾥不做过多赘述,完整项⽬代码请参考。下边实例代码模拟了⼀个查⽤户信息的应⽤场景来实现查询出数据库的数据时同时使⽤Redis来缓存当前查询的⽤户信息的场景。
使⽤JedisPool来构造Redis连接池,然后通过Resource()来获取Redis客户端对象。
优点:简单易操作
缺点:冗余代码较多,如果有多个服务请求需要进⾏Redis操作,那么每个请求都需要来获取和释放Redis客户端对象,同时还要来对Java对象进⾏字符串的序列化转换。
核⼼的代码操作
步骤1:添加相关依赖包
<properties>
<java-version>1.8</java-version>
<org.springframework-version>5.1.5.RELEASE</org.springframework-version>
<org.slf4j-version>1.7.12</org.slf4j-version>
<!-- json -->
&dehaus.version>1.9.13</dehaus.version>
<aspect-version>1.8.0</aspect-version>
&tor.java.version>8.0.11</tor.java.version>
<druid.version>1.1.20</druid.version>
<sdr.version>2.1.5.RELEASE</sdr.version>
<lettuce.version>5.1.4.RELEASE</lettuce.version>
<jedis.version>2.9.2</jedis.version>
</properties>
<dependencies>
<!--spring相关依赖包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${org.springframework-version}</version>redis支持的五种数据类型
</dependency>
<!--jdbctemplate依赖包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>${org.springframework-version}</version>
</dependency>
<!-- 数据库驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>${tor.java.version}</version>
</dependency>
<!-- 加⼊druid数据源依赖包 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>${druid.version}</version>
</dependency>
<!-- Jackson JSON Processor -->
<dependency>
<groupId>com.</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.8</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.28</version>
</dependency>
<!--redis-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
<version>${jedis.version}</version>
</dependency>
<!-- 使⽤RedisTemplate所需要的依赖包-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-redis</artifactId>
<version>${sdr.version}</version>
</dependency>
<!--添加lombok依赖 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.12</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>${org.slf4j-version}</version>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>jcl-over-slf4j</artifactId>
<version>${org.slf4j-version}</version>
<scope>runtime</scope>
</dependency>
<dependency>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
<scope>runtime</scope>
</dependency>
<!-- 加⼊spring测试依赖包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>${org.springframework-version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<!-- Servlet -->
<dependency>
<groupId>at</groupId>
<artifactId>tomcat-servlet-api</artifactId>
<version>7.0.30</version>
<scope>provided</scope>
</dependency>
</dependencies>
步骤2:加载JedisPool以Javabean的形式注⼊到spring容器中
/**
* 通过注解的形式来配置spring中的bean
* @author young
*/
@Configuration
public class AppConfig {
/
**
* 使⽤Redis⽅式1:通过JedisPool来获取Redis client
* @return
*/
@Bean
public JedisPool jedisPool(){
JedisPool jedisPool = new JedisPool("127.0.0.1", 6379);
return jedisPool;
}
}
步骤3:编写服务层代码
/
**
* ⽤户服务层代码
*/
@Service
public class UserService {
private Logger logger = Logger(UserService.class);
@Autowired
JdbcTemplate jdbcTemplate;
@Autowired
JedisPool jedisPool;
/**
* ⽅式1:根据ID查询⽤户信息 (redis缓存,⽤户信息以json字符串格式存在(序列化))
* 好处:查询出所有的⽤户信息
* 坏处:如果只需要其中的部分信息,则会增加⽹络传输信息量⼤的压⼒
* @param userId
*/
public User findUserById(String userId) {
User user = null;
Jedis jedis = null;
try {
jedis = Resource();
// 1. 查询Redis是否有数据 -- string⽅式
String result = (userId);
if(result != null && !"".equals(result) ) {
//json字符串转换为User,把缓存中的值,返回给你的⽤户
user = JSONObject.parseObject(result, User.class);
// 命中缓存
return user;
}
// 2. 查询数据库
String sql = "select id,user_name,password,name,age from tb_user where id=?";
user = jdbcTemplate.queryForObject(sql, new String[]{userId}, new BeanPropertyRowMapper<>(User.class));            // 3. 数据塞到redis中 // ⽅式1:json格式字符串放⼊value为String类型的缓存中
String userJsonStr = JSONString(user);
jedis.set(userId,userJsonStr);
}catch (Exception e){
System.out.Message());
<(e.getMessage());
<("userservice error:",e);
}
finally {
if (jedis != null) {
jedis.close();
}
}
return user;
}
/**
* ⽅式1:根据ID查询⽤户信息
* (redis缓存,⽤户信息以Redis中hash的数据结构来存储))
* @param userId
*/
public User findUserById1(String userId) {
User user = null;
Jedis jedis = null;
try {
jedis = Resource();
// 1. 查询Redis是否有数据 -- 通过hash的⽅式获取
Map<String, String> userMap = jedis.hgetAll(userId);
//将获取的map对象转换为User对象
if(userMap != null && userMap.size() >0 ) {
logger.info("缓存命中");
user = new User();
user.setId(Integer.valueOf(userId));
user.("username"));
user.("password"));
user.("name"));
user.("age"));
user = jdbcTemplate.queryForObject(sql, new String[]{userId}, new BeanPropertyRowMapper<>(User.class));
// 3. 使⽤value为hash的数据结构,将⽤户数据放⼊Redis缓存中
HashMap<String, String> userInfo = new HashMap<>();
userInfo.put("username", String.UserName()));
userInfo.put("password", String.Password()));
userInfo.put("name", String.Name()));
userInfo.put("age", String.Age()));
jedis.hmset(userId, userInfo);
}catch (Exception e){
System.out.Message());
<(e.getMessage());
<("userservice error:",e);
}
finally {
if (jedis != null) {
jedis.close();
}
}
return user;
}
/**
* 根据ID查询⽤户名称(在缓存⽤户信息时使⽤hash的⽅式对⽤户信息进⾏存储,那么在获取⽤户的部分信息时就可以使⽤hget命令来获取⽤户具体某个字段的信息了)
*/
public String findUserNameById(String userId) {
String uname = null;
Jedis jedis = null;
try {
jedis = Resource();
// 1. 查询Redis是否有数据
uname = jedis.hget(userId, "username"); // 向远程的Redis发起查询请求
if (uname != null && !"".equals(uname)) {
return uname; // 命中缓存
}
return null;
} finally {
if (jedis != null) {
jedis.close();
}
}
}
}
如果使⽤string对⽤户的信息进⾏存储,那么如果查询的⽤户的信息量过⼤就会导致如果Redis缓存了之后,假如再有服务请求⽤户的部分信息时,如果使⽤该⽤户信息的缓存,就需要对获取⽤户的缓存所有内容进⾏解析,这样就会增加⽹络的负担;所以可以使⽤hash的数据结构来缓存⽤户信息,在获
取部分信息时,可以直接使⽤hget的⽅式来获取某⼀字段信息。
使⽤RedisTemplate来进⾏Redis缓存
优点:省去了创建客户端和关闭客户端的冗余代码,不需要对从Redis查询出来的对象或者从数据库查询出来的对象进⾏包装
缺点:同样对业务代码有侵⼊,有⼀定的耦合性,不利于对已开发系统的改造
核⼼代码操作
步骤⼀:添加RedisTemplate的maven依赖包,请参考上边⽰例中的maven
步骤⼆:在全局配置类中添加RedisTemplate相关的java bean
@Configuration
public class AppConfig {
/**
* 使⽤Redis⽅式2:通过RedisConnectionFactory来获取RedisTemplate,从⽽进⾏Redis的相关操作(注解⽅式同样需要)
* @return
*/
@Bean
public RedisConnectionFactory redisConnectionFactory(){
//配置Redis的主机和端⼝
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
//
RedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration);
return redisConnectionFactory;
}
/**
* ⽅式2:加载RedisTemplate bean
* @param redisConnectionFactory
* @return
*/
@Bean
public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory){
RedisTemplate redisTemplate = new RedisTemplate();
redisTemplate.setConnectionFactory(redisConnectionFactory);
redisTemplate.setKeySerializer(StringRedisSerializer.UTF_8);
return redisTemplate;
}
}
步骤三:编写服务层核⼼代码,注⼊RedisTemplate类
@Service
public class UserService {
private Logger logger = Logger(UserService.class);
@Autowired
JdbcTemplate jdbcTemplate;
/
**
* ⽅式2:使⽤模板⽅法来操作Redis客户端
*/
@Autowired
RedisTemplate redisTemplate;
/**
* ⽅式2:使⽤RedisTemplate实现Redis缓存
* 优点:省略了创建和关闭Redis客户单以及对查询对象的序列化解析和包装
* @param userId
* @return
*/
public User findUserById2(String userId){
User user = null;
user =(User) redisTemplate.opsForValue().get(userId);
// 1. 查询Redis是否有数据 -- 通过hash的⽅式获取
//判断缓存数据是否存在
if(user != null) {
logger.info("缓存命中");
user = jdbcTemplate.queryForObject(sql, new String[]{userId}, new BeanPropertyRowMapper<>(User.class));
// 3. 使⽤value为string的数据结构,将⽤户数据放⼊Redis缓存中
redisTemplate.opsForValue().set(userId,user);
return user;
}
}
这种⽅式同样的,如果仍然使⽤String作为⽤户信息的存储形式,同样在获取部分⽤户信息的数据上会不太⽅便,同样也可以使⽤hash的⽅式对⽤户信息进⾏存储。所以要根据项⽬的具体需求,
对⽤户信息采⽤Redis适当的存储⽅式进⾏存储。
使⽤Spring提供的缓存注解@EnableCache和@Cacheable进⾏缓存
优点:使⽤Spring提供的注解可以实现⽆侵⼊的功能改造,降低代码的耦合度,同时减少了对Redis客户端的连接和释放等冗余操作。
缺点:可读性相对较差,需要了解注解⾥每个参数的具体含义以及注解的使⽤。
核⼼代码操作如下(maven依赖同上)
步骤⼀:加载全局配置的Bean,通过注解@EnableCaching开启缓存
@Configuration
@EnableCaching
public class AppConfig {
/**
* 通过注释来获取properties配置⽂件中的⾃定义值
*/
@Value("${dis.host}")
String host;
@Value("${dis.port}")
int port;
/**
* 配置RedisConnectionFactory
* @return
*/
@Bean
public RedisConnectionFactory redisConnectionFactory(){
//配置Redis的主机和端⼝
RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
redisStandaloneConfiguration.setHostName(host);
redisStandaloneConfiguration.setPort(port);
RedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory(redisStandaloneConfiguration);
return redisConnectionFactory;
}
/**
* 使⽤⽅式3:使⽤注解⽅式来对业务代码的结果进⾏缓存时需要加载CacheManager对象
*/
@Bean
public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory){
RedisCacheWriter redisCacheWriter = LockingRedisCacheWriter(redisConnectionFactory);
//指定Redis的key和value序列化⽅式
RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()  .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())) .
serializeValuesWith(RedisSerializationContex        CacheManager cacheManager = new RedisCacheManager(redisCacheWriter,redisCacheConfiguration);
((RedisCacheManager) cacheManager).isTransactionAware();
return cacheManager;
}
}
步骤⼆:在服务类向对应的⽅法上添加@Cacheable注解并配置缓存key的相关参数
/**
* ⽅式3:使⽤注解⽅式实现Redis缓存,redis的key为注解的value+key;
*      其中key是⼀个EL表达式,根据⽅法传递的参数⽽⾃动变化
* 优点:减少缓存程序对已有业务代码的侵⼊,使它与业务代码解耦
* @param userId
* @return
*/
@Cacheable(value = "tb_user",key = "#userId")
public User findUserById3(String userId){
//1.在查询数据库前会查询缓存是否存在
User user;
// 2. 查询数据库
String sql = "select id,user_name,password,name,age from tb_user where id=?";
logger.info("执⾏的sql语句为:" + sql);
user = jdbcTemplate.queryForObject(sql, new String[]{userId}, new BeanPropertyRowMapper<>(User.class));
//3.查询数据库后会将数据写⼊Redis缓存
return user;
}
注解⽅式主要运⽤了Java的反射机制和Spring的AOP特性,我们同样的也可以通过⾃定义注解以及AOP来实现⾃⼰的缓存注解,从⽽满⾜项⽬的各种需求。
这⾥还需要注意⼀个问题:当⽤户查询数据的时候,将数据放⼊Redis缓存;同时在⽤户更新数据的时候,⽤户数据更新到数据库之后,也要有Redis的相关操作,⽐如删除原来的缓存。这样在⽤户下次进⾏查询的时候,会读取数据
库的最新数据,然后将最新数据再次缓存到Redis中。
使⽤Redis注解的⽅式进⾏缓存删除的代码如下:
/**
* ⽅式3:当有数据更新时,先更新数据库数据然后清除缓存,等待下⼀次有查询的时候再将数据库数据存⼊Redis缓存
* @param user ⽤户的更新信息
* @return 最新⽤户的信息
*/
@CacheEvict(value = "tb_user",key = "#user.id")
public User updateUser(User user) { // 同步关键字 --- 应该能够解决 --
//修改数据数据
String sql = "update tb_user set user_name = ? where id=?";
jdbcTemplate.update(sql, new String[]{UserName(), String.Id())});
return user;
}
总结
Redis在Java中的使⽤⽅式分为以下⼏种:
使⽤原始的Jedis或者JedisPool来获取Redis的客户端对象,然后对其进⾏相应的Redis操作。
使⽤RedisTemplate对象来对Redis客户端进⾏操作。
使⽤Spring提供的@EnableCaching和@Cacheable注解对相关⽅法进⾏Redis的缓存操作。
使⽤⾃⼰定义的缓存注解来对相关⽅法进⾏Redis缓存操作,具体的使⽤请参考。
各种⽅式各有优缺点,⼀般采⽤2或者3的⽅式相对来说较为简洁和⽅便,使⽤4⽅式更具有灵活性,具体的项⽬需要来决定采⽤哪种⽅式进⾏实现。

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。