JavaWeb|JDBC笔记

一、什么是JDBC

1.1、 JDBC概述

概念:JDBC(Java DataBase Connectivity Java ) 数据库连接, 使用Java语言操作数据库。

1.2、JDBC的本质

  JDBC本质:其实是官方(sun公司)定义的一套操作所有关系型数据库的规则,即接口。各个数据库厂商去实现这套接口,提供数据库驱动jar包。我们可以使用这套接口(JDBC)编程,真正执行的代码是驱动jar包中的实现类。

图解如下:
在这里插入图片描述

二、如何连接数据库

2.1、环境准备

1、导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
2、复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下
3、右键–>Add As Library

2.2、代码实现

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

public class Test {
    public static void main(String[] args) throws Exception {
        //1. 导入驱动jar包
        //2.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //3.获取数据库连接对象
        Connection com = DriverManager.getConnection("jdbc:mysql://localhost:3306/db1", "username", "password");
        //4.定义sql语句测试代码
        String sql = "update account set balance = 5000 where id = 1 ";
        //5.获取执行sql的对象 Statement
        Statement stmt = com.createStatement();
        //6.执行sql
        int count = stmt.executeUpdate(sql);
        //7.处理结果
        System.out.println(count);
        //8.释放资源
        stmt.close();
        com.close();
    }
}

三、语法详解

3.1、DriverManager:驱动管理对象

3.1.1、注册驱动

  注册驱动:告诉程序该使用哪一个数据库驱动jar

static void registerDriver(Driver driver) 
//注册与给定的驱动程序 DriverManager

  代码使用:

Class.forName("com.mysql.jdbc.Driver");

通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块

static {
            try {
                java.sql.DriverManager.registerDriver(new Driver());
            } catch (SQLException E) {
                throw new RuntimeException("Can't register driver!");
            }
        }

注意:MySQL5之后的驱动jar包可以省略注册驱动的步骤(因为jar包下的META-INF下的services中的java.sql.Driver会自动导入mysql.jdbc.Driver)

3.2.2、获取数据库连接

方法:

static Connection getConnection(String url, String user, String password) 

参数:url:指定连接的路径
语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
例子:jdbc:mysql://localhost:3306/db3
细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
user:用户名
password:密码

3.2、Connection:数据库连接对象(功能)

3.2.1、获取执行sql 的对象

方法:

 Statement createStatement()//此方法以后较为少用,存在SQL注入问题
 PreparedStatement prepareStatement(String sql)//此方法在以后的开发中较为常用

3.2.2、管理事务

回顾:事务:一个包含多个步骤的业务操作。如果这个业务操作被事务管理,则这多个步骤要么同时成功,要么同时失败。

1.开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
2.提交事务:commit() 
3.回滚事务:rollback() 

3.2.3、使用Connection对象来管理事务

开启事务:setAutoCommit(boolean autoCommit) :调用该方法设置参数为false,即开启事务
    在执行sql之前开启事务(利用静态代码块)
提交事务:commit()
    当所有sql都执行完提交事务
回滚事务:rollback()catch中回滚事务(做非空判断)

代码:

public class JDBCDemo10 {//利用事务实现转账功能
	    public static void main(String[] args) {
	        Connection conn = null;
	        PreparedStatement pstmt1 = null;
	        PreparedStatement pstmt2 = null;
	        try {
	            //1.获取连接
	            conn = JDBCUtils.getConnection();
	            //开启事务
	            conn.setAutoCommit(false);
	            //2.定义sql
	            //2.1 张三 - 500
	            String sql1 = "update account set balance = balance - ? where id = ?";
	            //2.2 李四 + 500
	            String sql2 = "update account set balance = balance + ? where id = ?";
	            //3.获取执行sql对象
	            pstmt1 = conn.prepareStatement(sql1);
	            pstmt2 = conn.prepareStatement(sql2);
	            //4. 设置参数
	            pstmt1.setDouble(1,500);
	            pstmt1.setInt(2,1);
	            pstmt2.setDouble(1,500);
	            pstmt2.setInt(2,2);
	            //5.执行sql
	            pstmt1.executeUpdate();
	            // 手动制造异常
	            int i = 3/0;
	            pstmt2.executeUpdate();
	            //提交事务
	            conn.commit();
	        } catch (Exception e) {
	            //事务回滚
	            try {
	                if(conn != null) {
	                    conn.rollback();
	                }
	            } catch (SQLException e1) {
	                e1.printStackTrace();
	            }
	            e.printStackTrace();
	        }finally {
	            JDBCUtils.close(pstmt1,conn);
	            JDBCUtils.close(pstmt2,null);
	        }
}

3.3、Statement:执行sql的对象

3.3.1、执行sql

方法:

boolean execute(String sql) 
//可以执行任意的sql
int executeUpdate(String sql) 
//1、执行DML(insert、update、delete)语句、DDL(create,alter、drop)语句
//2、返回值:影响的行数,可以通过这个影响的行数判断DML语句是否执行成功 返回值>0的则执行成功,反之,则失败。
ResultSet executeQuery(String sql)
//执行DQL(select)语句

3.3.2、练习

练习目标:

//1. account表 添加一条记录
String sql = "insert into account values(null ,'王五',3000)";
//2. account表 修改记录
String sql = "update account set balance =1500 where id = 4 ";
//3. account表 删除一条记录
String Sql = "delete from account where id =5 ";

删除为例:

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

public class JDBCDemo03 {
    public static void main(String[] args) {
        Connection conn = null;
        Statement stat = null;
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql:///db3", "username", "password");
            String Sql = "delete from account where id =5 ";
            stat = conn.createStatement();
            int count = stat.executeUpdate(Sql);
            if (count > 0) {
                System.out.println("执行成功");
            } else {
                System.out.println("执行失败");
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            if (stat != null) {
                try {
                    stat.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
            if (conn != null) {
                try {
                    stat.close();
                } catch (SQLException throwables) {
                    throwables.printStackTrace();
                }
            }
        }
    }
}

3.4、ResultSet:结果集对象,封装查询结果

boolean next(): 游标向下移动一行,判断当前行是否是最后一行末尾(是否有数据),如果是,则返回false,如果不是则返回true
getXxx(参数):获取数据
如:int getInt() String getString()
参数列表:int:则为列的编号,列编号从1开始,如: getString(1)
   		 String:代表列名称,如: getDouble("balance")

注:游标越界问题。

避免越界的使用方法:

 while(rs.next()){
	                int id = rs.getInt(1);
	                String name = rs.getString("name");
	                double balance = rs.getDouble(3);
	                System.out.println(id + "---" + name + "---" + balance);
	            }

3.5、将连接和释放封装成工具类

由于连接数据库和释放资源过于频繁,现在将这两个功能封装成工具类,便于以后的使用。

/* 获取连接和释放资源的工具类*/
public class JDBCUtils {
    //获取连接的方法
    public static Connection getConnection() {
        Connection conn = null;
        //1、注册数据库驱动
        try {
            Class.forName("com.mysql.jdbc.Driver");
            conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/test", "root", "root");
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        //2、获取连接
        return conn;
    }

    //释放资源的方法
    public static void releaseConnection(Connection conn, Statement st, ResultSet rs) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

3.6、Statement(存在的问题)

3.6.1、拼接SQL过于繁琐

例如以下sql语句

 String sql = "insert into employees(last_name,email,salary,deptId,m_id) values('" + employee.getLast_name() + "','" + employee.getEmail() + "'," + employee.getSalary() + "," + employee.getId() + "," + employee.getM_id() + ")";

3.6.2 、SQL注入问题

  使用静态的SQL语句,容易产生SQL注入的问题,而预编译SQL则不会。

看下面一段SQL代码:

String sql = "select * from user where username = '"+username+"' and password = '"+password+"' ";

1、由于username 和password都是拼接的字符串,所以在传参的时候,会存在安全问题。

如果username随意,密码为 x or ‘x’ = 'x 以c为例

2、sql的代码就是:select * from user where username = ‘fhdsjkf’ and password = ‘b’ or ‘a’ = ‘b’ ,显然可以为true,这样就能跳过登录了啊!多危险啊。

改进的代码:

 String sql = "select *from user where  username= ? and password =?";

3、解决sql注入问题:使用PreparedStatement对象来解决

4、预编译的SQL:参数使用?作为占位符,这样就解决了SQL注入的问题。

3.6.3 、不能插入Blob类型的值

  无法通过拼接插入Blob类型的值。

3.7、PreparedStatement:执行sql的对象

3.7.1 、使用步骤

  1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
  2. 注册驱动
  3. 获取数据库连接对象 Connection
  4. 定义sql
    注意:sql的参数使用?作为占位符。 如:select * from user where username = ? and password = ?;
  5. 获取执行sql语句的对象 PreparedStatement Connection.prepareStatement(String sql)
  6. 给?赋值:
    方法: setXxx(参数1,参数2)
    参数1:?的位置编号 从1 开始
    参数2:?的值
  7. 执行sql,接受返回结果,不需要传递sql语句
  8. 处理结果
  9. 释放资源

3.7.2 、曾删查

@Test
    public void testPreparedStatementUpdate() throws Exception { //  增加一个admin
        //1、获取数据库连接
        Connection conn = JDBCUtils.getConnection();
        //2、编写sql语句
        String sql = "insert into users(username,password,email) values(?,?,?)";
        //3、预编译SQL
        PreparedStatement pst = conn.prepareStatement(sql);
        //4、填充占位符
        pst.setString(1, "admin2");
        pst.setString(2, "666666");
        pst.setString(3, "admin2@qq.com");
        //5、执行更新操作
        int len = pst.executeUpdate();
        System.out.println(len > 0 ? "添加成功!" : "添加失败!");
        //6、释放资源
        JDBCUtils.releaseConnection(conn, pst, null);
    }

 @Test
    public void testPreparedStatementDelete() throws Exception { //  删除admin
//        1、获取数据库连接
        Connection conn = JDBCUtils.getConnection();
        //2、定义sql语句
        String sql = "delete from users where id =? or id = ?";
        //3、预编译SQL
        PreparedStatement pst = conn.prepareStatement(sql);
        //4、填充占位符
        pst.setInt(1, 3);
        pst.setInt(2, 4);
        //5、执行sql语句
        int len = pst.executeUpdate();
        System.out.println((len > 0 ? "操作成功!" : "操作失败!") + "影响行数:" + len);
        //6、释放资源
        JDBCUtils.releaseConnection(conn, pst, null);
    }

 @Test
    public void testPreparedStatementQuery() throws Exception { //  查询演示
        //1、获取数据库连接
        Connection conn = JDBCUtils.getConnection();
        //2、定义SQL语句
        String sql = "select *from users  where username = ? and password = ?";

        //3、预编译sql
        PreparedStatement pst = conn.prepareStatement(sql);
        //4、填充占位符
        pst.setString(1, "admin");
        pst.setString(2, "1234562");
        //5、执行sql语句 获取查询结果集
        ResultSet rs = pst.executeQuery();
        //6、处理结果集打印输出
        while (rs.next()) {
            int id = rs.getInt(1);
            String username = rs.getString(2);
            String password = rs.getString(3);
            String email = rs.getString(4);
            User user = new User(id, username, password, email);
            System.out.println(user);
        }
        //7、释放资源
        JDBCUtils.releaseConnection(conn, pst, rs);
    }

3.7.2 、获取自增长的键和值

1)通过Statement接口中的常量RETURN_GENERATED_KEYS返回自增长的键值
2)通过getGenerateKeys()方法来获取自增长的结果集

@Test
    public void testGetAutoKey() throws SQLException {
        //1、获取数据库连接
        Connection conn = JDBCUtils.getConnection();
        //2、定义SQL
        //String sql = "insert into users(username,password,email) values(?,?,?)";
        String sql = "insert into users(username,password,email) values(?,?,?)";
        //3、预编译SQL语句
        PreparedStatement pst = conn.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
        //4、填充占位符
        pst.setString(1, "admin3");
        pst.setString(2, "888888");
        pst.setString(3, "admin3@taobao.com");
        //5、执行sql语句
        int len = pst.executeUpdate();
        // System.out.println(len > 0 ? "插入成功!" : "插入失败!");
        if (len > 0) {
            //    获取自增长的键值
            //因为一次可以插入多条数据 可能有多个键值
            ResultSet rs = pst.getGeneratedKeys();
            while (rs.next()) {
                //    获取自增长的值为

                System.out.println("自增长的id值为" + rs.getInt(1));
            }
            //关闭资源
            rs.close();
        }
        //6、关闭资源
        JDBCUtils.releaseConnection(conn, pst, null);
    }

3.7.2 、批量插入数据

建表

CREATE TABLE t_batch(
id INT PRIMARY KEY,
`name` VARCHAR(255) NOT NULL
);

不加参数

 String url = "jdbc:mysql://localhost:3306/test";
 @Test
    public void testBatch() throws ClassNotFoundException, SQLException {
        //获取开始的时间
        long start = System.currentTimeMillis();
        //1、注册数据库驱动
        Class.forName("com.mysql.jdbc.Driver");
        //2、设置数据库连接的相关信息
        String url = "jdbc:mysql://localhost:3306/test";
        String username = "root";
        String password = "root";
        //3、获取连接
        Connection conn = DriverManager.getConnection(url, username, password);
        //4、定义sql语句
        String sql = "insert into t_batch values(?,?)";
        //5、预编译SQL
        PreparedStatement pst = conn.prepareStatement(sql);
        //6、填充占位符
        for (int i = 1; i <= 1000; i++) {
            pst.setObject(1, i);
            pst.setObject(2, "dept" + i);
            //将sql语句积攒起来 然后一起执行
            pst.addBatch();
        }
        //7、执行SQL
        pst.executeBatch();
        //8、释放资源
        JDBCUtils.releaseConnection(conn, pst, null);
        //获取结束的时间
        long end = System.currentTimeMillis();
        System.out.println("总耗时为:" + (end - start));
    }

在这里插入图片描述

不加参数的耗时:
在这里插入图片描述

加了参数后:

String url = "jdbc:mysql://localhost:3306/test?rewriteBatchedStatements=true";

在这里插入图片描述
truncate t_batch 删除表并重建

3.7.3 、开启事务

1)mysql中每个连接是自动提交的,如果要开启事务,需要设置成手动提交。
2)Connection.setAutoCommit(false) 开启事务
Connection.commit() 提交事务
Connection.rollback() 回滚事务’

Users表
在这里插入图片描述
books表

CREATE TABLE books(
	id INT PRIMARY KEY AUTO_INCREMENT,
	title VARCHAR(255) NOT NULL,
	price DOUBLE(11,2),
	sales INT NOT NULL,
	stock INT NOT NULL
);

插入数据

INSERT INTO books(title,price,sales,stock) VALUES
("Java的美",120.00,100,100),
("MySQL的美",100.00,100,100);

在这里插入图片描述

需求: 模拟一下,购买图书,一次只能买一本,用户花钱。
1)更新书的销售量
2)更新书的库存
3)更新用户的余额

