欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

蓝桥杯Java课程学习——JDBC(二)

程序员文章站 2022-06-26 13:31:13
...





JDBC操作数据库

蓝桥杯Java课程学习——JDBC(二)
通过使用 JDBC Statement, CallableStatementPreparedStatement接口定义的方法和属性,使可以使用 SQL 或 PL/SQL 命令和从数据库接收数据。它们还定义了许多方法,帮助消除 Java 和数据库之间数据类型的差异。

接口 应用场景
Statement 当在运行时使用静态 SQL 语句时(Statement 接口不能接收参数)
CallableStatement 当要访问数据库中的存储过程时(CallableStatement 对象的接口还可以接收运行时输入参数)
PreparedStatement 当计划多次使用 SQL 语句时(PreparedStatement 接口接收在运行时输入参数)

Statement

我们要使用Statement接口,第一步肯定是创建一个Statement对象了。我们需要使用Connection对象的createStatement()方法进行创建。

Statement stmt = null;
try {
   stmt = conn.createStatement( );
   . . .
}
catch (SQLException e) {
   . . .
}
finally {
   . . .
}

一旦创建了一个 Statement 对象,我们就可以用它来执行 SQL 语句了

方法 说明
boolean execute(String SQL) 如果 ResultSet 对象可以被检索返回布尔值 true,否则返回 false。使用这个方法来执行 SQL DDL 语句,或当需要使用真正的动态 SQL
int executeUpdate(String SQL) 用于执行 INSERT、UPDATE 或 DELETE 语句以及 SQLDDL(数据定义语言)语句。返回值是一个整数,指示受影响的行数(即更新计数)
ResultSet executeQuery(String SQL) 返回 ResultSet 对象。用于产生单个结果集的语句,例如 SELECT 语句

正如关闭一个 Connection 对象来释放数据库连接资源,出于同样的原因,也应该关闭 Statement 对象。

Statement stmt = null;
try {
   stmt = conn.createStatement( );
   . . .
}
catch (SQLException e) {
   . . .
}
finally {
   stmt.close();
}

注:如果关闭了 Connection 对象首先它会关闭 Statement 对象,然而应该始终明确关闭 Statement 对象,以确保正确的清除。

PreparedStatement

PreparedStatement 接口扩展了Statement接口,有利于高效地执行多次使用的 SQL 语句。我们先来创建一个PreparedStatement对象。 Statement为一条 SQL 语句生成执行计划。如果要执行两条 SQL 语句,会生成两个执行计划。一万个查询就生成一万个执行计划!

select colume from table where colume=1;
select colume from table where colume=2;

PreparedStatement 用于使用绑定变量重用执行计划。

select colume from table where colume=:x;

通过 set 不同数据,只需要生成一次执行计划,并且可以重用。

PreparedStatement pstmt = null;
try {

/*
在JDBC中所有的参数都被代表?符号,这是已知的参数标记。在执行SQL语句之前,必须提供值的每一个参数。
*/
   String SQL = "Update Students SET age = ? WHERE id = ?";
   pstmt = conn.prepareStatement(SQL);
   . . .
}
/*

setXXX()方法将值绑定到参数,其中XXX表示希望绑定到输入参数值的 Java 数据类型。如果忘了提供值,将收到一个 SQLException。
*/
catch (SQLException e) {
   . . .
}
finally {
//同理,我们需要关闭 PreparedStatement 对象
   pstmt.close();
}

示例

  1. 先创建数据库和相应的内容:
create database EXAMPLE;
use EXAMPLE

create table Students
(
id int not null,
age int not null,
name varchar(255),
primary key(id)
);
insert into Students values(1,18,'Tom'),
(2,20,'Aby'),(4,20,'Tomson');
  1. Java代码
import java.sql.*;

public class JdbcTest {
   // JDBC 驱动器的名称和数据库地址
   static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
   static final String DB_URL = "jdbc:mysql://localhost/EXAMPLE";


   static final String USER = "root";
   static final String PASS = "";

