第六章JDBC基础详解

什么是JDBC

数据库编程的必要条件

  • 编程语言,如Java,C、C++、Python等
  • 数据库,如Oracle,MySQL,SQL Server等
  • 数据库驱动包:不同的数据库,对应不同的编程语言提供了不同的数据库驱动包,如:MySQL提 供了Java的驱动包mysql-connector-java,需要基于Java操作MySQL即需要该驱动包。同样的, 要基于Java操作Oracle数据库则需要Oracle的数据库驱动包jdbc。
  • JAVA的驱动包就是JDBC
    • JDBC,即Java Database Connectivity,java数据库连接。是一种用于执行SQL语句的Java API,它是Java中的数据库连接规范。这个API由 java.sql.,javax.sql. 包中的一些类和接口组成,它为Java开发人员操作数据库提供了一个标准的API,可以为多种关系数据库提供统一访问

在这里插入图片描述

JDBC工作原理

img

  • JDBC 为多种关系数据库提供了统一访问方式,作为特定厂商数据库访问API的一种高级抽象,它主要包含一些通用的接口类
    • sun发布的Java程序与数据库通信的规范(也就是一系列接口)
    • Java语言访问数据库操作完全面向抽象接口编程
    • 开发数据库应用不用限定在特定数据库厂商的API
    • 程序的可移植性大大增强
    • JDBC只支持关系型数据库,也就是使用表结构的数据库
  • JDBC的驱动层是各大数据库厂商去实现JDBC的规范,然后将这些实现类打成压缩包,就是所谓的jar包

JDBC如何建立数据库连接

public class Demo1 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //1导入jar包——也就是对应数据块的驱动包
        //2加载驱动——驱动其实是一个类,在JVM中我们学到了关于反射的forname,会引起我们的类加载和初始化
        //也就把我们的相关的类加载到内存
        Class.forName("org.gjt.mm.mysql.Driver");
        //3通过驱动管理获取连接对象
           //3.1 准备url
        String url="jdbc:mysql://localhost:3306/fruitdb";
        //"jdbc:mysql://127.0.0.1:3306/rocket_class?characterEncoding=utf8&useSSL=false?characterEncoding=utf8&useSSL=false")
           //3.2准备用户名
        String user="root";
           //3.3准备密码
        String pwd="******";
        //4获取网络请求,根据上面的属性获得数据库的连接
        //java.sql.Connection 连接对象
        Connection con= DriverManager.getConnection(url,user,pwd);
        System.out.printf("Connection"+con);

    }
}
  • jar包的导入,只要把对应的项目中导入对应要操作数据库的jar包

  • 加载驱动,其实就是将对应的数据库的类加载到我们的内存中

  • 通过我们的驱动对象获取我们的连接对象,配置相关的数据库信息

    • url 类似我们的http协议的请求形式

      • jdbc:mysql://类似http://
      • localhost:3306 对应ip地址和端口
      • fruitdb对应着我们的数据库,我们的http中是一个文件路径来找到对应的资源
      • ?表示连接我们的设置,设置的形式是key:value这种形式,多个参数用&连接

    在这里插入图片描述

    • user 是我们的登录用户的账号

    • pwd 是我们登录用户的密码

JBDC的基本操作

添加操作

package com.lsc.jdbc;

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

/**
 * 实现JDBC的添加操作
 */
public class Demo2 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.jdbc.Driver");

        String url="jdbc:mysql://localhost:3306/fruitdb?characterEncoding=utf8&useSSL=false";
        String user="root";
        String pwd="*****";
        Connection connection= DriverManager.getConnection(url,user,pwd);
        //1编写sql语句
        String sql="insert into t_fruit values(0,?,?,?,?)";
        //2创建预处理命令对象
        PreparedStatement pst=connection.prepareStatement(sql);
        //3填充参数
        pst.setString(1,"菠萝蜜");
        pst.setInt(2,15);
        pst.setInt(3,100);
        pst.setString(4,"这是菠萝蜜");
        //4执行操作
        int count=  pst.executeUpdate();//对于增删改 我们使用executeUpdate 返回的是行数
        System.out.println(count>0?"添加成功":"添加失败");
        //5释放资源
        pst.close();
        connection.close();
    }
}

  • 添加操作使用的执行方法的executeUpdate(),返回值是对数据库影响的行数

修改操作

package com.lsc.jdbc;

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

/**
 * 添加操作
 */
public class Demo3 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Class.forName("org.gjt.mm.mysql.Driver");
        String url="jdbc:mysql://localhost:3306/fruitdb?characterEncoding=utf8&useSSL=false";
        String user="root";
        String pwd="******";
        Connection connection= DriverManager.getConnection(url,user,pwd);
        //编写sql语句
        String sql = "update t_fruit set fname = ? , remark = ? where fid = ? " ;
        PreparedStatement ps=connection.prepareStatement(sql);
        Fruit fruit=new Fruit(33,"猕猴桃","这是一个猕猴桃");
        ps.setString(1,fruit.getFname());
        ps.setString(2,fruit.getRemark());
        ps.setInt(3,fruit.getFid());
        int count=ps.executeUpdate();
        System.out.println(count>0?"修改成功":"修改失败");
        //5释放资源
        ps.close();
        connection.close();
    }
}

  • 修改操作使用的执行方法的executeUpdate(),返回值是对数据库影响的行数

