JAVA入门基础_从零开始的培训_JDBC和数据库连接池_基于MYSQL

JDBC是什么?

  • JDBC实际上就是为了能够访问不同的数据库,而提供的一套接口规范。
  • 各个数据库厂商实现这套接口规范再提供相对应的jar包让JAVA程序能够操作对应的数据库。
  • 学习JDBC就是在学习这一套接口规范,变成面向接口编程。
  • 既然是外部为JAVA提供这些jar包,因此这些接口规范一般都在 java.sql 和 javax.sql包中

  • 既然MYSQL实现了JDBC这套接口的规范,那么当然我们需要用到MYSQL实现这套接口的jar包
    mysql-connector-java-5.1.40.jar

使用JDBC连接数据库并进行操作的5个步骤

  • (1)注册相对应的数据库驱动
  • (2)获取连接对象
  • (3)获取到命令对象(操作数据库的对象)
  • (4)使用命令对象对数据库进行操作(若是查询操作会返回结果集)
  • (5)关闭连接(包括结果集、命令对象、连接对象)

获取数据库连接的最原始的方式

import com.mysql.jdbc.Driver;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.Statement;

/**
 * @author codeStars
 * @date 2022/8/17 9:35
 */
public class Test {

    public static void main(String[] args) throws Exception{
        // 准备连接数据库需要的数据
        String url = "jdbc:mysql://localhost:13306/mydb";
        String user = "root";
        String pwd = "abc123";

        // 1. 获取到驱动对象并进行驱动注册
        Driver driver = new com.mysql.jdbc.Driver();
        DriverManager.registerDriver(driver);

        // 2. 获取连接对象
        Connection connection = DriverManager.getConnection(url, user, pwd);

        // 3. 获取到命令对象
        Statement statement = connection.createStatement();

        // 4. 对数据库进行操作
        int affectedCount = statement.executeUpdate("INSERT INTO  animal VALUES(2,'王五')");
        System.out.println("影响的语句数量:" + affectedCount);

        // 5. 关闭连接
        statement.close();
        connection.close();
    }
}
  • (1) 在创建Driver对象时,其中的静态代码块就会完成驱动的注册(我们相当于多此一举)
  • 解决方法:只创建一个Driver对象,但是不再进行驱动的注册,但是还有如下的问题。

  • (2)虽然只创建了一个Driver对象,但是Driver类中的静态代码块有这么一句
    DriverManager.registerDriver(new Driver());

  • 由此可以看出,在Driver类加载时,就会创建一个新的Driver对象来进行驱动的注册。
  • 而我们又new了一个就会导致 创建了2次Driver对象,造成了资源的浪费。
  • 因此我们可以采用: Class.forName("com.mysql.jdbc.Driver");的方式完成驱动的注册。

  • (3)可以看到我们 连接数据库的参数都是直接编写在JAVA代码中的,这很不利于程序的扩展与维护。因此我们需要把配置信息提取出来。

  • (4)扩展:在MYSQL驱动5.1.6之后,在程序运行时会在MYSQL提供的jar包的 META-INF\services\java.sql.Driver文本中找到驱动的全限定类名,调用反射进行 自动注册。但还是建议加上 Class.forName("com.mysql.jdbc.Driver");

DriverManager驱动管理类

  • getConnection(String url,String user, String password):获取一个连接对象

Connection接口

  • createStatement():获取一个Statement对象
  • prepareStatement(String sql):获取一个PrepareStatement对象,传入一个sql进行预编译

Statement接口

  • exexuteUpdate(sql):执行增删改语句,返回影响的行数
  • executeQuery(sql):执行查询,返回ResultSet对象
  • execute(sql):执行任意的sql命令,返回boolean值

PrepareStatement接口

  • exexuteUpdate():执行增删改语句,返回影响的行数
  • executeQuery():执行查询,返回ResultSet对象
  • execute(sql):执行任意的sql命令,返回boolean值
  • setXxx(占位符索引,占位符的值):为占位符赋对应的值,索引从1开始
  • setObject(占位符索引,占位符的值):为占位符赋对应的值,索引从1开始

  • (1)Statement接口有个致命的缺陷,就是SQL注入问题

  • (2)PreparedStatement解决了SQL注入的问题。
  • (3)PreparedStatement会提前对sql命令进行预编译,使得sql命令的执行效率提升

封装JDBCUtils

  • (1)若是每个方法中都把 获取连接等重复代码全都写一遍,会造成大量代码的冗余。
  • (2)其实就是为了减少代码的冗余

  • (1)提供一个配置文件,放在src目录下

url=jdbc:mysql://localhost:13306/mydb
user=root
pwd=abc123
driver=com.mysql.jdbc.Driver
  • (2)编写JDBCUtils工具类
import java.io.FileInputStream;
import java.sql.*;
import java.util.Properties;

public class JDBCUtils {
    private static String user; //用户名
    private static String pwd; //密码
    private static String url; //url
    private static String driver; //驱动名

    static {
        try {
            // 1. 获取配置文件信息
            Properties props = new Properties();
            props.load(new FileInputStream("src\\jdbc.Properties"));

            // 2. 为静态变量赋值
            user = props.getProperty("user");
            pwd = props.getProperty("pwd");
            url = props.getProperty("url");
            driver = props.getProperty("driver");

            // 3. 注册驱动(其实不写也行)
            Class.forName(driver);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

    }

    /**
     * 获取连接
     * @return
     * @throws SQLException
     */
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection(url, user, pwd);
    }

    /**
     * 关闭连接
     */
    public static void close(ResultSet resultSet, Statement statement, Connection connection) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
            if (statement != null) {
                statement.close();
            }
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException throwables) {
            throw new RuntimeException(throwables);
        }
    }
}

事物(数据一致性问题)

  • 只需要在一个事务的最开头,将自动提交设置为false
  • 在事务结束位置,自动提交数据
  • 在catch中进行数据的回滚
public class Test {

    public static void main(String[] args) throws Exception{
        // 1.获取连接
        Connection connection = JDBCUtils.getConnection();

        // 2. 获取到命令对象
        String sql1 = "UPDATE account SET money = money - 100 WHERE id = 1";
        String sql2 = "UPDATE account SET money = money + 100 WHERE id = 2";

        PreparedStatement preparedStatement1 = connection.prepareStatement(sql1);
        PreparedStatement preparedStatement2 = connection.prepareStatement(sql2);

        try {
            // 设置自动提交为false
            connection.setAutoCommit(false);

            // 事务开始
            preparedStatement1.execute();
            int i = 1/0;
            preparedStatement2.execute();

            // 事务结束,提交数据
            connection.commit();
        }catch (Exception e) {
            // 出现异常,回滚
            connection.rollback();
            e.printStackTrace();
        }
    }
}

批处理

  • 连接数据库的URL需要添加上 rewriteBatchedStatements参数,示例如下:
  • url=jdbc:mysql://localhost:13306/mydb?rewriteBatchedStatements=true

  • addBatch():第一次时会创建一个ArrayList

  • executeBatch():发送一次请求给MYSQL进行批量的处理。
  • clearBatch():清空batchedArgs集合,这样之前添加的批处理对象就释放了。
// 3. 批处理添加1000条数据
    for (int i = 0; i < 1000; i++) {
        // 3.1 &#x6DFB;&#x52A0;&#x53C2;&#x6570;
        preparedStatement.setInt(1,i);

        // 3.2 &#x6DFB;&#x52A0;&#x5230;&#x6279;&#x5904;&#x7406;&#x4E2D;
        preparedStatement.addBatch();
    }
    // 3.3 &#x4E00;&#x6B21;&#x6027;&#x6279;&#x91CF;&#x5904;&#x7406;
    preparedStatement.executeBatch();

    // 3.4 &#x6E05;&#x7A7A;&#x6279;&#x91CF;&#x5904;&#x7406;
    preparedStatement.clearBatch();

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

  • (1)就目前而言, 每次连接数据库都需要获取连接,并且使用后就直接 关闭了与数据库的连接
  • (2)会占用 大量的网络资源,如果有大量的JAVA程序同时获取数据库的连接,那么数据库很可能不堪重负直接崩溃。
  • (3)针对如上问题,提出了线程池的思想。
  • 在程序刚运行时,就获取一定量的数据库连接对象,将其 放在一个容器当中。
  • 在使用完了该数据库连接对象时,不直接关闭该对象与数据库的连接,而是将其 放回容器当中。
  • 综上就可以完成数据库 连接对象的复用,减少了 反复获取连接而造成的 资源占用

C3P0线程池

  • 既然想要使用的是其他人写好的线程池,那么当然就需要获取到对应的jar包了
  • c3p0-0.9.2.1.jar
  • mchange-commons-java-0.2.20.jar
  • mysql-connector-java-5.1.40.jar

  • 在scr目录下添加一个配置文件c3p0-config.xml,创建ComboPooledDataSource对象时自动读取该配置文件

<c3p0-config>
    <!--使用默认的配置读取数据库连接池对象 -->
    <default-config>
        <!--  连接参数 -->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <property name="jdbcUrl">jdbc:mysql://localhost:13306/mydb?useSSL=false</property>
        <property name="user">root</property>
        <property name="password">abc123</property>

        <!-- 连接池参数 -->
        <!--初始化申请的连接数量-->
        <property name="initialPoolSize">5</property>
        <!--最大的连接数量-->
        <property name="maxPoolSize">10</property>
        <!--最小的连接数量,当线程池空闲时,会将线程池中的连接释放到只剩下5个 -->
        <property name="minPoolSize">5</property>
        <!--超时时间,如果超过该时间没有获取到数据库连接,就抛出异常,单位为毫秒 -->
        <property name="checkoutTimeout">3000</property>
    </default-config>

<!--  注意:在创建ComboPooledDataSource时,可以指定配置的名称,这里做一个示例  -->
    <!--        <named-config name="other_c3p0">-->
    <!--        <property name="driverClass">com.mysql.jdbc.Driver</property>-->
    <!--        <property name="jdbcUrl">jdbc:mysql://localhost:13306/mydb?useSSL=false</property>-->
    <!--        <property name="user">root</property>-->
    <!--        <property name="password">root</property>-->

    <!--        <property name="initialPoolSize">5</property>-->
    <!--        <property name="maxPoolSize">10</property>-->
    <!--        <property name="checkoutTimeout">3000</property>-->
    <!--    </named-config>-->
</c3p0-config>
// ComboPooledDataSource dataSource = new ComboPooledDataSource("other_c3p0");
    ComboPooledDataSource dataSource = new ComboPooledDataSource();
// &#x83B7;&#x53D6;&#x8FDE;&#x63A5;
    Connection connection = dataSource.getConnection();

// &#x8BE5;&#x884C;&#x5176;&#x5B9E;&#x5C31;&#x662F;&#x5C06;&#x8FDE;&#x63A5;&#x653E;&#x56DE;&#x7EBF;&#x7A0B;&#x6C60;&#x5F53;&#x4E2D;&#xFF0C;&#x5E76;&#x4E0D;&#x662F;&#x5207;&#x65AD;&#x4E0E;MYSQL&#x7684;&#x8FDE;&#x63A5;&#x3002;&#x6CE8;&#x610F;&#xFF01;&#xFF01;&#xFF01;
    connection.close();

Druid线程池(推荐)

  • 既然想要使用的是其他人写好的线程池,那么当然就需要获取到对应的jar包了
  • c3p0-0.9.2.1.jar
  • mchange-commons-java-0.2.20.jar
  • mysql-connector-java-5.1.40.jar

  • 在scr目录下添加一个配置文件c3p0-config.xml,创建ComboPooledDataSource对象时自动读取该配置文件

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:13306/mydb?useSSL=false
username=root
password=abc123

&#x521D;&#x59CB;&#x7EBF;&#x7A0B;&#x6570;
initialSize=5
 &#x6700;&#x5927;&#x7EBF;&#x7A0B;&#x6570;
maxActive=10
&#x8D85;&#x65F6;&#x65F6;&#x95F4;
maxWait=3000
public static void main(String[] args) throws Exception{
    // &#x83B7;&#x53D6;&#x5230;&#x914D;&#x7F6E;&#x6587;&#x4EF6;
    Properties props = new Properties();
    props.load(new FileInputStream("src\\druid.properties"));

    // 1. &#x83B7;&#x53D6;&#x6570;&#x636E;&#x6E90;
    DataSource dataSource = DruidDataSourceFactory.createDataSource(props);

    // 2. &#x83B7;&#x53D6;&#x8FDE;&#x63A5;
    Connection connection = dataSource.getConnection();
    System.out.println(connection);

    // 3. &#x5173;&#x95ED;&#x8FDE;&#x63A5;&#xFF08;&#x5176;&#x5B9E;&#x662F;&#x5C06;&#x8FDE;&#x63A5;&#x5BF9;&#x8C61;&#x653E;&#x56DE;&#x7EBF;&#x7A0B;&#x6C60;&#xFF09;
    connection.close();
}

ResultSet有什么弊端?

  • (1)ResultSet结果集与Connection连接是关联的,一旦Connection连接关闭,将会导致ResultSet不可用
  • (2)ResultSet不利于数据管理(因为只能使用一次,毕竟不可能一直不关闭连接)
  • (3)ResultSet返回的信息难以处理,只能通过getXxx这种方式获取,不利于维护。

先想一想,ResultSet返回的是数据表中一行行的数据,那么我们是不是可以定义一个JAVA类与其字段进行匹配呢,这个思想称之为: ORM对象关系映射,这样的类称之为JaveBean或pojo或domain

  • (1)获取到一个ResultSet集合,我们将其中的一行行数据分别存储到一个个对应的JavaBean当中,再使用一个集合(例如ArrayList)来进行存储
  • (2)完成了上述步骤后,Connection连接对象即便关闭了,也不会影响到ArrayList容器中已经存储好的数据
  • (3)而Apache-DBUtils就很好的帮我们封装了上述的内容,非常好用。

Apache-DBUtils 使用的准备工作

  • 准备jar包: commons-dbutils-1.7.jar

  • update():增删改

    JAVA入门基础_从零开始的培训_JDBC和数据库连接池_基于MYSQL
  • query():查询
    JAVA入门基础_从零开始的培训_JDBC和数据库连接池_基于MYSQL

处理器的类名 作用 ArrayHandler 把结果集中的第一行数据转成对象数组 ArrayListHandler 把结果集中的每一行数据都转成一个对象数组,再存放到List中 BeanHandler 将结果集中的第一行数据封装到一个对应的JavaBean中 BeanListHandler 将结果集中的每一行数据都封装到一个对应的JavaBean中,存放到List MapHandler 将结果集中的第一行数据封装到一个Map里,key是列名,value就是对应的值 MapListHandler 将结果集中的每一行数据都封装到一个Map里,然后再存放到List ColumnListHandler 将结果集中某一列的数据存放到List中 KeyedHandler(name) 将结果集中的每一行数据都封装到一个Map里(List

什么是DAO,为什么要使用DAO

  • DAO的英文全程是Data Access Object数据访问对象。理解为直接用于访问数据库的对象。
  • 思考:我们在编写代码时,如果需要获取多个不同数据表当中的数据,我们应该怎么办?

传统的解决思维: 不同的表,就对应不同的JavaBean。不过每次数据的获取,都要编写不同的代码, 比如编写sql时,A表就写select A表,B表就写select B表

如上的思维的问题在于:如果有 相同的功能,比如说都是想获取表中的所有数据,都是想要通过id取得一条数据,都是想进行一个普通的增删改,那么依然要编写不同的代码,造成代码的大量冗余,也不便于管理

这个时候我们就可以将通用的方法,抽取为一个 BasisDao,实现相对应的 通用方法。再编写其余访问 不同数据表的Dao来继承BasisDao,其中只需要编写专属于自己的特有方法。

编写工具类处理异常的小建议

  • 可以不抛出编译时异常,直接抛出运行时异常。
  • 这样的好处是:调用者可以选择捕获处理或者默认处理。

Original: https://www.cnblogs.com/itdqx/p/16594680.html
Author: code_Stars
Title: JAVA入门基础_从零开始的培训_JDBC和数据库连接池_基于MYSQL

原创文章受到原创版权保护。转载请注明出处:https://www.johngo689.com/594024/

转载文章受原作者版权保护。转载请注明原作者出处!

(0)

大家都在看

  • idea指定springboot运行配置文件

    博主首页 posted @2022-07-14 14:12 笑~笑 阅读(11 ) 评论() 编辑 Original: https://www.cnblogs.com/qq3763…

    Java 2023年5月29日
    086
  • 安装Nginx

    一、Nginx官网 1、官网地址 http://nginx.org/ 2、下载页面介绍 首先进入主页,然后点击右侧菜单的 download。 Mainline Version : …

    Java 2023年5月30日
    076
  • 质量问题不是不爆,时候未到

    没有质量,哪来效率,谈什么成本; 最近大半年,团队以极其曲折的方式,将一个支离破碎的应用从重构的边缘给拉了回来,最终项目回到了正常迭代的节奏中; 年初的时候,运营系统相关人员离职,…

    Java 2023年6月15日
    062
  • JAVA抽象类和接口异同点

    抽象类 在继承的层次结构中,每个新的子类都使类变得更加明确和具体。如果从一个子类向父类追溯,类就会变得更通用、更加不明确。类的设计应该确保父类包含它的子类的共同特征。有时候,一个父…

    Java 2023年6月5日
    093
  • ShardingSphere-JDBC进行分库分表

    一、前言:分库分表 在大型的互联网系统中,可能单台MySQL的存储容量无法满足业务的需求,这时候就需要进行扩容了。 和之前的问题一样,单台主机的硬件资源是存在瓶颈的,不可能无限制地…

    Java 2023年6月6日
    070
  • Java复制Word文档

    Microsoft Word 提供了许多易于使用的文档操作工具,同时也提供了丰富的功能集供创建复杂的文档使用。在使用的时候,你可能需要复制一个文档里面的内容到另一个文档。本文介绍使…

    Java 2023年6月15日
    082
  • Spring Ioc容器xml配置

    Spring Ioc容器xml配置基本结构: <?xml version="1.0" encoding="UTF-8"?> &l…

    Java 2023年6月15日
    066
  • Tomcat线程数与处理速度的关系

    问题:Tomcat线程数是不是越大越好呢? 答案肯定是否定的。 Tomcat的处理速度跟线程数不是完全成正比的,设置不恰当会出现相反的效果。服务的负载计算包括了CPU的使用率和资源…

    Java 2023年5月30日
    068
  • 安装pystaller

    安装命令 -i&#x6307;&#x5B9A;&#x4E0B;&#x8F7D;&#x5730;&#x5740;,&#x6B6…

    Java 2023年6月8日
    080
  • MongoDB基本介绍与安装(1)

    MongoDB是一个基于分布式文件存储的数据库。由C++语言编写。旨在为WEB应用提供可扩展的高性能数据存储解决方案。 MongoDB是一个介于关系数据库和非关系数据库之间的产品,…

    Java 2023年6月7日
    093
  • 2018年最新JAVA面试题总结之框架(4)

    转自于:https://zhuanlan.zhihu.com/p/40098726 1、谈谈对spring框架的了解 ,spring有什么作用(IOC,AOP),spring的核心…

    Java 2023年6月13日
    099
  • eShopOnContainers 是一个基于微服务的.NET Core示例框架

    找到一个好的示例框架很难,但不是不可能。大多数是小型Todo风格的应用程序,通常基于SimpleCRUD。值得庆幸的是,Microsoft已经为eShopOnContainers创…

    Java 2023年6月7日
    077
  • 《码处高效:Java开发手册》之代码风格

    流水淡,碧天长,鸿雁成行。编码风格,简捷清爽,反引无限风光。 在美剧《硅谷》中有这样一个经典镜头,主人公 Richard 与同为开发工程师的女友闹分手,理由是两人对缩进方式有着截然…

    Java 2023年6月5日
    073
  • Spring5 扩展篇之自定义xml标签

    本篇文章主要讲一下Spring 如何取自定义自己的XML标签: 1. 首先要自定义自己的XSD文件 说明: 首先这个文件最好建立在静态资源 resource文件夹下,我为了方便都将…

    Java 2023年6月7日
    080
  • LinkedList源码分析

    Node节点类 Node节点用于指向上一个节点和下一个节点、还有存在在链表中的值 private static class Node { // 传入链表中的值 E item; //…

    Java 2023年6月16日
    062
  • Python 的线程与进程

    404. 抱歉,您访问的资源不存在。 可能是网址有误,或者对应的内容被删除,或者处于私有状态。 代码改变世界,联系邮箱 contact@cnblogs.com 园子的商业化努力-困…

    Java 2023年6月8日
    087
亲爱的 Coder【最近整理,可免费获取】👉 最新必读书单  | 👏 面试题下载  | 🌎 免费的AI知识星球