JDBC&连接池

JDBC&连接池

今日内容介绍

jdbc:可以使用java语言操作数据库(发送sql语句,接受处理结果)

  • 四天之内需要记住.

连接池:存放连接的池子

一 JDBC

1 概述

Java 数据库连接(Java DataBase Connectivity)

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

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FnjpbBsu-1620723841175)(04.jdbc&连接池.assets/image-20200612092238490.png)]

小结:

  • jdbc是一套操作关系型数据库的接口
  • 由数据库厂商提供这套接口的实现类(叫做驱动)
  • 我们只需要掌握这套接口中的方法,操作那种数据库就导入那种数据库的驱动即可

2 快速入门

需求

​ 使用jdbc技术往day02数据库的account表中插入一条记录

步骤分析

  1. 创建一个模块(java模块或者web模块),导入mysql的驱动(昨天资料中)

  2. 编写代码

    1. 获取连接
    2. 编写sql
    3. 获取sql执行对象(执行者)
    4. 使用执行者执行sql语句,接受返回值
    5. 处理返回值
    6. 释放资源

代码实现

package com.itheima.jdbc;

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

public class A_HelloJdbc {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        //0.注册驱动的操作,但是可以省略
        //方式1:DriverManager.registerDriver(new Driver());
        //方式2:使用反射机制
        Class.forName("com.mysql.jdbc.Driver");

        //1.获取连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day02", "root", "1234");

        //2.编写sql
        String sql = "insert into account values(null,'jack',1000)";

        //3.创建sql语句的执行者
        Statement st = conn.createStatement();

        //4.执行sql,接受返回值
        int i = st.executeUpdate(sql);

        //5.处理返回值
        if(i==1){
            System.out.println("success");
        }else{
            System.out.println("fail");
        }

        //6.释放资源
        st.close();
        conn.close();
    }
}

3 API介绍

都在jdk的api中,都在java.sql包或者javax.sql包下

  • Driver:驱动 一般看不到 – 接口
  • DriverManager:驱动管理者 --类
    • 注册驱动:我们省略了
    • 获取连接
  • Connection:连接类 – 接口
    • 创建sql的执行者
    • 控制事务
  • Statement:sql语句执行类 – 接口
    • 执行dml语句:executeUpdate(sql) 返回值: int类型(影响的行数)
    • 执行dql语句:executeQuery(sql) 返回值: ResultSet类型(结果集)
  • ResultSet:查询出来的数据集合(结果集) – 接口
    • 判断是否有查询结果.若有的话需要获取每行中的数据.

DriverManager:

主要作用:

  • 注册驱动
  • 获取连接

常用方法:

方法名作用
static void registerDriver(Driver 驱动对象) --了解注册驱动
static Connection getConnection(连接路径,数据库用户名,数据库密码)获取连接
注册驱动-了解

翻看mysql的驱动类源码,发现里面有一个静态代码块,其中的逻辑是:注册驱动.静态代码块的执行时机就是加载类对象的时候就会执行.我们只需要将Driver类加载到内存就可以了.可以使用反射机制

  • 方式1: Class.forName(“com.mysql.jdbc.Driver”);
    • 更好的解耦合,推荐用这种方式.
  • 方式2: 类名.class;
  • 方式3: 对象.getClass();

从jdk1.5开始,不注册驱动也可以,jdk底层会帮我们自动的注册驱动.有些程序开发的比较早,使用的jdk比较老,为了兼容它们,所以最好留着注册驱动.

获取连接

static Connection getConnection(url,username,pwd):获取连接

  • url:目标数据库的url
    • 格式:

jdbc:子协议://ip地址或者域名:端口号/数据库名字

例如:

jdbc:mysql://localhost:3306/day02

  • 若连接的本机的3306端口号可以简写为

jdbc:mysql:///day02

  • 连接上也可以添加参数,例如下面的参数可以解决存储数据出现乱码问题,一般不用

    ?useUnicode=true&characterEncoding=utf-8

  • username:数据库的用户名

  • password:数据库的密码


Connection

数据库连接

主要作用:

  • 获取语句执行对象
  • 控制事务

常用方法:

方法名作用
Statement createStatement()创建statement语句执行对象
PreparedStatement prepareStatement(sql语句)创建PreparedStatement预编译语句执行对象
setAutoCommit(布尔值)设置事务是否自动提交,若设置为false就是手动开启事务
commit()提交事务
rollback()回滚事务

Statement

语句执行对象

主要作用:

  • 执行sql语句

常用方法:

方法名作用
int executeUpdate(sql)主要用来执行dml语句,增删改,返回值为:影响的行数
ResultSet executeQuery(sql)用来执行dql语句,查询操作,返回值为:查询到的结果集

ResultSet

查询语句执行后的结果集

常用的方法:

方法名作用
boolean next()向下移动指针,若有数据的话返回true,且移动指针;若无数据的话返回false
xxx getXxx(列名或者列号)获取指定列或者指定字段名的数据

getXxx(): xxx指的数据类型

  • 若获取的字符串 就有getString方法
  • 若获取的int 就有getInt方法
  • 通用的方法 getObject()方法

若数据库类型为int的话,可不可以使用getString方法获取? 可以

String id = rs.getString(“id”);

4 CRUD操作

package com.itheima.jdbc;

import org.junit.Test;

import java.sql.*;

public class B_CrudDemo {
    @Test
    public void testQuery() throws ClassNotFoundException, SQLException {
        //0.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //1.获取连接
        Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/day02","root","1234");
        //2.编写sql
        String sql = "select * from account";
        //3.创建sql执行者
        Statement st = conn.createStatement();
        //4.执行sql,接受返回值
        ResultSet rs = st.executeQuery(sql);
        //5.处理返回值
        while (rs.next()) {
            //获取id
            int id = rs.getInt("id");

            //获取name
            String name = rs.getString(2);

            //获取金额
            String money = rs.getString("money");

            System.out.print(id+"\t"+name+"\t"+money);

            System.out.println();
        }
        //6.释放资源
        rs.close();
        st.close();
        conn.close();
    }

    @Test
    public void testUpdate() throws ClassNotFoundException, SQLException {
        //0.注册驱动
        Class.forName("com.mysql.jdbc.Driver");
        //1.获取连接
        Connection conn = DriverManager.getConnection("jdbc:mysql:///day02","root","1234");
        //2.编写sql
        String sql = "update account set name = '鹏飞' ,money = 10000 where id = 1";
        //3.创建sql执行者
        Statement st = conn.createStatement();
        //4.执行sql,接受返回值
        int i = st.executeUpdate(sql);
        //5.处理返回值
        if (i==1){
            System.out.println("success");
        }else {
            System.out.println("fail");
        }
        //6.释放资源
        st.close();
        conn.close();
    }

}

抽取工具类(不用写)

每次操作的时候,都注册了驱动、获取了连接和释放了资源.这些操作都是一样的.我们可以将这些操作抽取到一个工具类中 ,例如:JdbcUtils1

工具类有如下操作:

  1. 注册驱动:只需要注册一次,放入到静态代码块中
  2. 获取连接:每次操作都需要获取连接
  3. 释放资源:每次操作都需要释放资源
package com.itheima.utils;

import java.sql.*;

public class JdbcUtils1 {
    //1.注册驱动,只需要一次,static
    static{
        try {
            Class.forName("com.mysql.jdbc.Driver");
        } catch (ClassNotFoundException e) {
            //静默处理
        }
    }

    //2.获取连接 因为调用者需要用conn创建其他的对象.
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection("jdbc:mysql:///day02","root","1234");
    }

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

            }
            //可以让垃圾回收器更快的回收对象
            rs = null;
        }

        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {

            }
            //可以让垃圾回收器更快的回收对象
            st = null;
        }

        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {

            }
            //可以让垃圾回收器更快的回收对象
            conn = null;
        }
    }
}

package com.itheima.jdbc;

import com.itheima.utils.JdbcUtils1;

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

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

        try {
            //1.获取连接
            conn = JdbcUtils1.getConnection();

            //2.编写sql
            String sql = "delete from account where id = 2";

            //3.获取语句执行者
            st = conn.createStatement();

            //4.执行sql,接受返回值
            int i = st.executeUpdate(sql);

            //5.处理返回值
            if (i==1){
                System.out.println("success");
            }else {
                System.out.println("fail");
            }
        } catch (SQLException e) {
            e.printStackTrace();
        } finally {
            //6.释放资源
            JdbcUtils1.release(conn,st,rs);
        }


    }
}