删除操作

/**
 * 删除操作
 */
public class Demo4 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Class.forName("org.gjt.mm.mysql.Driver");
        String url="jdbc:mysql://localhost:3306/fruitdb?characterEncoding=utf8&useSSL=false";
        String user="root";
        String pwd="*****";
        Connection connection= DriverManager.getConnection(url,user,pwd);
        //编写sql语句
        String sql = "delete  from t_fruit where fid= ?" ;
        PreparedStatement ps=connection.prepareStatement(sql);
        Fruit fruit=new Fruit(33,"猕猴桃","这是一个猕猴桃");
        ps.setInt(1,fruit.getFid());
        int count=ps.executeUpdate();
        System.out.println(count>0?"修改成功":"修改失败");
        //5释放资源
        ps.close();
        connection.close();
    }
}

  • 删除操作使用的执行方法的executeUpdate(),返回值是对数据库影响的行数

查询操作

/**
 * 查询操作
 */
public class Demo5 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Class.forName("org.gjt.mm.mysql.Driver");
        String url="jdbc:mysql://localhost:3306/fruitdb?characterEncoding=utf8&useSSL=false";
        String user="root";
        String pwd="*****";
        //获得连接
        Connection connection= DriverManager.getConnection(url,user,pwd);
        //编写sql语句
        String sql="select * from t_fruit";
        //获取预处理对象
        PreparedStatement ps=connection.prepareStatement(sql);
        //获得结果集
        ResultSet rs= ps.executeQuery();
        //解析结果集
        while (rs.next()){
            int fid=rs.getInt("fid");
            String fname=rs.getString("fname");
            int price=rs.getInt("price");
            int fcount=rs.getInt("fcount");
            String remark=rs.getString("remark");
            Fruit fruit=new Fruit(fid,fname,price,fcount,remark);
            System.out.println(fruit);
        }
        //7.释放资源
        rs.close();
        ps.close();
        connection.close();
    }
}

  • 查询操作使用的执行方法的executeQuery(),返回值是我们的ResultSet,里面存储着我们的数据

image-20221208211146786

  • 我们返回的ResultSet相当于这个橙色的箭头,next一下,箭头就会往下滑动,如果到头了,就会返回false

JDBC使用的步骤

  1. 导入相关数据库的jar包到我们的项目中

  2. 加载JDBC驱动程序

     Class.forName("org.gjt.mm.mysql.Driver");
    
  3. 获取数据库的连接

    String url="jdbc:mysql://localhost:3306/fruitdb?characterEncoding=utf8&useSSL=false";
    String user="root";
    String pwd="******";
    Connection connection= DriverManager.getConnection(url,user,pwd);
    
  4. 编写我们的SQL语句

  5. 获取预处理对象

    // 查询操作
    preparedStatement.executeQuery(sql);
    // 新增、修改、删除操作
    preparedStatement.executeUpdate(sql);
    
  6. 获得结果并处理

    // 新增、修改、删除操作 返回的是一个整数
    // 查询操作 返回的是ResultSet对象
    while (resultSet.next()) {
    	int xxx = resultSet.getInt("xxx");
    	String yyy= resultSet.getString("yyy");
    	...
    }
    
  7. 关闭资源

    try {
    	if(resultSet != null){
    		resultSet.close();
    	}
    	if(preparedStatement != null){
    		preparedStatement.close();
    	}
    	if(connection != null){
    		connection.close();
    	}
    } catch (SQLException e) {
    	e.printStackTrace();
    	throw new RuntimeException("数据库错误");
    }
    

JDBC的批处理操作

/**
 * 实现JDBC的批处理添加操作
 */
public class Demo1Batch {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.jdbc.Driver");
        //批处理操作1 如果要进行批处理任务,URL需要添加一个参数rewriteBatchedStatements=true
        String url="jdbc:mysql://localhost:3306/fruitdb?characterEncoding=utf8&useSSL=false&rewriteBatchedStatements=true";
        String user="root";
        String pwd="*****";
        Connection connection= DriverManager.getConnection(url,user,pwd);
        //1编写sql语句
        String sql="insert into t_fruit values(0,?,?,?,?)";
        //2创建预处理命令对象
        PreparedStatement pst=connection.prepareStatement(sql);
        //3批处理填充参数
        for(int i=0;i<10;i++){
            pst.setString(1,"批处理水果"+i);
            pst.setInt(2,15);
            pst.setInt(3,100);
            pst.setString(4,"这是批处理水果");
            //批处理操作2 addBatch()
            pst.addBatch();
            //如果批处理的次数太多,进行清空一次队列再进行
            if(i%1000==0){
                pst.executeBatch();
                pst.clearBatch();
            }
        }

