文章目录
4. SpringBoot整合数据库
4.1 整合Redis缓冲
是一个基于内存的单线程高性能Key-Value型数据库。整个数据库统统加载在内存当中进行操作,定期通过异步操作把数据库数据flash到硬盘上进行保存。因为是纯内存操作,Redis性能非常出色,每秒可以处理超过10万次读写操作,是已知性能最高的Key-Value数据库。
Redis可以存储键和五种不同类型的值之间的映射。键的类型只能为字符串,值支持五种数据类型:string(字符串)、list(列表)、set(集合)、hash(哈希类型)、Sorted Set(有序集合)。
4.1.1 Redis数据库操作
- 对字符串的操作
- 对List集合的操作
- 对set集合的操作
- 对Hash集合的操作
- 对zset的操作
4.1.2 SpringBoot整合Redis
-
创建SpringBoot项目
首先创建Spring Boot Web项目,添加Redis依赖,并添加Redis的缓冲连接池,常用的缓冲连接池为Lettuce和Jedis。Jedis在多线程使用同一个连接时,线程是不安全的,所以要使用连接池,得为每一个Jedis实例分配一个连接。Lettuce在多线程使用统一连接实例时,线程是安全的。Redis默认使用Lettuce连接池,依赖如下:
<!--Redis相关依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--Lettuce pool缓冲连接池--> <dependency> <groupId>org.apache.commons</groupId> <artifactId>commons-pool2</artifactId> </dependency>
-
配置文件
在application.properties中,添加如下配置信息:
# Redis数据库索引(默认为0) spring.redis.database=0 # Redis服务器地址 spring.redis.host=localhost # Redis服务器连接端口 spring.redis.port=6379 # Redis服务器连接密码(默认为空) spring.redis.password= # 连接池最大连接数(使用负值表示没有限制) spring.redis.lettuce.pool.max-active=8 # 连接池最大阻塞等待时间(使用负值表示没有限制) spring.redis.lettuce.pool.max-wait=-1ms # 连接池中的最大空闲连接 spring.redis.lettuce.pool.max-idle=8 # 连接池中的最小空闲连接 spring.redis.pool.min-idle=0
-
创建实体类
创建一个City类:
package com.shenziyi.db.entity; import java.io.Serializable; public class City implements Serializable { private int id; private String name; private String country; public City(int id, String name, String country) { this.id = id; this.name = name; this.country = country; } @Override public String toString() { return "City{" + "id=" + id + ", name='" + name + '\'' + ", country='" + country + '\'' + '}'; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getCountry() { return country; } public void setCountry(String country) { this.country = country; } }
-
创建Controller
实体类及Redis的连接信息添加完成后,创建一个CityController进行测试:
package com.shenziyi.db.controller; import com.shenziyi.db.entity.City; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.web.bind.annotation.GetMapping; public class CityController { @Autowired private RedisTemplate redisTemplate; @Autowired private StringRedisTemplate stringRedisTemplate; @GetMapping("/") public void testRedis(){ ValueOperations<String, String> ops = stringRedisTemplate.opsForValue(); //添加字符串 ops.set("name", "beixi"); String name=ops.get("name"); System.out.println(name); ValueOperations opsForValue = redisTemplate.opsForValue(); City city=new City(1, "北京", "中国"); //添加实体类 opsForValue.set("city", city); Boolean exists = redisTemplate.hasKey("city"); System.out.println("redis是否存在相应的key:"+exists); //删除 redisTemplate.delete("city"); //更新 redisTemplate.opsForValue().set("city", new City(2, "山西","中国")); //查询 City c2 = (City) redisTemplate.opsForValue().get("city"); System.out.println("从redis数据库中获取city:"+c2.toString()); } }
RedisTemplate和StringRedisTemplate都是Spring Data Redis为我们提供的模板类,用来对数据进行操作,都通过Spring提供的Serializer序列化到数据库。其中StringRedisTemplate是RedisTemplate的子类,只针对键值都是字符串的数据进行操作,采用的序列化方案是StringRedisSerializer,而RedisTemplate可以操作对象,采用的序列化方案是JdkSerializationRedisSerializer。在SpringBoot中默认提供这两个模板类,RedisTemplate和StringRedisTemplate都提供了Redis的基本操作方法。
RedisTemplate和StringRedisTemplate还为我们提供了下面几个数据访问方法:
- opsForList:操作list数据。
- opsForSet:操作Set数据。
- opsForZSet:操作ZSet数据。
- opsForHash:操作Hash数据。
-
测试:在浏览器中输入http://localhost:8080/访问,观察控制台上打印的日志信息:
也可以使用RedisClient客户端工具查看Redis缓冲数据库中的数据:
4.1.3 Redis缓冲在SpringBoot项目中的应用
在开发中,如果以相同的查询条件频繁查询数据库,会给数据库带来很大的压力。因此,我们需要对查询出来的数据进行缓存,这样客户端只需从数据库查询一次数据,然后放入缓存中,以后再次查询的时候可以从缓存中读取,这样便提高了数据的访问速度,并发步骤如下:
-
准备工作
开启Redis数据库,创建Spring Boot Web项目。使用MySql数据库创建user表用于项目开发数据的存储,如下:
-
导入依赖
在pom.xml文件中导入Redis数据库、持久层MyBatis、MySQL驱动等相关依赖(引入MySQL JDBC驱动时要根据自己安装的MySQL版本而定,win+r输入cmd,在终端输入mysql --version查看自己的MySQL版本,我的是8.0.24):
<!-- mybatis 与 spring boot 2.x的整合包 --> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.3.2</version> </dependency> <!--mysql JDBC驱动 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>8.0.24</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> <!--jedis连接池--> <dependency> <groupId>redis.clients</groupId> <artifactId>jedis</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-cache</artifactId> </dependency>
-
配置文件
接下来在application.yml中配置Redis连接信息、MySQL数据库连接等相关信息:
server: port: 8081 #数据库连接 spring: datasource: url: jdbc:mysql://localhost:3306/test?useUnicode=true driver-class-name: com.mysql.jdbc.Driver username: root password: admin123 ## Redis 配置 redis: ## Redis数据库索引(默认为0) database: 0 ## Redis服务器地址 host: localhost ## Redis服务器连接端口 port: 6379 ## Redis服务器连接密码(默认为空) password: jedis: pool: ## 连接池最大连接数(使用负值表示没有限制) #spring.redis.pool.max-active=8 max-active: 8 ## 连接池最大阻塞等待时间(使用负值表示没有限制) #spring.redis.pool.max-wait=-1 max-wait: -1 ## 连接池中的最大空闲连接 #spring.redis.pool.max-idle=8 max-idle: 8 ## 连接池中的最小空闲连接 #spring.redis.pool.min-idle=0 min-idle: 0 ## 连接超时时间(毫秒) timeout: 1200 #将themilef的默认缓存禁用,热加载生效 thymeleaf: cache: false #mybatis的下划线转驼峰配置 configuration: map-underscore-to-camel-case: true #另外一种打印语句的方式 log-impl: org.apache.ibatis.logging.stdout.StdOutImpl #打印sql时的语句 logging: level: com: acong: dao: debug
-
实体类
创建与数据库相对应的User实体类:
package com.shenziyi.db.entity; import java.io.Serializable; public class User implements Serializable { private int id; private String name; private String pwd; public User() { } public User(int id, String name, String pwd) { this.id = id; this.name = name; this.pwd = pwd; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPwd() { return pwd; } public void setPwd(String pwd) { this.pwd = pwd; } }
-
创建持久层
接着是Mapper持久层Dao,这里主要用注解写比较方便,也可以使用MyBatis的XML配置文件写SQL语句:
package com.shenziyi.db.Dao; import com.shenziyi.db.entity.User; import org.apache.ibatis.annotations.*; import java.util.List; @Mapper public interface UserDao { @Select("select * from user") List<User> queryAll(); @Select("select * from user where id = #{id}") User findUserById(int id); }
-
业务层
创建UserService,这里主要使用Redis模板来写:
package com.shenziyi.db.service; import com.shenziyi.db.Dao.UserDao; import com.shenziyi.db.entity.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.data.redis.core.ValueOperations; import org.springframework.stereotype.Service; import java.util.List; import java.util.concurrent.TimeUnit; @Service public class UserService { @Autowired private UserDao userDao; @Autowired private RedisTemplate redisTemplate; public List<User> queryAll() { return userDao.queryAll(); } /** * 获取用户策略:先从缓存中获取用户,没有则取数据表中 数据,再将数据写入缓存 */ public User findUserById(int id) { String key = "user_" + id; ValueOperations<String, User> operations = redisTemplate.opsForValue(); //判断redis中是否有键为key的缓存 boolean hasKey = redisTemplate.hasKey(key); if (hasKey) { User user = operations.get(key); System.out.println("从缓存中获得数据:" + user.getName()); System.out.println("------------------------------------"); return user; } else { User user = userDao.findUserById(id); System.out.println("查询数据库获得数据:" + user.getName()); System.out.println("------------------------------------"); // 写入缓存 operations.set(key, user, 5, TimeUnit.HOURS); return user; } } }
-
控制层
创建UserController,用于暴露访问接口:
package com.shenziyi.db.controller; import com.shenziyi.db.entity.User; import com.shenziyi.db.service.UserService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.util.HashMap; import java.util.List; import java.util.Map; @RestController public class UserController { @Autowired private UserService userService; @RequestMapping("/queryAll") public List<User> queryAll(){ List<User> lists = userService.queryAll(); return lists; } @RequestMapping("/findUserById") public Map<String, Object> findUserById(@RequestParam int id){ User user = userService.findUserById(id); Map<String, Object> result = new HashMap<>(); result.put("id", user.getId()); result.put("name", user.getName()); result.put("pwd", user.getPwd()); return result; } }
-
测试
这里主要使用RedisTemplate来对Redis操作,每次访问controller暴露的接口,首先判断Redis缓存中是否存在该数据,若不存在就从数据库中读取数据,然后保存在Redis缓存中,当下次访问的时候,就直接从缓存中取出来。这样就不用每次都执行SQL语句,能够提高访问速度。但是当数据保存到缓存中时,需要设置键和值及超时删除,注意设置超市删除缓存时间不用太长,否则会给服务器压力。
启动SpringBoot项目,在浏览器输入http://localhost:8081/findUserById?id=1,当我们第一次访问数据时从数据库获取,再次访问时则从缓存中获取保存的数据:(没有id为3的数据,所以报错)
4.2 整合MySQL
4.2.1 SpringBoot整合MySQL
SpringBoot集成MySQL非常简单。SpringBoot连接数据库有四种方式:
- 采用JDBC直接连接。
- 采用JdbcTemplate连接。
- 采用SpringDataJPA连接。
- 框架连接。
采用JDBC方式直接连接烦琐,易错,我们直接略过,不做考虑。通过MyBatis、SpringDataJPA等连接,我们后续再讲。JdbcTemplate在JDBC的基础上做了大量的封装,本节采用JdbcTemplate连接MySQL。
-
引入依赖
<!-- MySQL连接Java的驱动程序 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <!-- 支持通过JDBC连接数据库 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency>
-
添加数据库配置
在application.yml文件中添加如下配置:
spring: datasource: #MySQL连接信息 serverTimezone=GMT%2B8解决时区时间差报错问题 url: jdbc:mysql://localhost:3306/test?serverTimezone=GMT%2B8 # 账号 username: root # 密码 password: admin123 # 驱动 driver-class-name: com.mysql.jdbc.Driver
-
设计表和实体
配置完信息之后,在test数据库中新建一张用户表user(之前已建好)
表和数据准备好之后,在项目中新建User实体类:
package com.shenziyi.entity; public class User { private int id; private String name; private String password; @Override public String toString() { return "User{" + "id=" + id + ", name='" + name + '\'' + ", password='" + password + '\'' + '}'; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } }
-
控制层
创建UserController类:
package com.shenziyi.controller; import com.shenziyi.entity.User; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.core.RowMapper; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; import java.sql.ResultSet; import java.sql.SQLException; import java.util.List; @Controller public class UserController { @Autowired JdbcTemplate jdbcTemplate; @ResponseBody @RequestMapping("/list") public List mySqlTest(){ String sql="select * from user"; /*query()是JdbcTemplate对象中的方法,RowMapper对象可以查询数据库中的数据*/ List<User>users=jdbcTemplate.query(sql,new RowMapper<User>(){ @Override /*RowMapper对象通过调用mapRow()方法将数据库中的每一行数据封装成User对象,并返回*/ public User mapRow(ResultSet rs,int i)throws SQLException { User user = new User(); user.setId(rs.getInt("id")); user.setName(rs.getString("name")); user.setPassword(rs.getString("password")); return user; } }); System.out.println("查询成功: "+users); return users; } }
代码解释如下:
- JdbcTemplate:JDBC连接数据库的工具类。
- Query():query()是JdbcTemplate对象中的方法,RowMapper可以查询数据库中的数据。
- mapRow():RowMapper对象通过调用mapRow()方法将数据库中的每一行数据封装成User对象并返回。
访问http://localhost:8080/list路径即可查询数据信息: