5.数据连接方式解耦

5.1 BaseDao的问题

BaseDao这个JAVA类,包含如下java代码,与mysql数据库进行链接。

当我们的项目部署到客户服务器上的时候,客户的user,password,news(dbname)有变化,可能也有多次更改,不可能由程序员再多次更改我们的java代码。这是不合理的。怎么办?

思路:

​ 将这些需要配置的内容,放置到外部的配置文件

    static String driver="com.mysql.cj.jdbc.Driver";
    static String url="jdbc:mysql://localhost:3306/news?useTimezone=true&serverTimezone=CTT&useUnicode=true&characterEncoding=utf8&useSSL=false";
    static String user="root";
    static String password="root";

5.2 使用外部的db.properties文件

在src下根目录下,建立db.properties文件,这种文件和xml文件不同,里面放置的是key=value,格式的字符串

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/news? useTimezone=true&serverTimezone=CTT&useUnicode=true&characterEncoding=utf8&useSSL=false
user=root
password=root

5.3 使用Properties类

我们这时候,已经使用了外部的配置文件,读取这个外部的配置文件。

Properties(Java.util.Properties),该类主要用于读取Java的配置文件,不同的编程语言有自己所支持的配置文件,配置文件中很多变量是经常改变的,为了方便用户的配置,能让用户够脱离程序本身去修改相关的变量设置。就像在Java中,其配置文件常为.properties文件,是以键值对的形式进行参数配置的。

 //读取外部的配置文件,直接写,使用类加载机制,读取;将读取的文件,转换为输入字节流对象
InputStream is=BaseDaoTwo.class.getClassLoader().getResourceAsStream("db.properties");

load(InputStream inStream) 从输入字节流中读取属性列表(键和元素对)。

getProperty(String key) 在此属性列表中搜索具有指定键的属性。

 static Properties properties=new Properties();
     //读取外部的配置文件,直接写,使用类加载机制,读取;将读取的文件,转换为输入字节流对象
    static InputStream is=BaseDaoTwo.class.getClassLoader().getResourceAsStream("db.properties");
    //如何读取,读取外部4个key
    //定义四个静态的String变量;
    static String driver,url,user,password;
    //1.获取公共的静态连接资源;
    public static Connection getConn(){
        try {
            //加载到properties对象里面
            properties.load(is);
            //利用getProperty()得到key信息
            driver=properties.getProperty("driver");
            url=properties.getProperty("url");
            user=properties.getProperty("user");
            password=properties.getProperty("password");
            //加载驱动
            Class.forName(driver);
            //返回连接;
            conn= DriverManager.getConnection(url,user,password);
        } catch (Exception e) {
            e.printStackTrace();
        }
        return conn;
    }

完毕之后,大家自行测试以下即可,发现可以实现与之前一样的连接功能。

好处:

​ 是将之前在BaseDao java类里面写的数据库连接代码,配置信息,放到了外部文件。注意,以后,配置文件基本上都会存储数据库配置信息。

6.连接池技术

6.1什么情况下使用连接池?

​ 对于一个简单的数据库应用,由于对于数据库的访问不是很频繁。这时可以简单地在需要访问数据库时,就新创建一个连接,用完后就关闭它,这样做也不会带来什么明显的性能上的开销。如果程序出现异常而未能关闭,将会导致数据库系统中的内存泄漏,最终将导致重启数据库。

​ 但是对于一个复杂的数据库应用,有若干个数据库连接,比如说10000个,情况就完全不同了。数据库连接频繁的建立、关闭连接,会极大的减低系统的性能,因为对于连接的使用成了系统性能的瓶颈。连接过多,也可能导致内存泄漏,服务器崩溃。

6.2连接池的思想

为解决传统开发中的数据库连接问题,可以采用数据库连接池技术。

数据库连接池的基本思想就是为数据库连接建立一个“缓冲池”。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,只需从“缓冲池”中取出一个,使用完毕之后再放回去。

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。

