原文: http://zetcode.com/db/apachederbytutorial/jdbc/

在本章中,我们将创建将与 Derby 数据库一起使用的 Java 程序。

JDBC

JDBC 是 Java 编程语言的 API,用于定义客户端如何访问数据库。 它提供了查询和更新数据库中数据的方法。 JDBC 面向关系数据库。 从技术角度来看,API 是java.sql包中的一组类。 要将 JDBC 与特定数据库一起使用,我们需要该数据库的 JDBC 驱动程序。

客户端/服务器和嵌入式 Derby 应用

Derby 可以通过两种基本方式在 Java 应用中使用:客户端/服务器和嵌入式。 对于客户端/服务器应用,我们使用org.apache.derby.jdbc.ClientDriver;对于 Derby 嵌入式应用,我们使用org.apache.derby.jdbc.EmbeddedDriver

Maven 依赖

Derby 驱动程序有两个 Maven 依赖项:derbyderbynetderby依赖关系用于嵌入式应用,derbynet依赖关系用于客户端/服务器应用。

  1. <dependency>
  2. <groupId>org.apache.derby</groupId>
  3. <artifactId>derby</artifactId>
  4. <version>10.13.1.1</version>
  5. </dependency>

这是包含derby驱动程序的 Maven 依赖项。

  1. <dependency>
  2. <groupId>org.apache.derby</groupId>
  3. <artifactId>derbyclient</artifactId>
  4. <version>10.13.1.1</version>
  5. </dependency>

这是包含derbyclient驱动程序的 Maven 依赖项。

连接字符串

客户端/服务器和嵌入式应用的连接字符串不同。

  1. jdbc:derby://localhost:1527/dbname

这是客户端/服务器应用的连接 URL。

  1. jdbc:derby:dbname

这是嵌入式应用的连接 URL。

创建CARS

在我们的示例中,我们使用嵌入式 Derby 数据库。 在第一个示例中,我们将创建一个CARS表并在其中插入八行。

  1. $ $DERBY_HOME/bin/ij
  2. ij version 10.11
  3. ij> CONNECT 'jdbc:derby:testdb';
  4. ij> DROP TABLE USER12.CARS;
  5. 0 rows inserted/updated/deleted

如果在运行示例之前已经创建了CARS表,则应该从数据库中删除该表。

CreateCars.java

  1. package com.zetcode;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.SQLException;
  5. import java.sql.Statement;
  6. import java.util.logging.Level;
  7. import java.util.logging.Logger;
  8. public class CreateCars {
  9. public static void main(String[] args) {
  10. Connection con = null;
  11. Statement st = null;
  12. String url = "jdbc:derby:testdb;user=USER12";
  13. try {
  14. System.setProperty("derby.system.home", "/home/janbodnar/.derby");
  15. con = DriverManager.getConnection(url);
  16. st = con.createStatement();
  17. st.executeUpdate("CREATE TABLE CARS(ID INT PRIMARY KEY,"
  18. + "NAME VARCHAR(30), PRICE INT)");
  19. st.executeUpdate("INSERT INTO CARS VALUES(1, 'Audi', 52642)");
  20. st.executeUpdate("INSERT INTO CARS VALUES(2, 'Mercedes', 57127)");
  21. st.executeUpdate("INSERT INTO CARS VALUES(3, 'Skoda', 9000)");
  22. st.executeUpdate("INSERT INTO CARS VALUES(4, 'Volvo', 29000)");
  23. st.executeUpdate("INSERT INTO CARS VALUES(5, 'Bentley', 350000)");
  24. st.executeUpdate("INSERT INTO CARS VALUES(6, 'Citroen', 21000)");
  25. st.executeUpdate("INSERT INTO CARS VALUES(7, 'Hummer', 41400)");
  26. st.executeUpdate("INSERT INTO CARS VALUES(8, 'Volkswagen', 21600)");
  27. DriverManager.getConnection("jdbc:derby:;shutdown=true");
  28. } catch (SQLException ex) {
  29. Logger lgr = Logger.getLogger(CreateCars.class.getName());
  30. if (((ex.getErrorCode() == 50000)
  31. && ("XJ015".equals(ex.getSQLState())))) {
  32. lgr.log(Level.INFO, "Derby shut down normally", ex);
  33. } else {
  34. lgr.log(Level.SEVERE, ex.getMessage(), ex);
  35. }
  36. } finally {
  37. try {
  38. if (st != null) {
  39. st.close();
  40. }
  41. if (con != null) {
  42. con.close();
  43. }
  44. } catch (SQLException ex) {
  45. Logger lgr = Logger.getLogger(CreateCars.class.getName());
  46. lgr.log(Level.WARNING, ex.getMessage(), ex);
  47. }
  48. }
  49. }
  50. }

该示例以嵌入式模式连接到 Derby。 它创建一个CARS表,并向其中添加 8 行。 最终,它关闭了 Derby。

  1. String url = "jdbc:derby:testdb;user=USER12";

这是用于以嵌入式模式和USER12模式连接到testdb数据库的 URL。

  1. System.setProperty("derby.system.home", "/home/janbodnar/.derby");

我们为 Derby 系统目录设置了系统属性。

  1. con = DriverManager.getConnection(url);

创建与 Derby 数据库的连接。 创建连接后,将启动 Derby 数据库。

  1. st.executeUpdate("CREATE TABLE CARS(ID INT PRIMARY KEY,"
  2. + "NAME VARCHAR(30), PRICE INT)");
  3. st.executeUpdate("INSERT INTO CARS VALUES(1, 'Audi', 52642)");
  4. ...

