spring boot 源码解析(六)整合持久层

这一篇文章怎么说呢,实用性还好,但是我主要想学的是原理而不是使用。就很纠结,不过我决定还是完整的根据教程走一遍。就当是熟悉基础了。

JDBC

对于jdbc反正我这代技术出身都会很熟悉,毕竟这个是数据库连接的底层。我记得当时学设计模式也是用这个来举例桥接和适配器。反正总而言之于java程序员是个基础又重要的东西,毕竟现在的jpa或者mybatis,当年的ibatis和hibernate都离不开这个东西。其实这个用起来也不复杂,只不过代码相对而言比较多。下面让我们开始spring boot与jdbc的整合。

导包

想使用jdbc第一步肯定是导包。这里不管是创建spring boot项目直接选择还是后期引入都可以。主要有两个依赖,如下:

        
            org.springframework.boot
            spring-boot-starter-jdbc
        
        
            mysql
            mysql-connector-java
            runtime
        
配置连接的基本信息

这个其实挺常用的,就是几个基本的连接信息,比如用户名,密码,库名之类的。如下配置:

spring:
  datasource:
    username: root
#    password: 123456
    url: jdbc:mysql://localhost:3306/lsj-test
    driver-class-name: com.mysql.jdbc.Driver

注意我这里是自己做的测试环境,所以没设置数据库密码,所以这个配置注释掉了,如果有密码要如实填写并且打开这个注解。
这个就是最简版配置了吧大概,用户名,密码,数据库地址,驱动。
下面我们按照这个配置跑一遍试试:


spring boot 源码解析(六)整合持久层_第1张图片
按照配置跑起来
数据源的选择和注入原理

如上截图,事实证明我们如此简单的配置是成功了的,我们确确实实拿到了数据库连接。而且打印了下datasource的类。这里注意一下,SpringBoot1.x用的和我现在2.x用的不是一样的。1.X用的是tomcat的数据源。我这个用的hikari的(虽然这个我以前都不知道)。因为这个和课件不一样所以我说下。
继续往下,我们的那些配置文件中的配置是怎么被读取的?具体的数据源都有什么配置?这些当然要去源码中找了。老规矩,从autoconfigure中找:


spring boot 源码解析(六)整合持久层_第2张图片
数据源配置参数都有什么

其实我觉得这个还是挺好找的,毕竟名字也显而易见。当然也有可能是最近源码看多了思路比较顺了。我们在properties/yml中自己配置的东西会被绑定到这个配置参数类上。然后再用这个配置参数类注入到DatasourceConfiguration配置中。最终生成相应的bean让我们使用。我不知道中间有没有措辞不准确,但是这个步骤应该是没问题。下面我们先去properties中扫一眼参数,主要还是要看DatasourceConfiguration都注入了什么,怎么写的。
参数我就先不说了,见名知意,看不懂的还可以看上面的英文注释,实在不行百度翻译。从配置类说起:


spring boot 源码解析(六)整合持久层_第3张图片
数据源注入1

spring boot 源码解析(六)整合持久层_第4张图片
数据源注入2

我感觉我学了这么久源码这里是能看明白了,这几个注释,有点类似于之前servlet容器的选择:
首先都有一个注解:没有这个DataSource类才会生效,这个保证了只有一个数据源。其次根据引入依赖的不同,会注入不同的数据源。这里就包括我说的1.X默认的那个tomcat的。

当然了最后还有个保底:如果自己想用自定义的数据源,那么会用DataSourceBuilder利用反射创建相应type的数据源并绑定相关属性。挺有意思的代码。也很精巧,符合spring的一贯思路。

初始化sql语句

