(六) SpringBoot起飞之路-整合JdbcTemplate-Druid-MyBatis-Redis

2020-06-09 13:04:03 浏览数 (1)

说明:

  • SpringBoot 起飞之路 系列文章的源码,均同步上传到 github 了,有需要的小伙伴,随意去 down
    • https://github.com/ideal-20/Springboot-Study-Code
  • 才疏学浅,就会点浅薄的知识,大家权当一篇工具文来看啦,不喜勿喷哈 ~

引言

前面分别介绍了一下一些入门的配置和基础,同时引入了Thymeleaf模板引擎,而练习一些小 Demo 就要开始涉及到数据了,所以今天来讲一下如何在 SpringBoot 中整合常见数据相关的一些技术:JdbcTemplate、Druid、MyBatis,重点熟悉下后两者,用的也是比较多的

这一篇所介绍的内容,都不是新内容,不涉及太多的语法,关键是整合,关于这三样介绍也就简单提一下

最后开始之前,还有一个需要提及的

SpringBoot 中关于数据库相关的处理,均使用 Spring Data,它是 Spring 全家桶中的一个子项目,能同时支持关系/非关系型数据库的操作,能够极大地简化数据库访问的操作

更多内容,可以去看一下官网:

https://spring.io/projects/spring-data

(一) 整合 JdbcTemplate

虽然叫做 整合 JdbcTemplate,但本质上还是整合 JDBC ,只不过 JdbcTemplate 对原生 JDBC 进行了一些简化

(1) 引入依赖

我们首先创建一个 Project 或者 Module,然后初始化一个 SpringBoot 项目,除了基本的 Web 以外,再左侧 SQL 选项中选择 JDBC API ,这也就是引入关于整合 JDBC 的依赖

来看一下 pom,也就是引入了 spring-boot-starter-jdbc 这个启动器,其中一些依赖封装好了

详情可以参考官网文档,我贴了两个版本,更多版本,自己可以去翻阅一下

https://docs.spring.io/spring-boot/docs/2.3.0.RELEASE/reference/html/using-spring-boot.html#using-boot-structuring-your-code

https://docs.spring.io/spring-boot/docs/2.2.7.RELEASE/reference/html/using-spring-boot.html#using-boot-starter

代码语言:javascript复制
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

之前的旧版本,mysql-connector-java 应该也会被引入的,如果没有也不必担心,后面写数据库相关配置的时候,我们也会引入

(2) 修改配置

接着,我们需要在配置文件中配置数据库的一些信息,例如用户名密码等等,我们用 yml 配,用 properties 也是一样的

关于 url 这个选项多说一句,我的表名 springboot_mybatis_test,但是如果不设置时区或者一些编码,不同的版本使用中可能会出现一些错误(5 可以不配置时区,高版本的需要)

而在设定时区的时候,像我下面设置为 serverTimezone=UTC,会比中国时间早8个小时,所以中国可以选择Asia/Shanghai或者Asia/Hongkong

代码语言:javascript复制
spring:
  datasource:
    username: root
    password: root99
    # serverTimezone=Asia/Shanghai 这样解决时区的报错会好一些
    url: jdbc:mysql://localhost:3306/springboot_mybatis_test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver

还有一个需要注意的,就是在选择驱动的时候,如果前面初始化项目的时候,依赖中没有加入 mysql-connector-java 这个以来,这里配置是会报红的,点击或者快捷会提示你引入依赖,而就像下面这样,默认不指定版本的依赖是 8.x 版本的 mysql-connector-java

代码语言:javascript复制
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

所以配置时就要选择 com.mysql.cj.jdbc.Driver

如果使用旧版的就会提示,com.mysql.jdbc.Driver 过时了

Loading class com.mysql.jdbc.Driver'. This is deprecated. The new driver class iscom.mysql.cj.jdbc.Driver'. The driver is automatically registered via the SPI and manual loading of the driver class is generally unnecessary.

如果想要用旧版,你也可以显性的指定 mysql-connector-java 版本

代码语言:javascript复制
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>5.1.26</version>
</dependency>

(3) 使用 JdbcTemplate 进行 CURD

经过简单的配置,其实已经可以进行原生 JDBC 的增删改查了,原生的确实有点繁琐,Spring 已经给我们进行了一定的简化,也就是 JdbcTemplate,这个我们应该也很早就用过了

A:查询

关于查询,我给大家展示两种方式,一种就是如果我们存在实体对应数据库中的内容,还有一种就是没有对应的实体的查询方式,同时别忘记注入 JdbcTemplate

  • 有实体:在query中new 一个 BeanPropertyRowMapper,也就是让 JdbcTemplate 帮我们把查询结果集ResultSet 的每一行结果都使用 BeanPropertyRowMapper.mapRow() 方法,转化成我们想要的Java类对象
  • 没有实体:使用queryForList方法,返回一个集合,集合中是一个个 map