我们执行创建数据库并填充一些数据的 SQL 语句。 对于INSERTUPDATEDELETE语句以及类似CREATE TABLE的 DDL 语句,我们使用executeUpdate()方法。

  1. DriverManager.getConnection("jdbc:derby:;shutdown=true");

Derby 数据库引擎已关闭。

  1. } catch (SQLException ex) {
  2. Logger lgr = Logger.getLogger(CreateCars.class.getName());

我们抓到SQLExceptionLogger类用于记录错误消息。

  1. if (((ex.getErrorCode() == 50000)
  2. && ("XJ015".equals(ex.getSQLState())))) {
  3. lgr.log(Level.INFO, "Derby shut down normally", ex);
  4. }

当 Derby 引擎关闭时,将抛出SQLException。 我们捕获此异常并记录一条信息消息。

  1. } finally {
  2. try {
  3. if (st != null) {
  4. st.close();
  5. }
  6. if (con != null) {
  7. con.close();
  8. }

finally子句中,我们释放资源。

  1. Mar 22, 2017 12:22:15 PM com.zetcode.CreateCars main
  2. INFO: Derby shut down normally
  3. java.sql.SQLException: Derby system shutdown.
  4. ...

我们编译并运行该示例。 Derby 的关闭将以SQLException结尾。 这是 Derby 数据库的功能。

检索数据

接下来,我们将展示如何从数据库表中检索数据。 我们从CARS表中获取所有数据。

SelectAllCars.java

  1. package com.zetcode;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.ResultSet;
  5. import java.sql.SQLException;
  6. import java.sql.Statement;
  7. import java.util.logging.Level;
  8. import java.util.logging.Logger;
  9. public class SelectAllCars {
  10. public static void main(String[] args) {
  11. Connection con = null;
  12. Statement st = null;
  13. ResultSet rs = null;
  14. String url = "jdbc:derby:testdb";
  15. try {
  16. System.setProperty("derby.system.home", "/home/janbodnar/.derby");
  17. con = DriverManager.getConnection(url);
  18. st = con.createStatement();
  19. rs = st.executeQuery("SELECT * FROM USER12.CARS");
  20. while (rs.next()) {
  21. System.out.print(rs.getInt(1));
  22. System.out.print(" ");
  23. System.out.print(rs.getString(2));
  24. System.out.print(" ");
  25. System.out.println(rs.getString(3));
  26. }
  27. DriverManager.getConnection("jdbc:derby:;shutdown=true");
  28. } catch (SQLException ex) {
  29. Logger lgr = Logger.getLogger(SelectAllCars.class.getName());
  30. if (((ex.getErrorCode() == 50000)
  31. && ("XJ015".equals(ex.getSQLState())))) {
  32. lgr.log(Level.INFO, "Derby shut down normally", ex);
  33. } else {
  34. lgr.log(Level.SEVERE, ex.getMessage(), ex);
  35. }
  36. } finally {
  37. try {
  38. if (rs != null) {
  39. rs.close();
  40. }
  41. if (st != null) {
  42. st.close();
  43. }
  44. if (con != null) {
  45. con.close();
  46. }
  47. } catch (SQLException ex) {
  48. Logger lgr = Logger.getLogger(SelectAllCars.class.getName());
  49. lgr.log(Level.WARNING, ex.getMessage(), ex);
  50. }
  51. }
  52. }
  53. }

我们从CARS表中获得所有汽车,并将它们打印到控制台。

  1. st = con.createStatement();
  2. rs = st.executeQuery("SELECT * FROM USER12.CARS");

我们执行一个查询,该查询从CARS表中选择所有列。 我们使用executeQuery()方法。 该方法执行给定的 SQL 语句,该语句返回单个ResultSet对象。 ResultSet是 SQL 查询返回的数据表。 还要注意,由于我们尚未在 URL 中指定用户名,因此必须在 SQL 语句中显式提及架构名称。

  1. while (rs.next()) {
  2. System.out.print(rs.getInt(1));
  3. System.out.print(" ");
  4. System.out.print(rs.getString(2));
  5. System.out.print(" ");
  6. System.out.println(rs.getString(3));
  7. }

next()方法将光标移至结果集的下一条记录。 当结果集中没有更多行时,它将返回falsegetInt()getString()方法检索此ResultSet对象当前行中指定列的值; Java 编程语言中的intString

  1. 1 Audi 52642
  2. 2 Mercedes 57127
  3. 3 Skoda 9000
  4. 4 Volvo 29000
  5. 5 Bentley 350000
  6. 6 Citroen 21000
  7. 7 Hummer 41400
  8. 8 Volkswagen 21600
  9. Mar 22, 2017 12:28:36 PM com.zetcode.SelectAllCars main
  10. INFO: Derby shut down normally
  11. java.sql.SQLException: Derby system shutdown.
  12. ...

我们编译并运行该示例。 我们有testdb数据库的CARS表中的所有汽车的列表。

属性

通常的做法是将配置数据放在程序外部的单独文件中。 我们可以更改用户,密码或连接字符串,而无需重新编译程序。 它在需要大量测试,调试,保护数据等的动态环境中特别有用。

在 Java 中,Properties是经常用于存储基本配置数据的类。 该类用于轻松读取和保存键/值属性。

db.properties

  1. db.url=jdbc:derby:testdb;user=USER12
  2. db.user=USER12
  3. db.passwd=34klq*
  4. db.syshome=/home/janbodnar/.derby

我们有一个db.roperties文件,其中有四个键/值对。 这些是在程序执行期间动态加载的。 该文件位于src/main/resources目录中。

PropertiesExample.java

  1. package com.zetcode;
  2. import java.io.FileInputStream;
  3. import java.io.FileNotFoundException;
  4. import java.io.IOException;
  5. import java.sql.Connection;
  6. import java.sql.DriverManager;
  7. import java.sql.PreparedStatement;
  8. import java.sql.ResultSet;
  9. import java.sql.SQLException;
  10. import java.util.Properties;
  11. import java.util.logging.Level;
  12. import java.util.logging.Logger;
  13. public class PropertiesExample {
  14. public static void main(String[] args) {
  15. Connection con = null;
  16. PreparedStatement pst = null;
  17. ResultSet rs = null;
  18. Properties props = new Properties();
  19. FileInputStream in = null;
  20. try {
  21. in = new FileInputStream("src/main/resources/db.properties");
  22. props.load(in);
  23. } catch (FileNotFoundException ex) {
  24. Logger lgr = Logger.getLogger(PropertiesExample.class.getName());
  25. lgr.log(Level.SEVERE, ex.getMessage(), ex);
  26. } catch (IOException ex) {
  27. Logger lgr = Logger.getLogger(PropertiesExample.class.getName());
  28. lgr.log(Level.SEVERE, ex.getMessage(), ex);
  29. } finally {
  30. try {
  31. if (in != null) {
  32. in.close();
  33. }
  34. } catch (IOException ex) {
  35. Logger lgr = Logger.getLogger(PropertiesExample.class.getName());
  36. lgr.log(Level.SEVERE, ex.getMessage(), ex);
  37. }
  38. }
  39. String url = props.getProperty("db.url");
  40. String user = props.getProperty("db.user");
  41. String passwd = props.getProperty("db.passwd");
  42. try {
  43. System.setProperty("derby.system.home",
  44. props.getProperty("db.syshome"));
  45. con = DriverManager.getConnection(url, user, passwd);
  46. pst = con.prepareStatement("SELECT * FROM CARS");
  47. rs = pst.executeQuery();
  48. while (rs.next()) {
  49. System.out.print(rs.getInt(1));
  50. System.out.print(": ");
  51. System.out.println(rs.getString(2));
  52. }
  53. DriverManager.getConnection("jdbc:derby:;shutdown=true");
  54. } catch (SQLException ex) {
  55. Logger lgr = Logger.getLogger(PropertiesExample.class.getName());
  56. if (((ex.getErrorCode() == 50000)
  57. && ("XJ015".equals(ex.getSQLState())))) {
  58. lgr.log(Level.INFO, "Derby shut down normally", ex);
  59. } else {
  60. lgr.log(Level.SEVERE, ex.getMessage(), ex);
  61. }
  62. } finally {
  63. try {
  64. if (rs != null) {
  65. rs.close();
  66. }
  67. if (pst != null) {
  68. pst.close();
  69. }
  70. if (con != null) {
  71. con.close();
  72. }
  73. } catch (SQLException ex) {
  74. Logger lgr = Logger.getLogger(PropertiesExample.class.getName());
  75. lgr.log(Level.WARNING, ex.getMessage(), ex);
  76. }
  77. }
  78. }
  79. }

我们连接到testdb,然后从CARS表中选择所有汽车。 该示例的配置数据是从db.properties文件中读取的。

  1. Properties props = new Properties();
  2. FileInputStream in = null;
  3. try {
  4. in = new FileInputStream("src/main/resources/db.properties");
  5. props.load(in);

创建Properties类。 数据是从名为db.properties的文件中加载的,其中包含我们的配置数据。

  1. String url = props.getProperty("db.url");
  2. String user = props.getProperty("db.user");
  3. String passwd = props.getProperty("db.passwd");

使用getProperty()方法检索这些值。

  1. con = DriverManager.getConnection(url, user, passwd);

请注意,在默认的 Derby 配置中,密码将被忽略。

预备语句

现在,我们将以预备语句来关注自己。 在编写预备语句时,我们使用占位符,而不是直接将值写入语句中。 预准备的语句可提高安全性和性能。

在 Java 中,PreparedStatement是代表预编译的 SQL 语句的对象。

Prepared.java

  1. package com.zetcode;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.PreparedStatement;
  5. import java.sql.ResultSet;
  6. import java.sql.SQLException;
  7. import java.util.logging.Level;
  8. import java.util.logging.Logger;
  9. public class Prepared {
  10. public static void main(String[] args) {
  11. Connection con = null;
  12. PreparedStatement pst = null;
  13. ResultSet rs = null;
  14. String url = "jdbc:derby:testdb;user=USER12";
  15. int price = 58000;
  16. int id = 2;
  17. try {
  18. System.setProperty("derby.system.home", "/home/janbodnar/.derby");
  19. con = DriverManager.getConnection(url);
  20. pst = con.prepareStatement("UPDATE CARS SET PRICE = ? WHERE ID = ?");
  21. pst.setInt(1, price);
  22. pst.setInt(2, id);
  23. pst.executeUpdate();
  24. DriverManager.getConnection("jdbc:derby:;shutdown=true");
  25. } catch (SQLException ex) {
  26. Logger lgr = Logger.getLogger(Prepared.class.getName());
  27. if (((ex.getErrorCode() == 50000)
  28. && ("XJ015".equals(ex.getSQLState())))) {
  29. lgr.log(Level.INFO, "Derby shut down normally", ex);
  30. } else {
  31. lgr.log(Level.SEVERE, ex.getMessage(), ex);
  32. }
  33. } finally {
  34. try {
  35. if (rs != null) {
  36. rs.close();
  37. }
  38. if (pst != null) {
  39. pst.close();
  40. }
  41. if (con != null) {
  42. con.close();
  43. }
  44. } catch (SQLException ex) {
  45. Logger lgr = Logger.getLogger(Prepared.class.getName());
  46. lgr.log(Level.WARNING, ex.getMessage(), ex);
  47. }
  48. }
  49. }
  50. }

我们更改 ID 等于 2 的汽车的价格。

  1. int price = 58000;
  2. int id = 2;

这些是将要预备语句的值。 这些值可能来自用户,并且来自用户的所有内容都应被视为潜在危险。

  1. pst = con.prepareStatement("UPDATE CARS SET PRICE = ? WHERE ID = ?");

在这里,我们创建一个预备语句。 在编写预备语句时,我们使用占位符,而不是直接将值写入语句中。 预备语句更快,并且可以防止 SQL 注入攻击。 ?是一个占位符,稍后将填充。

  1. pst.setInt(1, price);
  2. pst.setInt(2, id);

值绑定到占位符。

  1. pst.executeUpdate();

执行预备语句。 当我们不希望返回任何数据时,我们使用语句对象的executeUpdate()方法。 这是当我们创建数据库或执行INSERTUPDATEDELETE语句时。

  1. ij> SELECT * FROM CARS WHERE ID=2;
  2. ID |NAME |PRICE
  3. ------------------------------------------------------
  4. 2 |Mercedes |58000
  5. 1 row selected

运行示例后,我们使用ij工具检查结果。

列标题

接下来,我们将展示如何使用数据库表中的数据打印列标题。 我们将列名称称为元数据。 元数据是有关数据库中核心数据的数据。

ColumnHeaders.java

  1. package com.zetcode;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.PreparedStatement;
  5. import java.sql.ResultSet;
  6. import java.sql.ResultSetMetaData;
  7. import java.sql.SQLException;
  8. import java.util.Formatter;
  9. import java.util.logging.Level;
  10. import java.util.logging.Logger;
  11. public class ColumnHeaders {
  12. public static void main(String[] args) {
  13. Connection con = null;
  14. PreparedStatement pst = null;
  15. ResultSet rs = null;
  16. String url = "jdbc:derby:testdb;user=USER12";
  17. try {
  18. System.setProperty("derby.system.home", "/home/janbodnar/.derby");
  19. con = DriverManager.getConnection(url);
  20. String query = "SELECT NAME, TITLE From AUTHORS, "
  21. + "Books WHERE AUTHORS.ID=BOOKS.AUTHOR_ID";
  22. pst = con.prepareStatement(query);
  23. rs = pst.executeQuery();
  24. ResultSetMetaData meta = rs.getMetaData();
  25. String colname1 = meta.getColumnName(1);
  26. String colname2 = meta.getColumnName(2);
  27. Formatter fmt1 = new Formatter();
  28. fmt1.format("%-21s%s", colname1, colname2);
  29. System.out.println(fmt1);
  30. while (rs.next()) {
  31. Formatter fmt2 = new Formatter();
  32. fmt2.format("%-21s", rs.getString(1));
  33. System.out.print(fmt2);
  34. System.out.println(rs.getString(2));
  35. }
  36. DriverManager.getConnection("jdbc:derby:;shutdown=true");
  37. } catch (SQLException ex) {
  38. Logger lgr = Logger.getLogger(ColumnHeaders.class.getName());
  39. if (((ex.getErrorCode() == 50000)
  40. && ("XJ015".equals(ex.getSQLState())))) {
  41. lgr.log(Level.INFO, "Derby shut down normally", ex);
  42. } else {
  43. lgr.log(Level.SEVERE, ex.getMessage(), ex);
  44. }
  45. } finally {
  46. try {
  47. if (rs != null) {
  48. rs.close();
  49. }
  50. if (pst != null) {
  51. pst.close();
  52. }
  53. if (con != null) {
  54. con.close();
  55. }
  56. } catch (SQLException ex) {
  57. Logger lgr = Logger.getLogger(ColumnHeaders.class.getName());
  58. lgr.log(Level.WARNING, ex.getMessage(), ex);
  59. }
  60. }
  61. }
  62. }

在此程序中,我们从AUTHORS表中选择作者,并从BOOKS表中选择他们的书。 我们打印结果集中返回的列的名称。 我们格式化输出。 用于创建表的 SQL 文件位于本教程的第一章中。

  1. String query = "SELECT NAME, TITLE From AUTHORS, "
  2. + "Books WHERE AUTHORS.ID=BOOKS.AUTHOR_ID";

这是将作者与他们的书联系在一起的 SQL 语句。

  1. ResultSetMetaData meta = rs.getMetaData();

要获取列名,我们需要获取ResultSetMetaData。 它是一个对象,可用于获取有关ResultSet对象中列的类型和属性的信息。 ResultSetMetaData是通过getMetaData()方法从ResultSet获得的。

  1. String colname1 = meta.getColumnName(1);
  2. String colname2 = meta.getColumnName(2);

从获得的元数据中,我们使用getColumnName()方法获得列名。

  1. Formatter fmt1 = new Formatter();
  2. fmt1.format("%-21s%s", colname1, colname2);
  3. System.out.println(fmt1);

我们将列名称打印到控制台。 Formatter对象格式化数据。

  1. while (rs.next()) {
  2. Formatter fmt2 = new Formatter();
  3. fmt2.format("%-21s", rs.getString(1));
  4. System.out.print(fmt2);
  5. System.out.println(rs.getString(2));
  6. }

我们将数据打印到控制台。 我们再次使用Formatter对象来格式化数据。 第一列为 21 个字符,并在左侧对齐。

  1. NAME TITLE
  2. Jack London Call of the Wild
  3. Jack London Martin Eden
  4. Honore de Balzac Old Goriot
  5. Honore de Balzac Cousin Bette
  6. Lion Feuchtwanger Jew Suess
  7. Emile Zola Nana
  8. Emile Zola The Belly of Paris
  9. Truman Capote In Cold blood
  10. Truman Capote Breakfast at Tiffany
  11. Mar 22, 2017 12:52:56 PM com.zetcode.ColumnHeaders main
  12. INFO: Derby shut down normally
  13. java.sql.SQLException: Derby system shutdown.
  14. ...

这是示例的输出。

写入图像

有些人喜欢将其图像放入数据库中,有些人则希望将其保留在文件系统中以供其应用使用。 当我们处理大量图像时,会出现技术难题。 图像是二进制数据。 Derby 具有一种特殊的数据类型来存储称为BLOB(二进制大对象)的二进制数据。

我们为此示例和以下示例创建一个名为IMAGES的新表。

  1. ij> CREATE TABLE IMAGES(ID INT PRIMARY KEY, DATA BLOB);
  2. 0 rows inserted/updated/deleted

DATA列具有BLOB类型。 在那里,我们将插入编码的二进制数据。

WriteImage.java

  1. package com.zetcode;
  2. import java.io.File;
  3. import java.io.FileInputStream;
  4. import java.io.FileNotFoundException;
  5. import java.io.IOException;
  6. import java.sql.Connection;
  7. import java.sql.DriverManager;
  8. import java.sql.PreparedStatement;
  9. import java.sql.SQLException;
  10. import java.util.logging.Level;
  11. import java.util.logging.Logger;
  12. public class WriteImage {
  13. public static void main(String[] args) {
  14. Connection con = null;
  15. PreparedStatement pst = null;
  16. String url = "jdbc:derby:testdb;user=USER12";
  17. try {
  18. System.setProperty("derby.system.home", "/home/janbodnar/.derby");
  19. con = DriverManager.getConnection(url);
  20. File imgFile = new File("woman.jpg");
  21. try (FileInputStream fin = new FileInputStream(imgFile)) {
  22. con = DriverManager.getConnection(url);
  23. pst = con.prepareStatement("INSERT INTO IMAGES(ID, DATA) VALUES(1, ?)");
  24. pst.setBinaryStream(1, fin, (int) imgFile.length());
  25. pst.executeUpdate();
  26. }
  27. DriverManager.getConnection("jdbc:derby:;shutdown=true");
  28. } catch (FileNotFoundException ex) {
  29. Logger lgr = Logger.getLogger(WriteImage.class.getName());
  30. lgr.log(Level.SEVERE, ex.getMessage(), ex);
  31. } catch (SQLException ex) {
  32. Logger lgr = Logger.getLogger(WriteImage.class.getName());
  33. if (((ex.getErrorCode() == 50000)
  34. && ("XJ015".equals(ex.getSQLState())))) {
  35. lgr.log(Level.INFO, "Derby shut down normally", ex);
  36. } else {
  37. lgr.log(Level.SEVERE, ex.getMessage(), ex);
  38. }
  39. } catch (IOException ex) {
  40. Logger.getLogger(WriteImage.class.getName()).log(Level.SEVERE, null, ex);
  41. } finally {
  42. try {
  43. if (pst != null) {
  44. pst.close();
  45. }
  46. if (con != null) {
  47. con.close();
  48. }
  49. } catch (SQLException ex) {
  50. Logger lgr = Logger.getLogger(WriteImage.class.getName());
  51. lgr.log(Level.WARNING, ex.getMessage(), ex);
  52. }
  53. }
  54. }
  55. }

在此示例中,我们从当前工作目录中读取 JPG 图像,然后插入IMAGES表中。

  1. File imgFile = new File("woman.jpg");
  2. try (FileInputStream fin = new FileInputStream(imgFile)) {

我们为图像文件创建一个File对象。 要从该文件读取字节,我们创建一个FileInputStream对象。

  1. pst = con.prepareStatement("INSERT INTO IMAGES(ID, DATA) VALUES(1, ?)");

该 SQL 语句将图像插入Images表。

  1. pst.setBinaryStream(1, fin, (int) img.length());

二进制流设置为预备语句。 setBinaryStream()方法的参数是要绑定的参数索引,输入流和流中的字节数。

  1. pst.executeUpdate();

我们使用executeUpdate()方法执行该语句。

读取图像

在前面的示例中,我们已将图像插入数据库表中。 现在,我们将从表中读取图像。

ReadImage.java

  1. package com.zetcode;
  2. import java.io.FileOutputStream;
  3. import java.io.IOException;
  4. import java.sql.Blob;
  5. import java.sql.Connection;
  6. import java.sql.DriverManager;
  7. import java.sql.PreparedStatement;
  8. import java.sql.ResultSet;
  9. import java.sql.SQLException;
  10. import java.util.logging.Level;
  11. import java.util.logging.Logger;
  12. public class ReadImage {
  13. public static void main(String[] args) {
  14. Connection con = null;
  15. PreparedStatement pst = null;
  16. ResultSet rs = null;
  17. String url = "jdbc:derby:testdb;user=USER12";
  18. try {
  19. System.setProperty("derby.system.home", "/home/janbodnar/.derby");
  20. System.out.println(System.getProperty("user.dir"));
  21. con = DriverManager.getConnection(url);
  22. String query = "SELECT DATA FROM IMAGES WHERE ID = 1";
  23. pst = con.prepareStatement(query);
  24. rs = pst.executeQuery();
  25. rs.next();
  26. String fileName = "src/main/resources/woman.jpg";
  27. try (FileOutputStream fos = new FileOutputStream(fileName)) {
  28. Blob blob = rs.getBlob("DATA");
  29. int len = (int) blob.length();
  30. byte[] buf = blob.getBytes(1, len);
  31. fos.write(buf, 0, len);
  32. }
  33. DriverManager.getConnection("jdbc:derby:;shutdown=true");
  34. } catch (IOException ex) {
  35. Logger lgr = Logger.getLogger(ReadImage.class.getName());
  36. lgr.log(Level.SEVERE, ex.getMessage(), ex);
  37. } catch (SQLException ex) {
  38. Logger lgr = Logger.getLogger(ReadImage.class.getName());
  39. if (((ex.getErrorCode() == 50000)
  40. && ("XJ015".equals(ex.getSQLState())))) {
  41. lgr.log(Level.INFO, "Derby shut down normally", ex);
  42. } else {
  43. lgr.log(Level.SEVERE, ex.getMessage(), ex);
  44. }
  45. } finally {
  46. try {
  47. if (rs != null) {
  48. rs.close();
  49. }
  50. if (pst != null) {
  51. pst.close();
  52. }
  53. if (con != null) {
  54. con.close();
  55. }
  56. } catch (SQLException ex) {
  57. Logger lgr = Logger.getLogger(ReadImage.class.getName());
  58. lgr.log(Level.WARNING, ex.getMessage(), ex);
  59. }
  60. }
  61. }
  62. }

我们从IMAGES表中读取了一张图像。

  1. String query = "SELECT DATA FROM IMAGES WHERE ID = 1";

选择一条记录。

  1. try (FileOutputStream fos = new FileOutputStream(fileName)) {

创建FileOutputStream对象以写入文件。 它旨在写入原始字节流,例如图像数据。

  1. Blob blob = result.getBlob("DATA");

我们通过调用getBlob()方法从DATA列中获取图像数据。

  1. int len = (int) blob.length();

我们找出斑点数据的长度。 换句话说,我们得到字节数。

  1. byte[] buf = blob.getBytes(1, len);

getBytes()方法以字节数组的形式检索BLOB对象的所有字节。

  1. fos.write(buf, 0, len);

字节被写入输出流。 该映像是在文件系统上创建的。

事务支持

事务是针对一个或多个数据库中数据的数据库操作的基本单位。 事务中所有 SQL 语句的影响可以全部提交给数据库,也可以全部回滚。

创建连接后,它处于自动提交模式。 这意味着每个单独的 SQL 语句都被视为事务,并在执行后立即自动提交。 对于所有 JDBC 驱动程序(包括 Derby 的驱动程序)都是如此。 要开始新的事务,我们关闭自动提交。

在直接 SQL 中,事务以BEGIN TRANSACTION语句开始,并以END TRANSACTION / COMMIT语句结束。 在 Derby 中,这些语句是BEGINCOMMIT。 但是,在使用驱动程序时,将省略这些语句。 它们由驱动处理。 确切的细节是特定于驱动程序的。 例如,psycopg2 Python 驱动程序在第一个 SQL 语句之后启动事务。 如果要使用自动提交模式,则必须将autocommit属性设置为 True。 相反,默认情况下,JDBC 驱动程序处于自动提交模式。 并且要开始新事务,必须关闭自动提交。

Transaction.java

  1. package com.zetcode;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.SQLException;
  5. import java.sql.Statement;
  6. import java.util.logging.Level;
  7. import java.util.logging.Logger;
  8. public class Transaction {
  9. public static void main(String[] args) {
  10. Connection con = null;
  11. Statement st = null;
  12. String url = "jdbc:derby:testdb;user=USER12";
  13. try {
  14. System.setProperty("derby.system.home", "/home/janbodnar/.derby");
  15. con = DriverManager.getConnection(url);
  16. st = con.createStatement();
  17. con.setAutoCommit(false);
  18. st.executeUpdate("UPDATE AUTHORS SET NAME = 'Leo Tolstoy' "
  19. + "WHERE Id = 1");
  20. st.executeUpdate("UPDATE BOOKS SET TITLE = 'War and Peace' "
  21. + "WHERE Id = 1");
  22. st.executeUpdate("UPDATE BOOKS SET TITL = 'Anna Karenina' "
  23. + "WHERE Id = 2");
  24. con.commit();
  25. DriverManager.getConnection("jdbc:derby:;shutdown=true");
  26. } catch (SQLException ex) {
  27. Logger lgr = Logger.getLogger(Transaction.class.getName());
  28. if (((ex.getErrorCode() == 50000)
  29. && ("XJ015".equals(ex.getSQLState())))) {
  30. lgr.log(Level.INFO, "Derby shut down normally", ex);
  31. } else {
  32. if (con != null) {
  33. try {
  34. con.rollback();
  35. } catch (SQLException ex1) {
  36. lgr.log(Level.WARNING, ex1.getMessage(), ex1);
  37. }
  38. }
  39. lgr.log(Level.SEVERE, ex.getMessage(), ex);
  40. }
  41. } finally {
  42. try {
  43. if (st != null) {
  44. st.close();
  45. }
  46. if (con != null) {
  47. con.close();
  48. }
  49. } catch (SQLException ex) {
  50. Logger lgr = Logger.getLogger(Transaction.class.getName());
  51. lgr.log(Level.WARNING, ex.getMessage(), ex);
  52. }
  53. }
  54. }
  55. }

在此程序中,我们想在AUTHORS表的第一行中更改作者的姓名。 我们还必须更改与该作者相关的书籍。 如果我们更改作者但不更改作者的书,则数据已损坏。

  1. con.setAutoCommit(false);

要处理事务,必须将autocommit设置为false。 默认情况下,数据库连接处于自动提交模式。 在这种模式下,每条语句在执行后都会立即提交给数据库。 声明无法撤消。 当自动提交关闭时,我们通过调用commit()提交更改,或通过调用rollback()方法将其回滚。

  1. st.executeUpdate("UPDATE BOOKS SET TITL = 'Anna Karenina' "
  2. + "WHERE Id = 2");

第三个 SQL 语句有一个错误。 BOOKS表中没有TITL列。

  1. con.commit();

如果没有异常,则提交事务。 如果自动提交关闭,则必须显式调用commit()方法。

  1. if (con != null) {
  2. try {
  3. con.rollback();
  4. } catch (SQLException ex1) {
  5. lgr.log(Level.WARNING, ex1.getMessage(), ex1);
  6. }
  7. }

如果发生 Derby 系统关闭以外的异常,则事务将回滚。 没有更改提交到数据库。

  1. Mar 22, 2017 2:00:40 PM com.zetcode.Transaction main
  2. SEVERE: 'TITL' is not a column in table or VTI 'USER12.BOOKS'.
  3. java.sql.SQLSyntaxErrorException: 'TITL' is not a column in table or VTI 'USER12.BOOKS'.

执行失败,并显示'TITL' is not a column in table消息。 引发异常。 事务已回滚,并且未进行任何更改。

  1. ij> CONNECT 'jdbc:derby:testdb';
  2. ij> SET CURRENT SCHEMA = USER12;
  3. ij> SELECT NAME, TITLE FROM AUTHORS, BOOKS WHERE AUTHORS.ID = BOOKS.AUTHOR_ID;
  4. NAME |TITLE
  5. ------------------------------------------------------------
  6. Jack London |Call of the Wild
  7. Jack London |Martin Eden
  8. Honore de Balzac |Old Goriot
  9. Honore de Balzac |Cousin Bette
  10. Lion Feuchtwanger |Jew Suess
  11. Emile Zola |Nana
  12. Emile Zola |The Belly of Paris
  13. Truman Capote |In Cold blood
  14. Truman Capote |Breakfast at Tiffany
  15. 9 rows selected

数据未损坏。

但是,如果没有事务,数据是不安全的。

NonTransaction.java

  1. package com.zetcode;
  2. import java.sql.Connection;
  3. import java.sql.DriverManager;
  4. import java.sql.SQLException;
  5. import java.sql.Statement;
  6. import java.util.logging.Level;
  7. import java.util.logging.Logger;
  8. public class NonTransaction {
  9. public static void main(String[] args) {
  10. Connection con = null;
  11. Statement st = null;
  12. String url = "jdbc:derby:testdb;user=USER12";
  13. try {
  14. System.setProperty("derby.system.home", "/home/janbodnar/.derby");
  15. con = DriverManager.getConnection(url);
  16. st = con.createStatement();
  17. st.executeUpdate("UPDATE AUTHORS SET NAME = 'Leo Tolstoy' "
  18. + "WHERE Id = 1");
  19. st.executeUpdate("UPDATE BOOKS SET TITLE = 'War and Peace' "
  20. + "WHERE Id = 1");
  21. st.executeUpdate("UPDATE BOOKS SET TITL = 'Anna Karenina' "
  22. + "WHERE Id = 2");
  23. DriverManager.getConnection("jdbc:derby:;shutdown=true");
  24. } catch (SQLException ex) {
  25. Logger lgr = Logger.getLogger(NonTransaction.class.getName());
  26. if (((ex.getErrorCode() == 50000)
  27. && ("XJ015".equals(ex.getSQLState())))) {
  28. lgr.log(Level.INFO, "Derby shut down normally", ex);
  29. } else {
  30. lgr.log(Level.SEVERE, ex.getMessage(), ex);
  31. }
  32. } finally {
  33. try {
  34. if (st != null) {
  35. st.close();
  36. }
  37. if (con != null) {
  38. con.close();
  39. }
  40. } catch (SQLException ex) {
  41. Logger lgr = Logger.getLogger(NonTransaction.class.getName());
  42. lgr.log(Level.WARNING, ex.getMessage(), ex);
  43. }
  44. }
  45. }
  46. }

我们有同样的例子。 这次,没有事务支持。

  1. Mar 22, 2017 2:08:40 PM com.zetcode.NonTransaction main
  2. SEVERE: 'TITL' is not a column in table or VTI 'USER12.BOOKS'.
  3. java.sql.SQLSyntaxErrorException: 'TITL' is not a column in table or VTI 'USER12.BOOKS'.
  4. ...
  5. ij> CONNECT 'jdbc:derby:testdb';
  6. ij> SET CURRENT SCHEMA = USER12;
  7. ij> SELECT NAME, TITLE FROM AUTHORS, BOOKS WHERE AUTHORS.ID = BOOKS.AUTHOR_ID;
  8. NAME |TITLE
  9. ----------------------------------------------------------------
  10. Leo Tolstoy |War and Peace
  11. Leo Tolstoy |Martin Eden
  12. Honore de Balzac |Old Goriot
  13. Honore de Balzac |Cousin Bette
  14. Lion Feuchtwanger |Jew Suess
  15. Emile Zola |Nana
  16. Emile Zola |The Belly of Paris
  17. Truman Capote |In Cold blood
  18. Truman Capote |Breakfast at Tiffany
  19. 9 rows selected

再次引发异常。 列夫·托尔斯泰(Leo Tolstoy)没有写马丁·伊登(Martin Eden):数据已损坏。

批量更新

当我们需要使用多个语句更新数据时,可以使用批处理更新。 批量更新可用于INSERTUPDATEDELETE语句以及CREATE TABLEDROP TABLE语句。

BatchUpdates.java

  1. package com.zetcode;
  2. import java.sql.*;
  3. import java.util.logging.Level;
  4. import java.util.logging.Logger;
  5. public class BatchUpdates {
  6. public static void main(String[] args) {
  7. Connection con = null;
  8. Statement st = null;
  9. ResultSet rs = null;
  10. String url = "jdbc:derby:testdb;user=USER12";
  11. try {
  12. System.setProperty("derby.system.home", "/home/janbodnar/.derby");
  13. con = DriverManager.getConnection(url);
  14. con.setAutoCommit(false);
  15. st = con.createStatement();
  16. st.addBatch("DELETE FROM CARS");
  17. st.addBatch("INSERT INTO CARS VALUES(1, 'Audi', 52642)");
  18. st.addBatch("INSERT INTO CARS VALUES(2, 'Mercedes', 57127)");
  19. st.addBatch("INSERT INTO CARS VALUES(3, 'Skoda', 9000)");
  20. st.addBatch("INSERT INTO CARS VALUES(4, 'Volvo', 29000)");
  21. st.addBatch("INSERT INTO CARS VALUES(5, 'Bentley', 350000)");
  22. st.addBatch("INSERT INTO CARS VALUES(6, 'Citroen', 21000)");
  23. st.addBatch("INSERT INTO CARS VALUES(7, 'Hummer', 41400)");
  24. st.addBatch("INSERT INTO CARS VALUES(8, 'Volkswagen', 21600)");
  25. st.addBatch("INSERT INTO CARS VALUES(9, 'Jaguar', 95000)");
  26. int counts[] = st.executeBatch();
  27. con.commit();
  28. System.out.println("Committed " + counts.length + " updates");
  29. DriverManager.getConnection("jdbc:derby:;shutdown=true");
  30. } catch (SQLException ex) {
  31. Logger lgr = Logger.getLogger(BatchUpdates.class.getName());
  32. if (((ex.getErrorCode() == 50000)
  33. && ("XJ015".equals(ex.getSQLState())))) {
  34. lgr.log(Level.INFO, "Derby shut down normally", ex);
  35. } else {
  36. if (con != null) {
  37. try {
  38. con.rollback();
  39. } catch (SQLException ex1) {
  40. lgr.log(Level.WARNING, ex1.getMessage(), ex1);
  41. }
  42. }
  43. lgr.log(Level.SEVERE, ex.getMessage(), ex);
  44. }
  45. } finally {
  46. try {
  47. if (rs != null) {
  48. rs.close();
  49. }
  50. if (st != null) {
  51. st.close();
  52. }
  53. if (con != null) {
  54. con.close();
  55. }
  56. } catch (SQLException ex) {
  57. Logger lgr = Logger.getLogger(BatchUpdates.class.getName());
  58. lgr.log(Level.WARNING, ex.getMessage(), ex);
  59. }
  60. }
  61. }
  62. }

这是用于批处理更新的示例程序。 我们从CARS表中删除所有行,并在其中插入 9 行。

  1. con.setAutoCommit(false);

进行批处理更新时,应始终关闭自动提交。

  1. st.addBatch("DELETE FROM CARS");
  2. st.addBatch("INSERT INTO CARS VALUES(1, 'Audi', 52642)");
  3. st.addBatch("INSERT INTO CARS VALUES(2, 'Mercedes', 57127)");
  4. st.addBatch("INSERT INTO CARS VALUES(3, 'Skoda', 9000)");
  5. ...

我们使用addBatch()方法向该语句添加新命令。

  1. int counts[] = st.executeBatch();

添加所有命令后,我们调用executeBatch()进行批量更新。 该方法返回已提交更改的数组。

  1. con.commit();

批处理更新在事务中提交。

  1. ij> SELECT * FROM CARS;
  2. ID |NAME |PRICE
  3. ------------------------------------------------------
  4. 1 |Audi |52642
  5. 2 |Mercedes |57127
  6. 3 |Skoda |9000
  7. 4 |Volvo |29000
  8. 5 |Bentley |350000
  9. 6 |Citroen |21000
  10. 7 |Hummer |41400
  11. 8 |Volkswagen |21600
  12. 9 |Jaguar |95000

我们已经成功地重新创建了CARS表。

在本章中,我们使用 Java 和 Derby 进行了一些 JDBC 编程。