java web jdbc

本文详细介绍了Java Web中JDBC的使用,包括DriverManager、Connection、Statement和ResultSet等核心概念,以及JDBC事务处理、数据库连接池的原理与实践。通过案例展示了JDBC在实际操作中的CRUD操作,同时探讨了SQL注入攻击及其解决方案。最后,对比分析了C3P0和Druid两种主流数据库连接池的使用方法。
摘要由CSDN通过智能技术生成

JDBC快速入门

1.jdbc的概念

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

2.jdbc的本质

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

3.jdbc的快速入门程序

  1. 导入jar包
    • 下载mysql-connector-java-8.0.26.jar
    • 把包放到libs文件夹下(没有就创建)
    • 添加到全局引用
  2. 注册驱动
  3. 获取数据库连接
  4. 获取执行者对象
  5. 执行sql语句并返回结果
  6. 处理结果
  7. 释放资源
package com.liu;

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

public class newJDBC {
    public static void main(String[] args) throws Exception{
        //2.注册驱动
        Class.forName("com.mysql.cj.jdbc.Driver");

        //3.获取连接
        Connection con=  DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456");

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

        //5. 执行sql语句并返回结果
        String sql = "SELECT * FROM qwe";
        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();
    }
}

JDBC功能类详解

DriverManager

DriverManager驱动管理对象

注册驱动
  • 注册给定的驱动程序:static void registerDriver(Driver driver)
  • 写代码使用:Class.forName("com.mysql.cj.jdbc.Driver")
    注意:
    我们不需要通过DriverManager调用静态方法 registerDriver(Driver driver),因为只要Driver 类被使用,则会执行其静态代码块完成注册驱动
    mysql5之后可以省略注册驱动的步骤。在jar包中,存在一个java.sql.Driver配置文件,文件中指定了com.mysql.jdbc.Driver
获取数据库连接
  • 获取数据库连接对象:static Connection getConnection(String url,String user,String passwor)
    参数:
    • url:指定连接的路径。语法:jdbc:mysql://ip地址(域名):端口号/数据库名称
    • user:用户名
    • password:密码
  • 返回值:Connection 数据库连接对象

Connection

Connection数据库连接对象

获取执行者对象
  • 获取普通执行者对象:Statement createStatement();
  • 获取预编译执行者对象:PreparedStatement preparedStatement(String sql);
管理事务
  • 开启事务:setAutoCommit(boolean autoCommit);参数为false,则开启事务。
  • 提交事务:commit();
  • 回滚事务:rollback();
释放资源
  • 立即将数据库连接对象释放:void close();

Statement

Statement执行sql语句的对象

  • 执行DML语句:int executeUpdate(String sql);
    • 返回值 int :返回影响的行数 。
    • 参数sql :可以执行insertupdatedelete语句。
  • 执行DQL语句:ResultSet executeQuery(String sql);
    • 返回值 ResultSet :封装查询的结果 。
    • 参数sql :可以执行select语句。
  • 释放资源:立即将数据库连接对象释放:void close();

ResultSet

ResultSet结果集对象

  • 判断结果集中是否还有数据:boolean next();
    • 有数据返回true,并将索引向下移动一行。
    • 没有数据返回false。
  • 获取结果集中的数据 :XXX getXxx("列名");
    • XXX代表数据类型(要获取某列数据,这一列的数据类型)。
    • 例如:
      • String getString("name");
      • int getInt("age");

JDBC 案例

案例需求:

使用JDBC完成对student表CRUD操作

数据准备:

  • 创建数据库和数据表
  • 创建domain包Student类(自定义类的功能是为了封装表中每列数据,成员变量和列保持一致所有基本数据类型需要使用对应包装类,以免表中null值无法赋值)