   public static void main(String[] args) {
       Connection conn = null;
       PreparedStatement stmt = null;
       try{
           //注册 JDBC 驱动器
           Class.forName("com.mysql.jdbc.Driver");

           //打开连接
           System.out.println("Connecting to database...");
           conn = DriverManager.getConnection(DB_URL,USER,PASS);

           //执行查询
           System.out.println("Creating statement...");
           //这里我们要更改一个同学的年龄,参数待定
           String sql = "UPDATE Students set age=? WHERE id=?";
           stmt = conn.prepareStatement(sql);

           //将值绑定到参数,参数从左至右序号为1,2...
           stmt.setInt(1, 22);  // 绑定 age 的值(序号为1)
           stmt.setInt(2, 1); // 绑定 ID 的值

           // 更新 ID 为1的同学的年龄
           int rows = stmt.executeUpdate();
           System.out.println("被影响的行数 : " + rows );

           // 查询所有记录,并显示.
           sql = "SELECT id, name, age FROM Students";
           ResultSet rs = stmt.executeQuery(sql);

           //处理结果集
           while(rs.next()){
               //检索
               int id  = rs.getInt("id");
               int age = rs.getInt("age");
               String name = rs.getString("name");


               //显示
               System.out.print("ID: " + id);
               System.out.print(", Age: " + age);
               System.out.print(", Name: " + name);
               System.out.println();
           }
           //清理
           rs.close();
           stmt.close();
           conn.close();
       }catch(SQLException se){
           se.printStackTrace();
       }catch(Exception e){
           e.printStackTrace();
       }finally{
           try{
               if(stmt!=null)
                   stmt.close();
           }catch(SQLException se2){
           }
    try{
         if(conn!=null)
                 conn.close();
      }catch(SQLException se){
              se.printStackTrace();
          }
       }
           System.out.println("Goodbye!");
   }
}

CallableStatement

CallableStatement对象为所有的 DBMS 提供了一种以标准形式调用存储过程的方法。存储过程储存在数据库中。对储存过程的调用是 CallableStatement 对象所含的内容。三种类型的参数有:INOUTINOUT

PreparedStatement对象只使用 IN 参数。 CallableStatement对象可以使用所有三个

参数 描述
IN 它的值是在创建 SQL 语句时未知的参数,将 IN 参数传给 CallableStatement 对象是通过 setXXX() 方法完成的
OUT 其值由它返回的 SQL 语句提供的参数。从 OUT 参数的 getXXX() 方法检索值
INOUT 同时提供输入和输出值的参数,绑定的 setXXX() 方法的变量,并使用 getXXX() 方法检索值

在 JDBC 中调用存储过程的语法如下所示。注意,方括号表示其间的内容是可选项;方括号本身并不是语法的组成部份。

{call 存储过程名[(?, ?, ...)]}

返回结果参数的过程的语法为:

{? = call 存储过程名[(?, ?, ...)]}

不带参数的存储过程的语法类似:

{call 存储过程名}

CallableStatement 对象是用 Connection 方法 prepareCall 创建的。

CallableStatement cstmt = null;
try {
   String SQL = "{call getEXAMPLEName (?, ?)}";
   cstmt = conn.prepareCall (SQL);
   . . .
}
catch (SQLException e) {
   . . .
}
finally {
   cstmt.close();
}

JDBC 结果集

结果集通常是通过执行查询数据库的语句生成,表示数据库查询结果的数据表。

ResultSet 介绍

ResultSet对象具有指向其当前数据行的光标。最初,光标被置于第一行之前。next方法将光标移动到下一行;因为该方法在ResultSet对象没有下一行时返回false,所以可以在while 循环中使用它来迭代结果集。光标可以方便我们对结果集进行遍历。默认的ResultSet对象不可更新,仅有一个向前移动的光标。因此,只能迭代它一次,并且只能按从第一行到最后一行的顺序进行。

ResultSet接口的方法可分为三类:

  • 导航方法:用于移动光标
  • 获取方法:用于查看当前行的光标所指向的列中的数据
  • 更新方法:用于更新当前行的列中的数据

JDBC 提供下列连接方法来创建所需的ResultSet语句:

createStatement(int RSType, int RSConcurrency);

prepareStatement(String SQL, int RSType, int RSConcurrency);

prepareCall(String sql, int RSType, int RSConcurrency);

RSType 表示 ResultSet 对象的类型
RSConcurrency 是 ResultSet 常量,用于指定一个结果集是否为只读或可更新。
ResultSet 的类型,如果不指定 ResultSet 类型,将自动获得一个是 TYPE_FORWARD_ONLY:

类型 描述
ResultSet.TYPE_FORWARD_ONLY 游标只能向前移动的结果集
ResultSet.TYPE_SCROLL_INSENSITIVE 游标可以向前和向后滚动,但不及时更新,就是如果数据库里的数据修改过,并不在 ResultSet 中反应出来
ResultSet.TYPE_SCROLL_SENSITIVE 游标可以向前和向后滚动,并及时跟踪数据库的更新,以便更改 ResultSet 中的数据