未开启事务之前:
Java代码

 @Test
    public void testTransaction() {
        //1、获取数据库的链接
        Connection conn = JDBCUtils.getConnection();
        //2、编写SQL语句
        String sql = "update books set sales = sales + 1 where id = ?";
        String sql2 = "update books set stock = stock - 1 where id = ?";
        String sql3 = "update users set balance = balance - ? where id = ?";
        PreparedStatement pst1 = null;
        PreparedStatement pst2 = null;
        PreparedStatement pst3 = null;
        try {
            //3、预编译SQL语句
            pst1 = conn.prepareStatement(sql);
            pst1.setInt(1, 1);
            pst2 = conn.prepareStatement(sql2);
            pst2.setInt(1, 1);
            pst3 = conn.prepareStatement(sql3);
            pst3.setDouble(1, 120);
            pst3.setInt(2, 1);
            //5、执行sql
            int len1 = pst1.executeUpdate();
            System.out.println(len1 > 0 ? "第一条SQL语句执行成功!" : "第一条SQL语句执行成功!");
            int len2 = pst2.executeUpdate();
            System.out.println(len1 > 0 ? "第二条SQL语句执行成功!" : "第二条SQL语句执行成功!");
            int len3 = pst3.executeUpdate();
            System.out.println(len1 > 0 ? "第三条SQL语句执行成功!" : "第三条SQL语句执行成功!");
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            JDBCUtils.releaseConnection(null, pst1, null);
            JDBCUtils.releaseConnection(null, pst2, null);
            JDBCUtils.releaseConnection(conn, pst3, null);
        }
    }

