JDBC的使用笔记

下午花了三四个小时,在慕课网上看了看关于JDBC的两个系列的视频,跟着做了做。因为之前也看过,所以一直都记得有这么一个视频存在,但是唯一不足的是没写上一些笔记。写个简易的学习笔记,加深一下对其中一些知识的理解,以后翻起来就不用去看视频了,看这个就好。还记录下自己在学习过程中所遇到的一些疑问以及自己所

下午花了三四个小时,在慕课网上看了看关于JDBC的两个系列的视频,跟着做了做。因为之前也看过,所以一直都记得有这么一个视频存在,但是唯一不足的是没写上一些笔记。写个简易的学习笔记,加深一下对其中一些知识的理解,以后翻起来就不用去看视频了,看这个就好。还记录下自己在学习过程中所遇到的一些疑问以及自己所找到的答案。

视频链接如下:

JDBC之“对岸的女孩看过来”JDBC之“对岸的女孩走过来”

JDBC是什么

JDBC(Java DataBase Connectivity,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成。从根本上说,JDBC是一种规范,它提供的接口,一套完整的,可移植的访问底层数据库的程序。具体实现由数据库厂商实现。

虽然在实践中,不会使用这个原始的工具,但是了解一下还是有一些好处的呢。

JDBC使用基本步骤

这里写图片描述

用自己的话描述复述如下:

  1. 注册驱动
  2. 获取连接
  3. 通过连接执行SQL

代码示例:

加载驱动与获取数据库连接

package db;
import java.sql.*;
public class DBUtil {
    private static final String URL = "jdbc:mysql://127.0.0.1:3306/imooc";
    private static final String NAME = "root";
    private static final String PASSWORD = "root";
    private static Connection conn;
    static {
        try {
            // 加载驱动程序
            Class.forName("com.mysql.jdbc.Driver");
            // 获得数据库连接
            conn = DriverManager.getConnection(URL, NAME, PASSWORD);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
    public static Connection getConnection(){
        return conn;
    }
}

执行SQL

public void deleteGoddess(int id) throws SQLException {
    Connection conn = DBUtil.getConnection();
    String sql = "" +
            " delete from imooc_goddess" +
            " where id = ?";
    PreparedStatement ptmt = conn.prepareStatement(sql);
    ptmt.setInt(1, id);
    ptmt.execute();
}

基本的CURD

package dao;

import db.DBUtil;
import model.Goddess;

import java.sql.*;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

public class GoddessDao {

    public void addGoddess(Goddess goddess) throws SQLException {
        Connection conn = DBUtil.getConnection();
        String sql = "" +
                "insert into imooc_goddess" +
                "(user_name, sex, age, birthday, email, mobile, "+
                "create_user, create_date, update_user, update_date, isdel)" +
                "values("+
                "?, ?, ?, ?, ?, ?, ?, current_date(), ?, current_date(), ?)";
        PreparedStatement ptmt = conn.prepareStatement(sql);
        ptmt.setString(1, goddess.getUser_name());
        ptmt.setInt(2, goddess.getSex());
        ptmt.setInt(3, goddess.getAge());
        ptmt.setDate(4, new Date(goddess.getBirthday().getTime()));
        ptmt.setString(5, goddess.getEmail());
        ptmt.setString(6, goddess.getMobile());
        ptmt.setString(7, goddess.getCreate_user());
        ptmt.setString(8, goddess.getUpdate_user());
        ptmt.setInt(9, goddess.getIsdel());
        ptmt.execute();
    }

    public void updateGoddess(Goddess goddess) throws SQLException {
        Connection conn = DBUtil.getConnection();
        String sql = "" +
                " update imooc_goddess" +
                " set user_name=?, sex=?, age=?, birthday=?, email=?, mobile=?, "+
                " update_user=?, update_date=current_date(), isdel=?"+
                " where id = ?";
        PreparedStatement ptmt = conn.prepareStatement(sql);
        ptmt.setString(1, goddess.getUser_name());
        ptmt.setInt(2, goddess.getSex());
        ptmt.setInt(3, goddess.getAge());
        ptmt.setDate(4, new Date(goddess.getBirthday().getTime()));
        ptmt.setString(5, goddess.getEmail());
        ptmt.setString(6, goddess.getMobile());
        ptmt.setString(7, goddess.getUpdate_user());
        ptmt.setInt(8, goddess.getIsdel());
        ptmt.setInt(9, goddess.getId());
        ptmt.execute();
    }

    public void deleteGoddess(int id) throws SQLException {
        Connection conn = DBUtil.getConnection();
        String sql = "" +
                " delete from imooc_goddess" +
                " where id = ?";
        PreparedStatement ptmt = conn.prepareStatement(sql);
        ptmt.setInt(1, id);
        ptmt.execute();
    }

    public List<Goddess> query() throws SQLException {
        List<Goddess> gs = new ArrayList<>();
        Connection conn = DBUtil.getConnection();
        Statement stmt = conn.createStatement();
        StringBuilder sb = new StringBuilder("select * from imooc_goddess");
        ResultSet rs = stmt.executeQuery(sb.toString());

        Goddess g = null;
        while (rs.next()){
            g = new Goddess();
            g.setId(rs.getInt("id"));
            g.setUser_name(rs.getString("user_name"));
            g.setAge(rs.getInt("age"));
            g.setSex(rs.getInt("sex"));
            g.setBirthday(rs.getDate("birthday"));
            g.setEmail(rs.getString("email"));
            g.setMobile(rs.getString("mobile"));
            g.setCreate_date(rs.getDate("create_date"));
            g.setCreate_user(rs.getString("create_user"));
            g.setUpdate_date(rs.getDate("update_date"));
            g.setUpdate_user(rs.getString("update_user"));
            g.setIsdel(rs.getInt("isdel"));
            gs.add(g);
        }
        return gs;
    }

    public List<Goddess> query(List<Map<String, Object>> params) throws SQLException {
        List<Goddess> gs = new ArrayList<>();
        Connection conn = DBUtil.getConnection();
        // 这里有点厉害
        StringBuilder sb = new StringBuilder("select * from imooc_goddess where 1=1");
        if (params != null && params.size() > 0){
            for (int i = 0; i < params.size(); i++) {
                Map<String, Object> map = params.get(i);
                sb.append(" and " + map.get("name") + " " + map.get("rela") + " " + map.get("value"));
            }
        }
        System.out.println(sb.toString());
        PreparedStatement ptmt = conn.prepareStatement(sb.toString());
        ResultSet rs = ptmt.executeQuery();

        Goddess g = null;
        while (rs.next()){
            g = new Goddess();
            g.setUser_name(rs.getString("user_name"));
            g.setAge(rs.getInt("age"));
            g.setSex(rs.getInt("sex"));
            g.setBirthday(rs.getDate("birthday"));
            g.setEmail(rs.getString("email"));
            g.setMobile(rs.getString("mobile"));
            g.setCreate_date(rs.getDate("create_date"));
            g.setCreate_user(rs.getString("create_user"));
            g.setUpdate_date(rs.getDate("update_date"));
            g.setUpdate_user(rs.getString("update_user"));
            g.setIsdel(rs.getInt("isdel"));
            gs.add(g);
        }
        return gs;
    }

    public Goddess get(int id) throws SQLException {
        Connection conn = DBUtil.getConnection();
        String sql = "" +
                " select * from imooc_goddess" +
                " where id = ?";
        PreparedStatement ptmt = conn.prepareStatement(sql);
        ptmt.setInt(1, id);

        ResultSet rs = ptmt.executeQuery();
        Goddess g = null;
        while (rs.next()){
            g = new Goddess();
            g.setUser_name(rs.getString("user_name"));
            g.setAge(rs.getInt("age"));
            g.setSex(rs.getInt("sex"));
            g.setBirthday(rs.getDate("birthday"));
            g.setEmail(rs.getString("email"));
            g.setMobile(rs.getString("mobile"));
            g.setCreate_date(rs.getDate("create_date"));
            g.setCreate_user(rs.getString("create_user"));
            g.setUpdate_date(rs.getDate("update_date"));
            g.setUpdate_user(rs.getString("update_user"));
            g.setIsdel(rs.getInt("isdel"));
        }
        return g;
    }
}

MVC架构

其实这就是我为啥想看这个系列视频的理由咯。都知道M(Model)、V(View)、C(Controller),但是在代码中是如何体现呢?非常想知道这一点。

这里写图片描述

在“本系列视频·上”中,V是使用命令行的黑窗口,通过黑窗口来输入命令。命令给到控制层后,由控制层进行相应的操作;而控制层则是通过调用dao来直接与数据库交流。其中黑窗口(View层)可学习的价值并不是很高。

学习时遇到的疑问

为什么执行Class.forName("xxxxx")驱动就加载好了?

com.mysql.jdbc.Driver中,有一段静态代码,就是向DriverManager注册自身这个驱动。JDBC的规范吧,都需要这么写。

static {
    try {
        DriverManager.registerDriver(new Driver());
    } catch (SQLException var1) {
        throw new RuntimeException("Can't register driver!");
    }
}
Registers the given driver with the {@code DriverManager}.A newly-loaded driver class should call the method {@code registerDriver} to make itself known to the {@code DriverManager}. If the driver is currently registered, no action is taken.
public static synchronized void registerDriver(java.sql.Driver driver,DriverAction da)
    throws SQLException {
    /* Register the driver if it has not already been added to our list */
    if(driver != null) {
        registeredDrivers.addIfAbsent(new DriverInfo(driver, da));
    } else {
        // This is for compatibility with the original DriverManager
        throw new NullPointerException();
    }
    println("registerDriver: " + driver);
}

后面的获取连接是通过DriverManager的getConnection方法,通过此方法会调用其同名不同参数方法getConnection(String url, java.util.Properties info, Class<?> caller),其中有段源代码是这样的:

// 容器registeredDrivers则是保存着我们在上面代码中所注册的所有驱动的相关信息
for(DriverInfo aDriver : registeredDrivers) {
    // If the caller does not have permission to load the driver then
    // skip it.
    if(isDriverAllowed(aDriver.driver, callerCL)) {
        try {
            println("    trying " + aDriver.driver.getClass().getName());
            Connection con = aDriver.driver.connect(url, info);
            if (con != null) {
                // Success!
                println("getConnection returning " + aDriver.driver.getClass().getName());
                //如果获取到的连接不为空,那么则通过其所需要的驱动,获取连接成功,并返回。
                return (con);
            }
        } catch (SQLException ex) {
            if (reason == null) {
                reason = ex;
            }
        }
    } else {
        println("    skipping: " + aDriver.getClass().getName());
    }
}

因此,Class.forName(xxxx)在加载该类时触发了其中的static代码块,并在其中向DriverManager注册自身。然后通过DriverManager来遍历所有的驱动信息,并用之加载相应的url,如果成功获取到连接,表示驱动加载成功,并获取到了连接;否则,驱动加载失败或尚未注册驱动,连接获取失败。

PreparedStatement与Statement安全性的差异在哪里?

使用Statement可能会被SQL注入。 **假设存在某个需求,对给定的用户id,返回该id所对应的所有信息。言外之意,只有返回相应id的信息才是正确的、可行的,如果获取得到了除了该id之外的信息,那么则将之称为信息泄露也不为过。**然而,使用Statement就可能会被有心机的用户利用,获取到除了自身之外的其它用户信息,这想一想都是不允许也不应该发生的。代码演示如下:

public List<Goddess> query(String id) throws SQLException {
    List<Goddess> gs = new ArrayList<>();
    Connection conn = DBUtil.getConnection();
    Statement stmt = conn.createStatement();
    String sql = "select * from imooc_goddess where id = " + id;
    ResultSet rs = stmt.executeQuery(sql);
    System.out.println(sql);
    Goddess g = null;
    while (rs.next()){
        g = new Goddess();
        g.setId(rs.getInt("id"));
        g.setUser_name(rs.getString("user_name"));
        g.setAge(rs.getInt("age"));
        gs.add(g);
    }
    return gs;
}

public static void main(String[] args) throws SQLException {
	// 别有心机的用户名"1 or 1 = 1"
    List<Goddess> list = new GoddessDao().query("1 or 1 = 1");
    for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i));
    }
}

