JDBC 数据库连接详解

JDBC

JAVA Database Connectivity java 数据库连接

1.1数据库访问的过程

  1. 客户端与Mysql服务器之间建立连接
  2. 客户端向Mysql服务器发送数据库请求
  3. Mysql服务器处理客户端请求,并返回结果给客户端
  4. 客户端接受Mysql服务器的响应,并按照自己的业务逻辑做响应处理。
    释放相关资源。

1.2 JDBC概述

Java DataBase Connecitivity,java数据库连接。Java程序访问数据库的方法。
JDBC本质:Sun公司制定了一套java语言访问数据库的规范(接口),==各大数据库生产厂商来实现相应的实现类,提供驱动jar包。==我们可以通过这套接口(JDBC)编程, 真正执行的代码是驱动jar包中的实现类
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SSAbuyZL-1605164240674)(E:\MarkDown笔记\jdbc\JDBC概述.png)]
组成JDBC的2个包:

  • java.sql.*:是核心包里的,是jdbc2.0之前的东西
  • javax.sql.*:是扩展包中的,包括了jdbc3.0的特性

1.3编写一个JDBC程序

快速入门:
    * 步骤:
        1. 导入驱动jar包 mysql-connector-java-5.1.37-bin.jar
            1.复制mysql-connector-java-5.1.37-bin.jar到项目的libs目录下
            2.右键–>Add As Library
        2. 注册驱动
        3. 获取数据库连接对象 Connection
        4. 定义sql
        5. 获取执行sql语句的对象 Statement
        6. 执行sql,接受返回结果
        7. 处理结果
        8. 释放资源

  1. 搭建实验环境

2. 在mysql中创建一个数据库

3. 新建一个java工程,并导入数据驱动

​    需要下载下Java连接Mysql 的驱动包 https://mvnrepository.com/artifact/mysql/mysql-connector-java/5.1.17

1. 导包 :导入下载的jar包,导包的时候level是project级别的时候,在其他module中不需要再重复导包。
     2. 右键–>Add as  Library  :将jar包导入项目

  1. 编写程序,在程序中加载数据库驱动
        DriverManager.registerDiver(Driver dirver)

  2. 建立连接(connection)
        Connection conn =DiverManager.getConnection(url,user,pass);
        url的写法:[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BPBlv7Eq-1605164240679)(E:\Markdown笔记\jdbc\url的写法.png)]
        常用数据库URL地址的写法:
            Oracle写法:jdbc:oracle:thin:@localhost:1521:dbname
            SqlServer—jdbc:microsoft:sqlserver://localhost:1433; DatabaseName=dbname
            MySql—------jdbc:mysql://localhost:3306/dbname
            Mysql的url地址的简写形式: jdbc:mysql:///sid
            如果你的主机地址默认是localhost  端口是3306

  3. 创建一个用于向数据库发送SQL的Statement对象,并发送SQL
        Statement st=conn.createQuery();
        Resultset rs=st.executeQuery(sql);

  4. 从代表结果集的 ResultSet中取出数据,打印到命令行窗口

while(rs.next()){
        int id = rs.getInt(“id”);
        String name = rs.getString(“name”);
        int age = rs.getInt(“age”);
          System.out.println(“id=”+id + “===name=”+name+"==age="+age)                }

  1. 断开和数据库的连接,并释放资源
import com.mysql.jdbc.Driver;
import java.sql.*;

public class Demo_01 {
    public static void main(String[] args) {
        try {
            DriverManager.registerDriver(new Driver());
            Connection conn =DriverManager.getConnection("jdbc:mysql://localhost:3306/mydemo?characterEncoding=utf8","root","123456");
            //创建发送sql对象的statement对象
            Statement st = conn.createStatement();
            //发送sql
            ResultSet rt = st.executeQuery("select * from student");
            rt.next();
           String name= rt.getString("name");
            System.out.println(name);
            rt.close();
            conn.close();
            rt.close();
        } catch (SQLException e) {
            e.printStackTrace();
        }

    }
}

1.4 对第一个JDBC程序的优化

  1. DriverManager.registerDriver(new Driver());通过查看Driver源码可以看到,这段代码其实执行了两次驱动注册,所以可以进一步的进行优化:Class.forName(“com.mysql.jdbc.Driver”);

  2. Connection connection = DriverManager.getConnection(“jdbc:mysql://localhost:3306/mydb4?characterEncoding=utf8”,  “root”, “123456”);

存在硬编码,如果今后数据库的用户名或者密码发生变化,以及从mysql数据库迁移到oracle或者其他数据库,需要手动去代码里面再去修改这部分代码,维护起来非常不便。

解决方案:利用配置文件来配置该信息。
   
3. 资源释放,写在finally代码块中

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K8EpUvBZ-1605164240683)(E:\MarkDown笔记\jdbc\jdbc的优化.png)]
4. 可以做一个工具类,将一些重复性代码放入工具类中,直接调用

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s3caAqmF-1605164240686)(E:\MarkDown笔记\jdbc\JDBC工具类.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PiTeONhZ-1605164240687)(E:\MarkDown笔记\jdbc\JDBC释放资源工具类.png)]