并发性的 ResultSet,如果不指定任何并发类型,将自动获得一个为 CONCUR_READ_ONLY

并发 描述
ResultSet.CONCUR_READ_ONLY 创建结果集只读。这是默认的
ResultSet.CONCUR_UPDATABLE 创建一个可更新的结果集

如初始化一个 Statement 对象来创建一个双向、可更新的 ResultSet 对象:

try {
   Statement stmt = conn.createStatement(
                           ResultSet.TYPE_SCROLL_INSENSITIVE,
                           ResultSet.CONCUR_UPDATABLE);
}
catch(Exception ex) {
   ....
}
finally {
   ....
}

导航

方法 说明
public void beforeFirst() throws SQLException 将光标移动到正好位于第一行之前
public void afterLast() throws SQLException 将光标移动到刚刚结束的最后一行
public boolean first() throws SQLException 将光标移动到第一行
public void last() throws SQLException 将光标移动到最后一行
public boolean absolute(int row) throws SQLException 将光标移动到指定的行
public boolean relative(int row) throws SQLException 从它目前所指向向前或向后移动光标行的给定数量
public boolean previous() throws SQLException 将光标移动到上一行。上一行关闭的结果集此方法返回 false
public boolean next() throws SQLException 将光标移动到下一行。如果没有更多的行结果集中的此方法返回 false
public int getRow() throws SQLException 返回的行号,该光标指向的行
public void moveToInsertRow() throws SQLException 将光标移动到一个特殊的行,可以用来插入新行插入到数据库中的结果集。当前光标位置被记住
public void moveToCurrentRow() throws SQLException 移动光标返回到当前行,如果光标在当前插入行,否则,这个方法不执行任何操作

示例

  1. 先创建数据库和相应的内容:
create database EXAMPLE;
use EXAMPLE

create table Students
(
id int not null,
age int not null,
name varchar(255),
primary key(id)
);
insert into Students values(1,18,'Tom'),
(2,20,'Aby'),(4,20,'Tomson');
  1. Java代码
import java.sql.*;

public class JdbcTest {
   // JDBC 驱动器名称 和数据库地址
   static final String JDBC_DRIVER = "com.mysql.jdbc.Driver";
   //数据库的名称为 EXAMPLE
   static final String DB_URL = "jdbc:mysql://localhost/EXAMPLE";

   // 数据库用户和密码
   static final String USER = "root";
   static final String PASS = "";

   public static void main(String[] args) {
       Connection conn = null;
       Statement stmt = null;
       try{
           //注册JDBC 驱动程序
           Class.forName("com.mysql.jdbc.Driver");

           //打开连接
           System.out.println("Connecting to database...");
           conn = DriverManager.getConnection(DB_URL,USER,PASS);



           System.out.println("Creating statement...");
           //创建所需的ResultSet,双向,只读
           stmt = conn.createStatement(
                           ResultSet.TYPE_SCROLL_INSENSITIVE,
                           ResultSet.CONCUR_READ_ONLY);
           String sql;
           sql = "SELECT id, name, age FROM Students";
           ResultSet rs = stmt.executeQuery(sql);

           // 将光标移到最后一行
           System.out.println("Moving cursor to the last...");
           rs.last();

           //处理结果集
           System.out.println("Displaying record...");
           int id  = rs.getInt("id");
           int age = rs.getInt("age");
           String name = rs.getString("name");


           //显示
           System.out.print("ID: " + id);
           System.out.print(", Age: " + age);
           System.out.print(", Name: " + name);
           System.out.println();

           // 将光标移到第一行
           System.out.println("Moving cursor to the first row...");
           rs.first();


           System.out.println("Displaying record...");
           id  = rs.getInt("id");
           age = rs.getInt("age");
           name = rs.getString("name");


           //显示
           System.out.print("ID: " + id);
           System.out.print(", Age: " + age);
           System.out.print(", Name: " + name);

           //将光标移至下一行
           System.out.println("Moving cursor to the next row...");
           rs.next();


           System.out.println("Displaying record...");
           id  = rs.getInt("id");
           age = rs.getInt("age");
           name = rs.getString("name");

           // 显示
           System.out.print("ID: " + id);
           System.out.print(", Age: " + age);
           System.out.print(", Name: " + name);

           rs.close();
           stmt.close();
           conn.close();
       }catch(SQLException se){
           se.printStackTrace();
       }catch(Exception e){
           e.printStackTrace();
       }finally{
           try{
               if(stmt!=null)
                   stmt.close();
           }catch(SQLException se2){
           }
           try{
               if(conn!=null)
                   conn.close();
           }catch(SQLException se){
               se.printStackTrace();
           }
       }
       System.out.println("Goodbye!");
   }
}