输出的结果如下

这里写图片描述

JDBC调用存储过程

存储过程是存储在数据库中的一个小代码,其标识符为Procedure,与开发语言中的函数类似。比如说在Pascal中,函数定义的标识符就是Procedure。在该函数中,可以不带参数,也可以带In类型参数、Out类型参数(顾名思义,传入、传出)。如何使用JDBC调用这些过程呢?

无参存储过程调用

过程

这里写图片描述

代码

这里写图片描述

IN类型参数存储过程调用

这里写图片描述

OUT类型参数存储过程调用

这里写图片描述

OUT类型参数的存储过程的定义如下:

这里写图片描述

JDBC使用事务

事务是作为单个逻辑工作单元执行的一些列操作,这些操作做为一个整体,要么都提交、要么都不执行。

这里写图片描述

JDBC中如何控制事务?

这里写图片描述

代码演示如下:

这里写图片描述

JDBC使用连接池

连接池有一点像线程池的意思,作用有点类似。常用的有dbcp和c3p0。

Read more

Volcano 与 Kubernetes GPU 调度学习笔记

本笔记系统整理 Volcano 调度器、Kubernetes 调度框架、GPU Device Plugin、HAMi 等云原生 AI 调度领域的核心知识,适合用于学习、复习和工程实践参考。 目录 * 第一部分:Volcano 入门 * 1. Volcano 是什么 * 2. 安装与快速使用 * 3. 核心特性一览 * 第二部分:Volcano 整体架构 * 4. Volcano 解决的核心问题 * 5. 整体架构与数据流 * 6. 三层抽象模型 * 第三部分:Volcano 核心实现原理 * 7. Session 机制 * 8. Gang Scheduling 实现 * 9. Queue 与 DRF 公平调度

