Skip to content

Latest commit

 

History

History
869 lines (595 loc) · 19 KB

File metadata and controls

869 lines (595 loc) · 19 KB

第26节 maven创建jdbc连接MySQL

❤️💕💕java的学习指南,从入门到大师篇章。Myblog:http://nsddd.top


[TOC]

导入模块

        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>8.0.30</version>
        </dependency>

这个时候就可以导入:

Class.forName("com.mysql.jdbc.Driver");

此时会有异常,IDEA贴心提醒,担心你后面写的东西错了,添加异常到方法签名

package org.example;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException {
        Class.forName("com.mysql.jdbc.Driver");
    }
}

我们需要对数据库进行操作

select * from students;
select * from students where id < 10 and score > 80;
update students set score=88 where id = 1;
select * from students where id = 1;

上面的代码可以将id = 1的学生分数改为88,将sql写入Java代码中:

导入的第二种方法

选择这个jar

image-20220921195107959

添加库

image-20220921195725489

主要代码

package org.example;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class Main {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        System.out.println("Hello world!");
        //注册驱动
        Class.forName("com.mysql.jdbc.Driver");

        //获取连接
        String url = "jdbc:mysql://localhost:49156/learnjdbc";
        String username = "root";
        String password = "123456";
        Connection conn = DriverManager.getConnection(url,username,password);

        //定义sql语句
        String sql = "update students set score=98 where id = 1;";

        //获取执行sql对象statement
        Statement stem = conn.createStatement();

        //执行sql   -- 返回值受影响的行数 -- 如果返回为1 说明1行sql执行成功
        int count = stem.executeUpdate(sql);

        //处理结果
        System.out.println(count);

        //释放资源 -- 先释放stml再释放conn
        stem.close();
        conn.close();
    }
}

执行代码:

Hello world!
1
进程已结束,退出代码0

我们马上去查询数据库 可以看出来数据会更新的

select * from students where id = 1

id|name|gender|grade|score|
--+----+------+-----+-----+
 1|小贝  |     0|    3|   88|

🔦 到目前为止,算是顺利执行了,有一点需要提醒,就是需要在

JDBC API详解

我们需要先去了解一下jdk官方文档,上面对各个接口有很多解释

JDBC结构

  • DriverManager类:依据数据库的不同,管理JDBC驱动
  • Connection接口:负责连接数据库并且担任传送数据的任务
  • Statement接口:由Connection产生、负责执行SQL语句
  • ResultSet接口:从数据源读取数据

DriverManager作用

  1. 注册驱动
  2. 获取数据库连接

注册驱动

//注册驱动
Class.forName("com.mysql.jdbc.Driver");

5.0版本之后可以省略,因为自带了,不需要再写了

获取数据库连接

/获取连接
String url = "jdbc:mysql://localhost:49156/learnjdbc";
String username = "root";
String password = "123456";
Connection conn = DriverManager.getConnection(url,username,password);
  • jdbc:mysql://localhost:49156/learnjdbc:如果是本地且3306,那么可以简写jdbc:mysql:///(我的是连接docker)
  • 配置useSSL=false参数,禁止用安全连接方式,解决警告提醒

Connection数据库连接对象

 //获取执行sql对象statement
 Statement stem = conn.createStatement();
  • 执行SQL的对象
  • 管理事务

普通执行SQL对象

Statement createStatement()

预编译SQL的执行SQL对象:防止SQL注入

PreparedStatement createStatement()

执行存储过程的对象

CallableStatement prepareCall(sql)

事务管理

事务操作:

mysql默认自动提交事务

  • 开启事务:begin;/start transaction
  • 提交事务:commit
  • 回滚事务:rollback

JDBC事务管理:Connection接口中定义了3个对应方法

  • 开启事务:setAutoCommit(boolean autoCommit)
    • ture : 自动提交
    • false: 手动提交
  • 提交事务:commit
  • 回滚事务:rollback()

jdbc的事务操作

有提个问题:当你出现错误,如何检测错误并且回滚?

