JDBC入门小结

一、JDBC入门

1、JDBC概念:

​ JDBC本质上是由java官方提供的一套接口,具体实现类由各大数据库厂商进行实现。

2、为什么要有JDBC?

​ 每一套数据库都有自己的API,那么对于开发人员每用一套数据库都要学习一套新的API,那么开发成本就会加大。所以java官方出面定义了一套 数据库接口,具体实现类由厂商实现。那么我们使用时,只需要导入jar包,使用Java定义的接口调用厂商提供的实现类对数据库进行连接。所以我们只需要面向接口编程.

3、JDBC的使用步骤
  • 导入jar包
  • 注册驱动 mysql 5 版本之后可以自动注册
  • 获取连接对象
  • 获取执行对象
  • 执行sql语句并获得返回结果
  • 处理返回结果
  • 释放资源

二、JDBC功能类详解

1、DriverManager类

​ 作用:让自己的电脑和数据库服务器建立连接

​ 格式:static Connection getConnection(String url, String user, String password);

2、Connection

​ 作用:获取执行对象

​ 获取普通执行对象:Statement creatStatement();

​ 获取预编译执行对象:PreparedStatement prepareStatement(String sql)

​ 管理事务:

​ 开启事务:setAutoCommit (boolean autoCommit); 默认为true,为自动提交

​ 提交事务:commit();

​ 回滚事务:rollback();

​ 释放资源:

​ 释放数据库连接对象:void close();

3、Statement

​ 作用:将sql语句发送到数据库并且执行

​ 根据查询语句的不同分为两类方法:

​ 执行DQL语句:ResultSet executeQuery(String sql)

​ 执行DML语句:int executeUpdate(String sql)

​ 释放资源:void close();

​ 扩展:boolean execute(String sql) 作用:执行SQL语句,可以执行任意的SQL语句,通常用来执行 DDL语句 ,返回值:如果有结果集返回true,如果没有结果集返回false查询有结果集,增删改没有结果集

4、ResultSet

​ 作用:用于接收查询返回来的结果集

​ 方法:

​ 判断结果集中是否还存在数据:boolean next(); 有数据返回true,并将索引向下移动一行,没有数据就返回false

​ 获取结果集中的数据: XXX getXxx(“列名”);根据数据库中列的数据类型, int getInt(“id”)

​ 释放资源:void close();

​ 以上对象的关闭顺序:先开的后关闭

数据库连接代码案例:

 		//1、加载类驱动,mysql版本5之后可以完成自动加载,
		Class.forName("com.mysql.jdbc.Driver")
        //2、获取连接对象
        Connection connection = DriverManager.getConnection("jdbc:mysql://数据库ip地址/数据库名", "用户名", "密码");
        //3、获取发送语句的对象
        Statement statement = connection.createStatement();
        String sql = "select * from account";
        ResultSet resultSet = statement.executeQuery(sql);
        //4、处理返回的数据对象
        while (resultSet.next()){      			System.out.println(resultSet.getInt("id")+"\t"+resultSet.getString("NAME")+"\t"+resultSet.getString("money"));
        }
		//5、关闭资源
		resultSet.close();
		statement.close();
        connection.close();

三、工具类的抽取

​ 为什么要抽取工具类?

​ 每一次连接数据库,创建连接对象的代码都是一样的。代码冗余度大,所以我们将创建连接对象的代码抽取出来成为一个工具类,提高代码的复用性。

package com.test.utils;

import java.sql.*;

/**
 * 创建工具类
 * 1. 直接获取连接对象
 * 2. 释放资源
 */
public class JdbcUtils {

    /**
     * 获取连接
     */
    public static Connection getConnection() throws SQLException {
        return DriverManager.getConnection("jdbc:mysql:///day22", "root", "root");
    }

