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

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

JDBC

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

导包

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

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>
配置连接的基本信息

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

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

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


按照配置跑起来
数据源的选择和注入原理

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


数据源配置参数都有什么

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


数据源注入1

数据源注入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是把这个功能单独写了一个初始化类中。如下位置:

自动执行sql的原理

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

自动扫描的文件位置和名称

看到这我们就知道了,系统默认的是类路径下的,schema.sql或者是schema-all.sql就会自动运行。下面我们实际操作下:
这里说一点注意:2.x以上版本默认是不初始化扫描sql文件的,这个要自己配置,如下方式:
开启初始化运行sql

很开心这个配置是我自己在DataSourceProperties中翻出来了,找了所有的字段,看不懂的就百度翻译最终觉得这个像,哈哈,下面让我们见证奇迹:
类路径放一个schema.sql文件

运行结果

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

JDBC操作数据库

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


jdbc配置类

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

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


    测试代码

    运行结果

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

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

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

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


    复制依赖贴到项目的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的数据源了:


成功使用druid数据源

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


先看看这个DataSource都有什么属性

我们在配置文件中添加一个初始化大小的属性重启,看看这个属性起作用了么

属性没有被注入

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

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

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

}

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


属性成功注入

刚刚就说了,这个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来访问控制台了:



控制台页面

如上就可以登陆这个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文件中引入:


mybatis适配springboot依赖

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


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了。附上截图:


    注解版mybatis的增删改查

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

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

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

源码截图

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

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源码要好看的多,如下:


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)//这个是和数据库表对应的列,可以指定长度。也可以不写。
主键默认就是自增

这里因为我看到这有点懵,所以特意查询了下,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,所以自带了很多方法了的。这里简单的写个查询和插入:

简单的使用demo

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

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,530评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 86,403评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,120评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,770评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,758评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,649评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,021评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,675评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,931评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,659评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,751评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,410评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,004评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,969评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,203评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,042评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,493评论 2 343

推荐阅读更多精彩内容