JavaJDBC:连接池

本篇内容包括:数据库连接池概述、JDBC 连接池原理、JDBC 连接池 Demo(addBatch demo、获取主键 demo、查看数据库的元数据 demo等)以及其他类型数据库连接池的介绍(比如 Druid)。

一、数据库连接池

数据库连接池负责分配、管理和释放数据库连接,它允许应用程序重复使用一个现有的数据库连接,而不是再重新建立一个;释放空闲时间超过最大空闲时间的数据库连接来避免因为没有释放数据库连接而引起的数据库连接遗漏。这项技术能明显提高对数据库操作的性能。

连接池基本的思想是在系统初始化的时候,将数据库连接作为对象存储在内存中,当用户需要访问数据库时,并非建立一个新的连接,而是从连接池中取出一个已建立的空闲连接对象。使用完毕后,用户也并非将连接关闭,而是将连接放回连接池中,以供下一个请求访问使用。而连接的建立、断开都由连接池自身来管理。同时,还可以通过设置连接池的参数来控制连接池中的初始连接数、连接的上下限数以及每个连接的最大使用次数、最大空闲时间等等。也可以通过其自身的管理机制来监视数据库连接的数量、使用情况等。

连接池优点:

  • 减少连接创建时间:虽然与其它数据库相比 GBase 提供了较为快速连接功能,但是创建新的 JDBC 连接仍会招致网络和 JDBC 驱动的开销。如果这类连接是“循环”使用的,使用该方式这些花销就可避免
  • 简化的编程模式:当使用连接池时,每一个单独的线程能够像创建了一个自己的 JDBC 连接一样操作,允许用户直接使用JDBC编程技术。
  • 受控的资源使用:如果用户不使用连接池,而是每当线程需要时创建一个新的连接,那么用户的应用程序的资源使用会产生非常大的浪费并且可能会导致高负载下的异常发生。连接池能够使性能最大化,同时还能将资源利用控制在一定的水平之下,如果超过该水平,应用程序将崩溃而不仅仅是变慢。

二、JDBC 连接池原理

JDBC 连接池的基本原理:

  1. 建立数据库连接池对象(服务器启动)。
  2. 按照事先指定的参数创建初始数量的数据库连接(即:空闲连接数)。
  3. 对于一个数据库的访问请求,直接从连接池中得到一个连接,如果数据库连接池对象中有空闲连接则直接使用、若没有空闲的链接,且连接数没有达到最大(即:最大活跃连接数),创建一个新的数据库连接来处理该请求,如果没有空闲连接并且达到最大活跃值则进行等待其它的链接释放再进行该请求的处理。
  4. 存取数据库。
  5. 关闭数据库,释放所有数据库连接(此时福安比数据库连接,并非真正的关闭,而是将其放入空闲队列中。如果实际空闲连接数大于初始空闲连接数则释放连接)。
  6. 释放数据库连接池对象(在服务器停止、维护期间,真正的释放数据库连接池对象,并释放所有资源)。

三、JDBC 连接池 Demo

1、添加 Maven 依赖
        <dependency>
            <groupId>commons-dbcp</groupId>
            <artifactId>commons-dbcp</artifactId>
            <version>1.4</version>
        </dependency>
2、添加一个配置文件 src/resources/jdbc.properties
# 配置数据库的连接信息
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/databaseName
username=userName
password=password
3、添加 DbUtils2 类
public class DbUtils2 {
    private static String driver;
    private static String url;
    private static String username;
    private static String password;
    /**
     * 声明数据源,用于配置数据库的链接信息
     */
    private static BasicDataSource dataSource;

    static {
        Properties prop = new Properties();
        InputStream ips = DbUtils2.class.getClassLoader().getResourceAsStream("jdbc.properties");
        try {
            //加载配置文件
            prop.load(ips);
            //获取我们在配置文件中配置的driver等信息
            driver = prop.getProperty("driver");
            url = prop.getProperty("url");
            username = prop.getProperty("username");
            password = prop.getProperty("password");
            //创建数据源
            dataSource = new BasicDataSource();
            //设置数据库的连接信息:
            dataSource.setDriverClassName(driver);
            dataSource.setUrl(url);
            dataSource.setUsername(username);
            dataSource.setPassword(password);
            //设置初始连接数量
            dataSource.setInitialSize(3);
            //设置最大连接数量
            dataSource.setMaxActive(3);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            try {
                //关闭资源
                ips.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

    public static Connection getConn() throws Exception {
        // 从连接池中获取连接
        Connection conn = dataSource.getConnection();
        return conn;
    }

    public static void close(Connection conn, Statement stat, ResultSet rs) {
        try {
            if (rs != null) {
                rs.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (stat != null) {
                stat.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (conn != null) {
                //打开自动提交
                conn.setAutoCommit(true);
                conn.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}
4、使用 PrepareStatement 来实现登陆验证(可以避免SQL注入):
    public static void main(String[] args) {
        Scanner scan = new Scanner(System.in);
        System.out.println("请输入用户名");
        String username = scan.nextLine();
        Scanner scr = new Scanner(System.in);
        System.out.println("请输入密码");
        String password = scr.nextLine();
        boolean b = login(username, password);
        if (b) {
            System.out.println("登陆成功");
        } else {
            System.out.println("登录失败");
        }
    }

    private static boolean login(String username, String password) {
        String sql = "select count(*) from table_user where username=? and password=?";
        Connection conn = null;
        PreparedStatement stat = null;
        ResultSet rs = null;
        try {
            conn = DbUtils2.getConn();
            stat = conn.prepareStatement(sql);
            stat.setString(1, username);
            stat.setString(2, password);
            rs = stat.executeQuery();
            while (rs.next()) {
                //得到查询的数量
                int count = rs.getInt(1);
                if (count > 0) {
                    return true;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtils2.close(conn, stat, rs);
        }
        return false;
    }
5、addBatch 批量操作 demo(使用Statement对象)
    @Test
    public void testAddBatch01() {
        String sql1 = "insert into table_user values(null,'悟空','aaa')";
        String sql2 = "insert into table_user values(null,'八戒','bbb')";
        String sql3 = "insert into table_user values(null,'沙僧','ccc')";
        Connection conn = null;
        Statement stat = null;
        ResultSet rs = null;
        try {
            conn = DbUtils2.getConn();
            stat = conn.createStatement();
            stat.addBatch(sql1);
            stat.addBatch(sql2);
            stat.addBatch(sql3);
            //执行批量操作
            stat.executeBatch();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtils2.close(conn, stat, rs);
        }
    }

6、addBatch 批量操作 demo(使用PrepareStatement对象)
    @Test
    public void testAddBatch02() {
        String sql = "insert into table_user values(null,?,?)";
        Connection conn = null;
        PreparedStatement stat = null;
        ResultSet rs = null;
        try {
            conn = DbUtils2.getConn();
            stat = conn.prepareStatement(sql);
            stat.setString(1, "刘备");
            stat.setString(2, "aaa");
            //添加到批量处理
            stat.addBatch();
            stat.setString(1, "关羽");
            stat.setString(2, "bbb");
            stat.addBatch();
            stat.setString(1, "张飞");
            stat.setString(2, "ccc");
            stat.addBatch();
            stat.executeBatch();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtils2.close(conn, stat, rs);
        }
    }
7、addBatch 批量操作 demo(避免内存溢出)
    @Test
    public void testAddBatch03() {
        String sql = "insert into table_user values(null,?,?)";
        Connection conn = null;
        PreparedStatement stat = null;
        ResultSet rs = null;
        try {
            conn = DbUtils2.getConn();
            stat = conn.prepareStatement(sql);
            for (int i = 0; i < 100; i++) {
                stat.setString(1, "name" + i);
                stat.setString(2, "admin");
                stat.addBatch();
                //下面写法可以避免内存溢出
                if (i % 20 == 0) {
                    stat.executeBatch();
                    //清除批处理内容
                    stat.clearBatch();
                }
            }
            stat.executeBatch();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtils2.close(conn, stat, rs);
        }
    }
8、获取主键 demo
    @Test
    public void testPrimaryKey() {
        Connection conn = null;
        Statement stat = null;
        ResultSet rs = null;
        try {
            conn = DbUtils2.getConn();
            stat = conn.createStatement();
            String sql = "insert into table_user values(null,'小明','abc')";
            //执行SQL并且制定需要获取自增主键值
            stat.executeUpdate(sql, Statement.RETURN_GENERATED_KEYS);
            //获取返回的主键值
            rs = stat.getGeneratedKeys();
            while (rs.next()) {
                int id = rs.getInt(1);
                System.out.println("自增主键" + id);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtils2.close(conn, stat, rs);
        }
    }
9、查看数据库的元数据 demo
    @Test
    public void testMetadata() {
        Connection conn = null;
        Statement stat = null;
        ResultSet rs = null;
        try {
            conn = DbUtils2.getConn();
            stat = conn.createStatement();
            //得到数据库的元数据
            DatabaseMetaData dbData = conn.getMetaData();
            System.out.println("驱动版本:" + dbData.getDriverMajorVersion());
            System.out.println("用户名:" + dbData.getUserName());
            System.out.println("链接地址:" + dbData.getURL());
            System.out.println("数据库名称:" + dbData.getDatabaseProductName());
            //获取表相关的元数据
            rs = stat.executeQuery("select * from table_user");
            ResultSetMetaData rsData = rs.getMetaData();
            //得到表字段的数量
            int count = rsData.getColumnCount();
            for (int i = 0; i < count; i++) {
                String name = rsData.getColumnName(i + 1);
                String type = rsData.getColumnTypeName(i + 1);
                System.out.println(name + ":" + type);
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            DbUtils2.close(conn, stat, rs);
        }
    }

四、其他类型数据库连接池

在 Java 程序中,开源的数据库连接池有以下几种 :

  1. C3P0:是一个开放源代码的 JDBC 连接池,它在 lib 目录中与 Hibernate 一起发布,包括了实现 jdbc3 和 jdbc2 扩展规范说明的 Connection 和 Statement 池的 DataSources 对象
  2. Proxool:是一个 Java SQL Driver 动程序,提供了对选择的其它类型的驱动程序的连接池封装。可以非常简单的移植到现存的代码中,完全可配置,快速、成熟、健壮。可以透明地为现存的JDBC驱动程序增加连接池功能。
  3. Jakarta DBCP:DBCP 是一个依赖 Jakarta commons-pool 对象池机制的数据库连接池。DBCP 可以直接的在应用程序中使用
  4. DDConnectionBroker:是一个简单、轻量级的数据库连接池
  5. DBPool:是一个高效、易配置的数据库连接池。它除了支持连接池应有的功能之外,还包括了一个对象池,使用户能够开发一个满足自己需求的数据库连接池
  6. XAPool:是一个 XA 数据库连接池。它实现了 javax.sql.XADataSource 并提供了连接池工具
  7. rimrose:是一个 Java 开发的数据库连接池。当前支持的容器包括 Tomcat4&5、Resin3 与J Boss3。它同样也有一个独立的版本,可以在应用程序中使用而不必运行在容器中。Primrose 通过一个 Web 接口来控制 SQL 处理的追踪、配置,以及动态池管理。在重负荷的情况下可进行连接请求队列处理
  8. SmartPool:是一个连接池组件,它模仿应用服务器对象池的特性。SmartPool 能够解决一些临界问题如连接泄漏(connection leaks)、连接阻塞、打开的 JDBC 对象(如Statements、PreparedStatements)等
  9. MiniConnectionPoolManager:是一个轻量级 JDBC 数据库连接池。它只需要 Java1.5(或更高)并且没有依赖第三方包
  10. BoneCP:是一个快速、开源的数据库连接池。帮用户管理数据连接,让应用程序能更快速地访问数据库。比C3P0/DBCP连接池速度快25倍
  11. Druid(推荐):Druid 不仅是一个数据库连接池,还包含一个 ProxyDriver、一系列内置的 JDBC 组件库、一个 SQL Parser,Druid 支持所有JDBC兼容的数据库,包括 Oracle、MySql、Derby、Postgresql、SQL Server、H2 等。 Druid 针对 Oracle 和 MySql 做了特别优化,比如:
    • Oracle 的 PS Cache 内存占用优化
    • MySql 的 ping检测优化
    • Druid 提供了 MySql、Oracle、Postgresql、SQL-92 的 SQL 的完整支持,这是一个手写的高性能 SQL Parser,支持 Visitor 模式,使得分析 SQL 的抽象语法树很方便。
    • 简单 SQL 语句用时 10 微秒以内,复杂 SQL 用时 30 微秒。
    • 通过 Druid 提供的 SQL Parser 可以在 JDBC 层拦截 SQL 做相应处理,比如说分库分表、审计等。Druid 防御SQL注入攻击的 WallFilter,就是通过 Druid 的 SQL Parser 分析语义实现的

这些池底层都是对 driver-class-name=com.mysql.jdbc.Driver 对这个驱动进行了封装

  • 12
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 9
    评论
JAVA相关基础知识 1、面向对象的特征有哪些方面 1.抽象: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一是过程抽象,二是数据抽象。 2.继承: 继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。 3.封装: 封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。 4. 多态性: 多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。 2、String是最基本的数据类型吗? 基本数据类型包括byte、int、char、long、float、double、boolean和short。 java.lang.String类是final类型的,因此不可以继承这个类、不能修改这个类。为了提高效率节省空间,我们应该用StringBuffer类 3、int 和 Integer 有什么区别 Java 提供两种不同的类型:引用类型和原始类型(或内置类型)。Int是java的原始数据类型,Integer是java为int提供的封装类。Java为每个原始类型提供了封装类。 原始类型封装类 booleanBoolean charCharacter byteByte shortShort intInteger longLong floatFloat doubleDouble 引用类型和原始类型的行为完全不同,并且它们具有不同的语义。引用类型和原始类型具有不同的特征和用法,它们包括:大小和速度问题,这种类型以哪种类型的数据结构存储,当引用类型和原始类型用作某个类的实例数据时所指定的缺省值。对象引用实例变量的缺省值为 null,而原始类型实例变量的缺省值与它们的类型有关。 4、String 和StringBuffer的区别 JAVA平台提供了两个类:String和StringBuffer,它们可以储存和操作字符串,即包含多个字符的字符数据。这个String类提供了数值不可改变的字符串。而这个StringBuffer类提供的字符串进行修改。当你知道字符数据要改变的时候你就可以使用StringBuffer。典型地,你可以使用StringBuffers来动态构造字符数据。 5、运行时异常与一般异常有何异同? 异常表示程序运行过程中可能出现的非正常状态,运行时异常表示虚拟机的通常操作中可能遇到的异常,是一种常见运行错误。java编译器要求方法必须声明抛出可能发生的非运行时异常,但是并不要求必须声明抛出未被捕获的运行时异常。 6、说出Servlet的生命周期,并说出Servlet和CGI的区别。 Servlet被服务器实例化后,容器运行其init方法,请求到达时运行其service方法,service方法自动派遣运行与请求对应的doXXX方法(doGet,doPost)等,当服务器决定将实例销毁的时候调用其destroy方法。 与cgi的区别在于servlet处于服务器进程中,它通过多线程方式运行其service方法,一个实例可以服务于多个请求,并且其实例一般不会销毁,而CGI对每个请求都产生新的进程,服务完成后就销毁,所以效率上低于servlet。 7、说出ArrayList,Vector, LinkedList的存储性能和特性 ArrayList和Vector都是使用数组方式存储数据,此数组元素数大于实际存储的数据以便增加和插入元素,它们都允许直接按序号索引元素,但是插入元素要涉及数组元素移动等内存操作,所以索引数据快而插入数据慢,Vector由于使用了synchronized方法(线程安全),通常性能上较ArrayList差,而LinkedList使用双向链表实现存储,按序号索引数据需要进行前向或后向遍历,但是插入数据时只需要记录本项的前后项即可,所以插入速度较快。 8、EJB是基于哪些技术实现的?并说出SessionBean和EntityBean的区别,StatefulBean和StatelessBean的区别。 EJB包括Session Bean、Entity Bean、Message Driven Bea
JDBC数据库连接池是一种管理数据库连接的技术,它可以在应用程序和数据库之间建立一个连接池,以便在需要时从池中获取连接,而不是每次都重新创建连接。这样可以提高应用程序的性能和可伸缩性,减少数据库服务器的负载。 以下是使用HikariCP连接池Java代码示例: ```java import java.sql.Connection; import java.sql.SQLException; import com.zaxxer.hikari.HikariConfig; import com.zaxxer.hikari.HikariDataSource; public class JdbcConnectionPool { private static HikariDataSource dataSource; static { HikariConfig config = new HikariConfig(); config.setJdbcUrl("jdbc:mysql://localhost:3306/mydatabase"); config.setUsername("username"); config.setPassword("password"); config.addDataSourceProperty("cachePrepStmts", "true"); config.addDataSourceProperty("prepStmtCacheSize", "250"); config.addDataSourceProperty("prepStmtCacheSqlLimit", "2048"); dataSource = new HikariDataSource(config); } public static Connection getConnection() throws SQLException { return dataSource.getConnection(); } } ``` 在上面的示例中,我们使用HikariCP连接池来管理MySQL数据库的连接。我们首先创建一个HikariConfig对象,设置数据库的URL、用户名和密码,以及一些其他的属性。然后,我们创建一个HikariDataSource对象,并将HikariConfig对象传递给它。最后,我们定义了一个静态方法getConnection(),该方法返回一个从连接池中获取的连接。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

栗筝i

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值