我们使用try - catch捕获

 // 事务  -- 下面两条数据要么同时成功,要么同时失败

try {
     //开启事务
     conn.setAutoCommit(false);
     //执行代码
     int count1 = stem.executeUpdate(sq2);
     System.out.println(count1);

     int count2 = stem.executeUpdate(sql2);
     System.out.println(count2);
     
     //提交事务
     conn.commit();
 } catch (SQLException e) {
     //回滚事务
     conn.rollback();  
     throw new RuntimeException(e);
 }

完整代码

package org.example;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;
import java.sql.Statement;

public class jdbc1 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        System.out.println("Hello world!");
        //注册驱动
        // Class.forName("com.mysql.jdbc.Driver");

        //获取连接
        String url = "jdbc:mysql://localhost:49156/learnjdbc";
        String username = "root";
        String password = "123456";
        Connection conn = DriverManager.getConnection(url,username,password);

        //定义sql语句
        String sql1 = "update students set score=100 where id = 1;";
        String sql2 = "update students set score=100 where id = 2;";
        //获取执行sql对象statement
        Statement stem = conn.createStatement();

        // 事务  -- 下面两条数据要么同时成功,要么同时失败

        try {
            //开启事务
            conn.setAutoCommit(false);
            //执行代码
            int count1 = stem.executeUpdate(sql2);
            System.out.println(count1);

            int count2 = stem.executeUpdate(sql2);
            System.out.println(count2);

            //提交事务
            conn.commit();
        } catch (SQLException e) {
            //回滚事务
            conn.rollback();
            throw new RuntimeException(e);
        }
        //提交事务

        //释放资源 -- 先释放stml再释放conn
        stem.close();
        conn.close();
    }
}

执行:

Hello world!
1
1

进程已结束,退出代码0
1	小贝	0	3	100
2	小强	0	2	100

甚至,我们可以改一下:

try {
    //开启事务
    conn.setAutoCommit(false);
    //执行代码
    int count1 = stem.executeUpdate(sql2);
    System.out.println(count1);
    int aa = 123/0;
    int count2 = stem.executeUpdate(sql2);
    System.out.println(count2);

    //提交事务
    conn.commit();
} catch (SQLException e) {
    //回滚事务
    conn.rollback();
    throw new RuntimeException(e);
} finally {
    System.out.println("hello word , 执行完成 ");
    //释放资源 -- 先释放stml再释放conn
    stem.close();
    conn.close();
}

finally是后面必须要执行的, int aa = 123/0;是一个溢出错误,这个溢出是静态语言是没办法检测出来的

Statement提供方法执行sql

int count1 = stem.executeUpdate(sql1);
System.out.println(count1);
int count2 = stem.executeUpdate(sql2);
System.out.println(count2);

执行sql语句

int executeUpdate(sql):执行DMLDDL语句;

返回值:

  • DML语句影响的行数
  • DDL语句执行后, 执行成功也可能返回0

DML 语句

我们可以写一个测试类

package org.example;
import org.junit.Test;

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

public class Jdbc2 {
    @Test
    public void testDML() throws SQLException {
        System.out.println("Hello world!");
        //注册驱动
        // Class.forName("com.mysql.jdbc.Driver");

        //获取连接
        String url = "jdbc:mysql://localhost:49156/learnjdbc";
        String username = "root";
        String password = "123456";
        Connection conn = DriverManager.getConnection(url,username,password);

        //定义sql语句
        String sql = "update students set score=10000 where id = 1;";
        //获取执行sql对象statement
        Statement stem = conn.createStatement();

        //执行sql   -- 返回值受影响的行数 -- 如果返回为1 说明1行sql执行成功
        int count = stem.executeUpdate(sql);
        //处理结果
        System.out.println(count);

        //释放资源 -- 先释放stml再释放conn
        stem.close();
        conn.close();
    }
}

我们可以把处理结果修改一下

if (count > 0) {
    System.out.Println("修改成功");
}else {
    System.out.Println("修改失败");  
}

添加删除语句

更新记录时要小心。如果省略该 WHERE子句,所有记录都将被更新!

删除记录

 String sql = "delete  from students  where id = 2";

添加记录

#1.指定列名和要插入的值:
INSERT INTO table_name (column1, column2, column3, ...)
VALUES (value1, value2, value3, ...);

#2. 如果要为表的所有列添加值,则不需要在 SQL 查询中指定列名。但是,请确保值的顺序与表中列的顺序相同。在这里, INSERT INTO语法如下:
INSERT INTO table_name
VALUES (value1, value2, value3, ...);

DDL 语句

创建数据库操作

package org.example;
import org.junit.Test;

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

public class Jdbc2 {
    @Test
    public void testDML() throws SQLException {
        System.out.println("Hello world!");
        //注册驱动
        // Class.forName("com.mysql.jdbc.Driver");

        //获取连接
        String url = "jdbc:mysql://localhost:49156/learnjdbc";
        String username = "root";
        String password = "123456";
        Connection conn = DriverManager.getConnection(url,username,password);

        //定义sql语句
        String sql = "create database db2";
        //获取执行sql对象statement
        Statement stem = conn.createStatement();

        //执行sql   -- 返回值受影响的行数 -- 如果返回为1 说明1行sql执行成功
        int count = stem.executeUpdate(sql);
        //处理结果
        if (count > 0) {
            System.out.println("创建数据库成功");
        }else {
            System.out.println("创建数据库失败");
        }

        //释放资源 -- 先释放stml再释放conn
        stem.close();
        conn.close();
    }
}

但是删除数据库的时候,还是为0

仔细想想,DDL执行后删除了不就是0 嘛

image-20220921221911040

ResultSet结果集对象

int count = stem.executeUpdate(sql);
  • 封装了DQL查询语句的结果
ResultSet stmt.executeUpdate(sql): 执行DQL语句,返回ResultSet对象
  • 获取返回结果
boobean next()
+ 将光标从当前位置向前移动一位
+ 判断当前行是否为有效行
		+ true:有效行
		+ false: 无效行
	
	
xxx getXxx(参数):获取数据
xxx:数据类型:如 int getInt(参数); String getString(参数)
	+ int:列的编号,从1开始
	+ String:列的名称

光标是移动的,从0开始,依次往下,所以代码是循环:

while(rs.next()) {
	//获取数据
    rs.getXxx
}

案例

use learnjdbc;
select * from students ;

当前数据库的表结构

image-20220922142734260

关键部分:

   //6. 处理结果,遍历所有
    //6.1 光标向下移动一行,且判断当前行是否有数据
    while (rs.next()) {
        //6.2 获取数据
        double id = rs.getDouble(1);
        String name = rs.getString(2);
        Boolean gender = rs.getBoolean(3);
        int grade = rs.getInt(4);
        int score = rs.getInt(5);

        System.out.println(id + " " + name + " " + gender + " " + grade + " " + score);
    }

可以写ID,也可以写名称

package org.example;
import org.junit.Test;

import java.sql.*;

/**
 * java API result
 */
public class Jdbc3 {
    @Test
    public void testDML() throws SQLException {
        String url = "jdbc:mysql://localhost:49156/learnjdbc";
        String username = "root";
        String password = "123456";
        Connection conn = DriverManager.getConnection(url,username,password);

        //3. 定义sql
        String sql = "SELECT * FROM students where id < 10";  //查询

        //4. 获取对象
        Statement stmt = conn.prepareStatement(sql);

        //执行sql
        ResultSet rs = stmt.executeQuery(sql);

        //6. 处理结果,遍历所有
        //6.1 光标向下移动一行,且判断当前行是否有数据
        while (rs.next()) {
            //6.2 获取数据
            Long id = rs.getLong("id");
            String name = rs.getString("name");
            int gender = rs.getInt("gender");
            int grade = rs.getInt("grade");
            int score = rs.getInt("score");

            System.out.println(id + " " + name + " " + gender + " " + grade + " " + score);
        }
        // 7. 释放资源
        rs.close();
        stmt.close();
        conn.close();
    }
}

image-20220922150640985