2 .程序详解

2.1 DriverManager:驱动管理对象

  • 功能:
            1. 注册驱动:告诉程序该使用哪一个数据库驱动jar
                static void registerDriver(Driver driver) :注册与给定的驱动程序 DriverManager 。 
                写代码使用:  Class.forName(“com.mysql.jdbc.Driver”);
                通过查看源码发现:在com.mysql.jdbc.Driver类中存在静态代码块
                 static {
                        try {
                            java.sql.DriverManager.registerDriver(new Driver());
                        } catch (SQLException E) {
                            throw new RuntimeException(“Can’t register driver!”);
                        }
                    }

注意:mysql5之后的驱动jar包可以省略注册驱动的步骤。
        2. 获取数据库连接:
            * 方法:static Connection getConnection(String url, String user, String password) 
            * 参数:
                * url:指定连接的路径
                    * 语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
                    * 例子:jdbc:mysql://localhost:3306/db3
                    * 细节:如果连接的是本机mysql服务器,并且mysql服务默认端口是3306,则url可以简写为:jdbc:mysql:///数据库名称
                * user:用户名
                * password:密码

2.2 Connection: 数据库连接对象

Jdbc程序中的Connection,它用于代表数据库的连接(桥梁), Connection是数据库编程中最重要的一个对象,客户端与数据库所有交互都是通过connection对象完成的,这个对象的常用方法:

功能:

1. 获取执行sql 的对象
    - createStatement():创建向数据库发送sql的statement对象。    
    - prepareStatement(sql) :创建向数据库发送预编译sql的PrepareSatement对象。
2. 管理事务:
   - setAutoCommit(boolean autoCommit):设置事务是否自动提交。 
   - commit() :在链接上提交事务。
   - rollback() :在此链接上回滚事务。

2.3  Statement:执行sql的对象

Jdbc程序中的Statement对象用于向数据库发送SQL语句, Statement对象常用方法:

执行sql:

  • executeQuery(String sql) :用于向数据发送查询语句。

  • executeUpdate(String sql):用于向数据库发送(DML语句)insert、update或delete语句,(DLL语句,使用不多) create,alter ,drop 语句

增删改返回的数据是int型代表的是受影响的行数

  • execute(String sql):用于向数据库发送任意sql语句
        它返回的是一个boolean类型的数据

2.4  ResultSet

Jdbc程序中的ResultSet用于代表Sql语句的执行结果。Resultset封装执行结果时,采用的类似于表格的方式。ResultSet 对象维护了一个指向表格数据行的游标,初始的时候,游标在第一行之前,调用ResultSet.next() 方法,可以使游标指向具体的数据行,进行调用方法获取该行的数据。
ResultSet既然用于封装执行结果的,所以该对象提供的都是用于获取数据的get方法:

参数都有两 种类型: int型和 String 型, int型代表的是列的编号,String型代表的是列的名称

  • 获取任意类型的数据
        - getObject(int index)
        - getObject(string columnName)
  • 获取指定类型的数据,(封装数据时方便)例如:
        - getString(int index)
        - getString(String columnName)
        -     
    ResultSet还提供了对结果集的游标进行滚动的方法:
  • next():移动到下一行
  • Previous():移动到前一行
  • absolute(int row):移动到指定行
  • beforeFirst():移动resultSet的最前面。
  • afterLast() :移动到resultSet的最后一行之后。
常用数据类型转换表
  • 提问:数据库中列的类型是varchar,获取该列的数据调用什么方法?Int类型呢?bigInt类型呢?Boolean类型?
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fxyGT8SV-1605164240689)(E:\MarkDown笔记\jdbc\常用数据类型转换表.png)]

2.4 释放资源

  • Jdbc程序运行完后,切记要释放程序在运行过程中,创建的那些与数据库进行交互的对象,这些对象通常是ResultSet, Statement和Connection对象。
  • 特别是Connection对象,它是非常稀有的资源,用完后必须马上释放,如果Connection不能及时、正确的关闭,极易导致系统宕机。Connection的使用原则是尽量晚创建,尽量早的释放。
  • 为确保资源释放代码能运行,资源释放代码也一定要放在finally语句中。

3.数据库注入问题

用户输入的账号密码在代码中是以字符串拼接的方式生成查询语句的,这样用户输入的内容很容易改变我们的原查询代码,这就相当一一个数据库注入问题。

解决方法:prepareStatement