这个其实也是一个很有意思的功能。我突然就想起来了当年用jpa的时候根据实体反向生成表的惊叹和敬佩。现在发现有些东西学会了之后也就那样。虽然我现在还不知道人家怎么实现的。但是起码是有思路来自己实现这个功能的了(目前的想法就是获取实体对象的属性,并自动编写成sql文件,再在spring boot启动的时候自动运行sql)。就好像这个功能:启动的时候自动运行指定位置指定名称的sql文件。
这块的代码1.x和2.x又大不相同(具体从哪个版本变化的我不知道,反正教程1.5.我用的2.2)。2.x是把这个功能单独写了一个初始化类中。如下位置:

spring boot 源码解析(六)整合持久层_第5张图片
自动执行sql的原理

说深奥其实也没多深奥,但是在不了解的时候可能会觉得很神奇。因为这个类是一环套一环的,所以我截图只能截取一部分。想要读懂要仔细的一个方法一个方法顺下来。这里因为是一个类我也不好一一画出来,只能告诉大家顺着这个方法: createSchema的代码往下顺,会发现思路很容易就能理清楚,下面我挑几个我觉得重点的列出来:
spring boot 源码解析(六)整合持久层_第6张图片
第三个参数是字面量

spring boot 源码解析(六)整合持久层_第7张图片
自动扫描的文件位置和名称

看到这我们就知道了,系统默认的是类路径下的,schema.sql或者是schema-all.sql就会自动运行。下面我们实际操作下:
这里说一点注意:2.x以上版本默认是不初始化扫描sql文件的,这个要自己配置,如下方式:
spring boot 源码解析(六)整合持久层_第8张图片
开启初始化运行sql

很开心这个配置是我自己在DataSourceProperties中翻出来了,找了所有的字段,看不懂的就百度翻译最终觉得这个像,哈哈,下面让我们见证奇迹:
spring boot 源码解析(六)整合持久层_第9张图片
类路径放一个schema.sql文件

spring boot 源码解析(六)整合持久层_第10张图片
运行结果

事实证明这个sql语句确实生成了。至此,初始化sql差不多就这样了。不过还有一些:我们也可以自己指定要初始化的sql文件(这里要带文件名称和路径)。其实刚刚仔细看了那个数据源初始化方法的应该看到这里了,这里的第二个参数是从配置文件中获取到的,与自己扫描的放一起了,所以说我们指定和扫描的都可以一起被执行。
spring boot 源码解析(六)整合持久层_第11张图片
第二个参是从配置文件获取的

JDBC操作数据库

刚刚说了好多都是说数据源的,但是落到实际上,我们开始说jdbc。其实jdbc也有她自己的配置参数类和配置类。而且配置类还俩,一个自动的一个不是。不过我们点开看就会发现自动配置的那个,其实注解里要先把不自动的那个注入进去了。不自动那个也没啥说的,代码就几行:


spring boot 源码解析(六)整合持久层_第12张图片
jdbc配置类

所以说这个jdbc是可以直接注入的,我们直接用一下试试:
因为已经有现成的jdbcTemplate了,所以直接注入。这里有两个注意点:

  1. 我们要么关闭自动运行sql,要么删除之前的sql文件,不然会重新创建表。
  2. 我才发现我之前表名称取错了,应该是下划线而不是短横杠,所以这里改回来了。不然一样的代码告诉我-user语句有问题。。


    spring boot 源码解析(六)整合持久层_第13张图片
    测试代码

    spring boot 源码解析(六)整合持久层_第14张图片
    运行结果

    至此,我们对jdbcTemplate的简单使用就成功了!
    其实还有一个挺好玩的事,说良心话,我大概两年没用过原生的jdbc了,今天算是重温了下jdbc,虽然用的也是模板。但是我发现其实现在来说,jdbc的功能居然也还是很强大的。不考虑可读性问题的话,我居然有点心动想怎么操作数据库就怎么操作数据库的jdbc了,感兴趣的朋友也可以看一看,挺有意思的。

阿里系完整配套的数据源:druid

