2021-02-24

本文详细介绍了JDBC的基础知识,包括JDBC的概念、入门案例和API讲解。从注册驱动、获取连接到执行SQL,再到处理结果集,展示了完整的CRUD操作流程。此外,还讲解了JDBCUtils工具类的实现,用于简化数据库操作,并讨论了SQL注入漏洞及其解决方案——使用PreparedStatement。最后,阐述了JDBC事务管理的重要性,强调了一组SQL操作的原子性。
摘要由CSDN通过智能技术生成

1【第一章】JDBC概念/入门案例/API讲解【理解】

1 JDBC概念

在这里插入图片描述

2 入门案例

2.1 入门案例步骤
【前提】:导入mysql的驱动jar包
【第一步】:注册驱动,告诉程序使用哪种数据
【第二步】:获取连接,需要url、用户名、密码
【第三步】:获取执行SQL的对象,负责CRUD
【第四步】:执行查询操作,获取结果
【第五步】:处理结果
【第六步】:释放资源
2.2 代码实现
  • 准备数据库数据
-- 创建db14数据库
CREATE DATABASE db14;
-- 使用db14数据库
USE db14;
-- 创建student表
CREATE TABLE student(
  sid INT PRIMARY KEY AUTO_INCREMENT,	-- 学生id
  NAME VARCHAR(20),			-- 学生姓名
  age INT,				-- 学生年龄
  birthday DATE				-- 学生生日
);
-- 添加数据
INSERT INTO student VALUES (NULL,'张三',23,'1999-09-23'),(NULL,'李四',24,'1998-08-10'),(NULL,'王五',25,'1996-06-06'),(NULL,'赵六',26,'1994-10-20');
  • 代码实现
public class JdbcDemo1 {
  public static void main(String[] args) throws ClassNotFoundException, SQLException {
    //【前提】:导入mysql的驱动jar包
    //【第一步】:注册驱动,告诉程序使用哪种数据
    Class.forName("com.mysql.jdbc.Driver");
    //【第二步】:获取连接,需要url、用户名、密码
    Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/db14", "root", "root");
    //【第三步】:获取执行SQL的对象,负责CRUD
    Statement statement = conn.createStatement();
    //【第四步】:执行查询操作,获取结果
    ResultSet rs = statement.executeQuery("select * from student");
    //【第五步】:处理结果
    while (rs.next()){ //如果返回true表示有下一行元素
      //获取并打印
      int sid = rs.getInt("sid");
      String name = rs.getString("name");
      int age = rs.getInt("age");
      String birthday = rs.getString("birthday");
      System.out.println(sid+","+name+","+age+","+birthday);
    }
    //【第六步】:释放资源
    rs.close();
    statement.close();
    conn.close();
  }
}

3 API讲解

3.1 DriverManager的作用
作用一:注册驱动

​ DriverManager.registerDriver(new Driver()),但是我们不使用,我们使用Class.forName(“com.mysql.jdbc.Driver”)。

作用二:获取连接

​ Connection conn = DriverManager.getConnection(“jdbc:mysql://localhost:3306/db14”, “root”, “root”);

参数1:连接的URL地址,如果是本机并且端口号是3306,那么localhost:3306可以省略不写,也就是**jdbc:mysql:///db14**。

参数2:用户名

参数3:密码

3.2 Connection的作用
作用一:创建执行SQL语句的执行者对象

​ Statement对象:Statement statement = conn.createStatement();

​ PreparedStatement预编译对象:PreparedStatement pstmt=conn.prepareStatement("")

作用二:事务管理
conn.setAutoCommit(false);  //设置自动提交为false就表示开启事务
conn.commit(); //提交事务
conn.rollback();//回滚事务
3.3 Statement的作用

作用:执行SQL语句

执行DQL语句(查询):ResultSet executeQuery(SQL);

​ 参数:传递要执行的查询语句

​ 返回值:封装查询结果的结果集对象。

执行DML语句(增删改):int executeUpdate(SQL);

​ 参数:要执行的增删改语句

​ 返回值:影响的行数,可以通过影响的行数判断是否执行成功。

3.4 ResultSet的作用

作用一:判断是否有下一行数据:boolean next()

作用二:获取改行的数据: Xxx getXxx(“列名”);

​ Xxx表示不同的数据类型,例如:int getInt(“sid”); String getString(“name”)

【第二章】JDBC案例【理解】

1 案例流程,以查询所有为例

在这里插入图片描述
20200914110357495.png)