package com.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 +
                '}';
    }
}
  • 创建daocontrollerservice

    • dao 层叫数据访问层,全称为data access object,属于一种比较底层,比较基础的操作,具体到对于某个表的增删改查,也就是说某个DAO一定是和数据库的某一张表一一对应的,其中封装了增删改查基本操作,建议DAO只做原子操作,增删改查。(面向表)
      • dao 包是定义接口(StudentDao接口类)和实现接口(StudentDaoImpl类)的
    //	StudentDao类
    package com.dao;
    import com.domain.Student;
    import java.util.ArrayList;
    
    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(String stu);
    
        // 删除学生信息
        public abstract int delete(Integer id);
    }
    
    //	StudentDaoImpl类
    package com.dao;
    import com.domain.Student;
    import java.util.ArrayList;
    
    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(String stu);
    
        // 删除学生信息
        public abstract int delete(Integer id);
    }
    
    • service层叫服务层,被称为服务,粗略的理解就是对一个或多个DAO进行的再次封装,封装成一个服务,所以这里也就不会是一个原子操作了,需要事物控制。

      • service 包是定义接口(StudentService 接口类)和实现接口(StudentServiceImpl类)的。和Dao包里的内容一样。
    • controler负责请求转发,接受页面过来的参数,传给Service处理,接到返回值,再传给页面。

      • controller 包是和页面交互使用的(StudentController类)
    package com.controller;
    import org.junit.Test;
    
    public class StudentController {
        @Test
        public void findAll(){
    
        }
    
        @Test
        public void findById(){
    
        }
    
        @Test
        public void insert(){
    
        }
    
        @Test
        public void update(){
    
        }
    
        @Test
        public void delete(){
    
        }
    }
    

需求实现:

  • 需求一:查询所有学生信息
  • 需求二:根据id查询学生信息
  • 需求三:新增学生信息
  • 需求四:修改学生信息
  • 需求五:删除学生信息

查询所有学生信息

dao层StudentDaoImpl 类findAll()方法写

package com.dao;

import com.domain.Student;
import java.sql.*;
import java.util.ArrayList;

