教程揭秘 | 动力节点内部Java零基础教学文档第三篇:JDBC

接上期后续

本期分享第三章节-JDBC

已经分享两章了,大家都跟上了吗?

跟上的我就不得不说真厉害~

没跟上的要加油了!

但时间还久,后面要学还有很多~

大家需保持耐心慢慢来

争取你们学习的速度!

跟上我更新的速度哦~ 

今日新篇章

【JDBC】

【主要内容】

1. JDBC概述

2. 使用JDBC完成添加操作

3. 使用JDBC完成更新和删除

4. DBUtils的简单封装

5. 使用JDBC完成查询

6. 使用JDBC完成分页查询

7. 常用接口详解

8. JDBC批处理

9. SQL注入问题

10. 事务处理解决转账问题

11. 连接池

12. 使用反射对DBUtils再次的封装

13. BaseDAO的封装

【学习目标】

1. JDBC概述

1.1 什么是JDBC

JDBC(Java DataBase Connectivity)就是Java数据库连接,说白了就是用Java语言来操作数据库。原来我们操作数据库是在控制台使用SQL语句来操作数据库,JDBC是用Java语言向数据库发送SQL语句。

1.2 JDBC的原理

早期SUN公司的天才们想编写一套可以连接天下所有数据库的API,但是当他们刚刚开始时就发现这是不可完成的任务,因为各个厂商的数据库服务器差异太大了。后来SUN开始与数据库厂商们讨论,最终得出的结论是,由SUN提供一套访问数据库的规范(就是一组接口),并提供连接数据库的协议标准,然后各个数据库厂商会遵循SUN的规范提供一套访问自己公司的数据库服务器的API出现。SUN提供的规范命名为JDBC,而各个厂商提供的,遵循了JDBC规范的,可以访问自己数据库的API被称之为驱动!

JDBC是接口,而JDBC驱动才是接口的实现,没有驱动无法完成数据库连接!每个数据库厂商都有自己的驱动,用来连接自己公司的数据库。

当然还有第三方公司专门为某一数据库提供驱动,这样的驱动往往不是开源免费的!

1.3 程序员,JDBC,JDBC驱动的关系及说明

1.3.1 JDBC API

提供者:Sun公司

内容:供程序员调用的接口与类,集成在java.sql和javax.sql包中,如

DriverManager类   作用:管理各种不同的JDBC驱动

Connection接口  

Statement接口     

ResultSet接口

1.3.2 JDBC 驱动

提供者:数据库厂商

作用:负责连接各种不同的数据库

1.3.3 Java程序员

         JDBC对Java程序员而言是API,对实现与数据库连接的服务提供商而言是接口模型。

1.3.4 三方关系

SUN公司是规范制定者,制定了规范JDBC(连接数据库规范)

数据库厂商微软、甲骨文等分别提供实现JDBC接口的驱动jar包

程序员学习JDBC规范来应用这些jar包里的类。

1.4 总结

简单地说,JDBC 可做三件事:与数据库建立连接、发送 操作数据库的语句并处理结果。

DriverManager :依据数据库的不同,管理JDBC驱动

Connection :负责连接数据库并担任传送数据的任务  

Statement :由 Connection 产生、负责发送执行SQL语句

ResultSet:负责保存Statement执行后所产生的查询结果

2. JDBC操作数据库的步骤

2.1 总体步骤

1. 加载一个Driver驱动

2. 创建数据库连接(Connection)

3. 创建SQL命令发送器Statement

4. 创建SQL

5. 通过Statement发送SQL命令并得到结果

6. 处理SQL结果(select语句)

7. 关闭数据库资源

ResultSet

Statement

Connection

2.2 详细步骤

2.2.1 加载驱动

加载JDBC驱动是通过调用方法java.lang.Class.forName(),下面列出常用的几种数据库驱动程序加载语句的形式 :

Class.forName(“oracle.JDBC.driver.OracleDriver”);//使用Oracle的JDBC驱动程序

Class.forName(“com.microsoft.JDBC.sqlserver.SQLServerDriver”);//使用SQL Server的JDBC驱动程序

Class.forName(“com.ibm.db2.JDBC.app.DB2Driver”);//使用DB2的JDBC驱动程序

Class.forName(“com.mysql.jdbc.Driver”);//使用MySql的JDBC驱动程序

2.2.2 创建数据库连接

与数据库建立连接的方法是调用DriverManager.getConnection(String url, String user, String password )方法

Connection conn=null;

String url=”jdbc:mysql://localhost:3306/whpowernode?useUnicode=true&useSSL=false&charsetUnicode=UTF8″;

String user=“root”;

String password=“123456″;

conn = DriverManager.getConnection(url, user, password);

2.2.3 创建Statement并发送命令

Statement对象用于将 SQL 语句发送到数据库中,或者理解为执行sql语句