运行截图:
在这里插入图片描述
在这里插入图片描述

将数据恢复并开启事务:
在这里插入图片描述

代码:

 @Test
    public void testTransaction() {
        //1、获取数据库的链接
        Connection conn = JDBCUtils.getConnection();
        //2、编写SQL语句
        String sql = "update books set sales = sales + 1 where id = ?";
        String sql2 = "update books set stock = stock - 1 where id = ?";
        String sql3 = "update users set balance = balance - ? where id = ?";
        PreparedStatement pst1 = null;
        PreparedStatement pst2 = null;
        PreparedStatement pst3 = null;
        try {
            //开启事务:
            conn.setAutoCommit(false);
            //3、预编译SQL语句
            pst1 = conn.prepareStatement(sql);
            pst1.setInt(1, 1);
            pst2 = conn.prepareStatement(sql2);
            pst2.setInt(1, 1);
            pst3 = conn.prepareStatement(sql3);
            pst3.setDouble(1, 120);
            pst3.setInt(2, 1);
            //5、执行sql
            int len1 = pst1.executeUpdate();
            System.out.println(len1 > 0 ? "第一条SQL语句执行成功!" : "第一条SQL语句执行成功!");
            int len2 = pst2.executeUpdate();
            System.out.println(len1 > 0 ? "第二条SQL语句执行成功!" : "第二条SQL语句执行成功!");
            int len3 = pst3.executeUpdate();
            System.out.println(len1 > 0 ? "第三条SQL语句执行成功!" : "第三条SQL语句执行成功!");
            //如果没有出现异常
            conn.commit();
        } catch (SQLException e) {
            //出现异常 回滚事务
            try {
                conn.rollback();
            } catch (SQLException ex) {
                ex.printStackTrace();
            }
            e.printStackTrace();
        } finally {
            JDBCUtils.releaseConnection(null, pst1, null);
            JDBCUtils.releaseConnection(null, pst2, null);
            JDBCUtils.releaseConnection(null, pst2, null);
        }
        //将事务设置为自动提交
        try {
            conn.setAutoCommit(true);
            conn.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }

在这里插入图片描述
还是得到同未开启事务的样子而数据库中的数据库确没有改变
‘’
修改图书的价格为 66元

UPDATE books SET price = 66.00 WHERE id = 1;
//吸怪此处的pt3.setDouble
pst3 = conn.prepareStatement(sql3);
pst3.setDouble(1, 66.00);

执行结果:
在这里插入图片描述
books表
在这里插入图片描述
Users表
在这里插入图片描述

注:

1、后期都会使用PreparedStatement来完成增删改查的所有操作

2、可以防止SQL注入

3、效率更高

四、数据库连接池

4.1、什么是数据库连池

连接对象的缓冲区。负责申请,分配管理,释放连接的操作。

4.2、为什么要使用数据库连接池

不使用数据库连接池,每次都通过DriverManager获取新连接,用完直接抛弃断开,连接的利用率太低,太浪费。
对于数据库服务器来说,压力太大了。我们数据库服务器和Java程序对连接数也无法控制,很容易导致数据库服务器崩溃。

我们就希望能管理连接。
我们可以建立一个连接池,这个池中可以容纳一定数量的连接对象,一开始,我们可以先替用户先创建好一些连接对象,
等用户要拿连接对象时,就直接从池中拿,不用新建了,这样也可以节省时间。然后用户用完后,放回去,别人可以接着用。
可以提高连接的使用率。当池中的现有的连接都用完了,那么连接池可以向服务器申请新的连接放到池中。
直到池中的连接达到“最大连接数”,就不能在申请新的连接了,如果没有拿到连接的用户只能等待。

4.3、市面上有很多现成的数据库连接池技术:

JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口(通常被称为数据源),该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现:

  • DBCP 是Apache提供的数据库连接池,速度相对c3p0较快,但因自身存在BUG,Hibernate3已不再提供支持
  • C3P0 是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以
  • Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
  • BoneCP 是一个开源组织提供的数据库连接池,速度快
  • Druid 是阿里提供的数据库连接池,据说是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池

4.4、阿里的德鲁伊连接池技术

(1)加入jar包
例如:druid-1.1.10.jar
(2)代码步骤
第一步:建立一个数据库连接池
第二步:设置连接池的参数
第三步:获取连接

配置缺省说明
name配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this)
jdbcUrl连接数据库的url,不同数据库不一样。例如:mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto
username连接数据库的用户名
password连接数据库的密码。如果你不希望密码直接写在配置文件中,可以使用ConfigFilter。详细看这里:https://github.com/alibaba/druid/wiki/使用ConfigFilter
driverClassName根据url自动识别 这一项可配可不配,如果不配置druid会根据url自动识别dbType,然后选择相应的driverClassName(建议配置下)
initialSize0初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive8最大连接池数量
maxIdle8已经不再使用,配置了也没效果
minIdle最小连接池数量
maxWait获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatementsfalse是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements-1要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrowtrue申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturnfalse归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdlefalse建议配置为true,不影响性能,并且保证安全性。申请连接的时候检测,如果空闲时间大于timeBetweenEvictionRunsMillis,执行validationQuery检测连接是否有效。
timeBetweenEvictionRunsMillis有两个含义: 1)Destroy线程会检测连接的间隔时间2)testWhileIdle的判断依据,详细看testWhileIdle属性的说明
numTestsPerEvictionRun不再使用,一个DruidDataSource只支持一个EvictionRun
minEvictableIdleTimeMillis
connectionInitSqls物理连接初始化的时候执行的sql
exceptionSorter根据dbType自动识别 当数据库抛出一些不可恢复的异常时,抛弃连接
filters属性类型是字符串,通过别名的方式配置扩展插件,常用的插件有: 监控统计用的filter:stat日志用的filter:log4j防御sql注入的filter:wall
proxyFilters类型是List,如果同时配置了filters和proxyFilters,是组合关系,并非替换关系
public class TestPool {
	public static void main(String[] args) throws SQLException {
		//1、创建数据源(数据库连接池)对象
		DruidDataSource ds =new DruidDataSource();
		
		//2、设置参数
		//(1)设置基本参数
		ds.setDriverClassName("com.mysql.jdbc.Driver");
		ds.setUrl("jdbc:mysql://localhost:3306/test");
		ds.setUsername("root");
		ds.setPassword("123456");
		
		//(2)设置连接数等参数
		ds.setInitialSize(5);//一开始提前申请好5个连接,不够了,重写申请
		ds.setMaxActive(10);//最多不超过10个,如果10都用完了,还没还回来,就会出现等待
		ds.setMaxWait(1000);//用户最多等1000毫秒,如果1000毫秒还没有人还回来,就抛异常了
		
		//3、获取连接
		for (int i = 1; i <=15; i++) {
			Connection conn = ds.getConnection();
			System.out.println("第"+i+"个:" + conn);
			
			//如果这里没有关闭,就相当于没有还
//			conn.close();#这里关闭,是还回池中
		}
	}
}

