JDBC开荒

news2024/11/18 11:23:27

docker 创建MySQL

一、简介

Java DataBase Connectivity ,是Java程序访问数据库的标准接口
Java访问DB的时候,并不是直接通过TCP连接的,而是通过JDBC接口,而JDBC接口又是通过JDBC驱动来访问的
JDBC是Java标准库自带的,具体的JDBC驱动是由数据库厂商提供的,所以JBDC借口都是统一的

┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
│  ┌───────────────┐  │
   │   Java App    │
│  └───────────────┘  │
           │
│          ▼          │
   ┌───────────────┐
│  │JDBC Interface │◀─┼─── JDK
   └───────────────┘
│          │          │
           ▼
│  ┌───────────────┐  │
   │ MySQL Driver  │◀───── Oracle
│  └───────────────┘  │
           │
└ ─ ─ ─ ─ ─│─ ─ ─ ─ ─ ┘
           ▼
   ┌───────────────┐
   │     MySQL     │
   └───────────────┘

一个MySQL的驱动就是一个jar包,本身也是纯java编写的,我们自己的代码只需要引用java.sql.*接口,接口再通过MySQL驱动的jar包访问DB

┌ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┐
   ┌───────────────┐
│  │   App.class   │  │
   └───────────────┘
│          │          │
           ▼
│  ┌───────────────┐  │
   │  java.sql.*   │
│  └───────────────┘  │
           │
│          ▼          │
   ┌───────────────┐     TCP    ┌───────────────┐
│  │ mysql-xxx.jar │──┼────────▶│     MySQL     │
   └───────────────┘            └───────────────┘
└ ─ ─ ─ ─ ─ ─ ─ ─ ─ ─ ┘
          JVM

二、JDBC查询

2.1 驱动

JDBC是一套标准的规范接口,在 java.sql 下 , 具体的实现类在厂家提供的驱动中
maven 添加驱动

        <!-- https://mvnrepository.com/artifact/com.mysql/mysql-connector-j -->
        <dependency>
            <groupId>com.mysql</groupId>
            <artifactId>mysql-connector-j</artifactId>
            <version>8.0.31</version>
        </dependency>

创造一些测试数据

-- 创建数据库learjdbc:
DROP DATABASE IF EXISTS learnjdbc;
CREATE DATABASE learnjdbc;

-- 创建登录用户learn/口令learnpassword
CREATE USER IF NOT EXISTS learn@'%' IDENTIFIED BY 'learnpassword';
GRANT ALL PRIVILEGES ON learnjdbc.* TO learn@'%' WITH GRANT OPTION;
FLUSH PRIVILEGES;

-- 创建表students:
USE learnjdbc;
CREATE TABLE students (
  id BIGINT AUTO_INCREMENT NOT NULL,
  name VARCHAR(50) NOT NULL,
  gender TINYINT(1) NOT NULL,
  grade INT NOT NULL,
  score INT NOT NULL,
  PRIMARY KEY(id)
) Engine=INNODB DEFAULT CHARSET=UTF8;

-- 插入初始数据:
INSERT INTO students (name, gender, grade, score) VALUES ('小明', 1, 1, 88);
INSERT INTO students (name, gender, grade, score) VALUES ('小红', 1, 1, 95);
INSERT INTO students (name, gender, grade, score) VALUES ('小军', 0, 1, 93);
INSERT INTO students (name, gender, grade, score) VALUES ('小白', 0, 1, 100);
INSERT INTO students (name, gender, grade, score) VALUES ('小牛', 1, 2, 96);
INSERT INTO students (name, gender, grade, score) VALUES ('小兵', 1, 2, 99);
INSERT INTO students (name, gender, grade, score) VALUES ('小强', 0, 2, 86);
INSERT INTO students (name, gender, grade, score) VALUES ('小乔', 0, 2, 79);
INSERT INTO students (name, gender, grade, score) VALUES ('小青', 1, 3, 85);
INSERT INTO students (name, gender, grade, score) VALUES ('小王', 1, 3, 90);
INSERT INTO students (name, gender, grade, score) VALUES ('小林', 0, 3, 91);
INSERT INTO students (name, gender, grade, score) VALUES ('小贝', 0, 3, 77);

