JDBC基础

JDBC编程步骤

1.注册驱动

//DriverManager.registerDriver(new com.mysql.jdbc.Driver());会对详细的驱动类产生依赖
Class.forName("com.mysql.jdbc.Driver");// 固定写法,推荐这样的方式,不会对详细的驱动类产生依赖

2.连接数据库(Connection)

String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
/*
  mysql端口号 -- 3306
  协议://主机地址:端口号/数据库名?参数1&参数2&参数3
  oralce端口号 -- 1521
  jdbc:oracle:thin:@localhost:1521:sid
 */
String username = "root";
String password = "123456pp";
Connection connection = DriverManager.getConnection(url, username, password);

user即为登录数据库的username,如root;password即为登录数据库的密码

通过Connection建立连接,Connection是一个接口类。其功能是与数据库进行连接(会话)。

建立Connection接口类对象:

Connection conn =DriverManager.getConnection(url, user, password);

3.获得执行SQL的对象(Statement)

Statement statement = connection.createStatement();

运行对象Statement负责运行SQL语句。由Connection对象产生。

4.运行SQL语句

String sql = "SELECT * FROM users WHERE id = '2'";// 查询语句
ResultSet resultSet = statement.executeQuery(sql);// 返回的结果集,结果集中封装了我们全部查询出来的结果
  • executeQuery(String sql),该方法用于运行实现查询功能的sql语句。返回类型为ResultSet(结果集)。
  • executeUpdate(String sql),该方法用于运行实现增、删、改功能的sql语句,返回类型为int,即受影响的行数。

5.处理运行结果

while (resultSet.next()) {
            System.out.println("id\t"+resultSet.getObject("id"));
            System.out.println("name\t"+resultSet.getObject("NAME"));
            System.out.println("pwd\t"+resultSet.getObject("PASSWORD"));
            System.out.println("email\t"+resultSet.getObject("email"));
            System.out.println("birth\t"+resultSet.getObject("birthday"));
            System.out.println("**********************************");
   }

ResultSet对象负责保存Statement运行后所产生的查询结果。

结果集ResultSet是通过游标来操作的。游标就是一个可控制的、能够指向随意一条记录的指针。有了这个指针我们就能轻易地指出我们要对结果集中的哪一条记录进行改动、删除,或者要在哪一条记录之前插入数据。一个结果集对象中仅仅包括一个游标。

6.释放资源

resultSet.close();
statement.close();
connection.close();

【演示】

package com.jin.demo01;

import java.sql.*;

public class Test01 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1.加载驱动
        //DriverManager.registerDriver(new com.mysql.jdbc.Driver());
        Class.forName("com.mysql.jdbc.Driver");// 固定写法,加载驱动
        // 2.用户信息和url
        // useUnicode=true&characterEncoding=utf8&useSSL=true
        String url = "jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true";
        String username = "root";
        String password = "123456pp";
        /*
        mysql -- 3306
        协议://主机地址:端口号/数据库名?参数1&参数2&参数3
        oralce -- 1521
        jdbc:oracle:thin:@localhost:1521:sid
         */
        // 3.连接成功,数据库对象 Connection代表数据库
        Connection connection = DriverManager.getConnection(url, username, password);
        // 4.执行SQL的对象Statement
        Statement statement = connection.createStatement();
        // 5.执行SQL的对象去执行SQL,可能存在结果,查看返回结果
        String sql = "SELECT * FROM users WHERE id = '2'";
        ResultSet resultSet = statement.executeQuery(sql);// 返回的结果集,结果集中封装了我们全部查询出来的结果
        while (resultSet.next()) {
            System.out.println("id\t" + resultSet.getObject("id"));
            System.out.println("name\t" + resultSet.getObject("NAME"));
            System.out.println("pwd\t" + resultSet.getObject("PASSWORD"));
            System.out.println("email\t" + resultSet.getObject("email"));
            System.out.println("birth\t" + resultSet.getObject("birthday"));
            System.out.println("**********************************");
        }
        // 6.释放连接
        resultSet.close();
        statement.close();
        connection.close();
    }
}