        //批处理执行操作3
        int [] count=pst.executeBatch();
        for (int i: count) {
            System.out.println(i);
        }
        //5释放资源
        pst.close();
        connection.close();
    }
}

  • 对于我们的批处理操作,我们的url必须要配置这个参数rewriteBatchedStatements=true
  • 利用循环给我们的批处理去填充参数 addBatch()
  • 进行执行批处理操作 executeBatch()

JDBC数据源连接池

先导入对应连接池的jar包

在这里插入图片描述

package com.lsc.jdbc;

import com.alibaba.druid.pool.DruidDataSource;

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

//验证连接池中的connection可以重复使用
public class Demo02Druid {
    public static void main(String[] args) throws SQLException, SQLException {

        DruidDataSource dataSource = new DruidDataSource();

        dataSource.setDriverClassName("org.gjt.mm.mysql.Driver");
        dataSource.setUrl("jdbc:mysql://localhost:3306/fruitdb?useSSL=false&useUnicode=true&characterEncoding=utf-8");
        dataSource.setUsername("root");
        dataSource.setPassword("123456");

        //证明两点:
        //1. 被close的连接对象并没有真正关闭,而是将状态重新设置为空闲状态,然后放回池子,这样下次获取连接对象,这个对象会被重复使用
        //2. 没有close的连接对象会被一直占用,那么下次继续获取连接对象,是不会获取到这个对象的(hashcode没有重复,只出现一次)
        for(int i = 0 ; i<5 ; i++){
            Connection conn1 = dataSource.getConnection();
            Connection conn2 = dataSource.getConnection();

            System.out.println(conn1);
            System.out.println(conn2);

            if(i%3==0){
                conn1.close();
                conn2.close();
            }
        }
    }
}
//输出结果
1com.mysql.jdbc.JDBC4Connection@2957fcb0
2com.mysql.jdbc.JDBC4Connection@1376c05c
    
3com.mysql.jdbc.JDBC4Connection@1376c05c
4com.mysql.jdbc.JDBC4Connection@2957fcb0
    
com.mysql.jdbc.JDBC4Connection@51521cc1
com.mysql.jdbc.JDBC4Connection@1b4fb997
    
com.mysql.jdbc.JDBC4Connection@deb6432
com.mysql.jdbc.JDBC4Connection@28ba21f3
    
com.mysql.jdbc.JDBC4Connection@28ba21f3
com.mysql.jdbc.JDBC4Connection@deb6432


  • 我们通过结果可以发现1和4的数据源是一样的
    • 被close的连接对象并没有真正关闭,而是将状态重新设置为空闲状态,然后放回池子,这样下次获取连接对象,这个对象会被重复使用
    • 没有close的连接对象会被一直占用,那么下次继续获取连接对象,是不会获取到这个对象的(hashcode没有重复,只出现一次)

两种方式

DateSource方式(实际都是使用这种)

  • 在内部创建Connection对象的连接池,池就是为了资源重复利用,当一个Connection对象调用close方法关闭不是真正关闭,这个对象会被DateSource回收,进入连接池,若此时有别的用户需要建立连接,不是创建一个新的连接,若此时连接池中有空闲连接,直接使用此链接,也就是不需要关闭物理连接,只重置,重置初始化后放入线程池
  • 市面上有不同的连接池实现方式

DriverMannger方式

  • DriverManager 类来获取的 Connection 连接,是无法重复利用的,每次使用完以后释放资源时,通过 connection.close() 都是关闭物理连接。

关于执行sql语句的对象

Statement

  • 用于执行不带参数的简单SQL语句 比如select * from p

PreparedStatement——预处理对象

  1. 用于执行带或者不带参数的SQL语句
    • 可以使用占位符 ? 下标从1开始
  2. SQL语句会预编译在数据库系统
  3. 执行速度快于Statement对象
    • 这个sql语句就会保存到mysql系统中,这个SQL语句的语法和词法分析都已经执行过了,下一次使用时候,只是简单的替换内容即可
//编写sql语句
String sql="select * from t_fruit";
//获取预处理对象
PreparedStatement ps=connection.prepareStatement(sql);
//获得结果集
ResultSet rs= ps.executeQuery();
  • 比statement更加安全,可以阻止常见的sql注入攻击
public class Demo06 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Class.forName("com.mysql.jdbc.Driver");
 
  Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/fruitdb?				    useSSL=false&useUnicode=true&characterEncoding=utf-8","root","123456");
        //String fname = "西瓜";
        String fname = "西瓜' or 1=1 or fname='";
        String sql = "select * from t_fruit where fname = '" + fname +"'";

        System.out.println(sql);

        Statement stmt = conn.createStatement() ;
        ResultSet rs = stmt.executeQuery(sql);

        while(rs.next()){
            System.out.println(rs.getInt(1));
            System.out.println(rs.getString("fname"));
            System.out.println(rs.getInt(3));
            System.out.println(rs.getInt("fcount"));
            System.out.println(rs.getString("remark"));
        }
    }
}
//我们这个sql语句就把所有的信息都查出来,因为1=1是恒成立的

在这里插入图片描述

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

库里不会投三分

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

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

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

打赏作者

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

抵扣说明:

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

余额充值