5 事务操作

api

Connection的方法

一件事情(业务)属于一个事务操作.业务代码写在service层,事务要加载service层上

service中一个方法就使用一个业务. service中获取Connection连接对象控制事务,dao层也需要使用Connection操作数据库.service中的Connection和dao中的Connection是同一个吗?必须是.

怎么就可以将service中的connection传递给dao层? 可以使用传参方案.

service中方法逻辑

  1. 获取连接,开启事务
  2. 执行所有的操作,若操作涉及到dao,务必要把service中connection传递给dao层
  3. 当所有的操作都成功的时候,需要提交事务
  4. 当任何一个操作失败的时候,需要回滚事务
  5. 最后无论如何都需要释放Connection

转账案例

步骤分析:

  1. 创建AccountDao类.编写转出方法(扣钱)和转入(加钱)方法
    1. transferIn(连接,收款人,金额) 转入方法
    2. transferOut(连接,汇款人,金额) 转出方法
  2. 创建AccountService类,编写转账方法(transfer),参数有:汇款人,收款人,金额
    1. 开启事务
    2. 调用dao中的扣钱的操作
    3. 调用dao中的加钱的操作
    4. 都成功提交事务
    5. 若有一个失败就回滚事务
    6. 释放connection
  3. 测试转账

代码实现:

package com.itheima.jdbc.d_account.dao;

import com.itheima.utils.JdbcUtils1;

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

public class AccountDao {
    //扣钱
    public int transferOut(Connection conn,String fromUser,int money){
        Statement st = null;
        try {
            //1.编写sql
            String sql = "update account set money = money - "+money+" where name = '"+fromUser+"'";

            //2.获取语句执行者
            st = conn.createStatement();

            //3.执行sql,接受返回值
            int i = st.executeUpdate(sql);

            //5,返回 返回值
            return i;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtils1.release(null,st,null);
        }

        return 0;
    }

    //加钱
    public int transferIn(Connection conn,String toUser,int money){
        Statement st = null;
        try {
            //1.编写sql
            String sql = "update account set money = money + "+money+" where name = '"+toUser+"'";

            //2.获取语句执行者
            st = conn.createStatement();

            //3.执行sql,接受返回值
            int i = st.executeUpdate(sql);

            //5,返回 返回值
            return i;
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            JdbcUtils1.release(null,st,null);
        }

        return 0;
    }
}

package com.itheima.jdbc.d_account.service;

import com.itheima.jdbc.d_account.dao.AccountDao;
import com.itheima.utils.JdbcUtils1;

import java.sql.Connection;
import java.sql.SQLException;

public class AccountService {
    AccountDao accountDao = new AccountDao();

    public void transfer(String fromUser,String toUser,int money) throws Exception {

        Connection conn = null;

        try {
            //1.获取conn 开启事务
            conn = JdbcUtils1.getConnection();
            conn.setAutoCommit(false);

            //2.调用dao的扣钱操作
            int i = accountDao.transferOut(conn, fromUser, money);
            if (i!=1) {
                throw new RuntimeException("转出失败");
            }

            //3,调用dao的加钱操作
            i = accountDao.transferIn(conn,toUser,money);
            if (i!=1) {
                throw new RuntimeException("转入失败");
            }
            //4.没有问题,提交事务
            conn.commit();
        } catch (Exception e) {
            e.printStackTrace();
            //5.有问题,回滚事务
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }

            //6.通知调用者,转账失败
            throw e;
        } finally {
            //6.都需要释放conn
            JdbcUtils1.release(conn,null,null);
        }

    }
}

测试代码

package com.itheima.jdbc.d_account;

import com.itheima.jdbc.d_account.service.AccountService;

public class MoNiZhuanZhang {
    public static void main(String[] args){
        AccountService accountService = new AccountService();

        try {
            accountService.transfer("tom","jackson",100);
            System.out.println("success");
        } catch (Exception e) {
            e.printStackTrace();
            System.out.println("fail");
        }
    }
}

6 案例:用户登录

需求

通过java控制台模拟,用户输入账号和密码,实现登录网站功能

通过控制台接受用户名和密码,判断数据库中有无记录,若有记录,打印用户的信息,若无记录则打印"查无此人"

user表

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `username` varchar(32) DEFAULT NULL,
  `password` varchar(32) DEFAULT NULL,
  `sex` varchar(2) DEFAULT NULL,
   PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=3 DEFAULT CHARSET=utf8;

INSERT INTO USER VALUES(NULL,'tom','123','男'),(NULL,'rose','abc','女');

分析:

  1. 使用scanner接受控制台输入的内容
  2. 使用jdbc技术去数据库中完成查询操作

步骤分析:

  1. 创建一个Scanner对象
  2. 提示用户输入用户名和密码,且接受
  3. 使用jdbc去查询数据库中的记录
    1. 获取连接
    2. 编写sql
    3. 创建语句执行者
    4. 执行查询语句,接受返回值
    5. 判断是否有记录
    6. 释放资源

代码实现:

public class E_LoginDemo {
    public static void main(String[] args) {
        //1.创建scanner对象
        Scanner sc = new Scanner(System.in);

        //2.提示用户输入用户名和密码,且接受
        System.out.println("请输入用户名");
        String username = sc.nextLine();
        System.out.println("请输入密码");
        String password = sc.nextLine();

        //3.使用jdbc操作数据库
        Connection conn = null;
        Statement st = null;
        ResultSet rs = null;

        try {
            //1. 获取连接
            conn = JdbcUtils1.getConnection();
            //2. 编写sql
            String sql = "select * from user where username = '"+username+"' and password = '"+password+"'";
            //3. 创建语句执行者
            st = conn.createStatement();
            //4. 执行查询语句,接受返回值
            rs = st.executeQuery(sql);
            //5. 判断是否有记录
            if (rs.next()) {
                System.out.println(rs.getString("username") + ":欢迎回来 , 性别:"+rs.getString("sex"));
            }else{
                System.out.println("用户名或密码错误");
            }
        } catch (SQLException e) {
            e.printStackTrace();
            System.out.println("服务器正在升级,请稍后再试");
        } finally {
            JdbcUtils1.release(conn,st,rs);
        }
    }
}


二 PreparedStatement

上面案例中,输入用户名的时候 输入:tom'# ,密码输入任何值都可以登陆.

1 概述

SQL注入问题

用户输入的内容作为了SQL语句语法的一部分,改变了原有SQL真正的意义,以上问题称为SQL注入。

解决sql注入问题

我们不能让用户输入的信息和SQL语句进行字符串拼接。需要使用PreparedSatement对象解决SQL注入。

2 使用步骤

  1. 编写sql的时候,使用?作为占位符,替换sql中的参数数据

    之前的sql:

    ​ select * from user where username = ‘"+username+"’ and password = ‘"+pwd+"’

    现在的sql:

    ​ select * from user where username = ? and password = ?

  2. 创建的预编译的语句执行者 PreparedStatement

    之前的对象:

    ​ Statement st = conn.createStatement();

    现在的对象

    ​ PreparedStatement st = conn.prepareStatement(sql);

  3. 给语句设置参数,替代?部分

    st.setXxx(第几个问号,实际值):Xxx指的是数据类型

    例如:

    ​ st.setString(1,“tom”);

    ​ st.setInt(1,10);

  4. 执行sql的时候要使用PreparedStatement的无参方法执行sql

    之前执行sql:

    ​ st.executeUpdate(sql) 或者 st.executeQuery(sql)

    现在使用无参方法:

    ​ st.executeUpdate()或者st.executeQuery()

3 优化登录案例

