JDBC基础

重难点梳理

一、JDBC快速入门

1.jdbc的概念

  • JDBC(Java DataBase Connectivity:java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系型数据库提供统一访问,它是由一组用Java语言编写的类和接口组成的。

  • JDBC的作用:可以通过java代码操作数据库

2.jdbc的本质

  • 其实就是java官方提供的一套规范(接口)。用于帮助开发人员快速实现不同关系型数据库的连接!

  • 图示:

  • 上图解释:

    • 如果没有jdbc,程序员想操作mysql,就需要学习mysql的一套操作规范

    • 程序员想操作oracle,就需要学习oracle的一套操作规范,db2也是

    • 这样做太麻烦了,这时候java就出了一套规范jdbc,用来操作所有的关系型数据库,然后各个数据库厂商支持这个规范就可以了

    • 那这样的话,程序员就只需要学习jdbc这一套规范即可

3.jdbc的快速入门程序

  1. 导入jar包

    资料中有一个压缩包:mysql-connector-java-5.1.37.zip
    解压出来,找到mysql-connector-java-5.1.37-bin.jar
  2. 注册驱动

    Class.forName("com.mysql.jdbc.Driver");
  3. 获取连接

    Connection con = DriverManager.getConnection("jdbc:mysql://localhost:3306/db2", "root", "root");
  4. 获取执行者对象

    Statement stat = con.createStatement();
  5. 执行sql语句,并接收返回结果

    String sql = "SELECT * FROM user";
    ResultSet rs = stat.executeQuery(sql);
  6. 处理结果

    while(rs.next()) {
       System.out.println(rs.getInt("id") + "\t" + rs.getString("name"));
    }
  7. 释放资源

    rs.close();
    stat.close();
    con.close();
  8. 创建一个java项目:JDBC基础

  9. 将jar包导入,并添加到引用类库

  10. 新建com.itheima01.JDBCDemo01

   package com.itheima01;
 
  import java.sql.Connection;
  import java.sql.DriverManager;
  import java.sql.ResultSet;
  import java.sql.Statement;
 
  public class JDBCDemo01 {
      public static void main(String[] args) throws Exception{
          //1.导入jar包
          //2.注册驱动
          Class.forName("com.mysql.jdbc.Driver");
 
          //3.获取连接 (连接的数据库名是db2,第二个第三个参数是连接数据库的用户名密码)
          Connection con = DriverManager.getConnection("jdbc:mysql://192.168.59.129:3306/db2","root","itheima");
 
          //4.获取执行者对象 (statement:表现,声明,跟程序意思不匹配)
          Statement stat = con.createStatement();
 
          //5.执行sql语句,并且接收结果
          String sql = "SELECT * FROM user";
          ResultSet rs = stat.executeQuery(sql); //execute执行,query:查询,resultset:结果集
 
          //6.处理结果
          while(rs.next()) {
              System.out.println(rs.getInt("id") + "\t" + rs.getString("name"));
          }
 
          //7.释放资源
          rs.close();
          stat.close();
          con.close();
      }
  }
 

二、JDBC各个功能类详解 ***

1.DriverManager

  • DriverManager:驱动管理对象

    • 注册驱动(告诉程序该使用哪一个数据库驱动)

      • 注册给定的驱动程序:static void registerDriver(Driver driver) (DriverManager的方法)

        • 我们在刚刚的入门案例中并没有注册驱动,也成功了,咋回事呢

      • 这是因为我们使用了Class.forName:Class.forName("com.mysql.jdbc.Driver")

        • 我们通过了给forName指定了是mysql的驱动

        • Class.forName()会加载,初始化指定的类

        • Driver类内部会帮助我们注册驱动,如下:

      • 在com.mysql.jdbc.Driver类中存在静态代码块(通过查看源码发现)

      //这是com.mysql.jdbc.Driver的静态代码块,只要使用这个类,就会执行这段代码
      //而Class.forName("com.mysql.jdbc.Driver")就正好使用到了这个类
      static {
      try {
      java.sql.DriverManager.registerDriver(new Driver());
      } catch (SQLException E) {
      throw new RuntimeException("Can't register driver!");
      }
      }
      • 注意:我们不需要通过DriverManager调用静态方法registerDriver(),因为只要Driver类被使用,则会执行其静态代码块完成注册驱动

      • mysql5之后可以省略注册驱动的步骤。在jar包中,存在一个java.sql.Driver配置文件,文件中指定了com.mysql.jdbc.Driver

      • 所以后边我们其实可以省略注册驱动的步骤(可以注释掉上个案例的注册驱动的步骤,也可以查询到数据)

  • 获取数据库连接(获取到数据库的连接并返回连接对象)

    • static Connection getConnection(String url, String user, String password);

      • 返回值:Connection数据库连接对象

      • 参数

        • url:指定连接的路径。语法:jdbc:mysql://ip地址(域名):端口号/数据库名称

        • user:用户名

        • password:密码

2.Connection

  • Connection:数据库连接对象

    • 获取执行者对象

      • 获取普通执行者对象:Statement createStatement();

      • 获取预编译执行者对象:PreparedStatement prepareStatement(String sql);

    • 管理事务

      • 开启事务:setAutoCommit(boolean autoCommit); 参数为false,则开启事务。

      • 提交事务:commit();

      • 回滚事务:rollback();

    • 释放资源

      • 立即将数据库连接对象释放:void close();

3.Statement

  • Statement:执行sql语句的对象

    • 执行DML语句:int executeUpdate(String sql);

      • 返回值int:返回影响的行数

      • 参数sql:可以执行insert、update、delete语句。

    • 执行DQL语句:ResultSet executeQuery(String sql);

      • 返回值ResultSet:封装查询的结果

      • 参数sql:可以执行select语句。

    • 释放资源

      • 立即将执行者对象释放:void close();

4.ResultSet

  • ResultSet:结果集对象

    • 判断结果集中是否还有数据:boolean next();

      • 有数据返回true,并将索引向下移动一行

      • 没有数据返回false

    • 获取结果集中的数据:XXX getXxx("列名");

      • XXX代表数据类型(要获取某列数据,这一列的数据类型)

      • 例如:String getString("name"); int getInt("age");

    • 释放资源

      • 立即将结果集对象释放:void close();

    • 游标,相当于是箭头

三、JDBC案例student学生表的CRUD ***

1. 数据准备

  • 数据库和数据表

-- 创建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');
  • 实体类

    • Student类,成员变量对应表中的列

    • 注意:所有的基本数据类型需要使用包装类,以防null值无法赋值

      • 数据库的查询结果可能是 null,因为自动拆箱,用基本数据类型接收有 空指针 风险

    • 新建com.itheima02.domain.Student (保存实体类的包名,可以用bean,也可以用domain)

package com.itheima02.domain;

import java.util.Date;

public class Student {
   private Integer sid;
   private String name;
   private Integer age;
   private Date birthday;

   public Student() {
  }

   public Student(Integer sid, String name, Integer age, Date birthday) {
       this.sid = sid;
       this.name = name;
       this.age = age;
       this.birthday = birthday;
  }

   public Integer getSid() {
       return sid;
  }

   public void setSid(Integer sid) {
       this.sid = sid;
  }

   public String getName() {
       return name;
  }

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

   public Integer getAge() {
       return age;
  }

   public void setAge(Integer age) {
       this.age = age;
  }

   public Date getBirthday() {
       return birthday;
  }

   public void setBirthday(Date birthday) {
       this.birthday = birthday;
  }

   @Override
   public String toString() {
       return "Student{" +
               "sid=" + sid +
               ", name='" + name + '\'' +
               ", age=" + age +
               ", birthday=" + birthday +
               '}';
  }
}
  • 本案例会使用分层思想,所以需要新建几个包

  • dao:数据持久层:操作数据库

    //定义dao接口:StudentDao
    //本案例要处理五个功能,所以dao接口中定义五个抽象方法
    package com.itheima02.dao;

    import com.itheima02.domain.Student;

    import java.util.ArrayList;

    /*
       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);
    }
  • service:业务层:处理业务逻辑,调用dao

    //定义service接口:StudentService
    package com.itheima02.service;

    import com.itheima02.domain.Student;

    import java.util.ArrayList;

    /*
       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);
    }
  • controller:控制层,调用业务层方法,将数据返回给前端界面(不过目前我们这个案例没有与界面结合)

    //定义控制层类:StudentController,提前准备好对应的测试方法
    package com.itheima02.controller;

    import org.junit.Test;

    public class StudentController {
       /*
           查询所有学生信息
        */
       @Test
       public void findAll() {
      }

       /*
           条件查询,根据id查询学生信息
        */
       @Test
       public void findById() {
      }

       /*
           添加学生信息
        */
       @Test
       public void insert() {
      }

       /*
           修改学生信息
        */
       @Test
       public void update() {
      }

       /*
           删除学生信息
        */
       @Test
       public void delete() {
           
      }
    }

2. 需求一:查询全部(查询所有学生信息)

  • 持久层:新建StudentDaoImpl实现StudentDao,并重写所有方法,我们先来处理第一个逻辑:

/*
    查询所有学生信息
*/
@Override
public ArrayList<Student> findAll() {
   ArrayList<Student> list = new ArrayList<>();
   Connection con = null;
   Statement stat = null;
   ResultSet rs = null;
   try{
       //1.注册驱动
       Class.forName("com.mysql.jdbc.Driver");

       //2.获取数据库连接
       con = DriverManager.getConnection("jdbc:mysql://192.168.59.129:3306/db14", "root", "itheima");

       //3.获取执行者对象
       stat = con.createStatement();

       //4.执行sql语句,并且接收返回的结果集
       String sql = "SELECT * FROM student";
       rs = stat.executeQuery(sql);

       //5.处理结果集
       while(rs.next()) {
           Integer sid = rs.getInt("sid");
           String name = rs.getString("name");
           Integer age = rs.getInt("age");
           Date birthday = rs.getDate("birthday");

           //封装Student对象
           Student stu = new Student(sid,name,age,birthday);

           //将student对象保存到集合中
           list.add(stu);
      }

  } catch(Exception e) {
       e.printStackTrace();
  } finally {
       //6.释放资源
       if(con != null) {
           try {
               con.close();
          } catch (SQLException e) {
               e.printStackTrace();
          }
      }

       if(stat != null) {
           try {
               stat.close();
          } catch (SQLException e) {
               e.printStackTrace();
          }
      }

       if(rs != null) {
           try {
               rs.close();
          } catch (SQLException e) {
               e.printStackTrace();
          }
      }
  }
   //将集合对象返回
   return list;
}
  • 业务层:新建StudentServiceImpl,实现StudentService,重写所有方法,然后先处理第一个逻辑:

private StudentDao dao = new StudentDaoImpl();
/*
   查询所有学生信息
*/
@Override
public ArrayList<Student> findAll() {
   return dao.findAll();
}
  • 控制层

private StudentService service = new StudentServiceImpl();
/*
查询所有学生信息
*/
@Test
public void findAll() {
ArrayList<Student> list = service.findAll();
for(Student stu : list) {
System.out.println(stu);
}
}

3. 需求二:条件查询(根据id查询学生信息)

  • 持久层

/*
条件查询,根据id查询学生信息
*/
@Override
public Student findById(Integer id) {
Student stu = new Student();
Connection con = null;
Statement stat = null;
ResultSet rs = null;
try{
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver"); //2.获取数据库连接
con = DriverManager.getConnection("jdbc:mysql://192.168.59.129:3306/db14", "root", "itheima"); //3.获取执行者对象
stat = con.createStatement(); //4.执行sql语句,并且接收返回的结果集
String sql = "SELECT * FROM student WHERE sid='"+id+"'";
// 引引加加
String sql = "SELECT * FROM student WHERE sid='"+id+"'";
// SELECT * FROM student WHERE sid='1'
String sql = "SELECT * FROM student WHERE sid='"+id+"'"; String sql = "SELECT * FROM student WHERE sid=" +id;
rs = stat.executeQuery(sql); //5.处理结果集
while(rs.next()) {
Integer sid = rs.getInt("sid");
String name = rs.getString("name");
Integer age = rs.getInt("age");
Date birthday = rs.getDate("birthday"); //封装Student对象
stu.setSid(sid);
stu.setName(name);
stu.setAge(age);
stu.setBirthday(birthday);
} } catch(Exception e) {
e.printStackTrace();
} finally {
//6.释放资源
if(con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
} if(stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
} if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//将对象返回
return stu;
}
  • 业务层

/*
条件查询,根据id查询学生信息
*/
@Override
public Student findById(Integer id) {
return dao.findById(id);
}
  • 控制层

/*
条件查询,根据id查询学生信息
*/
@Test
public void findById() {
Student stu = service.findById(3);
System.out.println(stu);
}

4. 需求三:新增数据 (添加学生)

  • 持久层

/*
添加学生信息
*/
@Override
public int insert(Student stu) {
Connection con = null;
Statement stat = null;
int result = 0;
try{
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver"); //2.获取数据库连接
con = DriverManager.getConnection("jdbc:mysql://192.168.59.129:3306/db14", "root", "itheima"); //3.获取执行者对象
stat = con.createStatement(); //4.执行sql语句,并且接收返回的结果集
Date d = stu.getBirthday();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String birthday = sdf.format(d);
// 字符串与多个变量进行拼接,将写死的内容 替换为 "+变量+"
String sql = "INSERT INTO student VALUES ('"+stu.getSid()+"','"+stu.getName()+"','"+stu.getAge()+"','"+birthday+"')";
result = stat.executeUpdate(sql); } catch(Exception e) {
e.printStackTrace();
} finally {
//6.释放资源
if(con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
} if(stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//将结果返回
return result;
}
  • 业务层

/*
新增学生信息
*/
@Override
public int insert(Student stu) {
return dao.insert(stu);
}
  • 控制层

/*
新增学生信息
*/
@Test
public void insert() {
Student stu = new Student(5,"周七",27,new Date());
int result = service.insert(stu);
if(result != 0) {
System.out.println("新增成功");
}else {
System.out.println("新增失败");
}
}

5. 需求四:修改数据(修改学生信息)

  • 持久层

/*
修改学生信息
*/
@Override
public int update(Student stu) {
Connection con = null;
Statement stat = null;
int result = 0;
try{
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver"); //2.获取数据库连接
con = DriverManager.getConnection("jdbc:mysql://192.168.59.129:3306/db14", "root", "itheima"); //3.获取执行者对象
stat = con.createStatement(); //4.执行sql语句,并且接收返回的结果集
Date d = stu.getBirthday();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String birthday = sdf.format(d);
String sql = "UPDATE student SET sid='"+stu.getSid()+"',name='"+stu.getName()+"',age='"+stu.getAge()+"',birthday='"+birthday+"' WHERE sid='"+stu.getSid()+"'";
result = stat.executeUpdate(sql); } catch(Exception e) {
e.printStackTrace();
} finally {
//6.释放资源
if(con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
} if(stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//将结果返回
return result;
}
  • 业务层

/*
修改学生信息
*/
@Override
public int update(Student stu) {
return dao.update(stu);
}
  • 控制层

/*
修改学生信息
*/
@Test
public void update() {
Student stu = service.findById(5);
stu.setName("周七七"); int result = service.update(stu);
if(result != 0) {
System.out.println("修改成功");
}else {
System.out.println("修改失败");
}
}

6. 需求五:删除数据(删除学生)

  • 持久层

/*
删除学生信息
*/
@Override
public int delete(Integer id) {
Connection con = null;
Statement stat = null;
int result = 0;
try{
//1.注册驱动
Class.forName("com.mysql.jdbc.Driver"); //2.获取数据库连接
con = DriverManager.getConnection("jdbc:mysql://192.168.59.129:3306/db14", "root", "itheima"); //3.获取执行者对象
stat = con.createStatement(); //4.执行sql语句,并且接收返回的结果集
String sql = "DELETE FROM student WHERE sid='"+id+"'";
result = stat.executeUpdate(sql); } catch(Exception e) {
e.printStackTrace();
} finally {
//6.释放资源
if(con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
} if(stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
}
//将结果返回
return result;
}
  • 业务层

/*
删除学生信息
*/
@Override
public int delete(Integer id) {
return dao.delete(id);
}
  • 控制层

/*
删除学生信息
*/
@Test
public void delete() {
int result = service.delete(5); if(result != 0) {
System.out.println("删除成功");
}else {
System.out.println("删除失败");
}
}

四、JDBC工具类

  • 为啥需要工具类呢?

  • 因为在上个案例中的dao层的代码,很多都是重复的

  • 程序员一但碰到重复代码,就要想办法解决

1.工具类的抽取 ***

  • 配置文件(在src下创建config.properties)

driverClass=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/db14
username=root
password=itheima
  • 工具类:com.itheima02.utils.JDBCUtils

package com.itheima02.utils;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties; /*
JDBC工具类
*/
public class JDBCUtils {
//1.私有构造方法
private JDBCUtils(){}; //2.声明配置信息变量
private static String driverClass;
private static String url;
private static String username;
private static String password;
private static Connection con; //3.静态代码块中实现加载配置文件和注册驱动
static{
try{
//通过类加载器返回配置文件的字节流
InputStream is = JDBCUtils.class.getClassLoader().getResourceAsStream("config.properties"); //创建Properties集合,加载流对象的信息
Properties prop = new Properties();
prop.load(is); //获取信息为变量赋值
driverClass = prop.getProperty("driverClass");
url = prop.getProperty("url");
username = prop.getProperty("username");
password = prop.getProperty("password"); //注册驱动
Class.forName(driverClass); } catch (Exception e) {
e.printStackTrace();
}
} //4.获取数据库连接的方法
public static Connection getConnection() {
try {
con = DriverManager.getConnection(url,username,password);
} catch (SQLException e) {
e.printStackTrace();
} return con;
} //5.释放资源的方法
public static void close(Connection con, Statement stat, ResultSet rs) {
if(con != null) {
try {
con.close();
} catch (SQLException e) {
e.printStackTrace();
}
} if(stat != null) {
try {
stat.close();
} catch (SQLException e) {
e.printStackTrace();
}
} if(rs != null) {
try {
rs.close();
} catch (SQLException e) {
e.printStackTrace();
}
}
} public static void close(Connection con, Statement stat) {
close(con,stat,null);
}
}

2.使用工具类优化student表的CRUD

  • 查询全部:修改StudentDaoImpl 中的方法

/*
查询所有学生信息
*/
@Override
public ArrayList<Student> findAll() {
ArrayList<Student> list = new ArrayList<>();
Connection con = null;
Statement stat = null;
ResultSet rs = null;
try{ con = JDBCUtils.getConnection(); //3.获取执行者对象
stat = con.createStatement(); //4.执行sql语句,并且接收返回的结果集
String sql = "SELECT * FROM student";
rs = stat.executeQuery(sql); //5.处理结果集
while(rs.next()) {
Integer sid = rs.getInt("sid");
String name = rs.getString("name");
Integer age = rs.getInt("age");
Date birthday = rs.getDate("birthday"); //封装Student对象
Student stu = new Student(sid,name,age,birthday); //将student对象保存到集合中
list.add(stu);
} } catch(Exception e) {
e.printStackTrace();
} finally {
//6.释放资源
JDBCUtils.close(con,stat,rs);
}
//将集合对象返回
return list;
}
  • 条件查询

/*
条件查询,根据id查询学生信息
*/
@Override
public Student findById(Integer id) {
Student stu = new Student();
Connection con = null;
Statement stat = null;
ResultSet rs = null;
try{ con = JDBCUtils.getConnection(); //3.获取执行者对象
stat = con.createStatement(); //4.执行sql语句,并且接收返回的结果集
String sql = "SELECT * FROM student WHERE sid='"+id+"'";
rs = stat.executeQuery(sql); //5.处理结果集
while(rs.next()) {
Integer sid = rs.getInt("sid");
String name = rs.getString("name");
Integer age = rs.getInt("age");
Date birthday = rs.getDate("birthday"); //封装Student对象
stu.setSid(sid);
stu.setName(name);
stu.setAge(age);
stu.setBirthday(birthday);
} } catch(Exception e) {
e.printStackTrace();
} finally {
//6.释放资源
JDBCUtils.close(con,stat,rs);
}
//将对象返回
return stu;
}
  • 新增数据

/*
添加学生信息
*/
@Override
public int insert(Student stu) {
Connection con = null;
Statement stat = null;
int result = 0;
try{
con = JDBCUtils.getConnection(); //3.获取执行者对象
stat = con.createStatement(); //4.执行sql语句,并且接收返回的结果集
Date d = stu.getBirthday();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String birthday = sdf.format(d);
String sql = "INSERT INTO student VALUES ('"+stu.getSid()+"','"+stu.getName()+"','"+stu.getAge()+"','"+birthday+"')";
result = stat.executeUpdate(sql); } catch(Exception e) {
e.printStackTrace();
} finally {
//6.释放资源
JDBCUtils.close(con,stat);
}
//将结果返回
return result;
}
  • 修改数据

/*
修改学生信息
*/
@Override
public int update(Student stu) {
Connection con = null;
Statement stat = null;
int result = 0;
try{
con = JDBCUtils.getConnection(); //3.获取执行者对象
stat = con.createStatement(); //4.执行sql语句,并且接收返回的结果集
Date d = stu.getBirthday();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String birthday = sdf.format(d);
String sql = "UPDATE student SET sid='"+stu.getSid()+"',name='"+stu.getName()+"',age='"+stu.getAge()+"',birthday='"+birthday+"' WHERE sid='"+stu.getSid()+"'";
result = stat.executeUpdate(sql); } catch(Exception e) {
e.printStackTrace();
} finally {
//6.释放资源
JDBCUtils.close(con,stat);
}
//将结果返回
return result;
}
  • 删除数据

/*
删除学生信息
*/
@Override
public int delete(Integer id) {
Connection con = null;
Statement stat = null;
int result = 0;
try{
con = JDBCUtils.getConnection(); //3.获取执行者对象
stat = con.createStatement(); //4.执行sql语句,并且接收返回的结果集
String sql = "DELETE FROM student WHERE sid='"+id+"'";
result = stat.executeUpdate(sql); } catch(Exception e) {
e.printStackTrace();
} finally {
//6.释放资源
JDBCUtils.close(con,stat);
}
//将结果返回
return result;
}

3.student表的CRUD整合页面

  • 接下来我们将程序与界面结合起来

  • 用户表的数据准备

-- 创建用户表
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');
  • 今天资料中有一个准备好的项目:JDBC基础网页版

  • 将上个案例的StudentDaoImpl复制到这个案例中,需要修改insert方法中的id为null,因为id是自增的

  • 或者直接复制如下代码:

public class StudentDaoImpl implements StudentDao {

    /*
查询所有学生信息
*/
@Override
public ArrayList<Student> findAll() {
Connection con = null;
Statement stat = null;
ResultSet rs = null;
ArrayList<Student> list = new ArrayList<>();
try {
//1.获取连接
con = JDBCUtils.getConnection(); //2.获取执行者对象
stat = con.createStatement(); //3.执行sql语句,并接收结果
String sql = "SELECT * FROM student";
rs = stat.executeQuery(sql); //4.处理结果,将每条记录封装成一个Student对象。将多个Student对象保存到集合中
while(rs.next()) {
Integer sid = rs.getInt("sid");
String name = rs.getString("name");
Integer age = rs.getInt("age");
Date birthday = rs.getDate("birthday"); Student stu = new Student(sid,name,age,birthday); list.add(stu);
} } catch (SQLException e) {
e.printStackTrace();
} finally {
//5.释放资源
JDBCUtils.close(con,stat,rs);
} return list;
} /*
条件查询,根据id查询学生信息
*/
@Override
public Student findById(Integer id) {
Connection con = null;
Statement stat = null;
ResultSet rs = null;
Student stu = new Student();
try {
//1.获取连接
con = JDBCUtils.getConnection(); //2.获取执行者对象
stat = con.createStatement(); //3.执行sql语句,并接收结果
String sql = "SELECT * FROM student WHERE sid='"+id+"'";
rs = stat.executeQuery(sql); //4.处理结果,将记录封装成一个Student对象。
if(rs.next()) {
Integer sid = rs.getInt("sid");
String name = rs.getString("name");
Integer age = rs.getInt("age");
Date birthday = rs.getDate("birthday"); stu.setSid(sid);
stu.setName(name);
stu.setAge(age);
stu.setBirthday(birthday);
} } catch (SQLException e) {
e.printStackTrace();
} finally {
//5.释放资源
JDBCUtils.close(con,stat,rs);
} return stu;
} /*
新增学生信息
*/
@Override
public int insert(Student stu) {
Connection con = null;
Statement stat = null;
int result = 0;
try{
//1.获取连接
con = JDBCUtils.getConnection(); //2.获取执行者对象
stat = con.createStatement(); //3.执行sql语句,并接收结果
Date date = stu.getBirthday();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String birthday = sdf.format(date);
String sql = "INSERT INTO student VALUES (null,'"+stu.getName()+"','"+stu.getAge()+"','"+birthday+"')";
result = stat.executeUpdate(sql); } catch (SQLException e) {
e.printStackTrace();
} finally {
//4.释放资源
JDBCUtils.close(con,stat);
} return result;
} /*
修改学生信息
*/
@Override
public int update(Student stu) {
Connection con = null;
Statement stat = null;
int result = 0;
try{
//1.获取连接
con = JDBCUtils.getConnection(); //2.获取执行者对象
stat = con.createStatement(); //3.执行sql语句,并接收结果
Date date = stu.getBirthday();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
String birthday = sdf.format(date);
String sql = "UPDATE student SET sid='"+stu.getSid()+"',name='"+stu.getName()+"',age='"+stu.getAge()+"',birthday='"+birthday+"' WHERE sid='"+stu.getSid()+"'";
result = stat.executeUpdate(sql); } catch (SQLException e) {
e.printStackTrace();
} finally {
//4.释放资源
JDBCUtils.close(con,stat);
} return result;
} /*
删除学生信息
*/
@Override
public int delete(Integer id) {
Connection con = null;
Statement stat = null;
int result = 0;
try{
//1.获取连接
con = JDBCUtils.getConnection(); //2.获取执行者对象
stat = con.createStatement(); //3.执行sql语句,并接收结果
String sql = "DELETE FROM student WHERE sid='"+id+"'";
result = stat.executeUpdate(sql); } catch (SQLException e) {
e.printStackTrace();
} finally {
//4.释放资源
JDBCUtils.close(con,stat);
} return result;
}
}
  • 测试:用户名zhangsan,密码1234

  • 找到学生管理

  • 然后可以添加,修改,删除

  • 那至于,界面和相关Servlet代码,咱们先不用管,后边会有类似的案例

五、SQL注入攻击

1.sql注入攻击的演示 ***

  • sql注入攻击:就是利用sql语句的漏洞来对系统进行攻击

  • 演示:

    • 在登录界面,输入一个错误的用户名或密码,也可以登录成功

  • 分析:为啥能够登陆成功?

    • 登陆的时候,会调用UserDaoImpl .findByLoginNameAndPassword 方法

    • 此方法中的sql语句如下

    //2.定义SQL语句
    String sql = "SELECT * FROM user WHERE loginname='"+loginName+"' AND password='"+password+"'";
    System.out.println(sql);
    //3.获取操作对象,执行sql语句,获取结果集
    st = conn.createStatement();
    rs = st.executeQuery(sql);
    //4.获取结果集
    if (rs.next()) {
    //5.封装
    user = new User();
    user.setUid(rs.getString("uid"));
    ....
    }
    //6.返回
    return user;
    • 输入aaa和aaa' or '1'='1 (注意引号)最终sql拼接完变为:

      SELECT * FROM user WHERE loginname='aaa' AND password='aaa' or '1'='1'
    • 这样的sql语句,会将user表中所有数据都查询出来(当然我们目前user中只有zhangsan一条数据)

    • 所以最终查询到用户了,就登陆进去了

2.sql注入攻击的原理

  • 按照正常道理来说,我们在密码处输入的所有内容,都应该认为是密码的组成

  • 但是现在Statement对象在执行sql语句时,将一部分内容当做查询条件来执行了

3.PreparedStatement的介绍

  • 预编译sql语句的执行者对象。在执行sql语句之前,将sql语句进行提前编译

  • 明确sql语句的格式后,就不会改变了。剩余的内容都会认为是参数

  • 参数使用?作为占位符

  • 为参数赋值的方法:setXxx(参数1,参数2);

    • 参数1:?的位置编号(编号从1开始)

    • 参数2:?的实际参数

  • 执行sql语句的方法

    • 执行insert、update、delete语句:int executeUpdate();

    • 执行select语句:ResultSet executeQuery();

4.PreparedStatement的使用 ***

/*
使用PreparedStatement的登录方法,解决注入攻击
*/
@Override
public User findByLoginNameAndPassword(String loginName, String password) {
//定义必要信息
Connection conn = null;
PreparedStatement pstm = null;
ResultSet rs = null;
User user = null;
try {
//1.获取连接
conn = JDBCUtils.getConnection();
//2.创建操作SQL对象
String sql = "SELECT * FROM user WHERE loginname=? AND password=?";
SELECT * FROM user WHERE loginname='aaa' AND password= "aaa' or '1' = '1"
pstm = conn.prepareStatement(sql);
//3.设置参数
pstm.setString(1,loginName);//设置第一个?参数
pstm.setString(2,password);//设置第二个?参数
System.out.println(sql);
//4.执行sql语句,获取结果集
rs = pstm.executeQuery();
//5.获取结果集
if (rs.next()) {
//6.封装
user = new User();
user.setUid(rs.getString("uid"));
user.setUcode(rs.getString("ucode"));
user.setUsername(rs.getString("username"));
user.setPassword(rs.getString("password"));
user.setGender(rs.getString("gender"));
user.setDutydate(rs.getDate("dutydate"));
user.setBirthday(rs.getDate("birthday"));
user.setLoginname(rs.getString("loginname"));
}
//7.返回
return user;
}catch (Exception e){
throw new RuntimeException(e);
}finally {
JDBCUtils.close(conn,pstm,rs);
}
}

5.使用PreparedStatement优化student表的CRUD(作业)

public class StudentDaoImpl implements StudentDao {

    @Override
public ArrayList<Student> findAll() {
//定义必要信息
Connection conn = null;
PreparedStatement pstm = null;
ResultSet rs = null;
ArrayList<Student> students = null;
try {
//1.获取连接
conn = JDBCUtils.getConnection();
//2.获取操作对象
pstm = conn.prepareStatement("select * from student");
//3.执行sql语句,获取结果集
rs = pstm.executeQuery();
//4.遍历结果集
students = new ArrayList<Student>();
while (rs.next()) {
//5.封装
Student student = new Student();
student.setSid(rs.getInt("sid"));
student.setName(rs.getString("name"));
student.setAge(rs.getInt("age"));
student.setBirthday(rs.getDate("birthday"));
//加入到集合中
students.add(student);
}
//6.返回
return students;
}catch (Exception e){
throw new RuntimeException(e);
}finally {
JDBCUtils.close(conn,pstm,rs);
}
} @Override
public Student findById(Integer sid) {
//定义必要信息
Connection conn = null;
PreparedStatement pstm = null;
ResultSet rs = null;
Student student = null;
try {
//1.获取连接
conn = JDBCUtils.getConnection();
//2.获取操作对象
pstm = conn.prepareStatement("select * from student where sid = ? ");
pstm.setInt(1,sid);
//3.执行sql语句,获取结果集
rs = pstm.executeQuery();
//4.遍历结果集
if (rs.next()) {
//5.封装
student = new Student();
student.setSid(rs.getInt("sid"));
student.setName(rs.getString("name"));
student.setAge(rs.getInt("age"));
student.setBirthday(rs.getDate("birthday"));
}
//6.返回
return student;
}catch (Exception e){
throw new RuntimeException(e);
}finally {
JDBCUtils.close(conn,pstm,rs);
}
} @Override
public int insert(Student student) {
//定义必要信息
Connection conn = null;
PreparedStatement pstm = null;
int result = 0;
try {
//1.获取连接
conn = JDBCUtils.getConnection();
//2.获取操作对象
pstm = conn.prepareStatement("insert into student(sid,name,age,birthday)values(null,?,?,?)");
//3.设置参数
//pstm.setInt(1,null);
pstm.setString(1,student.getName());
pstm.setInt(2,student.getAge());
pstm.setDate(3,new Date(student.getBirthday().getTime()));
//4.执行sql语句
result = pstm.executeUpdate();
}catch (Exception e){
throw new RuntimeException(e);
}finally {
JDBCUtils.close(conn,pstm);
}
return result;
} @Override
public int update(Student student) {
//定义必要信息
Connection conn = null;
PreparedStatement pstm = null;
int result = 0;
try {
//1.获取连接
conn = JDBCUtils.getConnection();
//2.获取操作对象
pstm = conn.prepareStatement("update student set name=?,age=?,birthday=? where sid=? ");
//3.设置参数
pstm.setString(1,student.getName());
pstm.setInt(2,student.getAge());
pstm.setDate(3,new Date(student.getBirthday().getTime()));
pstm.setInt(4,student.getSid());
//4.执行sql语句
result = pstm.executeUpdate();
}catch (Exception e){
throw new RuntimeException(e);
}finally {
JDBCUtils.close(conn,pstm);
}
return result;
} @Override
public int delete(Integer sid) {
//定义必要信息
Connection conn = null;
PreparedStatement pstm = null;
int result = 0;
try {
//1.获取连接
conn = JDBCUtils.getConnection();
//2.获取操作对象
pstm = conn.prepareStatement("delete from student where sid=? ");
//3.设置参数
pstm.setInt(1,sid);
//4.执行sql语句
result = pstm.executeUpdate();
}catch (Exception e){
throw new RuntimeException(e);
}finally {
JDBCUtils.close(conn,pstm);
}
return result;
}
}

六、JDBC管理事务 ***

1. 介绍

  1. JDBC如何管理事务

  • 管理事务的功能类:Connection

  • 开启事务:setAutoCommit(boolean autoCommit); 参数为false,则开启手动提交事务。

    • 自动提交

    • true:自动提交

    • false:不是自动提交(手动提交,程序员可以控制事务提交或回滚)

  • 提交事务:commit();

  • 回滚事务:rollback();

2. 演示

  • 演示批量添加数据并在业务层管理事务

  • 注意:事务的管理需要在业务层实现,因为dao层的功能要给很多模块提供功能的支撑,而有些模块是不需要事务的。

  • 批量添加用户

  • 我们在这里可以批量添加三个用户

3. 具体操作

  • UserService 接口

/*
批量添加 : batch:一批,成批处理
*/
void batchAdd(List<User> users);
  • UserServiceImpl 实现类:事务需要在service添加

/*
事务要控制在此处
*/
@Override
public void batchAdd(List<User> users) {
//获取数据库连接
Connection connection = JDBCUtils.getConnection();
try {
//开启事务
connection.setAutoCommit(false);
for (User user : users) {
//1.创建ID,并把UUID中的-替换
String uid = UUID.randomUUID().toString().replace("-", "").toUpperCase();
//2.给user的uid赋值
user.setUid(uid);
//3.生成员工编号
user.setUcode(uid); //模拟异常
//int n = 1 / 0; //4.保存
userDao.save(connection,user);
}
//提交事务
connection.commit();
}catch (Exception e){
try {
//回滚事务
connection.rollback();
}catch (Exception ex){
ex.printStackTrace();
}
e.printStackTrace();
}finally {
JDBCUtils.close(connection,null,null);
}
}
  • UserDao 接口

/**
支持事务的添加
*/
void save(Connection connection,User user);
  • UserDaoImpl 实现类

/*
支持事务的添加
*/
@Override
public void save(Connection connection, User user) {
//定义必要信息
PreparedStatement pstm = null;
try {
//1.获取连接
connection = JDBCUtils.getConnection();
//2.获取操作对象
pstm = connection.prepareStatement("insert into user(uid,ucode,loginname,password,username,gender,birthday,dutydate)values(?,?,?,?,?,?,?,?)");
//3.设置参数
pstm.setString(1,user.getUid());
pstm.setString(2,user.getUcode());
pstm.setString(3,user.getLoginname());
pstm.setString(4,user.getPassword());
pstm.setString(5,user.getUsername());
pstm.setString(6,user.getGender());
pstm.setDate(7,new Date(user.getBirthday().getTime()));
pstm.setDate(8,new Date(user.getDutydate().getTime()));
//4.执行sql语句,获取结果集
pstm.executeUpdate();
}catch (Exception e){
throw new RuntimeException(e);
}finally {
JDBCUtils.close(null,pstm,null);
}
}

JDBC基础:JDBC快速入门,JDBC工具类,SQL注入攻击,JDBC管理事务的更多相关文章

  1. 一、JDBC的概述 二、通过JDBC实现对数据的CRUD操作 三、封装JDBC访问数据的工具类 四、通过JDBC实现登陆和注册 五、防止SQL注入

    一.JDBC的概述###<1>概念 JDBC:java database connection ,java数据库连接技术 是java内部提供的一套操作数据库的接口(面向接口编程),实现对数 ...

  2. 【个人笔记】002-PHP基础-01-PHP快速入门-02-PHP语言相关介绍输

    002-PHP基础-01-PHP快速入门 02-PHP语言相关介绍 1.PHP是什么 Hypertext Preprocessor超文本预处理器 是一种通用开源脚本语言 Personal Home P ...

  3. 通过jdbc使用PreparedStatement,提升性能,防止sql注入

    为什么要使用PreparedStatement? 一.通过PreparedStatement提升性能 Statement主要用于执行静态SQL语句,即内容固定不变的SQL语句.Statement每执行 ...

  4. Objective-C基础语法快速入门

    Objective-C基础语法快速入门 2010-11-04 16:32 折酷吧 zheku8 字号:T | T 假如我们对面向对象的思维已经C语言都很熟悉的话,对于我们学习Objective-C将会 ...

  5. JAVA jdbc(数据库连接池)学习笔记(二) SQL注入

    PS:今天偶然间发现了SQL的注入...所以就简单的脑补了一下,都是一些简单的例子...这篇写的不怎么样...由于自己没有进行很深的研究... 学习内容: 1.SQL注入的概念...   所谓SQL注 ...

  6. Java学习笔记47(JDBC、SQL注入攻击原理以及解决)

    JDBC:java的数据库连接 JDBC本质是一套API,由开发公司定义的类和接口 这里使用mysql驱动,是一套类库,实现了接口 驱动程序类库,实现接口重写方法,由驱动程序操作数据库 JDBC操作步 ...

  7. JDBC中的PreparedStatement-防止SQL注入攻击

    在JDBC对数据库进行操作的时候,SQL注入是一种常见的针对数据库的注入攻击方式.如下面的代码所演示,在我们的提交字段中掺入了SQL语句,会使得程序的登录校验失效: package org.lyk.m ...

  8. 【个人笔记】003-PHP基础-01-PHP快速入门-03-PHP环境搭建

    003-PHP基础-01-PHP快速入门 03-PHP环境搭建 1.客户端(浏览器) IE FireFox CHROME Opera Safari 2.服务器 是运行网站的基本 是放置程序代码的地方 ...

  9. 【个人笔记】001-PHP基础-01-PHP快速入门-01-PHP职业路线及PHP前景

    001-PHP基础-01-PHP快速入门 01-PHP职业路线及PHP前景 PHP职业路线 PHP初级工程师 1年以下 3k-6k PHP中级工程师 1-3年6k-10k PHP高级工程师 3年以上  ...

随机推荐

  1. 【代码周边】Idea设置类注解和方法注解(带图)

    Idea版本: 类注解 打开setting→Editor→Code Style→File and Code Templates /** * Created with IntelliJ IDEA. * ...

  2. 当layui与分页相遇--bootstrap何去何从

    用了一段时间,感觉layui比bootstrap 方便了很多.在js操作上比bootstrap减少了许多的代码量. 今天遇到需要前台分页.当然,不是表格,如果是表格的话.使用yalui table和b ...

  3. javaweb登陆实例

    1 <%@ page language="java" contentType="text/html; charset=UTF-8" 2 pageEncod ...

  4. —用python写图片格式批量处理工具

    python爬取微博评论(无重复数据) 前言 一.整体思路 二.获取微博地址 1.获取ajax地址 2.解析页面中的微博地址 3.获取指定用户微博地址 三.获取主评论 四.获取子评论 1.解析子评论 ...

  5. vue element 表单验证不通过,滚动到固对应位置

    我们在使用elementIUI实现表单验证,内容比较多的时候,提示内容会被遮挡,这时候用户不清楚什么情况,还会连续点击提交按钮.这个时候需求来啦:我们需要在表单验证不通过的时候,页面滚动到对应的位置. ...

  6. js 的关键字

    1.get / set var test = { _Name: "Limei", _Age: 20, get name() { return this._Name;}, set a ...

  7. Win Docker 安装C盘清理方法之一

    背景 由于Docker默认安装到C盘,C盘空间越发的小了,虽然(win10)C盘满了并不会很大影响,但是强迫症患者是不能忍得 解决办法 查询https://stackoverflow.com/ques ...

  8. Lock锁 精讲

    1.为什么需要Lock 为什么synchronized不够用,还需要Lock Lock和synchronized这两个最常见的锁都可以达到线程安全的目的,但是功能上有很大不同. Lock并不是用来代替 ...

  9. 【SpringMVC】SpringMVC 拦截器

    SpringMVC 拦截器 文章源码 拦截器的作用 SpringMVC 的处理器拦截器类似于 Servlet 开发中的过滤器 Filter,用于对处理器进行预处理和后处理. 谈到拦截器,还有另外一个概 ...

  10. Head First 设计模式 —— 13. 代理 (Proxy) 模式

    思考题 如何设计一个支持远程方法调用的系统?你要怎样才能让开发人员不用写太多代码?让远程调用看起来像本地调用一样,毫无瑕疵? P435 已经接触过 RPC 了,所以就很容易知道具体流程:客户端调用目标 ...