【0】README

1) 本文文字描述+source code 均转自 core java volume 2 , 旨在理解 java数据库编程——执行SQL 语句 的基础知识 ;
2)for source code, please visit : https://github.com/pacosonTang/core-java-volume/tree/master/coreJavaAdvanced/chapter4/executeSQL
3)for database connection config, please visit : https://github.com/pacosonTang/core-java-volume/blob/master/coreJavaAdvanced/chapter4/database.properties


【1】java数据库编程——执行SQL 语句相关

1)执行 SQL 命令前, 首先需要创建一个 Statement 对象: 要创建 statement 对象,不需要调用 DriverManager.getConnection 方法所获得的 Connection对象;(干货——Statement object == 语句对象)

  • step1) Statement stat = conn.createStatement();
  • step2) 将要执行的 SQL 语句放入字符串中,如:

    String command = “update ….”;

  • step3) 然后,调用Statement 接口中的executeUpdate 方法:

    stat.executeUpdate(command); // executeUpdate 方法:将返回受SQL命令影响的行数, 或者对于不返回行数的语句返回0; (干货——executeUpdate返回受SQL命令影响的行数,或者0)

2)execute系列方法: (干货——execute系列方法:executeUpdate + executeQuery + execute)

  • 2.1)executeUpdate 方法:既可以执行诸如 insert, update, 和 delete之类的操作(DML), 也可以执行诸如 create , drop 之类的数据定义语句(DDL);
  • 2.2)executeQuery方法: 执行 select 查询语句时 必须使用 executeQuery 方法;
  • 2.3)execute 方法: 可以执行任意的sql 语句, 通常只用于用户提供的交互式查询;

3)查询结果(ResultSet 类型): executeQuery 方法返回一个ResultSet类型的对象, 可以通过它来每次一行地迭代遍历所有查询结果;

ResultSet rs = stat.executeQuery(“select * from books”);

  • 3.1)分析结果集时通常可以使用类似如下循环语句的代码:

    while(rs.next() )
    {
    look at a row of the result set
    }

  • Warnning)

    • W1)ResultSet接口的 迭代协议与 java.util.Iterator 接口稍有不同。 对于ResultSet 接口, 迭代器初始化时被设定在第一行之前的位置,必须调用 next 方法将它移动到第一行; (干货——ResultSet接口的 迭代协议与 java.util.Iterator 接口稍有不同)
    • W2)另外,它没有hasNext方法, 我们需要不断地调用 next, 直至该方法返回 false;
    • W3)结果集中行的顺序是任意的, 不能为行序强加任何意义; (干货——ResultSet结果集中行的顺序是任意的, 不能为行序强加任何意义)
  • 3.2)查看每一行,需要知道每一列的内容:

    String str = rs.getString(1);
    double price = rs.getDouble(1);

  • 3.3)不同的数据类型有不同的访问器: 比如 getString 和 getDouble; 每个访问器都有两种形式,一种接收数字类型参数,一个接收字符串类型参数;

  • Warning) 数据库的列序号从1开始算 ;

  • 3.4)当使用 字符串参数时, 指的是结果集中以该字符串为列名的列;如,
    rs.getDouble(“price”) 返回列名为 Price 的列所对应的值;

  • 3.5)当get方法的类型和列的数据类型不一致时, 每个get 方法都会进行合理的类型转换;

【2】管理连接、语句和结果集

1)每个Connection对象都可以创建一个或多个 Statement对象;

  • 1.1)同一个Statement对象可以用于不相关的命令和查询;
  • 1.2)但是,一个Statement对象最多只能有一个打开的结果集;
  • 1.3)需要说明的是: 至少有一种常用的数据库(Microsoft SQL Server) 的JDBC驱动程序只允许同时存在一个活动的 Statement 对象。使用 DatabaseMetaData 接口中的 getMaxStatements 方法可以获取JDBC 驱动程序支持的同时活动的语句对象的总数;
    • 1.3.1)这看上去很有局限性。实际上,我们通常并不需要同时处理多个 结果集。对数据库进行组合查询比使用 java 程序遍历多个结果集要高效得多; (干货——我们通常并不需要同时处理多个 结果集,对数据库进行组合查询比使用 java 程序遍历多个结果集要高效得多)
  • 1.4)close方法:使用完 ResultSet , Statement , Connection对象后,立即调用 close方法;
  • 1.5)closeOnCompletion方法: 在java 7中, 可以在 Statement 上调用 closeOnCompletion 方法, 在其所有结果集都被关闭后, 该语句会立即被自动关闭; (干货——java7引入的新方法closeOnCompletion)
  • 1.6)如果所用连接都是短时的,无需考虑关闭语句和结果集。 只需要将 close 语句放 在 带资源的try语句中, 以便确保最终连接对象不可能继续保持打开状态:

    try (Connnection conn =…)
    {
    Statement stat = conn.createStatement();
    ResultSet result = stat.executeQuery(queryStr);
    process query result
    }

Attention) 应该使用带资源的try 语句块来关闭连接,并使用一个单独的 try /catch 块处理异常。 分离 try 程序块可以 提高代码的可读性和可维护性;