上面也说了数据源有好多种,不管是1.x默认的tomcat的,还是现在默认的hikari。但是其实现在用的比较多的却是druid。
这个druid是何方神圣呢?打败了spring boot两次默认选择?上面标题其实就说了,这个是阿里系的东西。而且是有着完整配套的。这个的好处一会儿细说,先说怎么使用这个数据源。

  1. 导包。这个是最基本的,咱们去maven去找下druid的依赖:


    spring boot 源码解析(六)整合持久层_第15张图片
    复制依赖贴到项目的pom文件中
  2. 配置
    其实这个我们上面也说了可以指定数据源。在springboot源码中是没有可选项druid的,所以我们这里应该是用最后一个保底的builder来创建bean的。然后我们通过之前的源码分析能看出来决定创建哪个bean是由type的值来决定的。所以这里简单的指定下type为druid,如下配置:
spring:
  datasource:
    username: root
#    password: 123456
    url: jdbc:mysql://localhost:3306/lsj-test
    driver-class-name: com.mysql.jdbc.Driver
    initialization-mode: ALWAYS
    type: com.alibaba.druid.pool.DruidDataSource

这里的全限定名我是根据教程来敲的,一开始我就傻了吧唧的写了type: druid。果不其然的报错了,起都没起来。
反正就这样一句简单的配置,我们启动项目就会发现用的是druid的数据源了:


spring boot 源码解析(六)整合持久层_第16张图片
成功使用druid数据源

虽然这样简单的时候是ok了,但是一些属性的配置却是有门道的,我们先按照正常的思维来试试给数据源添加属性:


spring boot 源码解析(六)整合持久层_第17张图片
先看看这个DataSource都有什么属性

spring boot 源码解析(六)整合持久层_第18张图片
我们在配置文件中添加一个初始化大小的属性重启,看看这个属性起作用了么

spring boot 源码解析(六)整合持久层_第19张图片
属性没有被注入

事实证明这个属性并没有被注入。其实这个情况也是可以理解的,因为这个spring.datasuorce的属性都被绑定到datasourceProperties中,而这个类没有这些属性,所以哪怕配置了也是注入不进来的。

解决办法也容易,就是我们自己写dataSource绑定配置文件就好了(可以参考DataSourceProperties是怎么绑定的),如下实现:

@Configuration
public class MyConfig {
    
    @Bean
    @ConfigurationProperties(prefix = "spring.datasource")
    public DataSource dataSource() {
        return new DruidDataSource();
    }

}

还记不记得这个bean,是在不存在的时候才用builder构建的,现在我们自己写了这个类,就不用走springBoot的那个了。下面我们启动试下这个初始化大小注入进来了么:


spring boot 源码解析(六)整合持久层_第20张图片
属性成功注入