借了就还的情况:

 	/*
     * 测试德鲁伊数据库连接池
     * */
    @Test
    public void testDruidDataSource() {
        //1、创建数据库连接池的对象
        DruidDataSource dds = new DruidDataSource();
        //2、设置连接数据库的信息
        dds.setDriverClassName("com.mysql.jdbc.Driver");
        dds.setUrl("jdbc:mysql://localhost:3306/test");
        dds.setUsername("root");
        dds.setPassword("root");
        //3、设置链接参数
        dds.setInitialSize(5);
        //4、设置最大连接数
        dds.setMaxActive(10);
        //5、设置最大等待时间
        dds.setMaxWait(5000);
        //3、获取链接
        for (int i = 1; i < 11; i++) {
            try {
                Connection conn = dds.getConnection();
                System.out.println("第" + i + "个" + conn);
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

在这里插入图片描述
借了不还的情况:
在这里插入图片描述

4.5、DruidDataSourceFactory

创建druid.properties配置文件

username=root
password=root
url=jdbc:mysql://localhost:3306/test
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxWait=10
maxActive=10000
    @Test
    public void testDruidDataFactory() throws Exception {
        //1、创建properties对象
        Properties pro = new Properties();
        //2、加载类路径下的properties文件 作为一个流
        InputStream is = PoolTest.class.getClassLoader().getResourceAsStream("druid.properties");
        //3、加载properties文件
        pro.load(is);
        //4、创建数据源
        DataSource dataSource = DruidDataSourceFactory.createDataSource(pro);
        //5、获取连接
        Connection conn = dataSource.getConnection();
        System.out.println(conn);
        //6、关闭连接
        conn.close();
        conn.close();
    }

结果截图:
在这里插入图片描述

五、封装JDBCTools

配置文件:src/jdbc.properties

*
 * 通过德鲁伊连接池获取连接的工具类
 * */
public class JDBCUtils2 {
    private static DataSource dataSource;

    static {
        //1、创建properties对象
        Properties pro = new Properties();
        //2、将类路径下的properties文件读成一个流
        InputStream is = PoolTest.class.getClassLoader().getResourceAsStream("druid.properties");
        try {
            //3、将做为流的properties加载进来
            pro.load(is);
            //4、创建数据源
            dataSource = DruidDataSourceFactory.createDataSource(pro);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    //获取连接的方法
    public static Connection getConnection() {
        //从数据源中获取连接
        Connection conn = null;
        try {
            conn = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return conn;
    }

    //释放连接的方法
    public static void relreaseConnection(Connection conn, ResultSet rs, Statement st) {
        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
 /*测试JDBCUtils2
     * */
    @Test
    public void testJDBCUtils() {
        //1、获取连接
        Connection conn = JDBCUtils2.getConnection();
        System.out.println(conn);
        //2、释放连接
        JDBCUtils2.relreaseConnection(conn, null, null);
    }

在这里插入图片描述
测试JDBCUtils2
更新操作

//通过JDBCUtils2来实现更新的功能
    @Test
    public void testPreparedStatementUpdateJDBCUtils2() throws Exception { 
        //1、获取连接
        Connection conn = JDBCUtils2.getConnection();
        //2、写sql语句
        String sql = "update users set password = ?,email = ? where id = ?";
        //3、预编译sql语句
        PreparedStatement pst = conn.prepareStatement(sql);
        //4、填充占位符
        pst.setString(1, "111111");
        pst.setString(2, "admin@sina.com");
        pst.setInt(3, 1);
        //5、执行sql
        pst.executeUpdate();
        //6、释放资源
        JDBCUtils2.relreaseConnection(conn, null, pst);
    }

在这里插入图片描述
在这里插入图片描述
删除操作

   //通过JDBCUtils2来实现删除的操作
    @Test
    public void testPreparedStatementDeleteJDBCUtils2() throws Exception {
        //1、获取数据库连接
        Connection conn = JDBCUtils2.getConnection();
        //2、定义sql语句
        String sql = "delete from users where id = ?";
        //3、预编译sql
        PreparedStatement pst = conn.prepareStatement(sql);
        //4、填充占位符
        pst.setInt(1, 5);
        //5、执行sql语句
        pst.executeUpdate();
        //6、释放资源
        JDBCUtils2.relreaseConnection(conn, null, pst);
    }

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
一、概述: JDBC从物理结构上说就是Java语言访问数据库的一套接口集合。从本质上来说就是调用者(程序员)和实现者(数据库厂商)之间的协议。JDBC的实现由数据库厂商以驱动程序的形式提供。JDBC API 使得开发人员可以使用纯Java的方式来连接数据库,并进行操作。 ODBC:基于C语言的数据库访问接口。 JDBC也就是Java版的ODBC。 JDBC的特性:高度的一致性、简单性(常用的接口只有4、5个)。 1.在JDBC中包括了两个包:java.sql和javax.sql。 ① java.sql 基本功能。这个包中的类和接口主要针对基本的数据库编程服务,如生成连接、执行语句以及准备语句和运行批处理查询等。同时也有一些高级的处理,比如批处理更新、事务隔离和可滚动结果集等。 ② javax.sql 扩展功能。它主要为数据库方面的高级操作提供了接口和类。如为连接管理、分布式事务和旧有的连接提供了更好的抽象,它引入了容器管理的连接池、分布式事务和行集等。 注:除了标出的Class,其它均为接口。 API 说明 java.sql.Connection 与特定数据库的连接(会话)。能够通过getMetaData方法获得数据库提供的信息、所支持的SQL语法、存储过程和此连接的功能等信息。代表了数据库。 java.sql.Driver 每个驱动程序类必需实现的接口,同时,每个数据库驱动程序都应该提供一个实现Driver接口的类。 java.sql.DriverManager (Class) 管理一组JDBC驱动程序的基本服务。作为初始化的一部分,此接口会尝试加载在”jdbc.drivers”系统属性中引用的驱动程序。只是一个辅助类,是工具。 java.sql.Statement 用于执行静态SQL语句并返回其生成结果的对象。 java.sql.PreparedStatement 继承Statement接口,表示预编译的SQL语句的对象,SQL语句被预编译并且存储在PreparedStatement对象中。然后可以使用此对象高效地多次执行该语句。 java.sql.CallableStatement 用来访问数据库中的存储过程。它提供了一些方法来指定语句所使用的输入/输出参数。 java.sql.ResultSet 指的是查询返回的数据库结果集。 java.sql.ResultSetMetaData 可用于获取关于ResultSet对象中列的类型和属性信息的对象。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值