public class D_LoginPlusDemo {
    public static void main(String[] args) {
        //1. 创建一个Scanner对象
        Scanner sc = new Scanner(System.in);

        //2. 提示用户输入用户名和密码,且接受
        System.out.println("请输入用户名");
        String username = sc.nextLine();
        System.out.println("请输入密码");
        String pwd = sc.nextLine();

        //3. 使用jdbc去查询数据库中的记录
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;


        try {
            //   1. 获取连接
            conn = JdbcUtils1.getConnection();
            //   2. 编写sql
            String sql = "select * from user where username = ? and password = ?";
            //   3. 创建语句执行者
            st = conn.prepareStatement(sql);
            //   4. 设置参数
            st.setString(1,username);
            st.setString(2,pwd);
            //   5. 执行查询语句,接受返回值
            rs = st.executeQuery();

            //   5. 判断是否有记录
            if (rs.next()) {
                System.out.println("欢迎回来:"+rs.getString("username")+", 性别:"+rs.getString("sex"));
            }else{
                System.out.println("查无此人");
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //   6. 释放资源
            JdbcUtils1.release(conn,st,rs);
        }
    }
}

4 CRUD

PreparedStatement使用的步骤

  1. 获取连接
  2. 编写sql
  3. 创建预编译sql执行者
  4. 给sql设置参数
  5. 执行sql,接受结果
  6. 处理结果
  7. 释放资源
package com.itheima.jdbc;

import com.itheima.utils.JdbcUtils1;
import org.junit.Test;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class F_PP_CrudDemo {
    @Test
    public void testInsert() {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;


        try {
            //1.获取连接
            conn = JdbcUtils1.getConnection();
            //2.编写sql
            String sql = "insert into user values (null,?,?,?)";
            //3.创建预编译sql执行者
            st = conn.prepareStatement(sql);
            //4.设置参数
            st.setString(1,"jack");
            st.setString(2,"qwe");
            st.setString(3,"男");

            //5.执行sql,接受返回值
            System.out.println(st.executeUpdate());
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //7.释放资源
            JdbcUtils1.release(conn,st,rs);
        }

    }

    @Test
    public void testUpdate() {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;


        try {
            //1.获取连接
            conn = JdbcUtils1.getConnection();
            //2.编写sql
            String sql = "update user set username = ?,password = ? ,sex = ? where id = ?";
            //3.创建预编译sql执行者
            st = conn.prepareStatement(sql);

            //4.设置参数
            st.setString(1,"jackson");
            st.setString(2,"asd");
            st.setString(3,"女");
            st.setString(4,"5");

            //5.执行sql,接受返回值
            System.out.println(st.executeUpdate());

            //6.处理结果
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //7.释放资源
            JdbcUtils1.release(conn,st,rs);
        }

    }

    @Test
    public void testFind() {
        Connection conn = null;
        PreparedStatement st = null;
        ResultSet rs = null;


        try {
            //1.获取连接
            conn = JdbcUtils1.getConnection();
            //2.编写sql
            String sql = "select * from user order by id desc limit ?,?";
            //3.创建预编译sql执行者
            st = conn.prepareStatement(sql);

            //4.设置参数
            //st.setString(1,"username");
            st.setInt(1,0);
            st.setInt(2,2);

            //5.执行sql,接受返回值
            rs = st.executeQuery();

            //6.处理结果
            while (rs.next()) {
                System.out.println(rs.getString("id") + "==" + rs.getString("username"));
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            //7.释放资源
            JdbcUtils1.release(conn,st,rs);
        }

    }
}

order by 后面不能使用 ? ,使用了也没有生效

5 优点

  1. PreparedStatement要比Statement更加安全,没有sql注入问题
  2. PreparedStatement要比Statement执行效率高.

每条sql语句执行之前也需要编译,编译的动作是sql服务器自己完成的.

若使用PreparedStatement之后,sql语句可以进行预编译,执行的时候只是参数不同而已,sql不需要重复编译

注意:

1.	PreparedStatement是Statement的子类
2.	执行sql的时候使用的方法是自己的无参方法.否则就会报错

三 连接池

1 概述

连接池其实就是一个容器(集合),存放数据库连接的容器。

当系统初始化好后,容器被创建,容器中会申请一些连接对象,当用户来访问数据库时,从容器中获取连接对象,用户访问完之后,会将连接对象归还给容器。

优点

  • 节约资源,减轻服务器压力
  • 提高连接复用性,用户访问高效

规范

Java为数据库连接池提供了公共的接口: javax.sql.DataSource ,各个厂商去实现这套接口,提供jar包。

  • 从连接池中获取连接 : 调用连接池对象的 getConnection() 方法
  • 归还连接 : 调用连接的close()方法

常用的连接池技术

  • C3P0:数据库连接池技术,使用它的开源项目有Hibernate、Spring等。
  • Druid:(德鲁伊)阿里巴巴提供的数据库连接池技术,是目前最好的数据库连接池。
  • HikariCP:小日本开发的连接池技术,称之为效率最高的一款连接池,springboot整合的就是hikaricp连接池

其他的连接池(了解)

  • boneCP:数据库连接池技术,体积小速度快。
  • DBCP:Apache提供的数据库连接池技术。

2 Druid连接池

前提:

​ 导入jar包(druid.jar和mysql驱动)

硬编码方式:(掌握,框架时候用)

  1. 创建DruidDataSource
  2. 设置连接池的四个基本参数(url,驱动,数据库用户名和密码)
  3. 还可以设置其他的参数(例如:连接池初始化连接的数量,连接的最大数量,连接的最大空闲数量,获取连接的超时时间等等)
  4. 获取连接
  5. 使用连接
  6. 归还连接

public class DruidDemo {
    @Test
    //硬编码方式
    public void test1() throws SQLException {
        //1. 创建DruidDataSource
        DruidDataSource ds = new DruidDataSource();
        //2. 设置连接池的四个基本参数(url,驱动,数据库用户名和密码)
        ds.setUrl("jdbc:mysql:///day02");
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUsername("root");
        ds.setPassword("1234");

        //3. 还可以设置其他的参数(例如:连接池初始化连接的数量,连接的最大数量,连接的最大空闲数量,获取连接的超时时间等等)
        //连接池初始化连接的数量
        ds.setInitialSize(5);
        //连接的最大数量
        ds.setMaxActive(10);
        //获取连接的超时时间,若超出这个时间会报错
        ds.setMaxWait(3000);


        //4. 获取连接
        DruidPooledConnection conn = ds.getConnection();
        //5. 使用连接
        System.out.println(conn);
        //6. 归还连接
        conn.close();
    }

    @Test
    //硬编码方式
    /*  当获取的连接超出了最大数量的时候,程序就会等待一段时间(3000),
        在这段时间中有人归还了连接,直接拿过来用.一致没有人归还就会报错
    */
    public void test2() throws SQLException {
        //1. 创建DruidDataSource
        DruidDataSource ds = new DruidDataSource();
        //2. 设置连接池的四个基本参数(url,驱动,数据库用户名和密码)
        ds.setUrl("jdbc:mysql:///day02");
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUsername("root");
        ds.setPassword("1234");

        //3. 还可以设置其他的参数(例如:连接池初始化连接的数量,连接的最大数量,连接的最大空闲数量,获取连接的超时时间等等)
        //连接池初始化连接的数量
        ds.setInitialSize(5);
        //连接的最大数量
        ds.setMaxActive(10);
        //获取连接的超时时间,若超出这个时间会报错
        ds.setMaxWait(3000);

        for (int i = 1; i <= 11 ; i++) {
            //4. 获取连接
            DruidPooledConnection conn = ds.getConnection();
            //5. 使用连接
            System.out.println(conn);

            //归还一个连接
            if (i%7==0) {
                conn.close();
            }
        }

        //6. 归还连接
        //conn.close();
    }
}

配置文件方式:

步骤:

  1. 在properties文件中声明连接池的属性(属性不能乱写)
  2. 在程序中,加载properties文件
  3. 使用DruidDatasourceFactory的静态方法创建一个连接池
  4. 通过连接池获取连接
  5. 使用连接
  6. 释放连接
 @Test
    //配置文件的方式
    public void test3() throws Exception {
        //1.创建连接池对象
        //a.加载配置文件
        InputStream is = TestDruid.class.getClassLoader().getResourceAsStream("druid.properties");

        //b.创建配置对象
        Properties prop = new Properties();
        prop.load(is);

        //c.使用工厂类的静态方式创建连接池对象
        DataSource ds = DruidDataSourceFactory.createDataSource(prop);

        //2.获取连接,使用连接
        Connection conn = ds.getConnection();
        System.out.println(conn);

        //3.归还连接
        conn.close();
    }

3 抽取工具类

使用连接池技术获取连接

  1. 可以将连接池的4个基本参数放入一个db.properties
  2. 在工具类中声明一个连接池成员变量
  3. 在静态代码块中加载properties文件,初始化连接池对象
  4. 编写getConnection方法
    • 从连接池中获取一个连接池返回即可
  5. 编写释放资源的方法(拷贝上午写的代码)
  6. 编写获取连接池的方法(目前对我们来说没有用,年后学习框架的时候用的上)
package com.itheima.utils;

import com.alibaba.druid.pool.DruidDataSource;

import javax.sql.DataSource;
import java.sql.*;
import java.util.ResourceBundle;

public class JdbcUtils {
    //1.声明连接池对象
    private static DruidDataSource ds = null;

    //使用静态代码块完成连接池的初始化操作
    static {
        ds = new DruidDataSource();

        //使用ResourceBundle类加载db.properties文件,注意不需要写文件的后缀名
        ResourceBundle bundle = ResourceBundle.getBundle("db");

        //设置4个基本的参数
        ds.setDriverClassName(bundle.getString("jdbc.	driver"));
        ds.setUrl(bundle.getString("jdbc.url"));
        ds.setUsername(bundle.getString("jdbc.username"));
        ds.setPassword(bundle.getString("jdbc.password"));
    }

    //0,获取连接池方法,框架的时候采用
    public static DataSource getDS(){
        return ds;
    }

    //2.获取连接 因为调用者需要用conn创建其他的对象.
    public static Connection getConnection() throws SQLException {
        return ds.getConnection();
    }


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

            }
            //可以让垃圾回收器更快的回收对象
            rs = null;
        }

        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {

            }
            //可以让垃圾回收器更快的回收对象
            st = null;
        }

        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {

            }
            //可以让垃圾回收器更快的回收对象
            conn = null;
        }
    }
}