刚刚就说了,这个druid虽然不是性能最好的,但是用的最多的原因就是因为这个是一套完整的东西,其自带监控就是很实用的一个东西。不过这个据说2.x和1.x的区别还是挺大的,2.x的监控配置可以直接在配置文件中配置了。但是因为教程上老师的1.x的,所以我这里用的也是1.x的方式,直接把监控和servlet写在这个自己注入的bean这里就可以了。如下代码:

    @Bean
    public ServletRegistrationBean druidServlet() {
        // 现在要进行druid监控的配置处理操作
        ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(
                new StatViewServlet(), "/druid/*");
        // 白名单,多个用逗号分割, 如果allow没有配置或者为空,则允许所有访问
        servletRegistrationBean.addInitParameter("allow", "127.0.0.1,localhost");
        // 黑名单,多个用逗号分割 (共同存在时,deny优先于allow)
        servletRegistrationBean.addInitParameter("deny", "192.168.1.4");
        // 控制台管理用户名
        servletRegistrationBean.addInitParameter("loginUsername", "admin");
        // 控制台管理密码
        servletRegistrationBean.addInitParameter("loginPassword", "123456");
        // 是否可以重置数据源,禁用HTML页面上的“Reset All”功能
        servletRegistrationBean.addInitParameter("resetEnable", "false");
        return servletRegistrationBean ;
    }
    
    @Bean
    public FilterRegistrationBean filterRegistrationBean() {
        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean() ;
        filterRegistrationBean.setFilter(new WebStatFilter());
        //所有请求进行监控处理
        filterRegistrationBean.addUrlPatterns("/*"); 
        //添加不需要忽略的格式信息
        filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.css,/druid/*");
        return filterRegistrationBean ;
    }

配置完之后我们启动项目,就可以通过白名单+端口/druid来访问控制台了:


spring boot 源码解析(六)整合持久层_第21张图片

spring boot 源码解析(六)整合持久层_第22张图片
控制台页面

如上就可以登陆这个sql的监控后台中了,其中数据库信息啊,sql语句啊,还有sql参数啊之类的都可以看到。可能在项目中会有一定的应用吧(我个人是觉得应该挺有用的,但是我确确实实工作中没用到过这个)。反正这个功能我们知道了就行了,继续往下吧。

整合MyBatis
注解实现

之前说了半天数据源,但是我们大多数时候对数据库的操作还是用框架的。现在主流两个就是mybatis(mybatis plus本质上也是mybatis)和jpa。这里先说MyBatis的整合。
第一步肯定还是导包这个没异议。而且这个有个很有意思的事。spring-boot整合啥都是spring-boot-starter-xxxx.但是这个mybatis的写法是mybatis-spring-boot-starter。
其实看到很容易猜到:这个不是springBoot出产的,而是mybatis写的。我们先去maven仓库找到这个依赖并在pom文件中引入:


spring boot 源码解析(六)整合持久层_第23张图片
mybatis适配springboot依赖

引入进来以后我们可以点进去瞅瞅这个依赖里都有什么:


spring boot 源码解析(六)整合持久层_第24张图片
mybatis-spring-boot-starter的pom文件

其实这个真的是清晰明了的一个jar,也让我们清清楚楚的认识到,这个所谓的mybatis-spring-boot-starter适配器,差不多就是把mybatis和springBoot整合到一起而已。而且它自带jdbc-starter。所以我们其实自己可以不引用这个依赖了。
剩下的mybatis的简单应用就不详细说了,简单叙述下:
  1. 有表,有实体。驼峰对应(这个驼峰对应要设置)。
  2. 写个Dao层接口,用@Mapper注解修饰。就可以直接在这个接口中写方法了。
  3. 方法的实现有两种:注解和xml。xml的写法比较麻烦,先说注解:
    在接口中写抽象方法,上面用@Select/@Insert/@Delete/@Update修饰。然后里面的参数直接是sql语句就ok了。附上截图:


    spring boot 源码解析(六)整合持久层_第25张图片
    注解版mybatis的增删改查

    当然了,这里还有一个小小的知识点,就是主键自增的那种,插入的时候要多一个注解:

@Options(useGeneratedKeys=true,keyProperty="id")

这个注解可以设置把自增的主键回填到实体对象中

spring boot 源码解析(六)整合持久层_第26张图片
源码截图

还有一个上面提到的驼峰映射:

mapUnderscoreToCamelCase:true

但是看了这么久源码其实有一点很容易达成共识,所有在配置文件中配置的东西我们都可以通过定制在代码中配置,虽然我个人也觉得配置文件比较简单,但是定制器的使用还是要会的。如下代码:

@Configuration
public class MyConfig {
    
    @Bean
    public ConfigurationCustomizer configurationCustomizer() {
        return new ConfigurationCustomizer() {          
            @Override
            public void customize(org.apache.ibatis.session.Configuration configuration) {
                configuration.setMapUnderscoreToCamelCase(true);                
            }
        };
    }
}

其实mybatis的东西比较少,所以相对于庞大的springBoot源码要好看的多,如下:


spring boot 源码解析(六)整合持久层_第27张图片
mybatis源码

这三个一个是定制器,一个是配置类,一个是配置属性类。简单明了,感兴趣的可以自己去看看。
还有一点:最开始就说了dao层要mapper注解,但是这个其实也可以换种方式,直接在启动类上添加mapper扫描注解:

@MapperScan(value="lsj.dao")

这个值就是想扫描的包的全名。

配置文件实现

上面说的都是注解实现的,简单的很,但是可读性也不是那么强。听说大厂都要求用配置文件的,反正我也不知道。
但是这个xml文件版的,我是觉得没啥可说的了,毕竟当年都要写吐了。简单来说就是自定义参数类型和接受类型,然后写sql。好处是这个xml配置文件的形式支持动态sql。对于大量不确定的筛选条件来说这个动态sql就有用的多。
简单说一下我目前工作的话,简单sql用mybatis-plus的条件构造器,复杂的sql还是用xml的。尤其是很多表联查几乎都是xml。还有的就是一个实体对象关联了5,6个其他对象这种,也是xml特别方便。然后map接收反正。
这里因为比较基础,所以就不多说了,继续往下讲jpa。

JPA(Java Persistence API)

JPA本身是一个规范,而实现这个规范的框架也很多,比如hibernate,toplink,openJPA等。而我们常说的jpa其实应该是SpringData JPA。这个是spring整合hibernate的一个框架。下面我们主要说说如何使用springData jpa。

实体类

最基本的表的对应实体就不说了。简单说下JPA的一些注解:

@Entity  //告诉JPA这个类是一个实体类(和数据表映射的类)
@Table(name = "sys_user") //表名称。如果省略的话会默认表明是类名小写
@Id //主键注解,告诉JPA这个是主键
@GeneratedValue(strategy = GenerationType=IDENTITY)//主键自增
@Column(name = "last_name",length = 50)//这个是和数据库表对应的列,可以指定长度。也可以不写。
spring boot 源码解析(六)整合持久层_第28张图片
主键默认就是自增

这里因为我看到这有点懵,所以特意查询了下,identity和auto的区别:

GenerationType.IDENTITY
多数数据库支持IDENTITY列,数据库会在新行插入时自动给ID赋值,这也叫做ID自增长列
GenerationType.Auto
把主键生成策略交给JPA厂商(Persistence Provider),由它根据具体的数据库选择合适的策略,可以是Table/Sequence/Identity中的一种。假如数据库是Oracle,则选择Sequence

需要注意的是上面这些注解一般都是javax.persistence包下的。

Dao层编写

接下来要编写一个Dao来操作数据库。
这里对Dao层的要求就是继承JPARepository。这个类有两个参数,一个是实体对象(泛型),还有一个参数需要注意,这个参数是主键的类型。我们都知道jpa封装了一些根据主键查询的方法,这个主键的类型就是这里传入的!
这个接口是不用加任何注解可以直接注入的。

反向生成表

这个怎么说呢,根据实体生成表算是JPA的一大特色吧。这里只需要做点简单的配置(JPA的配置是spring.jpa.xxx):

spring:
  jpa: 
    hibernate: 
       ddl-auto: update #每次启动更新/创建数据表结构
    show-sql: true  #控制台显示sql

这个时候我们启动项目就会发现已经根据实体创建表了。

直接使用JPA

这个就是通常的使用了,因为继承JPARepository,所以自带了很多方法了的。这里简单的写个查询和插入:

spring boot 源码解析(六)整合持久层_第29张图片
简单的使用demo

至此,这个jpa的简单使用就到这里了。
如果想要更复杂的操作我的建议就是去看官方文档。写的挺清楚的。而且要针对不同的需求才有不同的做法。
本篇笔记就记到这里,如果稍微帮到你了记得点个喜欢点个关注。也祝大家工作顺顺利利!这一篇其实感觉也没说啥东西,就是一些基本知识的回顾。因为不习惯跳过教程所以给持久层做了个温习。

你可能感兴趣的:(spring boot 源码解析(六)整合持久层)