2.2 连接

使用JDBC 需要首先了解什么是Connection:一个jdbc连接,相当于java程序到数据库的连接,链接DB需要:URL username pw 口令
url:jdbc:mysql://:/?key1=value1&key2=value2

jdbc:mysql://localhost:3306/learnjdbc?useSSL=false&characterEncoding=utf8

useSSL=false&characterEncoding=utf8 不使用SSL加密,使用UTF-8作为字符编码

// JDBC连接的URL, 不同数据库有不同的格式:
String JDBC_URL = "jdbc:mysql://localhost:3306/test";
String JDBC_USER = "root";
String JDBC_PASSWORD = "password";
// 获取连接:
Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
// TODO: 访问数据库...
// 关闭连接:
conn.close();

核心代码是DriverManager 提供的静态方法getConnection()。Driver会自动扫描classpath,找到所有的JDBC驱动,然后根据URL挑选一个合适的驱动

因为JDBC连接是昂贵的资源,用后及时释放 用 try(resource)来自动释放JDBC是一个好方法

try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
    ...
}

2.3 查询

package com.ifeng;

import java.sql.*;

/**
 * Hello world!
 *
 */
public class App 
{
    public static void main( String[] args ) throws SQLException {

        // JDBC连接的URL, 不同数据库有不同的格式:
        String JDBC_URL = "jdbc:mysql://localhost:3306/learnjdbc";
        String JDBC_USER = "root";
        String JDBC_PASSWORD = "123456";

        Connection connection;
        Statement statement = null;
        ResultSet resultSet = null;
        //获取链接
        try {
             connection = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
             statement = connection.createStatement();
             resultSet = statement.executeQuery("select * from students");
            while (resultSet.next()){
                Long id = resultSet.getLong(1);
                String name = resultSet.getString(2);
                String gender = resultSet.getString(3);
                String grade = resultSet.getString(4);
                String score = resultSet.getString(5);
                System.out.println("id = " + id + " , name = " + name + " , gender = " + gender + " , grade = " + grade + " , score = " + score);
            }
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            if(statement != null) statement.close();
            if(resultSet != null) resultSet.close();
        }

    }
}

statement & resultSet 都是稀缺资源,及时关闭
resultSet.next() 用于判断是否有下一行,如果有 自动移入下一行
ResultSet 获取列时,索引从1开始

2.4 SQL注入

使用statement 非常容易引发SQL注入的问题
例如:验证登陆的方法:

User login(String name, String pass) {
    ...
    stmt.executeQuery("SELECT * FROM user WHERE login='" + name + "' AND pass='" + pass + "'");
    ...
}

name & pass 都是从前端传过来的

name = "bob' OR pass=", pass = " OR pass='"

SELECT * FROM user WHERE login='bob' OR pass=' AND pass=' OR pass=''

避免SQL注入,针对所有的字符串进行转义,但很麻烦
还有一个方法就是PreparedStatement :Prepared 始终使用 ? 作为占位符,并且把数据 & SQL 本身传递给DB

User login(String name, String pass) {
    ...
    String sql = "SELECT * FROM user WHERE login=? AND pass=?";
    PreparedStatement ps = conn.prepareStatement(sql);
    ps.setObject(1, name);
    ps.setObject(2, pass);
    ...
}

改造上面的查询

        try {
             connection = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
            PreparedStatement preparedStatement = connection.prepareStatement("select * from students WHERE gender=? AND grade=?");
            preparedStatement.setObject(1,"M"); // 设置占位符的值
            preparedStatement.setObject(2,3);
            resultSet1 = preparedStatement.executeQuery();// 最后仍然是ResultSet
            while (resultSet1.next()){
                Long id = resultSet1.getLong(1);
                String name = resultSet1.getString(2);
                String gender = resultSet1.getString(3);
                String grade = resultSet1.getString(4);
                String score = resultSet1.getString(5);
                System.out.println("id = " + id + " , name = " + name + " , gender = " + gender + " , grade = " + grade + " , score = " + score);
            }
        }

三、JDBC更新

DB操作总结起来就是增删改查,CRUD:Create Retrieve Update Delete
查询用上面的PreparedStatement

3.1 插入

insert ,本质上也是用PreparedStatement执行一条SQL,不过执行者不是executeQuery() 而是executeUpdate()

             connection = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
            PreparedStatement preparedStatement = connection.prepareStatement("INSERT INTO students (id, grade, name, gender, score) VALUES (?,?,?,?,?)");
            preparedStatement.setObject(1,10);
            preparedStatement.setObject(2,3);
            preparedStatement.setObject(3,"Bob");
            preparedStatement.setObject(4,2);
            preparedStatement.setObject(5,101);

            int i = preparedStatement.executeUpdate(); // 使用executeUpdate来更新
            System.out.println("executeUpdate = " + i);

设置参数和查询是一样的,有几个?占位符就设置几个对应参数,
要严格执行不能手动拼接SQL字符串的原则,避免安全漏洞

3.2 插入并获取主键

表设置了自增主键,insert后 数据库会自动分配主键,如何获取主键?
在创建PreparedStatement的时候,指定一个RETURN_GENERATED_KEYS标志位,表示JDBC驱动必须返回插入的自增主键

             connection = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
            PreparedStatement preparedStatement = connection.prepareStatement(
                    "INSERT INTO students (id, grade, name, gender, score) VALUES (?,?,?,?)",
                    Statement.RETURN_GENERATED_KEYS
            );
            preparedStatement.setObject(1,5);
            preparedStatement.setObject(2,"Jerry");
            preparedStatement.setObject(3,2);
            preparedStatement.setObject(4,101);

            int i = preparedStatement.executeUpdate(); // 使用executeUpdate来更新
            try(ResultSet rs = preparedStatement.getGeneratedKeys()){
                if (rs.next()) {
                    System.out.println(rs.getLong(1));
                }
            }

3.3 更新

try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
    try (PreparedStatement ps = conn.prepareStatement("UPDATE students SET name=? WHERE id=?")) {
        ps.setObject(1, "Bob"); // 注意:索引从1开始
        ps.setObject(2, 999);
        int n = ps.executeUpdate(); // 返回更新的行数
    }
}

3.4 删除

try (Connection conn = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD)) {
    try (PreparedStatement ps = conn.prepareStatement("DELETE FROM students WHERE id=?")) {
        ps.setObject(1, 999); // 注意:索引从1开始
        int n = ps.executeUpdate(); // 删除的行数
    }
}

四、事务

DB事务(Transaction)由若干个SQL语句构成一个操作序列,类似Java的synchronized同步,一个SQL要么全部成功,或者全部不成功:ACID

  • Atomicity:原子性
  • Consistency:一致性
  • Isolation:隔离性
  • Durability:持久性

数据库事务可以并发执行,从效率角度,定义了不同的隔离级别
在这里插入图片描述

  • 脏读:A事务读到B事务更新但未提交的数据,B回滚了
  • 不重复读:A事务第一次读取数据后,B事务修改了数据,A又读取数据,两次数据不一致
  • 幻读:A事务查询记录,没有,然后更新这条记录,竟然成功了

在JDBC中执行事物,本质是把多条SQL包裹在一个数据库事务中执行

Connect conn = openConnection();
try{
	//关闭自动提交
	conn.setAutoCommit(false);
	//执行多条SQL
	insert();update();delete;
	//提交事物
	conn.commit();
}catch(SQLException e){
	// 回滚事物;
	conn.rollback();
}