【3】分析 SQL 异常(SQLException)

1) java 6 改进了 SQLException 类,让其实现了 Iterable接口, 其 iterator() 方法可以产生一个 Iterable< Throwable>;

  • 1.1) 这个迭代器是可以迭代这两个链, 首先迭代第一个 SQLException 的成因链, 然后迭代下一个 SQLException ,以此类推;如,

    for(Throwable t : sqlException)
    {
    do sth with t
    }

  • 1.2)可以在 SQLException 上调用 getSQLState 和 getErrorCode 方法来进一步分析它;

2) SQLException 按照层次结构树的方式组合到了一起, 如下图所示:
这里写图片描述

  • 2.1)数据库驱动程序将非致命问题作为警告报告,我们可以从连接(Connection), 语句(Statement)或结果集(ResultSet)中获取这些警告;
  • 2.2)SQLWarning: 是 SQLException的子类,我们可以调用 getSQLState 和 getErrorCode 来获取有关警告的更多信息; (干货——SQLWarning 定义)
  • 2.3)要获得所有警告,使用下面的循环:

    SQLWarning w = stat.getWarning()
    while(w != null)
    {
    do wth with w
    w = w.nextWarning();
    }

  • 2.4)当数据从数据库中读出并意外被截断时, SQLWarning 的 DataTruncation 子类就派上用场了; 如果数据截断发送在更新语句中, 那么DataTrucation 将会被当做异常抛出;


【4】组装数据库

1)看个荔枝:(用java 操作数据库

  • step1)连接数据库

    • step1.1) getConnection 方法:读取database.properties 文件中的 属性信息,并将属性jdbc.drivers 添加到系统属性中;
    • step1.2) 驱动程序管理器:使用属性jdbc.drivers 加载相应的驱动程序;
    • step1.3) getConnection方法: 使用 jdbc.url , jdbc.username, jdbc.password 等属性打开数据库连接;
  • step2)使用 sql 语句打开文件;

  • step3) 使用 泛化的execute 方法执行每条语句;
  • step4) 如果产生了结果集, 则打印结果;
  • step5) 如果运行过程中出现 SQL 异常, 则打印出这个异常以及所有可能包含在其中的与其连接在一起的相关异常;
  • step6)关闭数据库连接;

2)运行结果如下:

(java -classpath .;D:\Software_Cluster\Development\mysql\mysql-connector-java-5.1.17\mysql-connector-java-5.1.17-bin.jar com.corejava.chapter4.ExecSQL)
这里写图片描述

3)source code at a glance

class ExecSQL
{
    private static String cur_dir = System.getProperty("user.dir") + File.separator +  
            "com" + File.separator + "corejava" + File.separator +  "chapter4" + File.separator;

   public static void main(String args[]) throws IOException
   {
      try
      {
         Scanner in = args.length == 0 ? new Scanner(System.in) : new Scanner(Paths.get(args[0]));

         try (Connection conn = getConnection())
         {
            Statement stat = conn.createStatement();

            while (true)
            {
               if (args.length == 0) System.out.println("Enter command or EXIT to exit:");

               if (!in.hasNextLine()) return;

               String line = in.nextLine();
               if (line.equalsIgnoreCase("EXIT")) return;
               if (line.trim().endsWith(";")) // remove trailing semicolon
               {
                  line = line.trim();
                  line = line.substring(0, line.length() - 1);
               }
               try
               {
                  boolean isResult = stat.execute(line);
                  if (isResult)
                  {
                     ResultSet rs = stat.getResultSet(); //结果集
                     showResultSet(rs); // 打印结果集
                  }
                  else
                  {
                     int updateCount = stat.getUpdateCount();
                     System.out.println(updateCount + " rows updated");
                  }
               }
               catch (SQLException ex)
               {
                  for (Throwable e : ex)
                     e.printStackTrace();
               }
            }
         }
      }
      catch (SQLException e)
      {
         for (Throwable t : e)
            t.printStackTrace();
      }
   }

   public static Connection getConnection() throws SQLException, IOException
   {
      Properties props = new Properties();
      try (InputStream in = Files.newInputStream(Paths.get(cur_dir + "database.properties")))
      {
         props.load(in);
      }

      String drivers = props.getProperty("jdbc.drivers");
      if (drivers != null) System.setProperty("jdbc.drivers", drivers);

      String url = props.getProperty("jdbc.url");
      String username = props.getProperty("jdbc.username");
      String password = props.getProperty("jdbc.password");

      return DriverManager.getConnection(url, username, password);
   }

   public static void showResultSet(ResultSet result) throws SQLException
   {
      ResultSetMetaData metaData = result.getMetaData(); // 结果集元数据
      int columnCount = metaData.getColumnCount();

      for (int i = 1; i <= columnCount; i++)
      {
         if (i > 1) System.out.print(", ");
         System.out.print(metaData.getColumnLabel(i));
      }
      System.out.println();

      while (result.next()) // 循环打印结果集
      {
         for (int i = 1; i <= columnCount; i++)
         {
            if (i > 1) System.out.print(", ");
            System.out.print(result.getString(i));
         }
         System.out.println();
      }
   }
}
Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