代码语言:javascript复制
@Controller
@RequestMapping("/jdbc")
public class JdbcTemplateController {
    @Autowired
    JdbcTemplate jdbcTemplate;

    //有对应实体
    @RequestMapping("/userList")
    public void userList(){
        String sql = "select * from user";
        ArrayList<User> users = (ArrayList<User>) jdbcTemplate.query(sql,
                new BeanPropertyRowMapper<User>(User.class));
        for (User user : users){
            System.out.println(user);
        }
    }

    //没有对应实体
    @RequestMapping("/userList")
    @ResponseBody
    public List<Map<String, Object>> userList(){
        String sql = "select * from user";
        List<Map<String, Object>> maps = jdbcTemplate.queryForList(sql);
        return maps;
    }
}

这是查到的对象集合

代码语言:javascript复制
User{uid=1, username='zhangsan', password='666', nickname='飞翔的企鹅'}
User{uid=2, username='lisi', password='666', nickname='伤心小男孩'}

这是查到的map集合

代码语言:javascript复制
[{"uid":1,"username":"zhangsan","password":"666","nickname":"飞翔的企鹅"},{"uid":2,"username":"lisi","password":"666","nickname":"伤心小男孩"}]

B:插入

代码语言:javascript复制
@RequestMapping("/addUser")
@ResponseBody
public String addUser(){
    //插入语句,注意时间问题
    String sql = "insert into user(uid, username,password,nickname) values (NULL,'wangwu','888','王五昵称')";
    jdbcTemplate.update(sql);
    //查询
    return "添加用户成功";
}

C:修改

代码语言:javascript复制
//修改用户信息
@RequestMapping("/updateUser/{uid}")
@ResponseBody
public String updateUser(@PathVariable("uid") int uid){
    //插入语句
    String sql = "update user set username=?,password=?,nickname=? where uid="  uid;
    //数据
    String[] s = new String[3];
    s[0] = "jack";
    s[1] = "666";
    s[2] = "杰克";
    jdbcTemplate.update(sql,s);
    //查询
    return "修改用户信息成功";
}

D:删除

代码语言:javascript复制
//删除用户
@RequestMapping("/deleteUser/{uid}")
@ResponseBody
public String delUser(@PathVariable("uid") int uid){
    //插入语句
    String sql = "delete from user where uid=?";
    jdbcTemplate.update(sql,uid);
    //查询
    return "删除用户成功";
}

(二) 整合 Druid

(1) SpringBoot 默认连接池

首先要知道 Druid 是阿里的开源的一款数据库连接池,而连接池就是用来解决数据库建立关闭等消耗资源,而影响性能的问题的,我们以前应该用过挺多种的,例如 C3P0,或者 DBCP等等

而 SpringBoot 默认是有配置数据源的,我们可以简单看一下,就在前面 JDBC 的例子上看,在测试类中看一下

代码语言:javascript复制
@SpringBootTest
class Springboot08JdbcApplicationTests {

    @Autowired
    DataSource dataSource;

    @Test
    void contextLoads() throws SQLException {
        // 数据源
        System.out.println(dataSource.getClass());
        //获得连接
        Connection connection = dataSource.getConnection();
        System.out.println(connection);
        connection.close();
    }

}

看一下打印的结果

代码语言:javascript复制
class com.zaxxer.hikari.HikariDataSource
HikariProxyConnection@118492650 wrapping com.mysql.cj.jdbc.ConnectionImpl@712cfb63

可以看到,SpringBoot 默认使用的是 Hikari 连接池,如果想要看一下相关源码,可以去看一下这两个类

DataSourceAutoConfiguration、DataSourceConfiguration

这个数据库连接池,现在号称速度最快,不过 Hikari 和 Druid 都是很优秀的数据库连接池,Druid 不过功能也更加多一些,它还有一些例如统计或者 sql 拦截等等功能,后面整合就可以看到

(2) 引入依赖

首先在 Pom 中添加 Druid 的依赖,可以去下列网址看一下最新的版本

https://mvnrepository.com/artifact/com.alibaba/druid

代码语言:javascript复制
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.22</version>
</dependency>

(3) 修改配置

接着在配置中使用 spring.datasource.type 指定自定义的数据源类型,值就要用的连接池实现的完全限定名,例如:com.alibaba.druid.pool.DruidDataSource

我们用接着用上面的 yml 配置,而指定完 Druid 后,进行进行一些默认的配置,例如连接初始化大小、最大最小连接数等等吗,具体内容不展开讲,对着官方文档很好理解

代码语言:javascript复制
spring:
  datasource:
    username: root
    password: root99
    url: jdbc:mysql://localhost:3306/springboot_mybatis_test?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
    # 自定义数据源
    type: com.alibaba.druid.pool.DruidDataSource 

    #druid 数据源专有配置
    initialSize: 5
    minIdle: 5
    maxActive: 20
    maxWait: 60000
    timeBetweenEvictionRunsMillis: 60000
    minEvictableIdleTimeMillis: 300000
    validationQuery: SELECT 1 FROM DUAL
    testWhileIdle: true
    testOnBorrow: false
    testOnReturn: false
    poolPreparedStatements: true

    #配置监控统计拦截的filters
    #stat:监控统计、log4j:日志记录、wall:防御sql注入
    filters: stat,wall,log4j
    maxPoolPreparedStatementPerConnectionSize: 20
    useGlobalDataSourceStat: true
    connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500

如果运行的时候报错 java.lang.ClassNotFoundException: org.apache.log4j.Priority 则导入 log4j 依赖即可

地址:https://mvnrepository.com/artifact/log4j/log4j

代码语言:javascript复制
<dependency>
    <groupId>log4j</groupId>
    <artifactId>log4j</artifactId>
    <version>1.2.17</version>
</dependency>

(4) 绑定自定义配置参数

写了上面的参数,还是有问题的,例如下面的初始化大小,或者最大最小连接数等一些私有化的配置是不会生效的,因为Spring Boot 默认是不注入这些属性值的,所以我们需要自己进行绑定

创建一个包为 config ,创建 DruidConfig 类