有三种 Statement对象:

Statement:用于执行不带参数的简单SQL语句;

PreparedStatement(从 Statement 继承):用于执行带或不带参数的预编译SQL语句;

CallableStatement(从PreparedStatement 继承):用于执行数据库存储过程的调用。

2.2.4 处理ResultSet结果

ResultSet对象是executeQuery()方法的返回值,它被称为结果集,它代表符合SQL语句条件的所有行,并且它通过一套getXXX方法(这些get方法可以访问当前行中的不同列)提供了对这些行中数据的访问。

ResultSet里的数据一行一行排列,每行有多个字段,且有一个记录指针,指针所指的数据行叫做当前数据行,我们只能来操作当前的数据行。我们如果想要取得某一条记录,就要使用ResultSet的next()方法 ,如果我们想要得到ResultSet里的所有记录,就应该使用while循环。

ResultSet对象自动维护指向当前数据行的游标。每调用一次next()方法,游标向下移动一行。

初始状态下记录指针指向第一条记录的前面,通过next()方法指向第一条记录。循环完毕后指向最后一条记录的后面。

2.2.5 关闭数据库资源

作为一种好的编程风格,应在不需要Statement对象和Connection对象时显式地关闭它们。关闭Statement对象和Connection对象的语法形式为:

public void close() throws SQLException

用户不必关闭ResultSet。当它的 Statement 关闭、重新执行或用于从多结果序列中获取下一个结果时,该ResultSet将被自动关闭。

注意:要按先ResultSet结果集,后Statement,最后Connection的顺序关闭资源,因为Statement和ResultSet是需要连接是才可以使用的,所以在使用结束之后有可能其他的Statement还需要连接,所以不能先关闭Connection。

3. 准备工作

3.1 创建数据并创建student表

3.2 创建项目

3.3 创建lib目录并引入MYSQL驱动包

3.4 把lib包引入项目环境

4. 使用JDBC完成添加操作

4.1 步骤

Ø 加载MySQL的JDBC驱动

Ø 建立数据的连接

Ø 创建SQL命令的发送器

Ø 编写SQL

Ø 使用SQL命令发送器发送SQL命令并得到结果

Ø 处理结果

Ø 关闭数据库资源

4.2 代码

package com.bjpowernode.jdbc;

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

public class Test01Add {
    // 驱动器路径
    private static final String DRIVER = “com.mysql.jdbc.Driver”;
    //连接数据库地址
    private static final String URL = “jdbc:mysql://localhost:3306/whpowernode?useUnicode=true&useSSL=false&characterEncoding=UTF8”;
    //数据库用户名
    private static final String USER_NAME = “root”;
    //数据库密码
    private static final String USER_PASSWORD = “123456”;
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 加载JDBC访问Oracle的驱动
        Class.forName(DRIVER);
        // 建立和数据库的连接
        Connection conn = DriverManager.getConnection(URL, USER_NAME, USER_PASSWORD);
        // 创建SQL命令发送器
        Statement stmt = conn.createStatement();
        // 使用SQL命令发送器发送SQL命令并得到结果
        String sql = “insert into student values(1,’小刚’,32,’男’,’湖北省武汉市’)”;
        int n = stmt.executeUpdate(sql);
        // 处理结果
        if (n > 0) {
            System.out.println(“添加成功”);
        } else {
            System.out.println(“添加失败”);
        }
        // 关闭数据库资源
        stmt.close();
        conn.close();
    }
}

4.3 URL详解

4.3.1 为什么要定义URL

Java和MySQL是厂商的,Java程序和MySQL数据库此时不在同一个进程下,此时Java程序需要向MySQL发送请求。

4.3.2 如何发送请求呢?

jdbc:mysql://localhost:3306/whpowernode?useUnicode=true&useSSL=false&characterEncoding=UTF-8

使用URL的方式发送

jdbc 主协议

mysql 子协议

localhost MySQL服务器的地址,如果服务器就是我自己的主机,那么定义localhost就可以了

3306 MySQL服务器的端口号

whpowernode MySQL数据库服务器的数据库名称

useUnicode=true  Java和MySQL交互使用Unicode编码

useSSL=false Java和MySQL交互不使用安全层协议

characterEncoding=UTF-8 Java和MySQL交互的编码方式为UTF-8  【如果不设置会有乱码的】

4.4 查看数据库

4.4.1 一个URL由哪些部分组成

Ø 协议://服务器主机:端口/服务器路径?查询参数

Ø 协议 jdbc:mysql:

Ø 服务器主机 localhost

Ø 端口 3306

Ø 服务器路径 whpowernode

Ø 参数useUnicode=true&useSSL=false&characterEncoding=UTF8

5. 使用JDBC完成更新和删除

5.1 步骤