conn.setAtuoCommit(false) 代表自动提交关闭
conn.commit() 手动提交

//可以设定隔离级别,默认REPEATABLE_READ
conn.setTrasactionIsolation(Connection.TRANSACTION_READ_COMMITTED)

五、Batch

批量操作,可通过循环来执行PreparedStatement来执行,但是性能很低。只有参数不同若干语句可以作为batch执行(批量),它有特别的优化,速度远远快于循环执行的每个SQL

public class JDBCTest5
{
    public static void main( String[] args ) throws SQLException {

        List<Student> students = List.of(
                new Student("小红", 20L,3L,48)
                ,new Student("小红2", 20L,3L,49)
                ,new Student("小红3", 20L,3L,50)
                ,new Student("小红4", 20L,3L,49)
                ,new Student("小红5", 20L,3L,55)
        );

        // JDBC连接的URL, 不同数据库有不同的格式:
        String JDBC_URL = "jdbc:mysql://localhost:3306/learnjdbc";
        String JDBC_USER = "root";
        String JDBC_PASSWORD = "123456";

        Connection connection;
        Statement statement = null;
        ResultSet resultSet1 = null;
        //获取链接
        try {
             connection = DriverManager.getConnection(JDBC_URL, JDBC_USER, JDBC_PASSWORD);
            PreparedStatement preparedStatement = connection.prepareStatement(
                    "INSERT INTO students (grade, name, gender, score) VALUES (?,?,?,?)"
            );
            //对于同一个PreparedStatement反复设置参数并调用addBatch()
            for(Student s : students){
                preparedStatement.setObject(1,s.grade);
                preparedStatement.setObject(2,s.name);
                preparedStatement.setObject(3,s.gender);
                preparedStatement.setObject(4,s.score);
                preparedStatement.addBatch();//添加到batch
            }
            //执行batch
            int[] executeBatch = preparedStatement.executeBatch();
            for(int n:executeBatch){
                System.out.println(n + " inserted.");
            }
            
        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            if(statement != null) statement.close();
            if(resultSet1 != null) resultSet1.close();
        }

    }
}

六、连接池

JDBC连接是一种俺贵的资源,创建线程也是一种昂贵的操作,频繁的创建销毁JDBC,会造成大量消耗

JDBC链接池有一个标准的接口 javax.sql.DataSource , 接口在标准库中。要使用JDBC连接池,必须用它的实现类:

  • HikariCP
  • C3P0
  • BoneCP
  • Druid
    使用最广泛的是HikariCP以此为例,先添加HikariCP的依赖
<!-- https://mvnrepository.com/artifact/com.zaxxer/HikariCP -->
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
    <version>2.7.9</version>
</dependency>

配置连接池


        HikariConfig hikariConfig = new HikariConfig();
        hikariConfig.setJdbcUrl("jdbc:mysql://localhost:3306/learnjdbc");
        hikariConfig.setUsername("root");
        hikariConfig.setPassword("123456");
        hikariConfig.addDataSourceProperty("connectionTimeout","1000");//连接超时 1s
        hikariConfig.addDataSourceProperty("idleTimeout","6000");//空闲超时 6s
        hikariConfig.addDataSourceProperty("maximumPoolSize","10");//最大连接数
        HikariDataSource hikariDataSource = new HikariDataSource(hikariConfig);

使用连接池

 connection = hikariDataSource.getConnection();
...

connection.close();

get close 都不是真的创建销毁,而是从连接池中获取 放回

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/128541.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

(HP)next.js入门

推荐文档&#xff1a;生成<head> - 《next.js v7.0 中文文档》 - 书栈网 BookStack 1&#xff0c;解决的问题 SPA单页面应用的两个问题&#xff1a;首屏加载过慢&#xff0c;不能SEO(搜索引擎抓取&#xff09; 2&#xff0c;它是一个react服务端渲染框架 3&#xff0c;…