2 环境搭建

2.1 Student类
public class Student {
    private Integer sid;
    private String name;
    private Integer age;
    private String birthday;
  //空参/有参构造、getter()/setter()方法、toString()方法自己生成
}
2.2 StudentDao接口
/*
    Dao层接口
 */
public interface StudentDao {
    //查询所有学生信息
    public abstract ArrayList<Student> findAll();

    //条件查询,根据id获取学生信息
    public abstract Student findById(Integer id);

    //新增学生信息
    public abstract int insert(Student stu);

    //修改学生信息
    public abstract int update(Student stu);

    //删除学生信息
    public abstract int delete(Integer id);
}
2.3 StudentService接口
/*
    Service层接口
 */
public interface StudentService {
  //查询所有学生信息
  public abstract ArrayList<Student> findAll();

  //条件查询,根据id获取学生信息
  public abstract Student findById(Integer id);

  //新增学生信息
  public abstract int insert(Student stu);

  //修改学生信息
  public abstract int update(Student stu);

  //删除学生信息
  public abstract int delete(Integer id);
}

需求一:查询所有学生信息

1 StudentDaoImpl代码实现
@Override
public ArrayList<Student> findAll() {
  Connection connection = null;
  Statement statement = null;
  ResultSet rs = null;
  ArrayList<Student> list= null;
  try {
    //1 注册驱动
    Class.forName("com.mysql.jdbc.Driver");
    //2 获取连接
    connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db14", "root", "root");
    //3 创建执行SQL的对象
    statement = connection.createStatement();
    //4 执行SQL得到结果
    rs = statement.executeQuery("select * from student");
    list = new ArrayList<>();
    //5 处理结果
    while (rs.next()){
      int sid = rs.getInt("sid");
      String name = rs.getString("name");
      int age = rs.getInt("age");
      String birthday = rs.getString("birthday");
      //封装成Student对象,保存到ArrayList集合中
      list.add(new Student(sid,name,age,birthday));
    }
  } catch (Exception e) {
    e.printStackTrace();
  } finally {
    //6 释放资源
    if(rs!=null){
      try {
        rs.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    if(statement!=null){
      try {
        statement.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    if(connection!=null){
      try {
        connection.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
  }

  //7 返回结果
  return list;
}
2 StudentServiceImpl代码实现
private StudentDao studentDao=new StudentDaoImpl();
@Override
public ArrayList<Student> findAll() {
  return studentDao.findAll();
}

3 StudentController代码实现
@Test
public void testFindAll(){
  ArrayList<Student> list = studentService.findAll();
  //Lambda表达式语法:()->{}
  list.forEach(student ->System.out.println(student));
}

需求二:根据id查询学生信息

1 StudentDaoImpl代码实现
@Override
public Student findById(Integer id) {
  Connection connection = null;
  Statement statement = null;
  ResultSet rs = null;
  Student student=null;
  try {
    //1 注册驱动
    Class.forName("com.mysql.jdbc.Driver");
    //2 获取连接
    connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db14", "root", "root");
    //3 创建执行SQL的对象
    statement = connection.createStatement();
    //4 执行SQL得到结果
    rs = statement.executeQuery("select * from student where sid="+id);
    //5 处理结果
    if (rs.next()){
      int sid = rs.getInt("sid");
      String name = rs.getString("name");
      int age = rs.getInt("age");
      String birthday = rs.getString("birthday");
      //封装成Student对象
      student=new Student(sid,name,age,birthday);
    }
  } catch (Exception e) {
    e.printStackTrace();
  } finally {
    //6 释放资源
    if(rs!=null){
      try {
        rs.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    if(statement!=null){
      try {
        statement.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    if(connection!=null){
      try {
        connection.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
  }
  return student;
}
2 StudentServiceImpl代码实现
@Override
public Student findById(Integer id) {
  return studentDao.findById(id);
}
3 StudentController代码实现
@Test
public void testFindById(){
  Student student = studentService.findById(3);
  System.out.println(student);
}

需求三:添加学生信息

1 StudentDaoImpl代码实现
@Override
public int insert(Student stu) {
  Connection connection = null;
  Statement statement = null;
  int count=0;
  try {
    //1 注册驱动
    Class.forName("com.mysql.jdbc.Driver");
    //2 获取连接
    connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db14", "root", "root");
    //3 创建执行SQL的对象
    statement = connection.createStatement();
    //4 执行SQL得到结果
    String sql="insert into student values(null,'" + stu.getName() + "'," + stu.getAge() + ",'" + stu.getBirthday() + "')";
    count= statement.executeUpdate(sql);
    //5 处理结果
  } catch (Exception e) {
    e.printStackTrace();
  } finally {
    //6 释放资源
    if(statement!=null){
      try {
        statement.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    if(connection!=null){
      try {
        connection.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
  }
  return count;
}
2 StudentServiceImpl代码实现
@Override
public int insert(Student stu) {
  return studentDao.insert(stu);
}
3 StudentController代码实现
@Test
public void testInsert(){
  Student stu=new Student(null,"tom",20,"2020-9-14");
  int count = studentService.insert(stu);
  //判断是否执行成功
  System.out.println(count>0?"添加成功":"添加失败");
}

需求四:修改学生信息

1 StudentDaoImpl代码实现
@Override
public int update(Student stu) {
  Connection connection = null;
  Statement statement = null;
  int count=0;
  try {
    //1 注册驱动
    Class.forName("com.mysql.jdbc.Driver");
    //2 获取连接
    connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db14", "root", "root");
    //3 创建执行SQL的对象
    statement = connection.createStatement();
    //4 执行SQL得到结果
    String sql="update student set name='"+stu.getName()+"',age="+stu.getAge()+",birthday='"+stu.getBirthday()+"' where sid="+stu.getSid();
    count= statement.executeUpdate(sql);
    //5 处理结果
  } catch (Exception e) {
    e.printStackTrace();
  } finally {
    //6 释放资源
    if(statement!=null){
      try {
        statement.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    if(connection!=null){
      try {
        connection.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
  }
  return count;
}
2 StudentServiceImpl代码实现
@Override
public int update(Student stu) {
  return studentDao.update(stu);
}
3 StudentController代码实现
@Test
public void testUpdate(){
  Student stu=new Student(5,"rose",21,"2019-9-14");
  int count = studentService.update(stu);
  //判断是否执行成功
  System.out.println(count>0?"修改成功":"修改失败");
}

需求五:删除学生信息

1 StudentDaoImpl代码实现
@Override
public int delete(Integer id) {
  Connection connection = null;
  Statement statement = null;
  int count=0;
  try {
    //1 注册驱动
    Class.forName("com.mysql.jdbc.Driver");
    //2 获取连接
    connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/db14", "root", "root");
    //3 创建执行SQL的对象
    statement = connection.createStatement();
    //4 执行SQL得到结果
    String sql="delete from student where sid="+id;
    count= statement.executeUpdate(sql);
    //5 处理结果
  } catch (Exception e) {
    e.printStackTrace();
  } finally {
    //6 释放资源
    if(statement!=null){
      try {
        statement.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    if(connection!=null){
      try {
        connection.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
  }
  return count;
}
2 StudentServiceImpl代码实现
@Override
public int delete(Integer id) {
  return studentDao.delete(id);
}
3 StudentController代码实现
@Test
public void testDelete(){
  int count = studentService.delete(5);
  //判断是否执行成功
  System.out.println(count>0?"删除成功":"删除失败");
}

【第三章】JDBC工具类【重点】

1 将哪些操作抽取到工具类中【重要】

为什么要抽取工具类?
		我们在执行CRUD的过程中,有太多的重复代码需要写,例如:注册驱动、获取连接、释放资源...
1 加载properties配置文件,获取连接数据库的相关参数,4个
2 注册驱动
	加载一次,写到static静态代码块中
3 获取连接
4 释放资源

2 在src中书写jdbc.properties配置文件【重要】

driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db14
username=root
password=root

注意:不要写到包中了

3 书写JDBCUtils工具类【重要】

public class JDBCUtils {
  private static String url;
  private static String username;
  private static String password;

  static {
    //1 加载properties配置文件,获取连接数据库的相关参数,4个

    //1.1 创建Properties对象
    Properties properties=new Properties();
    //1.2 调用load方法加载文件
    InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
    try {
      properties.load(is);
      //1.3 根据key获取value值
      String driverClass = properties.getProperty("driverClass");
      url = properties.getProperty("url");
      username = properties.getProperty("username");
      password = properties.getProperty("password");

      //2 注册驱动
      Class.forName(driverClass);

    } catch (Exception e) {
      e.printStackTrace();
    }
  }

  //3 获取连接
  public static Connection getConnection() throws SQLException {
    return DriverManager.getConnection(url,username,password);
  }

  //4 释放资源
  public static void close(Statement statement,Connection connection){
    close(null,statement,connection);
  }
 public static void close(ResultSet rs, Statement statement,Connection connection){
    if(rs!=null){
      try {
        rs.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    if(statement!=null){
      try {
        statement.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
    if(connection!=null){
      try {
        connection.close();
      } catch (SQLException e) {
        e.printStackTrace();
      }
    }
  }
}

4 将页面我们的dao层代码整合到给定的web项目中

4.1 在db14数据库中添加user表
-- 创建用户表
CREATE TABLE USER(
  uid VARCHAR(50) PRIMARY KEY,	-- 用户id
  ucode VARCHAR(50),		-- 用户标识
  loginname VARCHAR(100),		-- 登录用户名
  PASSWORD VARCHAR(100),		-- 登录密码
  username VARCHAR(100),		-- 用户名
  gender VARCHAR(10),		-- 用户性别
  birthday DATE,			-- 出生日期
  dutydate DATE                   -- 入职日期
);

-- 添加一条测试数据
INSERT INTO USER VALUES ('11111111', 'zhangsan001', 'zhangsan', '1234', '张三', '男', '2008-10-28', '2018-10-28');
4.2 修改config.properties属性文件
driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db14
username=root
password=root
4.3 将自己的StudentDaoImpl替换给的dao

【第四章】JDBC的SQL注入漏洞【重点】

1 SQL注入漏洞的原因

使用Statement对象执行SQL时,如果SQL中拼接的变量保护SQL关键字,那么就会出现SQL注入漏洞。

2 SQL注入漏洞的解决办法

使用PreparedStatement对象代替Statement对象执行SQL,该对象可以预编译SQL语句,固定SQL的格式和关键字,用?占位符表示传递的数据,后期设置数据即可。

3 使用PreparedStatement对象的思路【重点】

【第一步】:使用Connection对象获取PreparedStatement对象预编译sql
【第二步】:如果SQL中有几个?占位符,那么就设置几个参数。
【第三步】:调用executeQuery()或者executeUpdate()方法执行SQL,但是不用再传递SQL。

例如:

//3 创建执行SQL的对象
String sql="insert into student values(null,?,?,?)";
pstmt = connection.prepareStatement(sql);
//有几个?就设置几个参数
pstmt.setString(1,stu.getName());
pstmt.setInt(2,stu.getAge());
pstmt.setString(3,stu.getBirthday());
//4 执行SQL得到结果
count= pstmt.executeUpdate();

【第五章】JDBC事务管理【理解】

1 事务概念

一组SQL操作要么同时成功要么同时失败。

2 JDBC事务操作的API

conn.setAutoCommit(false);  //设置自动提交为false就表示开启事务
conn.commit(); //提交事务
conn.rollback();//回滚事务

3 修改之前的批量添加的代码

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-flKgYN7B-1614146449675)(D:/Program%20Files/feiq/Recv%20Files/Java124&125RecvFiles/123Java125%E6%9C%9F%E8%B5%84%E6%96%99-%E9%98%B3%E5%93%A5%E9%98%B6%E6%AE%B5/MySQL%E7%AC%94%E8%AE%B0(%E6%9B%B4%E6%96%B0%E5%90%8E)]/Jdbc125%E6%9E%B6%E5%8C%85%E5%8F%8A%E8%B5%84%E6%96%99/jdbc-web%E7%BB%BC%E5%90%88%E6%A1%88%E4%BE%8B%E7%AC%94%E8%AE%B0/JDBC%E7%AC%94%E8%AE%B0/JDBC%E7%AC%AC%E4%B8%80%E5%A4%A9%E7%AC%94%E8%AE%B0/%E7%AC%94%E8%AE%B0/assets/image-20200914180357243.png)

总结

1 jdbc相关api的作用【理解】
	DriverManager:
		1 注册驱动(不用)	
		2 获取连接:DriverManager.getConnection(url,username,password);
	Connection:
		1 获取执行SQL的对象(PreparedStatement)	
		2 事务管理
			前提:一组SQL操作必须使用同一个Connection对象
			setAutoCommit(false); //获取连接之后开启事务
			commit();//提交事务
			rollback();//回滚事务
		PreparedStatement:
			1 预编译SQL:connection.preparedStatement(SQL)
			2 执行DQL语句:executeQuery();
			3 执行DML语句:executeUpdate();
		ResultSet:结果集对象
			1 判断是否有下一个元素:boolean next();
			2 根据列名获取值:Xxx getXxx("列名");  //Xxx表示数据类型
'2 JDBCUtils工具类【重点】
	public class JDBCUtils{
		//静态代码块中的代码很关键
		static{
			 //1 加载jdbc.properties配置文件
			 Properties properties=new Properties()
			 
			 InputStream is=JDBCUtils.class.getClassLoader().getResourceAsStream("jdbc.properties");
			 properties.load(is);
			 
			 //后面就是获取值
			 String driverClass=properties.getProperty("driverClass"); //容易将参数的引号掉了
			 ...
      //2 注册驱动
			Class.forName(driverClass);
		}
		//3 对外提供获取连接的方法
		public static Connection getConnection(){}
		//4 对外提供释放资源的方法
		public static void close(Connection conn,Statemet stmt){}
		public static void close(Connection conn,Statemet stmt,ResultSet rs){}
	}

'3 JDBC的SQL注入漏洞【重要】
	原因:使用Statement对象执行SQL时,如果拼接的参数中有SQL关键字,就会出现注入漏洞。
	解决:使用PreparedStatement对象代替Statement对象执行SQL。
	原理:在获取PreparedStatement对象可以预编译SQL,固定SQL的格式和关键字,后期的参数使用?占位符代替,将来给占位符传参即可。
	
	'PreparedStatement对象的使用步骤:【重要】
	执行增删改:
		1 通过JDBCUtils工具类获取连接得到Connection对象
		2 通过Connection对象获取PreparedStatement对象,预编译SQL。
		3 有几个?就设置几个参数
		4 执行增删改操作,得到影响的行数。
		5 处理结果。
		6 释放资源。
	执行查询:
		1 通过JDBCUtils工具类获取连接得到Connection对象
		2 通过Connection对象获取PreparedStatement对象,预编译SQL。
		3 有几个?就设置几个参数
		4 执行查询操作,得到ResultSet结果集对象。
		5 处理结果(遍历/判断是否有下一行)6 释放资源。
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值