Ø 加载MySQL的JDBC驱动

Ø 建立数据的连接

Ø 创建SQL命令的发送器

Ø 编写SQL

Ø 使用SQL命令发送器发送SQL命令并得到结果

Ø 处理结果

Ø 关闭数据库资源

5.2 修改代码

package com.bjpowernode.jdbc;

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

public class Test02Update {
    // 驱动器路径
    private static final String DRIVER = “com.mysql.jdbc.Driver”;
    //连接数据库地址
    private static final String URL = “jdbc:mysql://localhost:3306/whpowernode?useUnicode=true&useSSL=false&characterEncoding=UTF8”;
    //数据库用户名
    private static final String USER_NAME = “root”;
    //数据库密码
    private static final String USER_PASSWORD = “123456”;
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 加载Oracle的JDBC驱动
        Class.forName(DRIVER);
        // 建立数据的连接
        Connection conn=DriverManager.getConnection(URL, USER_NAME, USER_PASSWORD);
        // 创建SQL命令的发送器
        Statement stat=conn.createStatement();
        // 编写SQL
        String sql=”update student set name=’小明’,age=23,sex=’女’,address=’武汉’ where id=1″;
        // 使用SQL命令发送器发送SQL命令并得到结果
        int res=stat.executeUpdate(sql);
        // 处理结果
        if(res>0){
            System.out.println(“修改成功”);
        }
        else{
            System.out.println(“处理失败”);
        }
        // 关闭数据库资源
        stat.close();
        conn.close();
    }
}

5.3 删除代码

package com.bjpowernode.jdbc;

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

public class Test03Delete {
    // 驱动器路径
    private static final String DRIVER = “com.mysql.jdbc.Driver”;
    //连接数据库地址
    private static final String URL = “jdbc:mysql://localhost:3306/whpowernode?useUnicode=true&useSSL=false&characterEncoding=UTF8”;
    //数据库用户名
    private static final String USER_NAME = “root”;
    //数据库密码
    private static final String USER_PASSWORD = “123456”;
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        // 加载Oracle的JDBC驱动
        Class.forName(DRIVER);
        // 建立数据的连接
        Connection conn=DriverManager.getConnection(URL, USER_NAME, USER_PASSWORD);
        // 创建SQL命令的发送器
        Statement stat=conn.createStatement();
        // 编写SQL
        String sql=”delete from student where id=1″;
        // 使用SQL命令发送器发送SQL命令并得到结果
        int res=stat.executeUpdate(sql);
        // 处理结果
        if(res>0){
            System.out.println(“删除成功”);
        }
        else{
            System.out.println(“删除失败”);
        }
        // 关闭数据库资源
        stat.close();
        conn.close();
    }
}

6. DBUtils的简单封装

6.1 为什么要封装

从我们上面的代码大家可以看到,每一次写我们创建一个连接,创建一个发送SQL的对象,最后还要关闭,那么我们可以考虑把这重复的代码提取出来!

6.2 创建DBUtils封装代码

package com.bjpowernode.utils;