ArcGIS去除黑边方法汇总

概述 在使用ArcGIS对影像进行应用的时候&#xff0c;如果出现了黑边&#xff0c;除了影响美观之外&#xff0c;进行镶嵌处理也可能会有问题&#xff0c;这里&#xff0c;我们介绍一下几种ArcGIS去除黑边的方法&#xff0c;希望能够对大家有所帮助。 数据来源 教程所使用的实…

【C++进阶】类型转换

&#x1f387;C学习历程&#xff1a;入门 博客主页&#xff1a;一起去看日落吗持续分享博主的C学习历程博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 也许你现在做的事情&#xff0c;暂时看不到成果&#xff0c;但不要忘记&…

蓝桥杯Python练习题8-查找整数

资源限制   内存限制&#xff1a;256.0MB C/C时间限制&#xff1a;1.0s Java时间限制&#xff1a;3.0s Python时间限制&#xff1a;5.0s 问题描述   给出一个包含n个整数的数列&#xff0c;问整数a在数列中的第一次出现是第几个。 输入格式   第一行包含一个整数n。  …

欢迎谷歌回归中国,但有前提!李彦宏也发了条朋友圈

2018年&#xff0c;Google部分功能成功回归中国大陆 周一&#xff0c;人民日报在海外社交媒体平台Twitter和Facebook上刊文&#xff0c;针对谷歌计划以过滤版搜索引擎重返中国大陆的消息回应称&#xff0c;欢迎谷歌重返中国大陆&#xff0c;但前提是必须遵守中国法律。李彦宏在…

云计算IaaS、PaaS(iPaaS/aPaaS)以及SaaS以及发展趋势

一、云计算IaaS、PaaS以及SaaS架构 云计算涉及了很多产品与技术&#xff0c;表面上看起来的确有点纷繁复杂&#xff0c;但是云计算本身还是有迹可循和有理可依的&#xff0c;下面介绍一套云计算的架构&#xff0c;具体请看图&#xff1a; 上面这个云架构共分为服务和管理这两…

车载入侵检测与防御系统介绍

作者 | 田铮 上海控安可信软件创新研究院项目经理 来源 | 鉴源实验室 引言&#xff1a;上一篇文章&#xff08;智能网联汽车网络安全攻击与防御技术概述&#xff09;介绍了智能网联汽车中的网络安全攻击案例和具体攻击类型。而本篇文章中&#xff0c;我们将对汽车网络安全风险…

SpringSecurity——OAuth2框架鉴权实现源码分析

SpringSecurity——OAuth2框架鉴权实现源码分析一、ManagedFilter迭代过滤器链1.4 springSecurityFilterChain1.4.7 OAuth2AuthenticationProcessingFilter①.OAuth2AuthenticationProcessingFilter.class②.CookieTokenExtractor.class&#xff08;我们自己重写的方法&#xf…

瞄准智慧园区 东方恩拓与用友协同开拓新商机

在数字化转型升级浪潮中&#xff0c;传统园区也在寻求新的发展方向&#xff0c;从传统园区向智慧园区甚至未来园区不断演进。随着国家“数字中国”、“中国智造”、“新基建”等战略的部署&#xff0c;智慧园区也迎来了新的发展机遇&#xff0c;园区的数字化、网络化、智能化是…

户外运动耳机选择哪个、最适合户外运动的蓝牙耳机推荐

还有哪个季节比秋天更适合爬山和徒步等户外运动的吗&#xff1f;秋天—没有夏日的骄阳&#xff0c;没有冬天的万物凋零&#xff0c;放眼望去都是墨绿和金黄&#xff0c;上山的话还可以采摘成熟的各种各样的果子…但是一个人的话难免有些落寞&#xff0c;要是有音乐的陪伴则会增…

公网远程连接windows SQL Server数据库【内网穿透】