容器镜像(4):镜像的常用工具箱

容器镜像(4):镜像的常用工具箱

前几篇在讲多架构镜像时已经用过 skopeo 和 crane 做镜像复制,这篇系统整理这两个工具的完整能力,同时介绍几个日常操作镜像时同样好用的工具。 一、skopeo:不依赖 Daemon 的镜像瑞士军刀 skopeo 的核心价值是绕过 Docker daemon,直接与 Registry API 交互。上一篇用它做镜像复制和离线传输,但它的能力远不止于此。 1.1 安装 # Ubuntu / Debian sudo apt install -y skopeo skopeo --version # skopeo version 1.15.1 1.2 inspect:免拉取检查镜像元数据 docker inspect 需要先把镜像拉到本地,skopeo inspect 直接向 Registry

容器镜像(3):多架构镜像构建

容器镜像(3):多架构镜像构建

一、什么是多架构镜像 1.1 OCI Image Index 上一篇介绍了单平台镜像的结构:一个 Manifest 指向 Config 和若干 Layer blob。多架构镜像在此之上多了一层——OCI Image Index(也叫 Manifest List),是一个轻量的索引文件,把多个单平台 Manifest 组织在一起: $ docker manifest inspect golang:1.22-alpine { "schemaVersion": 2, "mediaType": "application/vnd.oci.image.index.v1+json", "manifests&

容器镜像(2):containerd 视角下的镜像

容器镜像(2):containerd 视角下的镜像

一、为什么需要了解 containerd 如果你只用 docker run 跑容器,从来不关心底层,那可以不了解 containerd。但如果你在用 Kubernetes,或者想真正理解"容器运行时"是什么,containerd 是绕不开的。 事实上,当你执行 docker run 的时候,containerd 早就在后台悄悄工作了——Docker 从 1.11 版本开始,就把核心运行时剥离出来交给 containerd 负责。 1.1 Docker 的架构演变 早期的 Docker(1.10 及之前)是一个"大一统"的单体程序:一个 dockerd