获取

ResultSet接口中我们经常使用 get方法来查看结果集。

方法 说明
public int getInt(String columnName) throws SQLException 当前行中名为 ColumnName 列的值
public int getInt(int columnIndex) throws SQLException 当前行中指定列的索引的值。列索引从 1 开始,意味着一个行的第一列是 1,行的第二列是 2,依此类推

当然还有getString()等等。

更新

方法 说明
public void updateString(int columnIndex, String s) throws SQLException 指定列中的字符串更改为 s 的值
public void updateString(String columnName, String s) throws SQLException 类似于前面的方法,不同之处在于由它的名称,而不是它的索引指定的列

类似的还有updateDouble()等等。

我们在更新了结果集中的内容,当然需要更新一下数据库了。我们可以调用下面的方法更新数据库。

方法 说明
public void updateRow() 通过更新数据库中相应的行更新当前行
public void deleteRow() 从数据库中删除当前行
public void refreshRow() 刷新在结果集的数据,以反映最新变化在数据库中
public void cancelRowUpdates() 取消所做的当前行的任何更新
public void insertRow() 插入一行到数据库中。当光标指向插入行此方法只能被调用
Statement stmt = conn.createStatement(
                           ResultSet.TYPE_SCROLL_INSENSITIVE,
                           ResultSet.CONCUR_UPDATABLE);

String sql = "SELECT id, name, age FROM Students";
ResultSet rs = stmt.executeQuery(sql);

//结果集中插入新行
rs.moveToInsertRow();
rs.updateInt("id",5);
rs.updateString("name","John");
rs.updateInt("age",21);
//更新数据库
rs.insertRow();

JDBC 事务

我们在编写 java 程序的时候,在默认情况下,JDBC 连接是在自动提交模式下,即每个 SQL 语句都是在其完成时提交到数据库。但有时候我们为了提高程序运行的性能或者保持业务流程的完整性,以及使用了分布式事务管理方式,这个时候我们可能想关闭自动提交而自己管理和控制自己的事务。
让多条 SQL 在一个事务中执行,并且保证这些语句是在同一时间共同执行的时候,我们就应该为这多条语句定义一个事务。一个事务是把单个 SQL 语句或一组 SQL 语句作为一个逻辑单元,并且如果事务中任何语句失败,则整个事务失败。
如果我们要启动一个事务,而不是让 JDBC 驱动程序默认使用 auto-commit 模式支持。这个时候我们就要使用 Connection 对象的setAutoCommit()方法。我们传递一个布尔值 false 到setAutoCommit()中,就可以关闭自动提交。反之我们传入一个 true 便将其重新打开。

Connection conn = null;
conn = DriverManager.getConnection(URL);
//关闭自动提交
conn.setAutoCommit(false);

我们关闭了自动提交后,**如果我们要提交数据库更改怎么办呢?**这时候就要用到我们的提交和回滚了。我们要提交更改,可以调用commit()方法:

conn.commit();

尤其不要忘记,在 catch 块内添加回滚事务,表示操作出现异常,撤销事务:

conn.rollback();

插入数据

JDBC 插入数据使用的频率非常高,接下来说明如何使用 JDBC 插入数据。

示例

import java.sql.*;

public class JdbcInsertTest {
    public static Connection connection = null;
    public static void main(String[] args) {
        try {
            Class.forName("com.mysql.jdbc.Driver");
            connection = DriverManager.getConnection("jdbc:mysql://localhost:3306/EXAMPLE", "root", "");
            Statement statement = connection.createStatement();
            //单条插入
            boolean execute = statement.execute("insert into Students values (0,1,'shiyanlou')");
            if (execute) {
                System.out.println("插入失败");
            }else {
                System.out.println("单条插入成功");
            }
            // 批量插入  需要关闭自动提交
            connection.setAutoCommit(false);
            String sql = "insert into Students values (?,?,?)";
            PreparedStatement preparedStatement = connection.prepareStatement(sql);
            //设置插入的值
            for (int i = 1; i <= 10; i++) {
                preparedStatement.setInt(1, i);
                preparedStatement.setInt(2, i + 10);
                preparedStatement.setString(3, "shiyanlou");
                preparedStatement.addBatch();
            }
            //执行批量插入,使用executeBatch 方法
            preparedStatement.executeBatch();
            //提交到数据库
            connection.commit();
            System.out.println("提交批量插入完成");
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }

    }
}