    /**
     * 关闭连接
     * 查询调用这个方法
     */
    public static void close(Connection connection, Statement statement, ResultSet resultSet) {
        try {
            if (resultSet != null) {
                resultSet.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (statement != null) {
                statement.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
        try {
            if (connection != null) {
                connection.close();
            }
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }


    /**
     * 关闭连接
     * 增删改没有结果集
     */
    public static void close(Connection connection, Statement statement) {
       //直接调用上面的方法
        close(connection, statement, null);
    }
}

四、SQL注入问题

​ 使用字符串拼接的方式,将sql语句和要查询的条件进行字符串拼接,那么恶意的用户可以在输入框输入恶意字符串将sql语句拼接起来。这样哪怕就算数据库中没有数据也可以登录成功。

​ **SQL注入问题的解决 **:不能让用户输入的密码和我们的SQL语句进行简单的字符串拼接。

​ 我们通过使用prepareStatement接口来解决SQL注入的问题:

PreparedStatement接口

​ PrepareStatement extends Statement

​ MySQL中的语句也是要编译以后才执行,编译是需要消耗时间的。Prepared 翻译为事先的,准备好的。

PrepareStatement比父接口更强大的地方:

  1. SQL语句会预先编译,执行效率会更高。
  2. 解决SQL注入的问题,更安全
  3. SQL语句代码的可读性更好,所有要替换的参数使用占位符,占位符是?问号

PreparedSatement的执行原理:如果执行相同的SQL语句,只需要编译一次,而且是预先编译。在创建语句对象的时候就提供了SQL语句,而不是等到执行的时候才提供SQL语句。

PreparedStatement中相关的方法
Connection创建PreparedStatement对象
Connection接口中的方法描述
PreparedStatement prepareStatement(String sql)通过连接对象创建预编译语句对象,
创建的时候要提供SQL语句,语句有可能有占位符
如果有占位符,后面需要替换占位符为真实的值

注意与Statement的不同,这里是将sql语句传入对象中,Statement是将sql语句传入方法中。在创建语句执行对象时就将sql语句发送到数据库,这里很好的体现了预编译的思想。

PreparedStatement接口中的方法描述
int executeUpdate()实现增删改的操作
返回影响的行数
ResultSet executeQuery()实现查询的操作,返回结果集

设置占位符的方法

PreparedStatement的方法说明
void set数据类型(int 参数1,数据类型 参数2)参数1:第几个占位符(从1开始)
参数2:替换占位符的真实值
示例代码:
package com.itheima;

import com.test.utils.JdbcUtils;
import org.junit.Test;

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

/**
 * 1. 向学生表中添加1条记录代码
 * 2. 将id为2的用户,姓名更新为"蜘蛛精",性别换成女
 * 3. 将id为4的学员删除
 */
public class Demo06Prepared {

    @Test
    public void testInsert() throws SQLException {
        //1.创建连接对象
        Connection connection = JdbcUtils.getConnection();
        //2.创建预编译的语句对象,提供了SQL语句
        PreparedStatement ps = connection.prepareStatement("insert into student (name, sex, birthday) values (?, ?, ?)");
        //3.替换占位符为真实的值
        ps.setString(1, "二郎神");
        ps.setBoolean(2, true);
        ps.setDate(3, Date.valueOf("2011-02-20"));   //注:这是java.sql.Date类中静态方法
        //4.执行SQL语句
        int row = ps.executeUpdate();   //没有SQL语句
        //5.释放资源
        JdbcUtils.close(connection, ps);
        System.out.println("插入了" + row + "条记录");
    }

    //将id为2的用户,姓名更新为"蜘蛛精",性别换成女
    @Test
    public void testUpdate() throws SQLException {
        //1.创建连接对象
        Connection connection = JdbcUtils.getConnection();
        //2.创建预编译的语句对象,提供了SQL语句
        PreparedStatement ps = connection.prepareStatement("update student set name=?, sex=? where id = ?");
        //3.替换占位符为真实的值
        ps.setString(1, "蜘蛛精");
        ps.setBoolean(2, false);
        ps.setInt(3, 2);
        //4.执行SQL语句
        int row = ps.executeUpdate();   //没有SQL语句
        //5.释放资源
        JdbcUtils.close(connection, ps);
        System.out.println("更新了" + row + "条记录");
    }


    //将id为4的学员删除
    @Test
    public void testDelete() throws SQLException {
        //1.创建连接对象
        Connection connection = JdbcUtils.getConnection();
        //2.创建预编译的语句对象,提供了SQL语句
        PreparedStatement ps = connection.prepareStatement("delete from student where id=?");
        //3.替换占位符为真实的值
        ps.setInt(1, 4);
        //4.执行SQL语句
        int row = ps.executeUpdate();   //没有SQL语句
        //5.释放资源
        JdbcUtils.close(connection, ps);
        System.out.println("删除" + row + "条记录");
    }
}

五、JDBC的事务管理

​ 在默认情况下,jdbc是自动提交的。如果我们要进行多条sql语句的执行操作,那么我们就需要对事务进行管理。

​ 管理事务的功能类:Connection

​ 开启事务:setAutoConmit(boolean autoCommit); 参数为false则开启事务

​ 提交事务:commit();

​ 回滚事务:rollback();

六、元数据

什么元数据?

用来描述数据的数据,比如创建表的代码,用来描述表的结构,描述这一列数据的信息

JDBC中元数据分类

三种元数据说明如何得到元数据
数据库元数据描述数据库一些信息,如:数据库驱动版本号,数据库版本号等通过连接对象Connection
参数元数据描述占位符参数的一些信息,如:一共有几个参数通过PreparedStatement
结果集元数据描述结果集中信息,如:一共有多少列,每列的数据类型是什么通过ResultSet
参数元数据:ParameterMetaData

获取ParameterMetaData

PreparedStatement接口中的方法说明
ParameterMetaData getParameterMetaData()通过预编译的语句获取参数元数据
接口中的方法

因为是接口,所以需要数据库厂商驱动的支持,mysql对参数元数据的支持不是太理想

ParameterMetaData接口中的方法说明
int getParameterCount()获取参数的个数
String getParameterTypeName(int param)获取某一列参数的数据类型,参数就是列号,从1开始
只能获取varchar类型的数据

示例代码:

package com.itheima;

import com.test.utils.JdbcUtils;

import java.sql.*;

/**
 * 参数元数据
 */
public class Demo3 {

    public static void main(String[] args) throws SQLException {
        //1. 得到连接对象
        Connection connection = JdbcUtils.getConnection();
        //2. 得到语句对象,SQL语句是向学生表中插入一条记录
        PreparedStatement preparedStatement = connection.prepareStatement("insert into student (name, sex, birthday) values (?,?,?)");
        preparedStatement.setString(1,"猪八戒");
        preparedStatement.setInt(2, 1);   //使用整数
        preparedStatement.setDate(3, Date.valueOf("2000-11-11"));
        //3. 得到参数元数据
        ParameterMetaData metaData = preparedStatement.getParameterMetaData();
        //4. 输出参数的个数 getParameterCount()
        System.out.println("一共有多少个参数:" + metaData.getParameterCount());
        //5. 得到第1个参数的类型名字 getParameterTypeName()
        System.out.println("第1个参数的类型:" + metaData.getParameterTypeName(1));
        //6. 执行SQL语句,插入1条记录
        int row = preparedStatement.executeUpdate();
        System.out.println("插入了一条记录:" + row);
        //关闭连接
        JdbcUtils.close(connection, preparedStatement);
    }

}

七、连接池介绍

JDBC访问数据库时操作Connection对象

JDBC访问数据库的每个增删改查的操作都需要先创建连接,而且这个连接对象是不能共享的。每个用户每次访问都必须创建一个连接对象,经过测试,每次访问数据库最耗时的部分就是创建连接对象。每次使用完连接对象以后,就马上关闭。

为了提高效率,需要解决两个问题
  1. 如何提高创建连接对象的速度

  2. 如何提高连接对象的使用率

    使用连接池的目的就是为了解决以上2个问题

连接池的原理图

没有使用连接池的情况:每个用户访问数据库的时候,都是自己创建连接对象

在这里插入图片描述

使用连接池的情况:一开始系统启动的时候就创建了一定数量的连接对象,使用的时候直接从连接池中去获取就可以了。不需要自己来创建连接对象。

在这里插入图片描述

八、数据库连接池API

数据源接口

javax.sql.DataSource接口

实现类在哪?由第三方厂商来实现,只要实现这个接口都可以编写自己的连接池。

数据源接口中的方法
DataSource接口中的方法描述
Connection getConnection()从连接池中获取连接对象
常用连接池参数含义

每个连接池都有很多的参数,几乎所有的参数都是有默认的值的,我们也可以根据实际情况进行调整。参数名在不同的连接池中参数名是有区别的。

常用参数描述
初始连接数服务器启动的时候创建的连接对象数量
最大连接数连接池中最多可以允许放多少个连接对象
最长等待时间如果连接池中没有连接对象,设置用户等待的最长时间是多久,单位是毫秒。
如果超过这个时间就抛出异常
最长空闲回收时间如果一个连接对象长时间没有人使用,设置多久回收这个对象,默认是不回收。

九、c3p0连接池的介绍

常用连接池的介绍

DataSource本身只是Oracle公司提供的一个接口,没有具体的实现,它的实现由连接池的数据库厂商去实现。我们只需要学习这个工具如何使用即可。

常用的连接池实现组件有这些:

  1. 阿里巴巴-德鲁伊druid连接池:Druid是阿里巴巴开源平台上的一个项目
  2. DBCP(DataBase Connection Pool)数据库连接池,是Apache上的一个Java连接池项目,也是Tomcat使用的连接池组件。
  3. C3P0是一个开源的JDBC连接池,目前使用它的开源项目有Hibernate,Spring等。C3P0有自动回收空闲连接功能。
C3P0常用的配置属性

就算不设置任何参数,每个连接池都有默认的配置参数。不同的数据源实现厂商,下面这些属性名会不同。

常用参数描述
initialPoolSize初始连接数,一开始创建多少个连接对象
maxPoolSize连接池中最大连接数
checkoutTimeout最长等待时间,等多久没有获取到连接对象,抛出异常
maxIdleTime最长空闲回收时间,并不是到了这个时间马上回收,看连接池使用情况
配置文件的要求

使用c3p0的配置文件来配置连接池,配置文件有如下几个要求:

  1. 文件名:必须叫c3p0-config.xml
  2. 路径:这个文件必须要放在src目录下,即类路径下
  3. 分成两种配置
    1. 默认配置:在不指定配置名的情况下,默认使用的配置
    2. 命名配置:可以使用多个配置,通过名字来选择使用哪个配置
    3. 多个配置的好处:
      1. 可以使用不同的连接池参数
      2. 可以指定不同的数据库名,如:day20, day21
      3. 可以指定不同厂商的数据库,如:Oracle,MySQL
API的介绍

C3P0中的类

构造方法描述
ComboPooledDataSource()默认的构造方法,使用默认的配置来创建连接池
ComboPooledDataSource(命名配置)指定使用哪个命名配置来创建连接池

配置文件示例:c3p0-config.xml

<?xml version="1.0" encoding="utf-8"?>
<c3p0-config>
    <!-- 默认配置 -->
    <default-config>
        <!--1. 连接数据库有关的参数,键是固定的,值可以修改 -->
        <!--mysql驱动的名字-->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <!--连接字符串URL-->
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day24</property>
        <!--用户名-->
        <property name="user">root</property>
        <!--密码-->
        <property name="password">root</property>

        <!--2. 连接池配置有关的参数-->
        <!--初始连接数-->
        <property name="initialPoolSize">5</property>
        <!--最大连接数-->
        <property name="maxPoolSize">10</property>
        <!--过多久没有获取连接,超时抛出异常-->
        <property name="checkoutTimeout">3000</property>
    </default-config>

    <!--命名配置,可以使用多个-->
    <named-config name="otherc3p0">
        <!--mysql驱动的名字-->
        <property name="driverClass">com.mysql.jdbc.Driver</property>
        <!--连接字符串URL,修改了库名 -->
        <property name="jdbcUrl">jdbc:mysql://localhost:3306/day23</property>
        <!--用户名-->
        <property name="user">root</property>
        <!--密码-->
        <property name="password">root</property>

        <!--初始连接数-->
        <property name="initialPoolSize">3</property>
        <!--最大连接数-->
        <property name="maxPoolSize">15</property>
        <!--过多久没有获取连接,超时抛出异常-->
        <property name="checkoutTimeout">2000</property>
    </named-config>
</c3p0-config>

java代码

package com.test;

import com.mchange.v2.c3p0.ComboPooledDataSource;

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

/**
 * 1. 从连接池中得到10个连接对象,输出每个对象
 * 2. 分别设置默认配置和命名配置
 */
public class Demo1C3p0 {

    public static void main(String[] args) throws SQLException {
        //1.创建连接池,使用默认配置。ComboPooledDataSource实现了DataSource接口
        //ComboPooledDataSource ds = new ComboPooledDataSource();

        //使用命名配置
        ComboPooledDataSource ds = new ComboPooledDataSource("otherc3p0");
        //2.使用连接池,从连接池中获取10个连接对象
        for (int i = 1; i <= 16; i++) {
            Connection connection = ds.getConnection();
            System.out.println("第" + i + "个连接对象:" + connection);
            //第5个释放
            if (i==5) {
                //放回到连接池中
                connection.close();
            }
        }
    }
}

十、Druid的连接池

DRUID简介

Druid是阿里巴巴开发的号称为监控而生的数据库连接池,在功能、性能、扩展性方面,都超过其他数据库连接池。Druid已经在阿里巴巴部署了超过600个应用,经过一年多生产环境大规模部署的严苛考验。如:一年一度的双十一活动,每年春运的抢火车票。

Druid的下载地址:https://github.com/alibaba/druid

DRUID连接池使用的jar包:druid-1.0.9.jar

常用的配置参数
参数说明
url连接字符串
username用户名
password密码
driverClassName驱动类名,会自动根据URL识别,这一项可以不配置
initialSize初始连接数
maxActive最大连接数
maxWait最长等待时间
DRUID连接池API介绍

得到配置文件的输入流

Class类中的方法说明
InputStream getResourceAsStream(String path)加载类路径下配置文件,转成一个输入流对象

Properties类的方法,读取属性文件中所有的键和值,加载到集合中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EmxnNnaw-1592189228218)(D:/%E9%BB%91%E9%A9%AC%E5%AD%A6%E4%B9%A0/JavaWeb/day24%E3%80%90MyBatis%E7%AC%AC1%E5%A4%A9%E3%80%91/%E8%AE%B2%E4%B9%89/assets/image-20200513103942896.png)]

通过druid工厂类的静态方法创建连接池,提供属性集合做为参数

DruidDataSourceFactory的方法方法
public static DataSource createDataSource(Properties properties)通过属性集合中属性,创建一个连接池

示例代码:

在src目录下新建一个DRUID配置文件,命名为:druid.properties

url=jdbc:mysql://localhost:3306/test
username=root
password=root
driverClassName=com.mysql.jdbc.Driver
initialSize=5
maxActive=10
maxWait=2000

java代码

package com.test;

import com.alibaba.druid.pool.DruidDataSourceFactory;

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

public class Demo2Druid {

    public static void main(String[] args) throws Exception {
        //1.从类路径下加载配置文件,获取一个输入流。如果不指定路径,默认是读取同一个包下资源文件
        InputStream inputStream = Demo2Druid.class.getResourceAsStream("/druid.properties");
        //2.使用Properties对象的方法将配置文件中属性加载到Properties对象中
        Properties properties = new Properties();
        //加载了配置文件中所有的属性
        properties.load(inputStream);
        //3.通过druid的工厂类创建连接池
        DataSource dataSource = DruidDataSourceFactory.createDataSource(properties);

        //获取10个连接对象
        for (int i = 1; i <= 11; i++) {
            Connection connection = dataSource.getConnection();
            System.out.println("第" + i + "个连接对象:" + connection);
            //第3个连接关闭
            if (i==3) {
                connection.close();
            }
        }
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值