数据库连接池在初始化时将创建一定数量的数据库连接放到连接池中,这些数据库连接的数量是由最小数据库连接数来设定的。无论这些数据库连接是否被使用,连接池都将一直保证至少拥有这么多的连接数量。连接池的最大数据库连接数量限定了这个连接池能占有的最大连接数,当应用程序向连接池请求的连接数超过最大连接数量时,这些请求将被加入到等待队列中。

​ 用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长。假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库服务器内存溢出、拓机。如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lU2YuOky-1591174555879)(E:\政通路\课堂笔记\S2\Spring\assets\image-20200602094216914.png)]

​ 数据库连接是一种关键的有限的昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出.对数据库连接的管理能显著影响到整个应用程序的伸缩性和健壮性,影响到程序的性能指标.数据库连接池正式针对这个问题提出来的.数据库连接池负责分配,管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是重新建立一个。如下图所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Fr02X5Kf-1591174555883)(E:\政通路\课堂笔记\S2\Spring\assets\image-20200602094248738.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c77wYKr5-1591174555894)(E:\政通路\课堂笔记\S2\Spring\assets\image-20200603160044219.png)]

数据库连接池的最小连接数和最大连接数的设置要考虑到以下几个因素:

  1. 最小连接数:是连接池一直保持的数据库连接,所以如果应用程序对数据库连接的使用量不大,将会有大量的数据库连接资源被浪费.
  2. 最大连接数:是连接池能申请的最大连接数,如果数据库连接请求超过次数,后面的数据库连接请求将被加入到等待队列中,这会影响以后的数据库操作
  3. 如果最小连接数与最大连接数相差很大:那么最先连接请求将会获利,之后超过最小连接数量的连接请求等价于建立一个新的数据库连接.不过,这些大于最小连接数的数据库连接在使用完不会马上被释放,他将被放到连接池中等待重复使用或是空间超时后被释放.

6.3数据库连接池技术的优点

1.资源重用

由于数据库连接得以重用,避免了频繁创建,释放连接引起的大量性能开销。在减少系统消耗的基础上,另一方面也增加了系统运行环境的平稳性。

2.更快的系统反应速度

数据库连接池在初始化过程中,往往已经创建了若干数据库连接置于连接池中备用。此时连接的初始化工作均已完成。对于业务请求处理而言,直接利用现有可用连接,避免了数据库连接初始化和释放过程的时间开销,从而减少了系统的响应时间

3.新的资源分配手段

对于多应用共享同一数据库的系统而言,可在应用层通过数据库连接池的配置,实现某一应用最大可用数据库连接数的限制,避免某一应用独占所有的数据库资源

4.统一的连接管理,避免数据库连接泄露

在较为完善的数据库连接池实现中,可根据预先的占用超时设定,强制回收被占用连接,从而避免了常规数据库连接操作中可能出现的资源泄露

6.4连接池的实现

c3p0 dhcp

​ 数据库连接池的基本原理是在内部对象池中维护一定数量的数据库连接,并对外暴露数据库连接获取和返回方法。

外部使用者可通过 getConnection 方法获取连接,使用完毕后再通过 close 方法将连接返回,注意此时连接并没有关闭,而是由连接池管理器回收,并为下一次使用做好准备。

​ Java 中有一个 DataSource 接口, 数据库连接池就是 DataSource 的一个实现。

​ Druid(德鲁伊)是阿里巴巴开发的号称为监控而生的数据库连接池,Druid是目前最好的数据库连接池。在功能、性能、扩展性方面,都超过其他数据库连接池,同时加入了日志监控,可以很好的监控DB池连接和SQL的执行情况。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。

属性 说明 建议值
url 数据库的jdbc连接地址。一般为连接oracle/mysql。示例如下:  
  mysql : jdbc:mysql://ip:port/dbname?option1&option2&…  
  oracle : jdbc:oracle:thin:@ip:port:oracle_sid  
     
username 登录数据库的用户名  
password 登录数据库的用户密码  
initialSize 启动程序时,在连接池中初始化多少个连接 10-50已足够
maxActive 连接池中最多支持多少个活动会话  
maxWait 程序向连接池中请求连接时,超过maxWait的值后,认为本次请求失败,即连接池 100
  没有可用连接,单位毫秒,设置-1时表示无限等待  
minEvictableIdleTimeMillis 池中某个连接的空闲时长达到 N 毫秒后, 连接池在下次检查空闲连接时,将 见说明部分
  回收该连接,要小于防火墙超时设置  
  net.netfilter.nf_conntrack_tcp_timeout_established的设置  
timeBetweenEvictionRunsMillis 检查空闲连接的频率,单位毫秒, 非正整数时表示不进行检查  
keepAlive 程序没有close连接且空闲时长超过 minEvictableIdleTimeMillis,则会执 true
  行validationQuery指定的SQL,以保证该程序连接不会池kill掉,其范围不超  
  过minIdle指定的连接个数。  
minIdle 回收空闲连接时,将保证至少有minIdle个连接. 与initialSize相同
removeAbandoned 要求程序从池中get到连接后, N 秒后必须close,否则druid 会强制回收该 false,当发现程序有未
  连接,不管该连接中是活动还是空闲, 以防止进程不会进行close而霸占连接。 正常close连接时设置为true
removeAbandonedTimeout 设置druid 强制回收连接的时限,当程序从池中get到连接开始算起,超过此 应大于业务运行最长时间
  值后,druid将强制回收该连接,单位秒。  
logAbandoned 当druid强制回收连接后,是否将stack trace 记录到日志中 true
testWhileIdle 当程序请求连接,池在分配连接时,是否先检查该连接是否有效。(高效) true
validationQuery 检查池中的连接是否仍可用的 SQL 语句,drui会连接到数据库执行该SQL, 如果  
  正常返回,则表示连接可用,否则表示连接不可用  
testOnBorrow 程序 申请 连接时,进行连接有效性检查(低效,影响性能) false
testOnReturn 程序 返还 连接时,进行连接有效性检查(低效,影响性能) false
poolPreparedStatements 缓存通过以下两个方法发起的SQL: true
  public PreparedStatement prepareStatement(String sql)  
  public PreparedStatement prepareStatement(String sql,  
  int resultSetType, int resultSetConcurrency)  
maxPoolPrepareStatementPerConnectionSize 每个连接最多缓存多少个SQL 20
filters 这里配置的是插件,常用的插件有: stat,wall,slf4j
  监控统计: filter:stat  
  日志监控: filter:log4j 或者 slf4j  
  防御SQL注入: filter:wall  
connectProperties 连接属性。比如设置一些连接池统计方面的配置。  
  druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000  
  比如设置一些数据库连接属性:  

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Io6LyXR2-1591174555899)(E:\政通路\课堂笔记\S2\Spring\assets\image-20200603161443399.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7dEA2WjY-1591174555902)(E:\政通路\课堂笔记\S2\Spring\assets\image-20200603161646924.png)]

步骤:

1.在src下新建一个druid.properties 连接池配置文件

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/news?useTimezone=true&serverTimezone=CTT&useUnicode=true&characterEncoding=utf8&useSSL=false
user=root
password=root
initialSize=5
maxActive=10

|
| | 比如设置一些数据库连接属性: | |

[外链图片转存中…(img-Io6LyXR2-1591174555899)]

[外链图片转存中…(img-7dEA2WjY-1591174555902)]

步骤:

1.在src下新建一个druid.properties 连接池配置文件

driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/news?useTimezone=true&serverTimezone=CTT&useUnicode=true&characterEncoding=utf8&useSSL=false
user=root
password=root
initialSize=5
maxActive=10
  1.