ResultSet案例

查询账户数据,封装为Account对象,存储到ArrayList集合中

对于数据封装的对象,放在一个包:com.nsddd.top

  1. 定义实体类
  2. 查询数据,并且封装到account对象
  3. account对象存到arraylist集合中

代码

Jdb4

package org.example;
import org.junit.Test;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

/**
 * java API result
 */
public class Jdbc4 {
    @Test
    public void testDML() throws SQLException {
        String url = "jdbc:mysql://localhost:49156/db3";
        String username = "root";
        String password = "123456";
        Connection conn = DriverManager.getConnection(url,username,password);

        //3. 定义sql
        String sql = "SELECT * FROM java";  //查询

        //4. 获取对象
        Statement stmt = conn.prepareStatement(sql);

        //执行sql
        ResultSet rs = stmt.executeQuery(sql);

        //创建一个集合
        List<Account> list = new ArrayList<>();

        //6. 处理结果,遍历所有
        //6.1 光标向下移动一行,且判断当前行是否有数据
        while (rs.next()) {
            //创建对象
            Account account = new Account();

            //6.2 获取数据
            int id = rs.getInt(1);
            String name = rs.getString(2);
            int phone = rs.getInt(3);
//            int grade = rs.getInt("grade");
//            int score = rs.getInt("score");

            //对象赋值
            account.setId(id);
            account.setName(name);
            account.setPhone(phone);
       
            //toString
            System.out.println(account.toString());
            
            //存入集合 --- 对象
            list.add(account);
        }

        //打印集合
        System.out.println(list);

        // 7. 释放资源
        rs.close();
        stmt.close();
        conn.close();
    }
}

Account

package org.example;

public class Account {
    private int id;
    private String name;
    private int phone;

    @Override
    public String toString() {
        return "account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", phone=" + phone +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPhone() {
        return phone;
    }

    public void setPhone(int phone) {
        this.phone = phone;
    }


}

打印:

account{id=1, name='xiongxinwei', phone=17}
account{id=1, name='xiongxinwei', phone=17}
account{id=1, name='xiongxinwei', phone=17}
account{id=2, name='zhangsan', phone=112}
account{id=1, name='xiongxinwei', phone=17}
account{id=2, name='zhangsan', phone=112}
account{id=1, name='xiongxinwei', phone=17}
account{id=2, name='zhangsan', phone=112}

[account{id=1, name='xiongxinwei', phone=17}, account{id=1, name='xiongxinwei', phone=17}, account{id=1, name='xiongxinwei', phone=17}, account{id=2, name='zhangsan', phone=112}, account{id=1, name='xiongxinwei', phone=17}, account{id=2, name='zhangsan', phone=112}, account{id=1, name='xiongxinwei', phone=17}, account{id=2, name='zhangsan', phone=112}]

PreparedStatement

PreparedStatement继承于Statement

作用:预编译SQL语句并且执行:防止SQL注入问题

  • 预编译:性能更高
  • 防止sql注入:将敏感字符进行转义

SQL注入是通过操作输入来修改事先定义好的SQL语句,用以来达到对服务器进行攻击的方式

解决SQL注入我们首先要明白为什么发生sql注入:

因为

PreparedStatement开启

需要在mysql中开启,具体谷歌sou'suo

useServerPrepStmts=true

sql注入样式

完成用户登陆

select * from tb_user where username = "xiongxiwne" and password="1234"

关键代码

String name = "xiongxinwei";
String pwd = "123421";

//定义sql
String sql = "select * from tb_user where username = ? and password = ?";

//获取pstmt对象
PreparedStatement pstmt = conn.preparedStatement(sql);

//设置 ? 占位符  -- 两个?
pstmt.setString(1,name);
pstmt.setString(2,pwd);

PreparedStatement防止SQL注入原理

  1. 在获取PreparedStatement对象时候,将sql语句发送到MySQL服务器: 进行检测,编译
  2. 执行时就不用再进行这些步骤,速度更快
  3. 如果sql模板都一样,只需要进行一次检查

END 链接