public class StudentDaoImpl implements 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.cj.jdbc.Driver");

            //2.获取连接
            con=  DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456");

            //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 (Exception e){
                    e.printStackTrace();
                }
            }
            if (rs !=null){
                try{
                    rs.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if (stat !=null){
                try{
                    stat.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }

        // 将集合对象返回
        return list;
    }

    @Override
    public Student findById(Integer id) {
        return null;
    }

    @Override
    public int insert(Student stu) {
        return 0;
    }

    @Override
    public int update(String stu) {
        return 0;
    }

    @Override
    public int delete(Integer id) {
        return 0;
    }
}

service 层StudentServiceImpl 类里引入 StudentDaoImpl 类findAll()方法进行二次分装

package com.service;

import com.dao.StudentDao;
import com.dao.StudentDaoImpl;
import com.domain.Student;
import java.util.ArrayList;

public class StudentServiceImpl implements StudentService{

    private StudentDao dao =new StudentDaoImpl();

    @Override
    public ArrayList<Student> findAll() {
        return dao.findAll();
    }

    @Override
    public Student findById(Integer id) {
        return null;
    }

    @Override
    public int insert(Student stu) {
        return 0;
    }

    @Override
    public int update(String stu) {
        return 0;
    }

    @Override
    public int delete(Integer id) {
        return 0;
    }
}

controller 层StudentController 类里引入 StudentServiceImpl 类findAll()方法 进行请求转发

package com.controller;

import com.domain.Student;
import com.service.StudentService;
import com.service.StudentServiceImpl;
import org.junit.Test;

import java.util.ArrayList;

public class StudentController {
    private StudentService Service = new StudentServiceImpl();

    @Test
    public void findAll(){
        ArrayList<Student> list = Service.findAll();
        for (Student stu : list){
            System.out.println(stu);
        }
    }

    @Test
    public void findById(){ }

    @Test
    public void insert(){ }

    @Test
    public void update(){ }

    @Test
    public void delete(){ }
}

其他四个一起贴出

dao层StudentDaoImpl 类基础方法实现

package com.dao;

import com.domain.Student;
import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

public class StudentDaoImpl implements 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.cj.jdbc.Driver");

            //2.获取连接
            con=  DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456");

            //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 (Exception e){
                    e.printStackTrace();
                }
            }
            if (rs !=null){
                try{
                    rs.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if (stat !=null){
                try{
                    stat.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }

        // 将集合对象返回
        return list;
    }

    @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.cj.jdbc.Driver");

            //2.获取连接
            con=  DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456");

            //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. 释放资源
            if (con !=null){
                try{
                    con.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if (rs !=null){
                try{
                    rs.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
            if (stat !=null){
                try{
                    stat.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }

        // 将集合对象返回
        return stu;
    }

    @Override
    public int insert(Student stu) {
        Connection con=null;
        Statement stat =null;
        int result = 0 ;
        try{
            //1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            //2.获取连接
            con=  DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456");

            //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 (Exception e){
                    e.printStackTrace();
                }
            }
            if (stat !=null){
                try{
                    stat.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }

        // 将集合对象返回
        return result;
    }

    @Override
    public int update(Student stu) {
        Connection con=null;
        Statement stat =null;
        int result = 0 ;
        try{
            //1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            //2.获取连接
            con=  DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456");

            //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 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 (Exception e){
                    e.printStackTrace();
                }
            }
            if (stat !=null){
                try{
                    stat.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }

        // 将集合对象返回
        return result;
    }

    @Override
    public int delete(Integer id) {
        Connection con=null;
        Statement stat =null;
        int result = 0 ;
        try{
            //1.注册驱动
            Class.forName("com.mysql.cj.jdbc.Driver");

            //2.获取连接
            con=  DriverManager.getConnection("jdbc:mysql://localhost:3306/test","root","123456");

            //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 (Exception e){
                    e.printStackTrace();
                }
            }
            if (stat !=null){
                try{
                    stat.close();
                }catch (Exception e){
                    e.printStackTrace();
                }
            }
        }

        // 将集合对象返回
        return result;
    }
}

service 层StudentServiceImpl 类里引入 StudentDaoImpl 类的方法进行二次分装

package com.service;

import com.dao.StudentDao;
import com.dao.StudentDaoImpl;
import com.domain.Student;
import java.util.ArrayList;

public class StudentServiceImpl implements StudentService{

    private StudentDao dao =new StudentDaoImpl();

    @Override
    public ArrayList<Student> findAll() {
        return dao.findAll();
    }

    @Override
    public Student findById(Integer id) {
        return dao.findById(id);
    }

    @Override
    public int insert(Student stu) {
        return dao.insert(stu);
    }

    @Override
    public int update(Student stu) {
        return dao.update(stu);
    }

    @Override
    public int delete(Integer id) {
        return dao.delete(id);
    }
}

controller 层StudentController 类里引入 StudentServiceImpl 类findAll()方法 进行请求转发

package com.controller;

import com.domain.Student;
import com.service.StudentService;
import com.service.StudentServiceImpl;
import org.junit.Test;

import java.util.ArrayList;
import java.util.Date;

public class StudentController {
    private StudentService Service = new StudentServiceImpl();

    @Test
    public void findAll(){
        ArrayList<Student> list = Service.findAll();
        for (Student stu : list){
            System.out.println(stu);
        }
    }

    @Test
    public void findById(){
        Student stu = Service.findById(3);
        System.out.println(stu);
    }

    @Test
    public void insert(){
        Student stu = new Student(5,"xiaoxiao",20,new Date());
        int result = Service.insert(stu);
        if (result!=0){
            System.out.println("添加成功!");
        }else {
            System.out.println("添加失败!");
        }
    }

    @Test
    public void update(){
        Student stu = Service.findById(5);
        stu.setName("zazaza");

        int result = Service.update(stu);
        if (result!=0){
            System.out.println("修改成功!");
        }else {
            System.out.println("修改失败!");
        }
    }

    @Test
    public void delete(){
        int result = Service.delete(5);
        if (result!=0){
            System.out.println("删除成功!");
        }else {
            System.out.println("删除失败!");
        }

    }
}

JDBC工具类

抽取工具类

编写配置文件

src目录下创建config.properties配置文件

driverClass = com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=123456

编写jdbc工具类

创建新的utils包JDBCUtils 工具类

package com.utils;

import java.io.InputStream;
import java.sql.*;
import java.util.Properties;

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 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.提供获取数据库连接方法
    private 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 {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (rs!=null){
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }

    // 方法重载(有三个参数调用上面的close方法,两个对象调用下面的close方法)
    public static void close(Connection con, Statement stat){
        if (con!=null){
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stat!=null){
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

使用jdbc工具类优化student表的CRUD操作

package com.dao;

import com.domain.Student;
import com.utils.JDBCUtils;

import java.sql.*;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;

public class StudentDaoImpl implements StudentDao{

    @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;
    }

    @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 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;
    }
}

SQL注入攻击

利用sql语句的漏洞来对系统进行攻击

解决SQL注入攻击

  • PreparedStatement 预编译执行者对象

    • 在执行sql语句之前,将sql语句进行提前编译。明确sql语句的格式后,就不会改变了。剩余的内容都会认为是参数!
    • SQL语句中的参数使用?作为占位符
  • 为?占位符赋值方法:setXxx(参数1,参数2);

    • Xxx代表:数据类型
    • 参数1:?的位编号(编号从1开始)
    • 参数2:?的实际参数
  • 执行SQL语句

    • 执行insertupdatedelete语句:int executeUpdate();
    • 执行select语句:ResultSet executeQuery();
public Student login(String account,String password) {
    Student stu = new Student();

    Connection conn=null;
    PreparedStatement st =null;
    ResultSet rs = null;

    try{
        conn = JDBCUtils.getConnection();

        String sql = "SELECT * FROM student WHERE loginname=? AND password=?";
        st = conn.prepareStatement(sql);
        st.setString(1,account);
        st.setString(2,password);
        rs = st.executeQuery();

        //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(conn,st,rs);
    }

    // 将集合对象返回
    return stu;
}

JDBC 事务

JDBC如何管理事务

管理事务的功能类:Connection

  • 开启事务:setAutoCommit(boolean autoCommit);参数为false,则开启事务。
  • 提交事务:commit();
  • 回滚事务:rollback();

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

在do层的方法(有些操作不需要事务,所以事务的操作放在 service层)

/*
    支持事务的添加
 */
@Override
public void save(Connection connection, User user) {
    //定义必要信息
    PreparedStatement pstm = null;
    try {
        //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);
    }
}

在 service层

/*
    事务要控制在此处
 */
@Override
public void batchAdd(List<User> users) {
    //获取数据库连接对象
    Connection con = JDBCUtils.getConnection();
    try {
        //开启事务
        con.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(con,user);
        }

        //提交事务
        con.commit();

    }catch (Exception e){
        //回滚事务
        try {
            con.rollback();
        } catch (SQLException e1) {
            e1.printStackTrace();
        }
        e.printStackTrace();
    } finally {
        //释放资源
        JDBCUtils.close(con,null);
    }
}

JDBC高级

数据库连接池

数据库连接池的概念

1.数据库连接的背景

  • 数据库连接是一种关键的、有限的、昂贵的资源,这一点在多用户的网页应用程序中体现的尤为突出
  • 对数据库连接的管理能显著影响到整个应用程序的性能指标,数据库连接池正是针对这个问题提出来的

2.数据库连接池

  • 数据库连接池负责分配管理释放数据库连接,它是允许应用程序重复使用一个现有的数据库连接,而不是在重新建立一个。这项技术明显提高对数据库操作的性能

自定义数据库连接池

DataSource
DataSource 接口概述
  • javax.sql.DataSource 接口:数据源(数据库连接池)。Java官方提供数据库连接池规范(接口)
  • 如果想完成数据库连接池技术,就必须实现DataSource接口
  • 核心功能:获取数据库连接对象:Connection getConnection();
自定义数据库连接池
  • 定义一个类,实现DataSource接口。
  • 定义一个容器,用于保存过个Connection连接对象。
  • 定义静态代码块,通过JDBC工具类获取10个连接保存到容器中。
  • 重写getConnection方法,从容器中获取一个连接并返回。
  • 定义getSize方法,用于获取容器的大小并返回。
package com.liu;

import com.utils.JDBCUtils;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;

public class MyDataSource implements DataSource {

    //1.定义一个容器,用于保存过个`Connection`连接对象
    private static List<Connection> pool = Collections.synchronizedList(new ArrayList<>());

    //2.定义静态代码块,通过JDBC工具类获取10个连接保存到容器中
    static {
        for (int i=1;i <=10 ;i++){
            Connection con = JDBCUtils.getConnection();
            pool.add(con);
        }
    }

    //3.重写` getConnection`方法,从容器中获取一个连接
    @Override
    public Connection getConnection() throws SQLException {
        if (pool.size() >0){
            Connection con = pool.remove(0);
            return con;
        }else{
            throw new RuntimeException("连接的数量已用尽");
        }
    }

    //4.定义`getSize`方法,用于获取容器的大小
    public int getSize(){
        return pool.size();
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}
自定义数据库连接池测试
package com.liu;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class MyDataSourceTest {
    public static void main(String[] args) throws Exception {
        // 1.创建连接池对象
        MyDataSource dataSource = new MyDataSource();

        System.out.println("使用之前的对象:"+dataSource.getSize());

        //2.通过连接池对象获取连接对象
        Connection con = dataSource.getConnection();
        System.out.println(con.getClass());     // 查看他的类      class com.mysql.cj.jdbc.ConnectionImpl

        //3.查询学生表的全部信息
        String sql = "SELECT * FROM student";
        PreparedStatement pst = con.prepareStatement(sql);

        //4.执行sql语句,接受结果集
        ResultSet rs = pst.executeQuery();

        //5.处理结果集
        while (rs.next()){
            System.out.println(rs.getInt("sid")+"\t"+rs.getString("name")+"\t"+rs.getInt("age")+"\t"+rs.getDate("birthday"));
        }

        //6.释放资源
        rs.close();
        pst.close();
        con.close();	// 还是关闭了

        System.out.println("使用之后的对象:"+dataSource.getSize());
    }
}

还是关闭了没有解决问题

归还连接
归还数据库连接的方式
装饰设计模式(太多重写方法,不适合)

新建 MyConnection 类

package com.liu;

import java.sql.*;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

// 1.定义一个类,实现Connection接口
public class MyConnection implements Connection {

    //2.定义连接对象和连接池容器对象的成员变量
    private Connection con;
    private List<Connection> pool;

    //3.通过有参构造方法为成员变量赋值
    public MyConnection(Connection con, List<Connection> pool) {
        this.con = con;
        this.pool = pool;
    }

    //4.重写close方法,完成归还连接
    @Override
    public void close() throws SQLException {
        pool.add(con);
    }

    //5.剩余方法,还是调用原有的连接对象中的功能即可
    @Override
    public Statement createStatement() throws SQLException {
        return con.createStatement();
    }

    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        return con.prepareStatement(sql);
    }

    @Override
    public CallableStatement prepareCall(String sql) throws SQLException {
        return con.prepareCall(sql);
    }

    ... 		//后面都一样调用原有的连接对象中的功能
}

在 连接池类(MyDataSource )里面修改一下(第三步)代码

 //3.重写` getConnection`方法,从容器中获取一个连接
 @Override
 public Connection getConnection() throws SQLException {
     if (pool.size() >0){
         Connection con = pool.remove(0);
         //6.通过自定义的连接对象,对原有的连接对象进行包装
         MyConnection myCon = new MyConnection(con,pool);
         return myCon;
     }else{
         throw new RuntimeException("连接的数量已用尽");
     }
 }
适配器设计模式(还是麻烦)

1.适配器设计模式归还数据库连接的思想

  • 我们可以提供一个适配器类,实现Connection接口,将所有方法进行实现(除了close方法)
  • 自定义连接类只需要继承这个适配器类,重写需要改进的close()方法即可

创建 MyAdapter 适配器类

package com.liu;

import java.sql.*;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.Executor;

// 1.定义一个适配器类,实现Connection接口
public abstract class MyAdapter implements Connection {

    //2.定义连接对象成员变量
    private Connection con;

    //3.通过有参构造方法为成员变量赋值
    public MyAdapter(Connection con) {
        this.con = con;
    }

    //4.重写所有的抽象方法(除了close),还是调用原有的连接对象中的功能即可
    @Override
    public Statement createStatement() throws SQLException {
        return con.createStatement();
    }

    @Override
    public PreparedStatement prepareStatement(String sql) throws SQLException {
        return con.prepareStatement(sql);
    }

    ... 		//后面都一样调用原有的连接对象中的功能
}

在 MyConnection 类里继承 MyAdapter 适配器类

package com.liu;

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

// 5.定义一个类,继承适配器类
public class MyConnection extends MyAdapter {

    //6.定义连接对象和连接池容器对象的成员变量
    private Connection con;
    private List<Connection> pool;

    //7.通过有参构造方法为成员变量赋值
    public MyConnection(Connection con, List<Connection> pool) {
        super(con);
        this.con = con;
        this.pool = pool;
    }

    //8.重写close方法,完成归还连接
    @Override
    public void close() throws SQLException {
        pool.add(con);
    }
}

在 MyDataSource 连接池类里 包装

//3.重写` getConnection`方法,从容器中获取一个连接
@Override
public Connection getConnection() throws SQLException {
    if (pool.size() >0){
        Connection con = pool.remove(0);
        //9.通过自定义的连接对象,对原有的连接对象进行包装
        MyConnection myCon = new MyConnection(con,pool);
        return myCon;
    }else{
        throw new RuntimeException("连接的数量已用尽");
    }
}
动态代理方式(最简单)
  • 动态代理:在不改变目标对象方法的类情况下对方法进行增强
  • 组成
    • 被代理的对象:真实的对象
    • 代理对象:在内存的一个对象
  • 要求:代理对象必须和被代理对象实现相同的接口
  • 实现:Proxy.newProxyInstance()

直接在 MyDataSource 连接池类里修改

package com.liu;

import com.utils.JDBCUtils;

import javax.sql.DataSource;
import java.io.PrintWriter;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.sql.Connection;
import java.sql.SQLException;
import java.sql.SQLFeatureNotSupportedException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Logger;

public class MyDataSource implements DataSource {

    //1.定义一个容器,用于保存过个`Connection`连接对象
    private static List<Connection> pool = Collections.synchronizedList(new ArrayList<>());

    //2.定义静态代码块,通过JDBC工具类获取10个连接保存到容器中
    static {
        for (int i=1;i <=10 ;i++){
            Connection con = JDBCUtils.getConnection();
            pool.add(con);
        }
    }

    //3.动态代理方式
    @Override
    public Connection getConnection() throws SQLException {
        if (pool.size() >0){
            Connection con = pool.remove(0);

            Connection proxyCon = (Connection) Proxy.newProxyInstance(con.getClass().getClassLoader(), new Class[]{Connection.class}, new InvocationHandler() {

                /*
                * 执行Connection实现类连接对象所有的方法都会经过invoke
                * 如果是close方法,归还连接
                * 如果不是,直接执行连接对象原有的功能即可
                * */
                @Override
                public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                    if (method.getName().equals("close")){
                        pool.add(con);
                        return null;
                    }else{
                        return method.invoke(con,args);
                    }
                }
            });
            return proxyCon;
        }else{
            throw new RuntimeException("连接的数量已用尽");
        }
    }


    //4.定义`getSize`方法,用于获取容器的大小
    public int getSize(){
        return pool.size();
    }

    @Override
    public Connection getConnection(String username, String password) throws SQLException {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> iface) throws SQLException {
        return null;
    }

    @Override
    public boolean isWrapperFor(Class<?> iface) throws SQLException {
        return false;
    }

    @Override
    public PrintWriter getLogWriter() throws SQLException {
        return null;
    }

    @Override
    public void setLogWriter(PrintWriter out) throws SQLException {

    }

    @Override
    public void setLoginTimeout(int seconds) throws SQLException {

    }

    @Override
    public int getLoginTimeout() throws SQLException {
        return 0;
    }

    @Override
    public Logger getParentLogger() throws SQLFeatureNotSupportedException {
        return null;
    }
}

开源数据库连接池的使用

C3P0

使用步骤:

  • 导入jar包(c3p0-0.9.5.2.jarmchange-commons-java-0.2.12.jar
  • 导入配置文件(c3p0-config.xml)到src目录下
<c3p0-config>
  <!-- 使用默认的配置读取连接池对象 -->
  <default-config>
  	<!--  连接参数 -->
    <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
    <property name="user">root</property>
    <property name="password">123456</property>
    
    <!-- 连接池参数 -->
    <!-- 初始化的连接数量 -->
    <property name="initialPoolSize">5</property>
    <!-- 最大连接数量 -->
    <property name="maxPoolSize">10</property>
    <!-- 超时时间 -->
    <property name="checkoutTimeout">3000</property>
  </default-config>

  <!-- 不指定名字,就用上面的默认设置 -->
  <named-config name="otherc3p0"> 
    <!--  连接参数 -->
    <property name="driverClass">com.mysql.cj.jdbc.Driver</property>
    <property name="jdbcUrl">jdbc:mysql://localhost:3306/test</property>
    <property name="user">root</property>
    <property name="password">123456</property>
    
    <!-- 连接池参数 -->
    <property name="initialPoolSize">5</property>
    <property name="maxPoolSize">8</property>
    <property name="checkoutTimeout">1000</property>
  </named-config>
</c3p0-config>
  • 创建C3P0连接池对象
  • 获取数据库连接进行使用

注意:C3P0的配置文件会自动加载,但是必须叫c3p0-config.xml 或 c3p0-config.properties

创建 C3P0Test 测试类

package com.liu;

import com.mchange.v2.c3p0.ComboPooledDataSource;

import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class C3P0Test {
    public static void main(String[] args) throws Exception {
        //1.创建c3p0的数据库连接池对象
        DataSource dataSource= new ComboPooledDataSource();

        //2.通过连接池对象获取连接对象
        Connection con = dataSource.getConnection();

        //3.查询学生表的全部信息
        String sql = "SELECT * FROM student";
        PreparedStatement pst = con.prepareStatement(sql);

        //4.执行sql语句,接受结果集
        ResultSet rs = pst.executeQuery();

        //5.处理结果集
        while (rs.next()){
            System.out.println(rs.getInt("sid")+"\t"+rs.getString("name")+"\t"+rs.getInt("age")+"\t"+rs.getDate("birthday"));
        }

        //6.释放资源
        rs.close();
        pst.close();
        con.close();
    }
}
Druid

使用步骤:

  • 导入jar包(druid-1.0.9.jar
  • 导入配置文件(druid.properties,可以自定义命名)到src目录下
driverClassName=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=root
password=123456
initialSize=5		#初始化的连接数量
maxActive=10		#最大连接数量
maxWait=3000		#超时时间
  • 通过Properties集合加载配置文件。
  • 通过Druid 连接池工厂类获取数据库连接池对象。
  • 获取数据库连接进行使用

Druid不会自动加载配置文件,需要我们手动加载,但是文件名可以自定义

package com.liu;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Properties;

public class DruidTest {
    public static void main(String[] args) throws Exception {
        //获取配置文件的流对象
        InputStream is = DruidTest.class.getClassLoader().getResourceAsStream("druid.properties");

        //1.通过Properties集合加载配置文件
        Properties prop = new Properties();
        prop.load(is);

        //2.通过Druid 连接池工厂类获取数据库连接池对象
        DataSource dataSource = DruidDataSourceFactory.createDataSource(prop);

        //3.通过连接池对象获取数据库连接进行使用
        Connection con=dataSource.getConnection();

        //查询学生表的全部信息
        String sql = "SELECT * FROM student";
        PreparedStatement pst = con.prepareStatement(sql);

        //执行sql语句,接受结果集
        ResultSet rs = pst.executeQuery();

        //处理结果集
        while (rs.next()){
            System.out.println(rs.getInt("sid")+"\t"+rs.getString("name")+"\t"+rs.getInt("age")+"\t"+rs.getDate("birthday"));
        }

        //释放资源
        rs.close();
        pst.close();
        con.close();
    }
}
连接池的工具类

以 Druid 为例
创建 DruidSourceUtils工具类

package com.utils;

import com.alibaba.druid.pool.DruidDataSourceFactory;

import javax.sql.DataSource;
import java.io.InputStream;
import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Properties;

public class DataSourceUtils {
    //1.私有构造方法
    private DataSourceUtils(){};

    //2.声明数据源变量
    private static DataSource dataSource;

    //3.提供静态代码块,完成配置文件的加载和获取数据库连接池对象
    static {
        try {
            //获取配置文件的流对象
            InputStream is = DataSourceUtils.class.getClassLoader().getResourceAsStream("druid.properties");

            //通过Properties集合加载配置文件
            Properties prop = new Properties();
            prop.load(is);

            //通过Druid 连接池工厂类获取数据库连接池对象
            dataSource = DruidDataSourceFactory.createDataSource(prop);
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    //4.提供一个获取数据库连接的方法
    public static Connection getConnection(){
        Connection con =null;
        try {
            con = dataSource.getConnection();
        } catch (SQLException e) {
            e.printStackTrace();
        }
        return con;
    }

    //5.供一个获取数据库连接池对象的方法
    public static DataSource getDataSource(){
        return dataSource;
    }

    //6.释放资源
    public 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){
        if (con!=null){
            try {
                con.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
        if (stat!=null){
            try {
                stat.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
        }
    }
}

测试

package com.liu;

import com.utils.DataSourceUtils;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

public class DruidTest {
    public static void main(String[] args) throws Exception {
        //1.通过连接池工具类获取一个数据库连接

        Connection con= DataSourceUtils.getConnection();

        //查询学生表的全部信息
        String sql = "SELECT * FROM student";
        PreparedStatement pst = con.prepareStatement(sql);

        //执行sql语句,接受结果集
        ResultSet rs = pst.executeQuery();

        //处理结果集
        while (rs.next()){
            System.out.println(rs.getInt("sid")+"\t"+rs.getString("name")+"\t"+rs.getInt("age")+"\t"+rs.getDate("birthday"));
        }

        DataSourceUtils.close(con,pst,rs);
    }
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值