import java.io.Closeable;
import java.io.IOException;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DBUtils {
    // 驱动器路径
    private static final String DRIVER = “com.mysql.jdbc.Driver”;
    // 连接数据库地址
    private static final String URL = “jdbc:mysql://localhost:3306/whpowernode?useUnicode=true&useSSL=false&characterEncoding=UTF8”;
    // 数据库用户名
    private static final String USER_NAME = “root”;
    // 数据库密码
    private static final String USER_PASSWORD = “123456”;

    /**
     * 静态加载驱动程序
     */
    static {
        try {
            Class.forName(DRIVER);
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
    }
    /**
     * @return 连接对象
     */
    public static Connection getConn() {
        try {
            return  DriverManager.getConnection(URL, USER_NAME, USER_PASSWORD);
        } catch (SQLException e) {
            e.printStackTrace();
            System.out.println(“创建连接对象异常”);
        }
        return null;
    }

    /**
     * 关闭资源
     */
    public static void close(AutoCloseable closeable) {
        try {
            if (closeable != null) {
                closeable.close();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

6.3 创建上面的工具类对象前面的代码进行改造

package com.bjpowernode.jdbc;

import com.bjpowernode.utils.DBUtils;

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

public class Test01Add {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Connection conn = DBUtils.getConn();
        // 创建SQL命令发送器
        Statement stmt = conn.createStatement();
        // 使用SQL命令发送器发送SQL命令并得到结果
        String sql = “insert into student values(1,’小刚’,32,’男’,’湖北省武汉市’)”;
        int n = stmt.executeUpdate(sql);
        // 处理结果
        if (n > 0) {
            System.out.println(“添加成功”);
        } else {
            System.out.println(“添加失败”);
        }
        // 关闭数据库资源
        DBUtils.close(stmt);
        DBUtils.close(conn);
    }
}

 

7. 使用JDBC完成查询

7.1 循环向student表里面插入20条数

package com.bjpowernode.jdbc;

import com.bjpowernode.utils.DBUtils;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Random;

public class Test01Add20 {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Connection conn = DBUtils.getConn();
        // 创建SQL命令发送器
        Statement stmt = conn.createStatement();
        // 使用SQL命令发送器发送SQL命令并得到结果
        Random random=new Random();
        for (int i = 1; i <=20 ; i++) {
            Integer id=i;
            String name=”小明”+i;
            int age=random.nextInt(100);
            String sex=random.nextBoolean()?”男”:”女”;
            String address=”武汉”+i;

            String sql = “insert into student values(“+i+”,'”+name+”‘,”+age+”,'”+sex+”‘,'”+address+”‘)”;
            int n = stmt.executeUpdate(sql);
            // 处理结果
            if (n > 0) {
                System.out.println(“添加成功”);
            } else {
                System.out.println(“添加失败”);
            }
        }
        // 关闭数据库资源
        DBUtils.close(stmt);
        DBUtils.close(conn);
    }
}

7.2 查询

package com.bjpowernode.jdbc;

import com.bjpowernode.utils.DBUtils;

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

public class Test04Query {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Connection conn = DBUtils.getConn();
        // 创建SQL命令发送器
        Statement stmt = conn.createStatement();
        // 编写SQL
        String sql=”select * from student”;
        // 使用SQL命令发送器发送SQL命令并得到结果
        ResultSet rs=stmt.executeQuery(sql);
        // 处理结果
        while(rs.next()){
            int id=rs.getInt(1);
            String name=rs.getString(2);
            int age=rs.getInt(3);
            String sex=rs.getString(4);
            String address=rs.getString(5);
            System.out.println(id+”  “+name+”  “+age+”   “+sex+”   “+address);
        }
        // 关闭数据库资源
        DBUtils.close(rs);
        DBUtils.close(stmt);
        DBUtils.close(conn);
    }
}

8. 使用JDBC完成分页查询

package com.bjpowernode.jdbc;

import com.bjpowernode.utils.DBUtils;

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

public class Test05QueryForPage {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Connection conn = DBUtils.getConn();
        // 创建SQL命令发送器
        Statement stmt = conn.createStatement();
        int pageNum=2; //页码
        int pageSize=5;//每页显示的条数
        // 编写SQL
        String sql=”select * from student limit “+(pageNum-1)*pageSize+”,”+pageSize;
        // 使用SQL命令发送器发送SQL命令并得到结果
        ResultSet rs=stmt.executeQuery(sql);
        // 处理结果
        while(rs.next()){
            int id=rs.getInt(1);
            String name=rs.getString(2);
            int age=rs.getInt(3);
            String sex=rs.getString(4);
            String address=rs.getString(5);
            System.out.println(id+”  “+name+”  “+age+”   “+sex+”   “+address);
        }
        // 关闭数据库资源
        DBUtils.close(rs);
        DBUtils.close(stmt);
        DBUtils.close(conn);
    }
}

9. 常用接口详解

9.1 DriverManager

用于管理JDBC驱动的服务类。程序中使用该类的的主要功能是获取Connection对象,该类包含如下方法:

public static Connection getConnection(String url, String user, String password) throws SQLException

该方法获得url对应数据库的连接;

9.2 Connection

代表数据库连接对象,每个Connection代表一个物理连接会话。要想访问数据库,必须先得到数据库连接。该接口的常用方法如下:

Statement createStatement() throws SQLException; 该方法返回一个Statement对象;

PreparedStatement prepareStatement(String sql)throws SQLException;该方法返回预编译的Statement对象,即将SQL语句提交到数据库进行预编译;

CallableStatement prepareCall(String sql) throws SQLException;

该方法返回CallableStatement对象,该对象用于调用存储过程。

上面上个方法都返回用于执行sql语句的Statement对象,PreparedStatement和CallableStatement是Statement的子类,只有获得了Statement之后才可以执行sql语句;

除此之外,Connection还有如下几个用于控制事务的方法。

Savepoint setSavepoint() throws SQLException;创建一个保存点;

Savepoint setSavepoint(String name) throws SQLException;以指定名字来创建一个保存点;

void setTransactionIsolation(int level) throws SQLException;设置事务的隔离级别;

void rollback() throws SQLException;回滚事务;

void rollback(Savepoint savepoint) throws SQLException;将事务回滚到指定的保存点;

void setAutoCommit(boolean autoCommit) throws SQLException;关闭自动提交,打开事务;

void commit() throws SQLException;提交事务;

9.3 Statement

用于执行sql语句的工具接口。该对象既可以执行DDL,DCL语句,也可以用于执行DML语句,还可以用于执行sql查询。当执行sql查询时,返回查询到的结果集。它的常用方法如下:

ResultSet executeQuery(String sql) throws SQLException;该方法用于执行查询语句,并返回查询结果对应ResultSet对象。该方法只能用于执行查询语句。

int executeUpdate(String sql) throws SQLException;该方法用于执行DML语句,并返回受影响的行数;该方法也可用于执行DDL语句,执行DDL语句将返回0;

boolean execute(String sql) throws SQLException;改方法可以执行任何sql语句。如果执行后第一个结果为ResultSet对象,则返回true;如果执行后第一个结果为受影响的行数或没有任何结果,则返回false;

9.4 PreparedStatement

预编译的Statement对象,PreparedStatement是Statement的子接口,它允许数据库预编译sql语句(这些sql语句通常带有参数),以后每次只改变sql命令的参数,避免数据库每次都需要编译sql语句,无需再传入sql语句,

只要为预编译的sql语句传入参数值即可。所以它比Statement多了如下方法:

void setXxx(int parameterIndex, Xxx value):该方法根据传入参数值的类型不同,需要使用不同的方法。传入的值根据索引传给sql语句中指定位置的参数。

9.5 ResultSet

结果集对象。该对象包含访问查询结果的方法,ResultSet可以通过列索引或列名获得列数据。它包含了如下常用方法来移动记录指针。

void close() throws SQLException;释放ResultSet对象;

boolean absolute( int row ) throws SQLException;将结果集的记录指针移动到第row行,如果row是负数,则移动到倒数第row行,如果移动后的记录指针指向一条有效记录,则该方法返回true;

boolean next() throws SQLException;将结果集的记录指针定位到下一行,如果移动后的记录指针指向一条有效的记录,则该方法返回true;

boolean last() throws SQLException;将结果集的记录指针定位到最后一行,如果移动后的记录指针指向一条有效的记录,则该方法返回true;

 

10. SQL注入问题

10.1 问题引入

10.1.1 创建sys_user表并初始化数据

10.1.2 编写代码实现登陆

package com.bjpowernode.jdbc;

import com.bjpowernode.utils.DBUtils;

import java.sql.Connection;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Scanner;

public class Test06Login {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        Connection conn = DBUtils.getConn();
        // 创建SQL命令发送器
        Statement stmt = conn.createStatement();
        //从键盘输入
        Scanner scanner=new Scanner(System.in);
        System.out.println(“请输入用户名:”);
        String username = scanner.nextLine();
        System.out.println(“请输入密码:”);
        String password=scanner.nextLine();
        // 编写SQL
        String sql=”select * from sys_user where username='”+username+”‘ and password='”+password+”‘”;
        // 使用SQL命令发送器发送SQL命令并得到结果
        ResultSet rs=stmt.executeQuery(sql);
        System.out.println(sql);
        // 处理结果
        if(rs.next()){
            System.out.println(“登陆成功”);
            int id=rs.getInt(1);
            String name=rs.getString(2);
            String pwd=rs.getString(3);
            System.out.println(id+”  “+name+”  “+pwd);
        }
        // 关闭数据库资源
        DBUtils.close(rs);
        DBUtils.close(stmt);
        DBUtils.close(conn);
    }
}

10.1.3 测试登陆

10.2 解决办法【使用PreparedStatement】

10.2.1 技术原理

该 PreparedStatement接口继承Statement,并与之在两方面有所不同:

PreparedStatement 实例包含已编译的 SQL 语句。这就是使语句“准备好”。包含于 PreparedStatement 对象中的 SQL 语句可具有一个或多个 IN 参数。IN参数的值在 SQL 语句创建时未被指定。相反的,该语句为每个 IN 参数保留一个问号(“?”)作为占位符。每个问号的值必须在该语句执行之前,通过适当的setXXX 方法来提供。

由于 PreparedStatement 对象已预编译过,所以其执行速度要快于 Statement 对象。因此,多次执行的 SQL 语句经常创建为 PreparedStatement 对象,以提高效率。

作为 Statement 的子类,PreparedStatement 继承了 Statement 的所有功能。另外它还添加了一整套方法,用于设置发送给数据库以取代 IN 参数占位符的值。同时,三种方法 execute、 executeQuery 和 executeUpdate 已被更改以使之不再需要参数。这些方法的 Statement 形式(接受 SQL 语句参数的形式)不应该用于 PreparedStatement 对象。

 

10.2.2 创建对象

以下的代码段(其中 con 是 Connection 对象)创建包含带两个 IN 参数占位符的 SQL 语句的 PreparedStatement 对象:

PreparedStatement pstmt = con.prepareStatement(“UPDATE table4 SET m = ? WHERE x = ?”);

pstmt 对象包含语句 “UPDATE table4 SET m = ? WHERE x = ?”,它已发送给DBMS,并为执行作好了准备。

 

10.2.3 传递参数

在执行 PreparedStatement 对象之前,必须设置每个 ? 参数的值。这可通过调用 setXXX 方法来完成,其中 XXX 是与该参数相应的类型。例如,如果参数具有Java 类型 long,则使用的方法就是 setLong。setXXX 方法的第一个参数是要设置的参数的序数位置,第二个参数是设置给该参数的值。例如,以下代码将第一个参数设为 123456789,第二个参数设为 100000000:

pstmt.setLong(1, 123456789);

pstmt.setLong(2, 100000000);

一旦设置了给定语句的参数值,就可用它多次执行该语句,直到调用clearParameters 方法清除它为止。在连接的缺省模式下(启用自动提交),当语句完成时将自动提交或还原该语句。

如果基本数据库和驱动程序在语句提交之后仍保持这些语句的打开状态,则同一个 PreparedStatement 可执行多次。如果这一点不成立,那么试图通过使用PreparedStatement 对象代替 Statement 对象来提高性能是没有意义的。

 

10.2.4 修改代码

package com.bjpowernode.jdbc;

import com.bjpowernode.utils.DBUtils;

import java.sql.*;
import java.util.Scanner;

public class Test07Login {
    public static void main(String[] args) throws ClassNotFoundException, SQLException {
        //从键盘输入
        Scanner scanner=new Scanner(System.in);
        System.out.println(“请输入用户名:”);
        String username = scanner.nextLine();
        System.out.println(“请输入密码:”);
        String password=scanner.nextLine();
        Connection conn = DBUtils.getConn();
        // 编写SQL
        String sql=”select * from sys_user where username=? and password=? “;
        System.out.println(sql);
        // 创建SQL命令发送器
        PreparedStatement pstmt = conn.prepareStatement(sql);
        pstmt.setString(1,username);
        pstmt.setString(2,password);
        // 使用SQL命令发送器发送SQL命令并得到结果
        ResultSet rs=pstmt.executeQuery();
        // 处理结果
        if(rs.next()){
            System.out.println(“登陆成功”);
            int id=rs.getInt(1);
            String name=rs.getString(2);
            String pwd=rs.getString(3);
            System.out.println(id+”  “+name+”  “+pwd);
        }
        // 关闭数据库资源
        DBUtils.close(rs);
        DBUtils.close(pstmt);
        DBUtils.close(conn);
    }
}

10.2.5 再次测试

11. 1事务处理解决转账问题

11.1 什么是事务

是数据库操作的最小工作单元,是作为单个逻辑工作单元执行的一系列操作;这些操作作为一个整体一起向系统提交,要么都执行、要么都不执行;事务是一组不可再分割的操作集合(工作逻辑单元)

11.2 事务的四大特性:

11.2.1 原子性

事务是数据库的逻辑工作单位,事务中包含的各操作要么都做,要么都不做

11.2.2 一致性

事 务执行的结果必须是使数据库从一个一致性状态变到另一个一致性状态。因此当数据库只包含成功事务提交的结果时,就说数据库处于一致性状态。如果数据库系统 运行中发生故障,有些事务尚未完成就被迫中断,这些未完成事务对数据库所做的修改有一部分已写入物理数据库,这时数据库就处于一种不正确的状态,或者说是 不一致的状态。

11.2.3 隔离性

一个事务的执行不能其它事务干扰。即一个事务内部的操作及使用的数据对其它并发事务是隔离的,并发执行的各个事务之间不能互相干扰。

11.2.4 持久性

也称永久性,指一个事务一旦提交,它对数据库中的数据的改变就应该是永久性的。接下来的其它操作或故障不应该对其执行结果有任何影响。

11.3 需求描述

完成转账操作

要求,两次数据库操作,要么全成功。要么全失败

11.4 准备工作

11.4.1 创建表

CREATE TABLE account

(

    aid INT PRIMARY KEY AUTO_INCREMENT,

    aname VARCHAR(30) NOT NULL,

    amount  DECIMAL(10,2) NOT NULL

);

11.4.2 初始化数据

11.5 代码实现

package com.bjpowernode.jdbc;

import com.bjpowernode.utils.DBUtils;

import java.sql.*;
import java.util.Scanner;

public class Test08Transaction {
    public static void main(String[] args) {
        //声明连接对象
        Connection conn=null;
        //声明发送SQL的接口对象
        Statement stmt=null;
        try {
            //创建连接对象
            conn = DBUtils.getConn();
            // 关闭自动提交事务
            conn.setAutoCommit(false);
            // 编写SQL
            String sql1 = “update account set amount = amount-1000 where aid=1”;
            String sql2 = “update account set amount = amount+1000 where aid=2”;
            // 创建SQL命令发送器
            stmt = conn.createStatement();
            stmt.executeUpdate(sql1);
            stmt.executeUpdate(sql2);
                    }catch (Exception e){
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        }finally {

conn.commit();

            // 关闭数据库资源
            DBUtils.close(stmt);
            DBUtils.close(conn);
        }

    }
}

 

11.6 测试

在两个执行的sql中间模拟异常,看事务是否会提交

 

 

12. JDBC批处理

12.1 什么是批处理

批处理是建立一次连接(创建一个Connection对象)的情况下批量执行多个DML语句,这些DML语句要么全部成功要么全部失败。如何确保全部成功or全部失败呢?在JDBC中开启事务,使用事务管理DML语句。

12.2 需求描述及操作步骤

使用批处理根据id批量的删除student表中的数据

1 定义SQL配置文件

2 创建Connection对象

3 创建PreparedStatement对象

4 将提交方式设置为手动提交,开启事务

5 设置占位符

6 将占位符添加到批处理中(相当于收集若干个本子,放入包包中)

7 执行批处理

8 提交事务

9 如果批处理失败,在catch块中回滚事务

10 关闭资源

12.3 案列代码

package com.bjpowernode.jdbc;

import com.bjpowernode.utils.DBUtils;

import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Arrays;
import java.util.List;

public class Test09Batch {
    public static void main(String[] args) {
        //模拟要删除的数据
        List<Integer> ids= Arrays.asList(1,2,3,4,5);
        //声明连接对象
        Connection conn=null;
        //声明发送SQL的接口对象
        PreparedStatement pstmt=null;
        try {
            //创建连接对象
            conn = DBUtils.getConn();
            // 关闭自动提交事务
            conn.setAutoCommit(false);
            // 编写SQL
            String sql = “delete from student where id = ?;”;
            // 创建SQL命令发送器
            pstmt = conn.prepareStatement(sql);
            for (Integer id : ids) {
                pstmt.setInt(1,id);
                pstmt.addBatch();
            }
            int[] rows = pstmt.executeBatch();
            System.out.println(“受影响的行数为:”+Arrays.toString(rows));
            conn.commit();
        }catch (Exception e){
            e.printStackTrace();
            try {
                conn.rollback();
            } catch (SQLException e1) {
                e1.printStackTrace();
            }
        }finally {
            // 关闭数据库资源
            DBUtils.close(pstmt);
            DBUtils.close(conn);
        }

    }
}

13. 数据库连接池

13.1 什么是连接池

连接池是创建和管理一个连接的缓冲池的技术,这些连接准备好被任何需要它们的线程使用。

连接池是装有连接的容器,使用连接的话,可以从连接池中进行获取,使用完成之后将连接归还给连接池。

13.2 为什么使用连接池

连接对象创建和销毁是需要耗费时间的,在服务器初始化的时候就初始化一些连接。把这些连接放入到内存中,使用的时候可以从内存中获取,使用完成之后将连接放入连接池中。从内存中获取和归还的效率要远远高于创建和销毁的效率。(提升性能)。

13.3 连接池的工作原理

13.4 市面上有哪些可用的连接池

Ø c3p0  老了

Ø druid 阿里的

Ø dbcp 老了

Ø Hikari  小日本的,springboot官方推荐的

14. BaseDAO的封装【难点】

14.1 概述 husband————————->Husband(List<T> 、T)

对于JDBC市面上有一些封装的非常好的ORM(对象关系映射)框架,如mybatis  mybatis plus hibernate,但是我们现在还没有学到框架,如何自己做模拟一个ORM的框架呢,接下来我们来讲解BaseDAO的封装和使用

 

14.2 代码

package com.bjpowernode.dao.impl;

import com.bjpowernode.utils.DBUtils;

import java.lang.reflect.Field;
import java.sql.*;
import java.util.ArrayList;
import java.util.List;

public class BaseDao {

    /**
     * @param sql    sql 指令
     * @param clss   orm关联类
     * @param params sql中占位符 对应的参数
     * @param <T>
     * @return
     * @throws Exception
     */
    public  <T> List<T> selectList(String sql, Class<T> clss, Object… params)  {
        //创建一个空容器  规避空指针异常问题
        List<T> data = new ArrayList<>(); // 需要改动
        //2. 创建连接 使用驱动管理器 创建连接
        Connection conn = DBUtils.getConnection();
        //4. 获取指令的指令对象
        PreparedStatement prep = null;
        ResultSet rs = null;
        try {
            //4. 获取指令的指令对象
            prep = conn.prepareStatement(sql);
            //设置预编译参数
            for (int i = 0; i < params.length; i++) {
                Object param = params[i]; //获取参数
                //设置参数
                prep.setObject(i + 1, param);
            }
            //5. 执行指令
            rs = prep.executeQuery();
            //获取结果的元信息
            ResultSetMetaData metaData = rs.getMetaData();
            //获取列数
            int columnCount = metaData.getColumnCount();
            //遍历结果
            while (rs.next()) {
                T t = clss.newInstance();
                for (int i = 0; i < columnCount; i++) {
                    //获取别名  也是属性名
                    String columnLabel = metaData.getColumnLabel(i + 1);
                    //根据别名获取值
                    Object columnValue = rs.getObject(columnLabel);
                    //获取属性
                    Field field = clss.getDeclaredField(columnLabel); //需要改动
                    //设置属性值
                    field.setAccessible(true);
                    field.set(t, columnValue);
                }
                data.add(t);//放入list
            }
        }catch (Exception e){
            e.printStackTrace();
        }finally {
            DBUtils.close(conn);
            DBUtils.close(prep);
            DBUtils.close(rs);
        }
        return data;
    }

    /**
     * 查询单个对象
     *
     * @param sql
     * @param clss
     * @param params
     * @param <T>
     * @return
     * @throws Exception
     */
    public  <T> T selectOne(String sql, Class<T> clss, Object… params)  {
        List<T> list = selectList(sql, clss, params);
        if (!list.isEmpty() && list.size() == 1) {
            return list.get(0);
        }
        return null;
    }

    /**
     * 通用的更新操作
     * @param sql
     * @param params
     * @return
     */
    public  boolean update(String sql, Object… params) {
        //1. 获取连接
        Connection conn = DBUtils.getConnection();
        PreparedStatement prep = null;
        try {
            prep = conn.prepareStatement(sql);
            //设置预编译参数
            for (int i = 0; i < params.length; i++) {
                Object param = params[i]; //获取参数
                //设置参数
                prep.setObject(i + 1, param);
            }
            //执行sql指令
            int m = prep.executeUpdate();

            return m >= 1;
        } catch (SQLException throwables) {
            throwables.printStackTrace();
        }finally {
            DBUtils.close(conn);
            DBUtils.close(prep);
        }
        return  false;
    }
}

14.3 使用

package com.bjpowernode.dao.impl;

public class UserDao  extends BaseDao{
    /**
     * 根据用户名 和 密码查询用户
     * @param username
     * @param password
     * @return
     */
    public User selectUser(String username,String password){
        String sql = “select  `id`, `username`, `password`, `realname`, `role`, `deleted`, `img`, `create_time` as createTime, `modify_time` as modifyTime, `deleted_time` as deletedTime from user where username = ? and password=?”;
        User user = super.selectOne(sql, User.class, username, password);
        return user;
    };

    public void updateState(String id, Integer delete) {
        String sql = “update user set deleted =  ?,deleted_time = now() where id = ?”;
        super.update(sql,delete,id);
    }
}

14.4 分页封装

14.4.1 创建分页结果封装类PageInfo<T>

package com.bjpowernode.common;

import java.util.List;

public class PageInfo<T> {

    private List<T> data;// 具体的数据
    private Long count; //符合条件的数据条数

    public List<T> getData() {
        return data;
    }

    public void setData(List<T> data) {
        this.data = data;
    }

    public Long getCount() {
        return count;
    }

    public void setCount(Long count) {
        this.count = count;
    }
}

14.4.2 修改BaseDao

/**
 * 分页的封装
 * @param sql
 * @param cls
 * @param page
 * @param limit
 * @param <T>
 * @return
 */
protected <T> PageInfo<T> selectPage(String sql, Class<T> cls, String page, String limit) {
    //查询符合条件 页码数据
    Integer pageNo = 0;
    if (Integer.parseInt(page) > 0){
        pageNo = (Integer.parseInt(page) – 1) * Integer.parseInt(limit);
    }
    String listSql = sql + ” limit  “+pageNo+”,”+limit;
    //查询列表数据
    List<T> users = selectList(listSql, cls);
    //查询总数据条数
    Long count = getCount(sql);
    PageInfo<T> pageInfo = new PageInfo<>();
    pageInfo.setData(users);
    pageInfo.setCount(count);
    return pageInfo;
}

/**
 * 查询总数据条数
 * @param sql
 * @return
 */
private Long getCount(String sql){
    String countSql = “select count(1) from (“+sql+”) rs”;
    Connection conn = DBUtils.getConnection();
    PreparedStatement prep = null;
    ResultSet rs = null;
    try {
        prep = conn.prepareStatement(countSql);
        rs = prep.executeQuery();
        rs.next();
        Long count = rs.getLong(1);
        return count;

    } catch (SQLException throwables) {
        throwables.printStackTrace();
    }
    return  0L;
};

更多干货我们下期再说!

下期会分享

第四章节

HTML_CSS_JavaScript

相关知识~

下期见! 

资源下载: