BD1 - Java 3-4 JDBC - 配置文件 连接池

此心不动,随机而动

Java第12天上午

上午主要复习了JDBC

详细内容可参见:
7.21 JDBC 学习总结

以下总结补充:

BD1 - Java 3-4 JDBC - 配置文件 连接池_第1张图片
JDBC用途

使用JDBC来操作数据库用到的类:


BD1 - Java 3-4 JDBC - 配置文件 连接池_第2张图片

遍历数据库的大致步骤:


BD1 - Java 3-4 JDBC - 配置文件 连接池_第3张图片
连接数据库的大致步骤

不遍历的话 则:


BD1 - Java 3-4 JDBC - 配置文件 连接池_第4张图片
步骤

PreparedStatement优势绝不仅仅是更灵活的参数化查询
请参见:PreparedStatement VS Statement

上午完成的经典代码:(遍历数据)

import java.sql.*;

public class TestJDBC {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stmt = null;
        ResultSet rs = null;
        String Driver = "com.mysql.jdbc.Driver";
        // 对应 第二步 
        String url = "jdbc:mysql://localhost:3306/itcast";
        String user = "root";
        String password = "root";
        
        try{
            // 第一步 先加载注册JDBC的驱动类
            Class.forName(Driver);
            // 第二步 提供JDBC连接的url
            
            // 第三步 创建数据库的连接
            conn = DriverManager.getConnection(url, user, password);
            // 第四步 创建一个statement对象
            stmt = conn.createStatement();
            // 第五步 执行sql语句
            rs = stmt.executeQuery("select * from student");
            // 第六步 循环遍历处理结果
            while(rs.next()){
                System.out.println(rs.getString("name"));
                System.out.println(rs.getString("birth").subSequence(0, 4));
            }
        } catch(ClassNotFoundException s){
            s.printStackTrace();
        } catch (SQLException e){
            e.printStackTrace();
        } finally {
            // 第七步 关闭JDBC对象
            try {
                if(rs != null) {
                    rs.close();
                 }
                if(stmt != null)(       
                    stmt.close();
                }
                if(conn != null){
                    conn.close();
                }
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

上午完成的经典代码:(插入数据)

import java.sql.*;

public class TestDML {

    public static void main(String[] args) {
        Connection conn = null;
        PreparedStatement pstmt = null;
        
        String Driver = "com.mysql.jdbc.Driver";
        // 对应 下面第二步 
        String url = "jdbc:mysql://localhost:3306/itcast";
        String user = "root";
        String password = "root";
        try{
            // 第一步 先加载注册JDBC的驱动类
            Class.forName(Driver);
            // 第二步 提供JDBC连接的url
            
            // 第三步 创建数据库的连接
            conn = DriverManager.getConnection(url, user, password);
            // 第四步 创建一个statement对象
            //pstmt = conn.prepareStatement("insert into student values(7,'你好','男',17)");
            pstmt = conn.prepareStatement("insert into student values(?,?,?,?,?)");
            pstmt.setInt(1, 0);
            pstmt.setString(2, "王33");
            pstmt.setString(3, "男");
            pstmt.setInt(4, 25);
            pstmt.setString(5, "2017");
            pstmt.executeUpdate();          
        } catch(ClassNotFoundException s){
            s.printStackTrace();
        } catch (SQLException e){
            e.printStackTrace();
        } finally {
            // 第七步 关闭JDBC对象
            try {
                if(pstmt != null) {
                    pstmt.close();
                }
                if(conn != null) {
                    conn.close();
                }           
            } catch (SQLException e) {
                // TODO Auto-generated catch block
                e.printStackTrace();
            }
        }
    }
}

下午主要学习了配置文件和连接池

Java配置文件

在上午代码基础上 我们考虑进一步优化代码结构

给定这样的数据库:


BD1 - Java 3-4 JDBC - 配置文件 连接池_第5张图片
数据库

先来看之前创建连接的结构:

// 第一步 先加载注册JDBC的驱动类
String Driver = "com.mysql.jdbc.Driver";
Class.forName(Driver);
// 第二步 提供JDBC连接的url
String url = "jdbc:mysql://localhost:3306/itcast";
String user = "root";
String password = "root";
// 第三步 创建数据库的连接
conn = DriverManager.getConnection(url, user, password);

可见 我们每创建一个连接 就需要重现以上代码

并且在开发时 有些参数是经常改变的
比如操作数据库时 我们可能连接本地的数据库 那么IP 、数据库名称、表名称和数据库主机等信息是我们本地的。

要使得操作数据的模块具有通用性,那么以上信息就不能写死在程序里。通常我们的做法是用配置文件来解决

关于更多java配置文件说明 请看:.properties Java 配置文件

所以先来新建一个properties (Java配置文件):
先右击src文件夹 新建一个File

BD1 - Java 3-4 JDBC - 配置文件 连接池_第6张图片
配置文件 - 新建一个File

选File 再点击Next:


BD1 - Java 3-4 JDBC - 配置文件 连接池_第7张图片
File

File名称设置为:db.properties


BD1 - Java 3-4 JDBC - 配置文件 连接池_第8张图片
db.properties

db.properties 文件新建成功:


BD1 - Java 3-4 JDBC - 配置文件 连接池_第9张图片
新建成功

可以点击右边的Add在弹窗中输入key - value 数据:


BD1 - Java 3-4 JDBC - 配置文件 连接池_第10张图片
输入key - value 数据

比如 我们输入 user - root 点击 Finish:


BD1 - Java 3-4 JDBC - 配置文件 连接池_第11张图片
输入-1
BD1 - Java 3-4 JDBC - 配置文件 连接池_第12张图片
输入-2

点击左下角的Source:


BD1 - Java 3-4 JDBC - 配置文件 连接池_第13张图片
输入-3

即我们也可以在Source中直接输入:(推荐做法)

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/itcast
user=root
password=root

(左边是key 右边是value)

输入结果如图:


BD1 - Java 3-4 JDBC - 配置文件 连接池_第14张图片
输入结果

即目前我们成功把:

// 第一步 先注册加载JDBC的驱动类
String Driver = "com.mysql.jdbc.Driver";
Class.forName(Driver);
// 第二步 提供JDBC连接的url
String url = "jdbc:mysql://localhost:3306/itcast";
String user = "root";
String password = "root";

信息写到了 配置文件中


下面我们来看如何调用配置信息:
同样在 src 目录下 新建一个 JdbcUtil 类:

BD1 - Java 3-4 JDBC - 配置文件 连接池_第15张图片
新建JdbcUtil
BD1 - Java 3-4 JDBC - 配置文件 连接池_第16张图片
新建JdbcUtil

直接给出 JdbcUtil.java 代码:

import java.io.IOException;
import java.util.Properties;
import java.sql.*;

public class JdbcUtil {

    private static String driver;
    private static String url;
    private static String uesrname;
    private static String password;
    
    static {
        try{
            Properties p = new Properties();
            p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));
            driver = p.getProperty("driver");
            url = p.getProperty("url");
            uesrname = p.getProperty("user");
            password = p.getProperty("password");
            Class.forName(driver);
        }catch(IOException e){
            e.printStackTrace();
        }catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    
    public static Connection createConnection(){
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(url, uesrname, password);
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }
    
    public static void close(Connection conn, Statement stat,ResultSet rs) {
        try {
            if(rs != null){
                rs.close();
            }
            if(stat != null){
                stat.close();
            }
            if(conn != null){
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    
}

代码说明:
1 4个成员变量分别用来接收 从配置文件读到的内容

private static String driver;
private static String url;
private static String uesrname;
private static String password;

对应配置文件中 左边的4个key:


BD1 - Java 3-4 JDBC - 配置文件 连接池_第17张图片
对应

2 中间的 静态代码块 完成 :

  • 获取配置文件的value:


    BD1 - Java 3-4 JDBC - 配置文件 连接池_第18张图片
    获取配置文件的value
  • 注册加载JDBC的驱动类:

Class.forName(driver);

注意

Properties p = new Properties();
p.load(Thread.currentThread().getContextClassLoader().getResourceAsStream("db.properties"));

load方法可以从 .properties属性文件(这里是db.properties) 对应的文件输入流中,加载属性列表到Properties类对象(这里是p),即通过对上面的 properties 文件进行装载来获取该文件中的所有键 - 值对。以供 getProperty ( String key) 来搜索。

另外Thread.currentThread().getContextClassLoader().getResourceAsStream() 请看:读取配置文件Properties的一种方案

另外补充:
Java中普通代码块,构造代码块,静态代码块区别及代码示例

3 createConnection 方法:(创建数据库的连接)

public static Connection createConnection(){
    Connection conn = null;
    try {
        conn = DriverManager.getConnection(url, uesrname, password);
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return conn;
}
下面测试

在src 下新建一个Test.java文件:

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

public class Test {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stat = null;
        ResultSet rs = null;
        conn = JdbcUtil.createConnection();
        String sql = "select * from student";
        
        try{
            stat = conn.createStatement();
            rs = stat.executeQuery(sql);
            while(rs.next()){
                System.out.println(rs.getString("name"));
                System.out.println(rs.getInt("age"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JdbcUtil.close(conn, stat, rs);
        }
        
    }
}

其中,我们以后可这样创建连接:

conn = JdbcUtil.createConnection();

(即利用JdbcUtil中的方法创建连接)

其中,我们也可以这样关闭资源:

JdbcUtil.close(conn, stat, rs);

即利用JdbcUtil中的close()方法关闭资源

JdbcUtil中的close()方法:

public static void close(Connection conn, Statement stat,ResultSet rs) {
    try {
        if(rs != null){
            rs.close();
        }
        if(stat != null){
            stat.close();
        }
        if(conn != null){
            conn.close();
        }
    } catch (SQLException e) {
        e.printStackTrace();
    }
}

运行一下Test.java文件 可见成功打印:


BD1 - Java 3-4 JDBC - 配置文件 连接池_第19张图片
运行结果

连接池

我们来考虑下通过配置文件创建连接的性能,在JdbcUtil类的createConnection()方法中利用系统的currentTimeMillis()函数获取前后用时:


BD1 - Java 3-4 JDBC - 配置文件 连接池_第20张图片
获取前后用时

运行可见用时476ms:
(不同电脑性能有差异,您的时间可能不同)


BD1 - Java 3-4 JDBC - 配置文件 连接池_第21张图片
QQ图片20170807211826.png

476ms已经不算少了 试想如果创建多个Connection 那么用时会很长,足以严重影响性能。

所以,我们提出连接池概念:

更多连接池概念请见:
1 - Java连接池详解
2 - 几个主流的Java连接池整理

一下不理解很正常,先有个概念:池技术是为了优化服务器应用程序的性能、提高程序执行效率和降低系统资源开销。

考虑下连接池的设计:
连接池(connection pool)

  • 使用集合 将Connection对象放入List中,反复使用
  • 连接池的初始化
    • 事先放入多个连接对象
  • 从连接池中获取连接对象
    • 如果池中无可用连接,则创建一个新的
    • 如果池中有可用连接,则将池中最后一个返回,同时,将该连接从池中remove,表示正在使用
  • 关闭连接
    • 不是真正的关闭,而是将用完的放回去

新建一个com.pool包 在包下新建 DBConnPool.java:

import java.sql.Connection;
import java.util.ArrayList;
import java.util.List;

import com.hkj.JdbcUtil;

public class DBConnPool {

    // 连接对象
    private static List pool;
    // 最大连接数
    private static final int POOL_MAX_SIZE = 100;
    // 最小连接数
    private static final int POOL_MIN_SIZE = 10;
    
    public DBConnPool(){
        initPool();
    }
    
    // 初始化连接池 让池中的连接数达到最小值
    public void initPool(){
        if(pool == null){
            pool = new ArrayList();
        }
        while(pool.size() < POOL_MIN_SIZE){
            pool.add(JdbcUtil.createConnection());
            System.out.println("初始化池,池中连接数:"+pool.size());
        }
    }
    
    // 从池取最后一个连接 
    public synchronized Connection getConnection(){
        Connection conn = null;
        int last_index = 0;
        if(pool.size() == 0) {
            pool.add(JdbcUtil.createConnection());          
        }
        last_index = pool.size()-1;
        conn = pool.get(last_index);
        pool.remove(last_index);
        return conn;
    }
    
    // 将连接池放回池中
    public synchronized void close(Connection conn){
        if(pool.size() >= POOL_MAX_SIZE){
            try {
                if(conn != null){
                    conn.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }else{
            pool.add(conn);
        }
    }
}

注意:


BD1 - Java 3-4 JDBC - 配置文件 连接池_第22张图片
初始化连接池

在此初始化方法中 循环add 以让池中的连接数达到最小值

TestPool.java代码:

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;

import com.pool.DBConnPool;

public class TestPool {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stat = null;
        ResultSet rs = null;

        DBConnPool dbpool = new DBConnPool();
        conn = dbpool.getConnection();
        String sql = "select * from student";
        
        try{
            stat = conn.createStatement();
            rs = stat.executeQuery(sql);
            while(rs.next()){
                System.out.println(rs.getString("name"));
                System.out.println(rs.getInt("age"));
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            dbpool.close(conn);
        }
        
    }
}

主要代码——新建连接池对象:

DBConnPool dbpool = new DBConnPool();

利用连接池对象的getConnection()获取一个连接:

conn = dbpool.getConnection();

利用连接池对象的close()将连接池放回池中:

dbpool.close(conn);

一定注意 close并不是真的关掉 而是放回池:


BD1 - Java 3-4 JDBC - 配置文件 连接池_第23张图片
close 放回池
拓展阅读:

Java中Synchronized的用法


世界上所有的追求都是因为热爱
一枚爱编码 爱生活 爱分享的IT信徒
— hongXkeX

你可能感兴趣的:(BD1 - Java 3-4 JDBC - 配置文件 连接池)