db.properties文件配置

driver = com.mysql.jdbc.Driver
//url = jdbc:mysql://localhost:3306/jinlupeng?useUnicode=true&characterEncoding=utf8&useSSL=true
url = jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username = root
password = 123456pp

将数据库连接、资源释放等封装成工具类

package com.jin.demo02.utils;

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

public class JdbcUtils {
    private static String driver = null;// 数据库驱动
    private static String url = null;// 数据库连接地址
    private static String username = null;// 数据库用户名称
    private static String password = null;// 数据库用户密码

    static {
        try {
            // 使用ClassLoader加载properties配置文件生成对应的输入流
            InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("db.properties");
            // 创建Properties类对象
            Properties properties = new Properties();
            // 从输入流中加载属性列表
            properties.load(in);
            // 获取数据库连接属性值
            driver = properties.getProperty("driver");
            url = properties.getProperty("url");
            username = properties.getProperty("username");
            password = properties.getProperty("password");
            // 注册驱动(驱动只加载一次)
            Class.forName(driver);
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }

    // 连接数据库
    public static Connection getConnection() throws SQLException {
        // 获取数据库连接对象
        Connection connection = DriverManager.getConnection(url, username, password);
        return connection;
    }

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

注:读取 .properties 属性文件时,使用 Class 对象的 getResourceAsStream()方法,把指定的属性文件读入输入流中,并使用 Properties 类中的 load() 方法,从输入流中读取属性列表(键/值对)。

Properties类的使用

1.查询(executeQuery)

package com.jin.demo02;

import com.jin.demo02.utils.JdbcUtils;

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

public class Test01Select {
    public static void main(String[] args) {
        Connection con = null;
        Statement st = null;
        ResultSet re = null;
        try {
            // 连接数据库
            con = JdbcUtils.getConnection();
            // 获得执行SQL的对象
            st = con.createStatement();
            // 运行SQL语句
            String str = "select * from student where sname = '张华'";
            re = st.executeQuery(str);// executeQuery返回类型为ResultSet
            // 处理运行结果
            while (re.next()) {
                System.out.println(re.getObject("sno"));
                System.out.println(re.getObject("sname"));
                System.out.println(re.getObject("ssex"));
                System.out.println(re.getObject("sbirthday"));
                System.out.println(re.getObject("saddress"));
                System.out.println(re.getObject("sdept"));
                System.out.println(re.getObject("speciality"));

            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 释放资源
            JdbcUtils.release(con, st, re);
        }
    }
}

2.插入

package com.jin.demo02;

import com.jin.demo02.utils.JdbcUtils;

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

public class Test02Insert {
    public static void main(String[] args) {
        Connection con = null;
        Statement st = null;
        ResultSet re = null;
        try {
            // 连接数据库
            con = JdbcUtils.getConnection();
            // 获得执行SQL的对象
            st = con.createStatement();
            // 运行SQL语句
            String str = "insert into users values(8, 'wangmazi', '123123', '123@qq.com', '2020-02-02')";
            int i = st.executeUpdate(str);// executeUpdate返回类型为int,即受影响的行数
            if (i > 0) {
                System.out.println("插入成功");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 释放资源
            JdbcUtils.release(con, st, re);
        }
    }
}

3.删除

package com.jin.demo02;

import com.jin.demo02.utils.JdbcUtils;

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

public class Test03Delete {
    public static void main(String[] args) {
        Connection con = null;
        Statement st = null;
        ResultSet re = null;
        try {
            // 连接数据库
            con = JdbcUtils.getConnection();
            // 获得执行SQL的对象
            st = con.createStatement();
            // 运行SQL语句
            String str = "delete from users where id = 8";
            int i = st.executeUpdate(str);
            if (i > 0) {
                System.out.println("删除成功");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 释放资源
            JdbcUtils.release(con, st, re);
        }
    }
}

4.更新

package com.jin.demo02;

import com.jin.demo02.utils.JdbcUtils;

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

public class Test04Update {
    public static void main(String[] args) {
        Connection con = null;
        Statement st = null;
        ResultSet re = null;
        try {
            // 连接数据库
            con = JdbcUtils.getConnection();
            // 获得执行SQL的对象
            st = con.createStatement();
            // 运行SQL语句
            String str = "update users set id = id + 10 where NAME = 'zhansan'";
            int i = st.executeUpdate(str);
            if (i > 0) {
                System.out.println("更新成功");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 释放资源
            JdbcUtils.release(con, st, re);
        }
    }
}

Statement会存在SQL注入

SQL注入是比较常见的网络攻击方式之一,它不是利用操作系统的BUG来实现攻击,而是针对程序员编写时的疏忽,通过SQL语句,实现无账号登录,甚至篡改数据库。

package com.jin.demo02;

import com.jin.demo02.utils.JdbcUtils;

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

public class SqlInject {
    public static void main(String[] args) {
        //login("zhansan", "123456");
        login(" ' or '1=1", "' or '1=1");// SQL注入
    }
    public static void login(String usersname, String password) {
        Connection con = null;
        Statement st = null;
        ResultSet re = null;
        try {
            // 连接数据库
            con = JdbcUtils.getConnection();
            // 获得执行SQL的对象
            st = con.createStatement();
            // 运行SQL语句
            //SELECT * FROM `users` WHERE `NAME` = 'zhansan' AND `PASSWORD` = '123456';
            String str = "SELECT * FROM `users` WHERE `NAME` = '"+usersname+"' AND `PASSWORD` = '"+password+"'";
            re = st.executeQuery(str);// executeQuery返回类型为ResultSet
            // 处理运行结果
            while (re.next()) {
                System.out.println(re.getObject("id"));
                System.out.println(re.getObject("NAME"));
                System.out.println(re.getObject("PASSWORD"));
                System.out.println(re.getObject("email"));
                System.out.println(re.getObject("birthday"));
                System.out.println("---------------------------------");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 释放资源
            JdbcUtils.release(con, st, re);
        }
    }
}

PreparedStatement类的使用

1.查询

package com.jin.demo03;

import com.jin.demo02.utils.JdbcUtils;

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

public class Test01Select {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            // 连接数据库
            con = JdbcUtils.getConnection();
            // 使用? 占位符代替参数
            String sql = "SELECT * FROM `users` WHERE `NAME` = ? AND `PASSWORD` = ?";
            st = con.prepareStatement(sql);// 预编译SQL,先写SQL,但是不执行
            // 手动给参数赋值
            st.setString(1, "zhansan");
            st.setString(2, "123456");
            rs = st.executeQuery();
            if (rs.next()) {
                System.out.println(rs.getInt("id"));
                System.out.println(rs.getString("email"));
                System.out.println(rs.getDate("birthday"));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(con, st, rs);
        }
    }
}
执行结果:
 1
zs@sina.com
1980-12-04

2.插入

package com.jin.demo03;

import com.jin.demo02.utils.JdbcUtils;

import java.sql.*;
import java.util.Date;

public class Test02Insert {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement st = null;
        //ResultSet rs = null;
        try {
            con = JdbcUtils.getConnection();
            String sql = "INSERT INTO users(id,`NAME`,`PASSWORD`,email,birthday) VALUES(?,?,?,?,?)";
            st = con.prepareStatement(sql);
            st.setInt(1, 4);
            st.setString(2, "wangmazi");
            st.setString(3, "123123");
            st.setString(4, "123321@qq.com");
            /*
            注意:sql.Date     数据库     java.sql.Date()
                 util.Date    java      new Date().getTime() 获得时间戳
             */
            st.setDate(5, new java.sql.Date(new Date().getTime()));
            int i = st.executeUpdate();
            if (i > 0) {
                System.out.println("插入成功");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(con, st, null);
        }
    }
}

3.删除

package com.jin.demo03;

import com.jin.demo02.utils.JdbcUtils;

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

public class Test03Delete {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement st = null;
        try {
            con = JdbcUtils.getConnection();// 连接数据库
            String sql = "DELETE FROM users WHERE id = ?";
            st = con.prepareStatement(sql);// 预编译SQL
            st.setInt(1, 4);// 手动给参数赋值
            int i = st.executeUpdate();
            if (i > 0) {
                System.out.println("删除成功");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(con, st, null);
        }
    }
}

4.更新

package com.jin.demo03;

import com.jin.demo02.utils.JdbcUtils;

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

public class Test04Update {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement st = null;
        try {
            con = JdbcUtils.getConnection();// 连接数据库
            String sql = "UPDATE users SET id = id + 8 WHERE `NAME` = ? AND email = ?";
            st = con.prepareStatement(sql);// 预编译SQL
            // 手动给参数赋值
            st.setString(1,"zhansan");
            st.setString(2,"zs@sina.com");
            int i = st.executeUpdate();
            if (i > 0) {
                System.out.println("更新成功");
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(con, st, null);
        }
    }
}

PreparedStatement 防止SQL注入

PreparedStatement 防止SQL注入的本质:把传进来的参数当做字符
如果其中存在转移字符,就直接忽略,如:‘ 会被直接转义

package com.jin.demo03;

import com.jin.demo02.utils.JdbcUtils;

import java.sql.*;

public class Test {
    public static void main(String[] args) {
        login("zhansan", "123456");
        //login(" ' or '1=1", "123456");// 登录失败
        //login(" ' or '1=1", "' or '1=1");// 登录失败 PreparedStatement可以防止SQL注入
    }

    public static void login(String usersname, String password) {
        Connection con = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            // 连接数据库
            con = JdbcUtils.getConnection();
            // 使用? 占位符代替参数
            String sql = "SELECT * FROM `users` WHERE `NAME` = ? AND `PASSWORD` = ?";
            st = con.prepareStatement(sql);// 预编译SQL,先写SQL,但是不执行
            // 手动给参数赋值
            st.setString(1, usersname);
            st.setString(2, password);
            rs = st.executeQuery();// 查询完返回一个结果集
            if (rs.next()) {
                System.out.println(rs.getInt("id"));
                System.out.println(rs.getString("email"));
                System.out.println(rs.getDate("birthday"));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        } finally {
            // 释放资源
            JdbcUtils.release(con, st, rs);
        }
    }
}

使用IDEA连接数据库

社区版:在File →Settings→Plugins→搜索框中输入Database Navigator→下载插件→安装完成重启IDEA

这样会报错,连接失败

【解决办法】

第一步:创建连接时不要选择具体数据库类型

第二步:输入URL:jdbc:mysql://localhost:3306/mysql?serverTimezone=UTC

JDBC操作事务

【演示1】

package com.jin.demo03;

import com.jin.demo02.utils.JdbcUtils;

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

public class Test05Affair {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement st = null;
        try {
            con = JdbcUtils.getConnection();// 连接数据库
            con.setAutoCommit(false);// 关闭数据库自动提交,会自动开启事务
            String sql1 = "UPDATE bank SET money=money-500 WHERE `name`='a';";// 一次只能执行一条操作语句
            String sql2 = "UPDATE bank SET money=money+500 WHERE `name`='b';";
            st = con.prepareStatement(sql1);
            st.executeUpdate();
            st = con.prepareStatement(sql2);
            st.executeUpdate();
            con.commit();// 提交事务
            con.setAutoCommit(true);// 开启数据库自动提交
            System.out.println("成功!");
        } catch (SQLException throwables) {
           /* try {
                con.rollback();// 如果失败就回滚事务(可以显示定义,如果不写失败会默认回滚)
            } catch (SQLException e) {
                e.printStackTrace();
            }*/
            throwables.printStackTrace();
        } finally {
            JdbcUtils.release(con, st, null);
        }
    }
}

【演示2】

package com.jin.demo03;

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

public class Test06Affair {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 1.加载驱动
        Class.forName("com.mysql.jdbc.Driver");// 加载驱动(需要抛出ClassNotFoundException 异常)
        // 2.连接数据库
        String url = "jdbc:mysql://localhost:3306/jinlupeng?useUnicode=true&characterEncoding=utf8&useSSL=true";
        String username = "root";
        String password = "123456pp";
        Connection con = DriverManager.getConnection(url, username, password);// 连接数据库(需要抛出SQLException 异常)
        // 3.关闭数据库自动提交,会自动开启事务
        con.setAutoCommit(false);
        // 4.执行事务
        String sql1 = "UPDATE bank SET money=money-500 WHERE `name`='a';";
        PreparedStatement st = con.prepareStatement(sql1);
        st.executeUpdate();
        String sql2 = "UPDATE bank SET money=money+500 WHERE `name`='b';";
        st = con.prepareStatement(sql2);
        st.executeUpdate();
        // 5.释放连接
        con.commit();
        st.close();
        con.close();
    }
}

数据库连接池

  • JDBC 的数据库连接池使用 javax.sql.DataSource 来表示,DataSource 只是一个接口,该接口通常由服务器(Weblogic, WebSphere, Tomcat)提供实现,也有一些开源组织提供实现:
    • DBCP 是Apache提供的数据库连接池。tomcat 服务器自带dbcp数据库连接池。速度相对c3p0较快,但因自身存在BUG,Hibernate3已不再提供支持。
    • C3P0 是一个开源组织提供的一个数据库连接池,速度相对较慢,稳定性还可以。hibernate官方推荐使用
    • Proxool 是sourceforge下的一个开源项目数据库连接池,有监控连接池状态的功能,稳定性较c3p0差一点
    • BoneCP 是一个开源组织提供的数据库连接池,速度快
    • Druid 是阿里提供的数据库连接池,据说是集DBCP 、C3P0 、Proxool 优点于一身的数据库连接池,但是速度不确定是否有BoneCP快
  • DataSource 通常被称为数据源,它包含连接池和连接池管理两个部分,习惯上也经常把 DataSource 称为连接池
  • DataSource用来取代DriverManager来获取Connection,获取速度快,同时可以大幅度提高数据库访问速度。
  • 特别注意:
    • 数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。
    • 当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close(); 但conn.close()并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。

C3P0

首先在在lib目录下导入jar包c3p0-0.9.1.2.jar

【方式一】

使用C3P0数据库连接池的方式,获取数据库的连接:不推荐

package com.jin.demo05;

import com.mchange.v2.c3p0.ComboPooledDataSource;
import com.mchange.v2.c3p0.DataSources;

import java.beans.PropertyVetoException;
import java.sql.Connection;
import java.sql.SQLException;

public class C3P0Test {
    public static void testGetConnection() throws PropertyVetoException, SQLException {
        // 获取C3P0数据库连接池
        ComboPooledDataSource cpds = new ComboPooledDataSource();
        cpds.setDriverClass("com.mysql.jdbc.Driver");
        cpds.setJdbcUrl("jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true");
        cpds.setUser("root");
        cpds.setPassword("123456pp");
        // 通过设置相关的参数,对数据库连接池进行管理
        cpds.setInitialPoolSize(10);// 设置初始时数据库连接池中的连接数
        // 连接数据库
        Connection con = cpds.getConnection();
        System.out.println(con);
        //DataSources.destroy(cpds);// 销毁C3P0数据库连接池(通常不使用)
    }

    public static void main(String[] args) throws PropertyVetoException, SQLException {
        testGetConnection();
    }
}

【方式二】推荐

使用C3P0数据库连接池的配置文件方式,获取数据库的连接:推荐

package com.jin.demo05;

import com.mchange.v2.c3p0.ComboPooledDataSource;

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

public class C3P0Test2 {
    public static void testGetConnection() throws SQLException {
        ComboPooledDataSource cpds = new ComboPooledDataSource("helloC3P0");
        Connection con = cpds.getConnection();
        System.out.println(con);
    }

    public static void main(String[] args) throws SQLException {
        testGetConnection();
    }
}

src下c3p0-config.xml文件配置

<?xml version="1.0" encoding="UTF-8" ?>
<c3p0-config>
    <named-config name="helloC3P0">
        <!-- 提供获取连接的4个基本信息 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/jdbcstudy</property>
        <property name="user">root</property>
        <property name="password">123456pp</property>

        <!-- 对数据库连接池进行管理的基本信息 -->
        <!-- 当数据库连接池中的连接数不够时,c3p0一次性向数据库申请的连接数 -->
        <property name="acquireIncrement">5</property>
        <!-- c3p0数据库连接池中初始化时的连接数 -->
        <property name="initialPoolSize">10</property>
        <!-- c3p0数据库连接池维护的最少连接数 -->
        <property name="minPoolSize">10</property>
        <!-- c3p0数据库连接池维护的最多连接数 -->
        <property name="maxPoolSize">100</property>
        <!-- c3p0数据库连接池维护的最多维护的Statement的个数 -->
        <property name="maxStatements">50</property>
        <!-- 每个连接中可以最多使用的Statement的个数 -->
        <property name="maxStatementsPerConnection">2</property>
    </named-config>
</c3p0-config>

使用C3P0数据库连接池技术

package com.jin.demo05.utils;

import com.mchange.v2.c3p0.ComboPooledDataSource;

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

public class JdbcUtils {
    // 数据库连接池只需提供一个即可
    private static ComboPooledDataSource cpds = new ComboPooledDataSource("helloC3P0");

    public static Connection getConnectionUseC3p0() throws SQLException {
        Connection con = cpds.getConnection();
        return con;
    }
}
package com.jin.demo05;

import com.jin.demo05.utils.JdbcUtils;

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

public class Test {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            // 连接数据库
            con = JdbcUtils.getConnectionUseC3p0();
            // 使用? 占位符代替参数
            String sql = "SELECT * FROM `users` WHERE `NAME` = ? AND `PASSWORD` = ?";
            st = con.prepareStatement(sql);// 预编译SQL,先写SQL,但是不执行
            // 手动给参数赋值
            st.setString(1, "zhansan");
            st.setString(2, "123456");
            rs = st.executeQuery();
            if (rs.next()) {
                System.out.println(rs.getInt("id"));
                System.out.println(rs.getString("email"));
                System.out.println(rs.getDate("birthday"));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

DBCP

  • DBCP 是 Apache 软件基金组织下的开源连接池实现,该连接池依赖该组织下的另一个开源系统:Common-pool。如需使用该连接池实现,应在系统中增加如下两个 jar 文件:
    • Commons-dbcp.jar:连接池的实现
    • Commons-pool.jar:连接池实现的依赖库
  • Tomcat 的连接池正是采用该连接池来实现的。该数据库连接池既可以与应用服务器整合使用,也可由应用程序独立使用。
  • 数据源和数据库连接不同,数据源无需创建多个,它是产生数据库连接的工厂,因此整个应用只需要一个数据源即可。
  • 当数据库访问结束后,程序还是像以前一样关闭数据库连接:conn.close(); 但上面的代码并没有关闭数据库的物理连接,它仅仅把数据库连接释放,归还给了数据库连接池。

配置属性说明:

属性 默认值 说明
initialSize 0 连接池启动时创建的初始化连接数量
maxActive 8 连接池中可同时连接的最大的连接数
maxIdle 8 连接池中最大的空闲的连接数,超过的空闲连接将被释放,如果设置为负数表示不限制
minIdle 0 连接池中最小的空闲的连接数,低于这个数量会被创建新的连接。该参数越接近maxIdle,性能越好,因为连接的创建和销毁,都是需要消耗资源的;但是不能太大。
maxWait 无限制 最大等待时间,当没有可用连接时,连接池等待连接释放的最大时间,超过该时间限制会抛出异常,如果设置-1表示无限等待
poolPreparedStatements false 开启池的Statement是否prepared
maxOpenPreparedStatements 无限制 开启池的prepared 后的同时最大连接数
minEvictableIdleTimeMillis 连接池中连接,在时间段内一直空闲, 被逐出连接池的时间
removeAbandonedTimeout 300 超过时间限制,回收没有用(废弃)的连接
removeAbandoned false 超过removeAbandonedTimeout时间后,是否进 行没用连接(废弃)的回收

首先在lib目录下导入commons-dbcp-1.4.jar和commons-pool-1.5.5.jar

【方式一】

package com.jin.demo06;

import org.apache.commons.dbcp.BasicDataSource;

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

public class DBCPTest {
    public static void testConnection() throws SQLException {
        // 创建DBCP数据库连接池
        BasicDataSource source = new BasicDataSource();
        // 设置基本信息,连接数据库
        source.setDriverClassName("com.mysql.jdbc.Driver");
        source.setUrl("jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true");
        source.setUsername("root");
        source.setPassword("123456pp");
        // 设置数据库连接池管理的相关属性
        source.setInitialSize(10);
        source.setMaxActive(10);
        //……

        Connection con = source.getConnection();
        System.out.println(con);
    }

    public static void main(String[] args) throws SQLException {
        testConnection();
    }
}

【方式二】推荐

使用DBCP数据库连接池的配置文件方式,获取数据库的连接:推荐

package com.jin.demo06;

import org.apache.commons.dbcp.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;

public class DBCPTest2 {
    public static void getConnection() throws Exception {
        Properties pro = new Properties();
        /*
        加载properties配置文件生成对应的输入流的两种方式:
        1.使用ClassLoader
        2.使用FileInputStream
         */
        InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties");
        //FileInputStream in = new FileInputStream(new File("src/dbcp.properties"));
        pro.load(in);// 从输入流中加载属性列表
        DataSource source = BasicDataSourceFactory.createDataSource(pro);
        Connection con = source.getConnection();
        System.out.println(con);
    }

    public static void main(String[] args) throws Exception {
        getConnection();
    }
}

src下dbcp.properties文件配置

driverClassName = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username = root
password = 123456pp

initialSize = 10
maxActive = 10

使用DBCP数据库连接池技术

package com.jin.demo06.utils;

import org.apache.commons.dbcp.BasicDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

public class JdbcUtils {
    private static DataSource source; // 数据库连接池只需提供一个即可
    static {
        Properties pro = new Properties();
        InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream("dbcp.properties");
        try {
            pro.load(in);// 从输入流中加载属性列表
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            source = BasicDataSourceFactory.createDataSource(pro);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Connection getConnectionUseDbcp() throws SQLException {
        Connection con = source.getConnection();
        return con;
    }
}
package com.jin.demo06;

import com.jin.demo06.utils.JdbcUtils;

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

public class Test {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            // 连接数据库
            con = JdbcUtils.getConnectionUseDbcp();
            // 使用? 占位符代替参数
            String sql = "SELECT * FROM `users` WHERE `NAME` = ? AND `PASSWORD` = ?";
            st = con.prepareStatement(sql);// 预编译SQL,先写SQL,但是不执行
            // 手动给参数赋值
            st.setString(1, "zhansan");
            st.setString(2, "123456");
            rs = st.executeQuery();
            if (rs.next()) {
                System.out.println(rs.getInt("id"));
                System.out.println(rs.getString("email"));
                System.out.println(rs.getDate("birthday"));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

Druid

首先在lib目录下导入jar包druid-1.1.10.jar

配置属性说明:

配置 缺省 说明
name 配置这个属性的意义在于,如果存在多个数据源,监控的时候可以通过名字来区分开来。 如果没有配置,将会生成一个名字,格式是:”DataSource-” + System.identityHashCode(this)
url 连接数据库的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(建议配置下)
initialSize 0 初始化时建立物理连接的个数。初始化发生在显示调用init方法,或者第一次getConnection时
maxActive 8 最大连接池数量
maxIdle 8 已经不再使用,配置了也没效果
minIdle 最小连接池数量
maxWait 获取连接时最大等待时间,单位毫秒。配置了maxWait之后,缺省启用公平锁,并发效率会有所下降,如果需要可以通过配置useUnfairLock属性为true使用非公平锁。
poolPreparedStatements false 是否缓存preparedStatement,也就是PSCache。PSCache对支持游标的数据库性能提升巨大,比如说oracle。在mysql下建议关闭。
maxOpenPreparedStatements -1 要启用PSCache,必须配置大于0,当大于0时,poolPreparedStatements自动触发修改为true。在Druid中,不会存在Oracle下PSCache占用内存过多的问题,可以把这个数值配置大一些,比如说100
validationQuery 用来检测连接是否有效的sql,要求是一个查询语句。如果validationQuery为null,testOnBorrow、testOnReturn、testWhileIdle都不会其作用。
testOnBorrow true 申请连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能。
testOnReturn false 归还连接时执行validationQuery检测连接是否有效,做了这个配置会降低性能
testWhileIdle false 建议配置为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,是组合关系,并非替换关系

【方式一】

package com.jin.demo07;

import com.alibaba.druid.pool.DruidDataSource;

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

public class DruidTest {
    public static void testConnection() throws SQLException {
        // 创建DBCP数据库连接池
        DruidDataSource source = new DruidDataSource();
        // 设置基本信息,连接数据库
        source.setDriverClassName("com.mysql.jdbc.Driver");
        source.setUrl("jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true");
        source.setUsername("root");
        source.setPassword("123456pp");
        // 设置数据库连接池管理的相关属性
        source.setInitialSize(10);
        source.setMaxActive(10);
        //……
        Connection con = source.getConnection();
        System.out.println(con);
    }

    public static void main(String[] args) throws SQLException {
        testConnection();
    }
}

【方式二】推荐

package com.jin.demo07;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.util.Properties;

public class DruidTest02 {
    public static void getConnection() throws Exception {
        Properties pro = new Properties();
        InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
        pro.load(in);
        DataSource source = DruidDataSourceFactory.createDataSource(pro);
        Connection con = source.getConnection();
        System.out.println(con);
    }

    public static void main(String[] args) throws Exception {
        getConnection();
    }
}

src下druid.properties文件配置

driverClassName = com.mysql.jdbc.Driver
url = jdbc:mysql://localhost:3306/jdbcstudy?useUnicode=true&characterEncoding=utf8&useSSL=true
username = root
password = 123456pp

initialSize = 10
maxActive = 10

使用Driuid数据库连接池技术

package com.jin.demo07.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.IOException;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;

public class JdbcUtils {
    private static DataSource source;
    static {
        Properties pro = new Properties();
        InputStream in = ClassLoader.getSystemClassLoader().getResourceAsStream("druid.properties");
        try {
            pro.load(in);
        } catch (IOException e) {
            e.printStackTrace();
        }
        try {
            source = DruidDataSourceFactory.createDataSource(pro);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    public static Connection getConnectionUseDruid() throws SQLException {
        Connection con = source.getConnection();
        return con;
    }
}

package com.jin.demo07;

import com.jin.demo07.utils.JdbcUtils;

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

public class Test {
    public static void main(String[] args) {
        Connection con = null;
        PreparedStatement st = null;
        ResultSet rs = null;
        try {
            // 连接数据库
            con = JdbcUtils.getConnectionUseDruid();
            // 使用? 占位符代替参数
            String sql = "SELECT * FROM `users` WHERE `NAME` = ? AND `PASSWORD` = ?";
            st = con.prepareStatement(sql);// 预编译SQL,先写SQL,但是不执行
            // 手动给参数赋值
            st.setString(1, "zhansan");
            st.setString(2, "123456");
            rs = st.executeQuery();
            if (rs.next()) {
                System.out.println(rs.getInt("id"));
                System.out.println(rs.getString("email"));
                System.out.println(rs.getDate("birthday"));
            }
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }
    }
}

posted @ 2021-03-15 16:31  火车上的老头  阅读(53)  评论(0编辑  收藏  举报