我们首先要做的就是将全局配置文件中的那些 Druid 的配置绑定到 com.alibaba.druid.pool.DruidDataSource,这样就可以生效了 (@ConfigurationProperties

然后将自定义的 Druid数据源添加到容器中,这样就不需要 SpringBoot 自己创建了

代码语言:javascript复制
@Configuration
public class DruidConfig {

    @ConfigurationProperties(prefix = "spring.datasource")
    @Bean
    public DataSource druidDataSource() {
        return new DruidDataSource();
    }

}

可以简单测试一下是不是生效了

代码语言:javascript复制
@SpringBootTest
class Springboot08JdbcApplicationTests {

    @Autowired
    DataSource dataSource;

    @Test
    void contextLoads() throws SQLException {
        // 数据源
        System.out.println(dataSource.getClass());
        //获得连接
        Connection connection = dataSource.getConnection();
        System.out.println(connection);

        DruidDataSource druidDataSource = (DruidDataSource) dataSource;
        System.out.println("数据源最大连接数:"   druidDataSource.getMaxActive());
        System.out.println("数据源初始化连接数:"   druidDataSource.getInitialSize());
        //关闭连接
        connection.close();
    }

}

运行能看出,那些基本的配置都是生效了的

代码语言:javascript复制
class com.alibaba.druid.pool.DruidDataSource
com.alibaba.druid.proxy.jdbc.ConnectionProxyImpl@74e6094b
druidDataSource 数据源最大连接数:20
druidDataSource 数据源初始化连接数:5

(5) 配置数据源监控

还有一个非常有特点的功能,那就是 Druid 的数据源监控,提供了一个后台管理页面

继续在上面新建的 DruidConfig 配置类中增加内容

  • 这里采用 Spring Boot 注册 Servlet 方式,是因为内置 Servlet 容器时没有web.xml文件,都是一些固定的写法
  • 关于例如用户名等的参数,可以再Druid下 StatViewServlet 的父类 ResourceServlet 中找到
  • 还可以设置访问人员与禁止访问人员相关的
代码语言:javascript复制
@Bean
public ServletRegistrationBean statViewServlet() {
    ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");

    Map<String, String> initParams = new HashMap<>();
    initParams.put("loginUsername", "admin"); //后台管理界面的登录账号
    initParams.put("loginPassword", "admin888"); //后台管理界面的登录密码

    //initParams.put("allow", "localhost"):表示只有本机可以访问
    //initParams.put("allow", ""):表示后台允许所有访问
    initParams.put("allow", "");

    //设置初始化参数
    bean.setInitParameters(initParams);
    return bean;
}

来访问一下:

我们在地址栏输入 http://localhost:8080/druid/ 下的任何路径都会访问后台 login 页面,登录后可以看到一些例如 SQL 监控等等功能

(三) 整合 MyBatis

这一块基本没什么难题,依旧是一张普通的用户表测试一下,为了看起来舒服点,这里没有指定 Druid,如果需要,自己按上面加上就可以了

(1) 引入依赖

引入 mybatis 启动器,以及连接 MySQL 驱动

代码语言:javascript复制
<!--mybatis起步依赖-->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.0.1</version>
</dependency>

<!-- MySQL连接驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

(2) 配置数据库

依旧是配置用户名密码登基本参数,还有就是关于 mybatis 的一些配置,配置 mapper-locations 就可以找到 recourses 下的自己创建 mapper 文件夹下的 xml 映射文件, type-aliases-package 配置的是放这我们的实体的包,也就是扫描包

代码语言:javascript复制
spring:
  datasource:
    username: root
    password: root99
    url: jdbc:mysql://localhost:3306/springboot_mybatis_test?serverTimezone=Asia/Shanghai&useUnicode=true&characterEncoding=utf-8
    driver-class-name: com.mysql.cj.jdbc.Driver
#    type: com.alibaba.druid.pool.DruidDataSource # 自定义数据源

mybatis:
  mapper-locations: classpath:mapper/*Mapper.xml
  type-aliases-package: cn.ideal.pojo

(3) 创建实体

代码语言:javascript复制
public class User {
    private Long uid;
    private String username;
    private String password;
    private String nickname;
    ...... get set toString
}

(4) 编写Mapper和Mapper.xml

UserMapper

代码语言:javascript复制
@Mapper
public interface UserMapper {
    public List<User> queryAllUser();
}

UserMapper.xml

此文件路径 srcmainresourcesmapperUserMapper.xml

代码语言:javascript复制
<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="cn.ideal.mapper.UserMapper">
    <select id="queryAllUser" resultType="cn.ideal.domain.User">
        select * from user
    </select>
</mapper>

(5) 测试

代码语言:javascript复制
@Controller
public class MybatisController {
    @Autowired
    private UserMapper userMapper;

    @RequestMapping("/queryAllUser")
    @ResponseBody
    public List<User> queryUser() {
        List<User> users = userMapper.queryAllUser();
        return users;
    }
}

返回结果:

代码语言:javascript复制
[{"uid":1,"username":"zhangsan","password":"666","nickname":"飞翔的企鹅"},{"uid":2,"username":"lisi","password":"666","nickname":"伤心小男孩"},{"uid":3,"username":"jack","password":"666","nickname":"杰克"}]

(四) 整合 Redis

趁着写完了整合 MyBatis,顺便简单提及一下 Redis,因为一会测试的时候,可以用上整合好的 MyBatis,用来判断数据来自数据库还是缓存

(1) 引入依赖

代码语言:javascript复制
<!-- 配置使用redis启动器 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

(2) 修改配置

这里用 properties 都一样,随意

代码语言:javascript复制
spring:
  redis:
    host: 127.0.0.1
    port: 6379

端口是 Redis 默认的,这时候同时可以打开 Redis 的客户端,装过的在目录下找就好了 redis-server.exe

(3) 通过RedisTemplate测试Redis

直接在测试类中测试吧,下面的内容就是,第一次访问从数据库查,然后把数据写入缓存,第二次因为,缓存中已经已经有数据了,所以就从缓存中查找了

代码语言:javascript复制
@RunWith(SpringRunner.class)
@SpringBootTest(classes = Springboot03MybatisApplication.class)
public class RedisTest {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    @Test
    public void test() throws JsonProcessingException {
        //从redis中查
        String userLists = redisTemplate.boundValueOps("user.queryAllUser").get();
        //如果redis中没内容
        if (null == userLists) {
            //查数据库数据
            List<User> all = userMapper.queryAllUser();
            //转json
            ObjectMapper om = new ObjectMapper();
            userLists = om.writeValueAsString(all);
            //将数据存储到redis中,下次在查询直接从redis中获得数据,不查数据库
            redisTemplate.boundValueOps("user.queryAllUser").set(userLists);
            System.out.println("这些数据从数据库获得!!!");
        } else {
            System.out.println("这些数据从redis缓存中获得!!!");
        }
        System.out.println(userLists);
    }

}

执行结果

第一次

代码语言:javascript复制
这些数据从数据库获得!!!
[{"uid":1,"username":"zhangsan","password":"666","nickname":"飞翔的企鹅"},{"uid":2,"username":"lisi","password":"666","nickname":"伤心小男孩"},{"uid":3,"username":"jack","password":"666","nickname":"杰克"}]

第二次

代码语言:javascript复制
这些数据从redis缓存中获得!!!
[{"uid":1,"username":"zhangsan","password":"666","nickname":"飞翔的企鹅"},{"uid":2,"username":"lisi","password":"666","nickname":"伤心小男孩"},{"uid":3,"username":"jack","password":"666","nickname":"杰克"}]

结尾

邮箱:ideal_bwh@163.com

如果能帮到你的话,那就来关注我吧!

如果您更喜欢微信文章的阅读方式,可以关注我的公众号

如果您更加喜欢PC端的阅读方式,可以访问我的个人博客

域名:www.ideal-20.cn

0 人点赞