4.扩展:C3P0

一般使用的配置文件方式.

要求:

  1. 配置文件的名字必须叫: c3p0.properties或者c3p0-config.xml
  2. 配置文件的路径必须放在src目录下
  3. 创建连接池对象 new ComboPooledDataSource();

导入c3p0的jar包和mysql驱动包

public class C3P0Demo {
    @Test
    public void test1() throws SQLException {
        //创建连接池
        DataSource ds = new ComboPooledDataSource();

        //获取连接
        Connection conn = ds.getConnection();

        //使用连接
        System.out.println(conn);

        //释放连接
        conn.close();
    }

    @Test
    public void test2() throws SQLException {
        //创建连接池 使用其他命名配置
        /*
            若找不到指定的配置,还会使用默认的配置
         */
        //DataSource ds = new ComboPooledDataSource("otherc3p0");
        DataSource ds = new ComboPooledDataSource("otherc3p0111");

        //获取连接
        Connection conn = ds.getConnection();

        //使用连接
        System.out.println(conn);

        //释放连接
        conn.close();
    }
}


四 总结

jdbc

牢牢的记住:jdbc是一套使用java语言操作关系型数据库的api,一套规范.使用jdbc的时候必须导入数据库的驱动

使用PreparedStatement步骤:

  1. 获取连接
  2. 编写sql
  3. 创建sql的预编译执行者
  4. 设置参数
  5. 执行sql(无参方法),接受返回值
  6. 处理返回值
  7. 释放资源

上面这个步骤,需要大家记住3天,3天之后大家就可以忘记了,

连接池(数据源)

连接的容器,在项目初始化的时候可以初始化一些连接对象放入容器中.获取连接的时候调用连接池的getConnection方法.若连接不用了就需要归还连接池(调用连接的close方法)

getConnection();

常见的连接池:

  • 课上讲的是druid连接池
  • 创建连接池对象:new DruidDataSource(); 设置基本参数即可使用

作业

1.使用duird抽取工具类

  • 创建连接池对象
  • 获取连接池
  • 获取连接
  • 释放资源

2.使用PreparedStatement完成账户的crud操作

3.完成转账案例,若有时间和精力的话改造成web方式

4.完成登陆案例,若有时间和精力的话改造成三层架构方式

5.若有时间和精力的话,可以把之前用户的crud操作(list)改成数据库方式

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值