文章目录1. 本地安装配置SQL Server2. 将本地sqlserver服务暴露至公网2.1 本地安装cpolar内网穿透2.2 创建隧道3. 公网远程连接sqlserver3.1 使用命令行远程连接sqlserver3.2 使用navicat premium图形界面远程连接sqlserver3.3 使用SSMS图形界面远程连接sqlserver疫情当下,居家…

win7电脑怎么录屏?超级简单的2种电脑录屏方法

相信还有不少朋友的电脑系统是win7系统。其实&#xff0c;win7电脑和win10电脑一样都有自带录屏功能。那win7电脑怎么录屏&#xff1f;在win7电脑上开启运行窗口即可&#xff0c;当然也可以通过使用专业的录屏软件来录制屏幕。 下面就由小编给大家介绍一下2个win7电脑录屏的方…

P8 PyTorch WhereGather

前言 这两个函数优点是通过GPU 运算速度快 目录&#xff1a; 1 where 2 Gather 一 where 原理&#xff1a; torch.where(condition,x,y) 输入参数&#xff1a; condition: 判断条件 x,y: Tensor 返回值&#xff1a; 符合条件时: 取x, 不满足取y 优点&#xff1a; 可以使…

关注re:Invent中国巡展,尽享数字时代红利

编辑&#xff5c;阿冒收获固然很甜蜜&#xff0c;但是收获也很辛苦。肯定会有人感觉莫名其妙&#xff0c;既然是收获&#xff0c;必然是甜蜜的&#xff0c;哪来的辛苦啊&#xff1f;且不要着急&#xff0c;容我慢慢分说。一年一度的亚马逊云科技re:Invent全球大会&#xff0c;向…

【操作系统】磁盘调度算法

文章目录影响其访问的时间因素磁盘调度&#xff08;移臂调度&#xff09;常见的磁盘调度算法1、先来先服务算法&#xff08;FCFS&#xff09;2、最短寻道时间优先算法&#xff08;SSTF&#xff09;3、电梯调度算法&#xff08;扫描算法SCAN&#xff09;4、循环扫描算法&#xf…

Microsoft Office 2016 VOL版下载

链接都是VOL版&#xff0c;和零售版功能是一样的&#xff0c;只是激活方便一些&#xff0c;三个下载链接&#xff0c;第一个是Office就是包含了Word、Excel、PPT那些的&#xff0c;另外两个一个是Visio&#xff0c;一个是Project&#xff0c;如果不需要的话&#xff0c;只下载第…

【HTML5】复习(二)

HTML5复习二1.代码一2.代码二3.CSS的引入方式4.选择器5.form表单的一些属性6.内联7. 音频视频8. 滑块、搜索、数字、URL9. 表单补充1.代码一 <!DOCTYPE html> <html><head><meta charset"utf-8"><title></title></head>&…

声明式事务的属性之隔离级别

声明式事务的属性之隔离级别 ①介绍 数据库系统必须具有隔离并发运行各个事务的能力&#xff0c;使它们不会相互影响&#xff0c;避免各种并发问题。一个事务与其他事务隔离的程度称为隔离级别。SQL标准中规定了多种事务隔离级别&#xff0c;不同隔离级别对应不同的干扰程度&…

智能家居创意DIY之智能灯泡

一、什么是智能灯 传统的灯泡是通过手动打开和关闭开关来工作。有时&#xff0c;它们可以通过声控、触控、红外等方式进行控制&#xff0c;或者带有调光开关&#xff0c;让用户调暗或调亮灯光。 智能灯泡内置有芯片和通信模块&#xff0c;可与手机、家庭智能助手、或其他智能…

12.29日报

今天完成了数据库TestMrl的增删改查四个接口的开发&#xff0c;测试。 测试接口getQRcodeandscene 遇到的问题及解决 不知道在mapper中的增删改方法返回值int的值&#xff0c;和含义&#xff0c;在调用方法时也没有定义int来接参&#xff0c;都是直接调用。于是我定义int i&…