prepareStatement采用一种预先编译好的机制。用户输入的任何内容仅仅会当做普通文本,而不会对里面可能包含的关键字来进行解析。

  • PreparedStatement继承自Statement,可以通过Connection的prepareStatement方法得到。

  • prepareStatement很明显的将sql命令语句与参数分开处理,其执行过程是,首先在sql语句真正执行之前,先把sql命令送到数据库中进行预编译,生成相应 的数据库命令,然后在获取sql中的参数,然后真正执行该sql语句。

  • 这样一来,用户输入的参数,只被当做参数而非命令来解析,就可以避免数据库注入这样的问题发生。

  • 弊端:单次执行PreparedStatement需要与数据库通信两次,效率,比之于单词执行Statement要低。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0ov20ikA-1605164240691)(E:\MarkDown笔记\jdbc\preparestatement解决数据库注入问题.png)]

JDBC批处理:

业务场景:当需要向数据库发送一批SQL语句执行时,应避免向数据库一条条的发送执行,而应采用JDBC的批处理机制,以提升执行效率。
默认情况下Mysql的批处理仍然是一条一条执行,需要在url后面添加rewriteBatchedStatements=true参数

实现批处理有两种方式:

  • 第一种方式:
    相当于使用小推车搬砖,本来是一块一块搬,有了小推车,addbatch将砖放到一个小推车里,executebatch相当于将小推车褪去目的地,clearbatch相当于将小推车里的砖卸了,然后推回目的地。
  1. Statement.addBatch(sql) :
  2. 执行批处理SQL语句
        - executeBatch()方法:执行批处理命令
        - clearBatch()方法:清除批处理命令
    优点:可以向数据库发送多条不同的SQL语句。
    缺点
            - SQL语句没有预编译。
            - 当向数据库发送多条语句相同,但仅参数不同的SQL语句时,需重复写上很多条SQL语句。
  • 实现批处理的第二种方式:
    PreparedStatement.addBatch()
        注意内存溢出问题
  • 优点:与数据库通信次数在批量操作时,PreparedStatment的通信次数远少于Statment。
  • 缺点:只能应用在SQL语句相同,但参数不同的批处理中。因此此种形式的批处理经常用于在同一个表中批量插入数据,或批量更新表的数据。

注意记得在配置文件url 后添加 rewriteBatchedStatements=true,如果不开启rewriteBatchedStatements=true,那么jdbc会把批量插入当做一行行的单条处理,也即没有达到批量插入的效果,和其他代码用&连接

例如:

url=jdbc:mysql://localhost:3306/myhome?characterEncoding=utf8username=rootpassword=123456driverClassName=com.mysql.jdbc.Driver

三种批量操作的对比:

import util.JdbcUtil;

import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;

public class BatchCompare {

    public static void main(String[] args) {
        long beginTime = System.currentTimeMillis();
        directInsert();
        long t1 = System.currentTimeMillis();
        statementBatch();
        long t2 = System.currentTimeMillis();
        prepareStatementBatch();
        long t3 = System.currentTimeMillis();
        System.out.println("逐条执行的时间为: " + (t1 - beginTime));
        System.out.println("statement批处理执行的时间为: " + (t2 - t1));
        System.out.println("prepareStatment批处理执行的时间为: " + (t3 - t2));
    }

    private static void prepareStatementBatch() {
        Connection connection = null;
        PreparedStatement preparedStatement = null;
        try {
            connection = JdbcUtil.getConnection();
            String sql = "insert into user values (null,?,?)";
            preparedStatement = connection.prepareStatement(sql);
            for (int i = 0; i < 500; i++) {
                //statement.addBatch("insert into user values (null,'" + i + "','" + i + "')");
                preparedStatement.setString(1,i + "");
                preparedStatement.setString(2,i + "");
                preparedStatement.addBatch();
                if(i == 249){
                    //statement.executeBatch();
                    preparedStatement.executeBatch();
                    //statement.clearBatch();
                    preparedStatement.clearBatch();
                }
            }
            //statement.executeBatch();
            preparedStatement.executeBatch();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            try {
                JdbcUtil.releaseConnection(connection,null,preparedStatement);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private static void statementBatch() {
        Connection connection = null;
        Statement statement = null;
        try {
            connection = JdbcUtil.getConnection();
            statement = connection.createStatement();
            // statement.addBatch(sql); 相当于将砖放到一个小推车里
            //  statement.executeBatch(); 推着小推车去目的地
            //   statement.clearBatch();  将小推车里面的砖块卸了,再回到目的地
            for (int i = 0; i < 500; i++) {
                statement.addBatch("insert into user values (null,'" + i + "','" + i + "')");
                //statement.addBatch("delete from user where id = 1");
                if(i == 249){
                    statement.executeBatch();
                    statement.clearBatch();
                }
            }
            statement.executeBatch();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            try {
                JdbcUtil.releaseConnection(connection,null,statement);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    private static void directInsert() {
        Connection connection = null;
        Statement statement = null;
        try {
            connection = JdbcUtil.getConnection();
            statement = connection.createStatement();
            for (int i = 0; i < 500; i++) {
                statement.executeUpdate("insert into user values (null,'" + i + "','" + i + "')");
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }finally {
            try {
                JdbcUtil.releaseConnection(connection,null,statement);
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值