文档

Java™ 教程
隐藏目录
使用结构化对象
路径: JDBC数据库访问
课程: JDBC基础

使用结构化对象

注意:MySQL和Java DB目前不支持用户定义的类型。因此,在本节描述的功能中,没有可用的JDBC教程示例。

以下主题包括:

结构化类型概述

SQL结构化类型和DISTINCT类型是用户可以在SQL中定义的两种数据类型。它们通常被称为UDT(用户定义类型),您可以使用SQL CREATE TYPE语句来创建它们。

回到The Coffee Break的例子,假设所有者的成功超出了所有的期望,并且一直在扩张新的分店。所有者决定向数据库中添加一个包含有关每个机构信息的STORES表。STORES将有四列:

所有者使列LOCATION成为SQL结构化类型,列COF_TYPES成为SQL ARRAY,列MGR成为REF(MANAGER),其中MANAGER是SQL结构化类型。

所有者首先必须定义地址和经理的新结构化类型。SQL结构化类型类似于Java编程语言中的结构化类型,因为它具有成员(称为属性),可以是任何数据类型。所有者编写以下SQL语句来创建新的数据类型ADDRESS:

CREATE TYPE ADDRESS
(
    NUM INTEGER,
    STREET VARCHAR(40),
    CITY VARCHAR(40),
    STATE CHAR(2),
    ZIP CHAR(5)
);

在此语句中,新类型ADDRESS具有五个属性,类似于Java类中的字段。属性NUM是INTEGER,属性STREET是VARCHAR(40),属性CITY是VARCHAR(40),属性STATE是CHAR(2),属性ZIP是CHAR(5)。

以下摘录中,con是一个有效的Connection对象,将ADDRESS的定义发送到数据库:

String createAddress =
    "CREATE TYPE ADDRESS " +
    "(NUM INTEGER, STREET VARCHAR(40), " +
    "CITY VARCHAR(40), STATE CHAR(2), ZIP CHAR(5))";
Statement stmt = con.createStatement();
stmt.executeUpdate(createAddress);

现在,ADDRESS结构化类型已经在数据库中注册为一个数据类型,所有者可以将其作为表列或结构化类型的属性的数据类型。

在结构化类型中使用DISTINCT类型

The Coffee Break的所有者计划在新的结构化类型MANAGER中包括一个属性,即经理的电话号码。由于所有者始终将电话号码列为10位数字(以确保包括区号),并且永远不会将其作为数字进行操作,所有者决定定义一个名为PHONE_NO的新类型,该类型由10个字符组成。这个数据类型的SQL定义如下:

CREATE TYPE PHONE_NO AS CHAR(10);

或者,如前所述,对于某些驱动程序,定义可能如下:

CREATE DISTINCT TYPE PHONE_NO AS CHAR(10);

DISTINCT类型总是基于另一个数据类型,该数据类型必须是预定义类型。换句话说,DISTINCT类型不能基于用户定义的类型(UDT)。要检索或设置一个值,该值是DISTINCT类型,请使用底层类型(基于的类型)的适当方法。例如,要检索基于CHAR类型的PHONE_NO实例,可以使用getString方法,因为这是检索CHAR的方法。

假设rsResultSet对象的当前行的第四列中的PHONE_NO类型的值,下面的代码行将其检索出来:

String phoneNumber = rs.getString(4);

同样,下面的代码行为将要发送到数据库的预编译语句设置了一个具有PHONE_NO类型的输入参数:

pstmt.setString(1, phoneNumber);

在前面的代码片段中添加,使用以下代码行将PHONE_NO的定义发送到数据库:

stmt.executeUpdate(
    "CREATE TYPE PHONE_NO AS CHAR(10)");

在将PHONE_NO类型注册到数据库后,所有者可以将其作为表的列类型或结构化类型的属性的数据类型。以下SQL语句中MANAGER的定义将PHONE_NO作为属性PHONE的数据类型:

CREATE TYPE MANAGER
(
    MGR_ID INTEGER,
    LAST_NAME VARCHAR(40),
    FIRST_NAME VARCHAR(40),
    PHONE PHONE_NO
);

重用之前定义的stmt,以下代码片段将结构化类型MANAGER的定义发送到数据库:

  String createManager =
    "CREATE TYPE MANAGER " +
    "(MGR_ID INTEGER, LAST_NAME " +
    "VARCHAR(40), " +
    "FIRST_NAME VARCHAR(40), " +
    "PHONE PHONE_NO)";
  stmt.executeUpdate(createManager);

使用结构化类型的引用

The Coffee Break的所有者创建了三个新的数据类型,这些类型在数据库中用作列类型或属性类型:结构化类型LOCATIONMANAGER,以及PHONE_NODISTINCT类型。企业家在新类型MANAGER的属性PHONE中使用了PHONE_NO作为类型,并在表STORES的列LOCATION中使用了ADDRESS作为数据类型。虽然MANAGER类型可以用作MGR列的类型,但企业家更喜欢使用REF(MANAGER)类型,因为一个人经常管理两个或三个商店。使用REF(MANAGER)作为列类型可以避免在一个人管理多个商店时重复存储所有MANAGER的数据。

已经创建了结构化类型MANAGER,所有者现在可以创建一个包含可以被引用的MANAGER实例的表。对MANAGER实例的引用将具有REF(MANAGER)类型。SQL中的REF仅仅是指向结构化类型的逻辑指针,因此REF(MANAGER)的实例作为指向MANAGER实例的逻辑指针。

由于SQL的REF值需要与它引用的结构化类型的实例永久关联,它存储在一个特殊的表中,与其关联的实例一起。程序员不直接创建REF类型,而是创建将存储可以被引用的特定结构化类型实例的表。每个要被引用的结构化类型都将有自己的表。当您将结构化类型的实例插入表中时,数据库会自动创建一个REF实例。例如,为了包含可以被引用的MANAGER实例,所有者使用SQL创建了以下特殊表:

  CREATE TABLE MANAGERS OF MANAGER
  (OID REF(MANAGER)
  VALUES ARE SYSTEM GENERATED);

此语句创建了一个带有特殊列OID的表,该列存储REF(MANAGER)类型的值。每当将MANAGER的实例插入表中时,数据库将生成一个REF(MANAGER)实例并将其存储在OID列中。此外,还会隐式存储插入到表中的MANAGER的每个属性的额外列。例如,以下代码片段显示了企业家创建了三个MANAGER结构化类型的实例来表示三个经理:

  INSERT INTO MANAGERS (
    MGR_ID, LAST_NAME,
    FIRST_NAME, PHONE) VALUES
  (
    000001,
    'MONTOYA',
    'ALFREDO',
    '8317225600'
  );

  INSERT INTO MANAGERS (
    MGR_ID, LAST_NAME,
    FIRST_NAME, PHONE) VALUES
  (
    000002,
    'HASKINS',
    'MARGARET',
    '4084355600'
  );

  INSERT INTO MANAGERS (
    MGR_ID, LAST_NAME,
    FIRST_NAME, PHONE) VALUES
  (
    000003,
    'CHEN',
    'HELEN',
    '4153785600'
   );

MANAGERS现在将有三行,每个经理插入一行。列OID将包含三个唯一的REF(MANAGER)对象标识符,每个MANAGER实例一个。这些对象标识符是由数据库自动生成的,并将永久存储在表MANAGERS中。隐式地,一个附加列存储MANAGER的每个属性。例如,在表MANAGERS中,一行包含一个引用Alfredo Montoya的REF(MANAGER),另一行包含一个引用Margaret Haskins的REF(MANAGER),第三行包含一个引用Helen Chen的REF(MANAGER)

要访问REF(MANAGER)实例,您需要从其表中选择它。例如,所有者使用以下代码片段检索了对Alfredo Montoya的引用,其ID号码为000001:

  String selectMgr =
    "SELECT OID FROM MANAGERS " +
    "WHERE MGR_ID = 000001";
  ResultSet rs = stmt.executeQuery(selectMgr);
  rs.next();
  Ref manager = rs.getRef("OID");

现在,变量manager可以用作引用Alfredo Montoya的列值。

创建SQL REF对象的示例代码

以下代码示例创建了表MANAGERS,这是一个可以被引用的结构类型MANAGER的实例表,并向表中插入了三个MANAGER的实例。这个表中的OID列将存储REF(MANAGER)的实例。执行此代码后,MANAGERS表将为每个插入的三个MANAGER对象插入一行,并且OID列中的值将是标识存储在该行中的MANAGER实例的REF(MANAGER)类型。

package com.oracle.tutorial.jdbc;

import java.sql.*;

public class CreateRef {

    public static void main(String args[]) {

        JDBCTutorialUtilities myJDBCTutorialUtilities;
        Connection myConnection = null;

        if (args[0] == null) {
            System.err.println("未在命令行中指定属性文件");
            return;
        } else {
            try {
                myJDBCTutorialUtilities = new JDBCTutorialUtilities(args[0]);
            } catch (Exception e) {
                System.err.println("读取属性文件时出现问题 " + args[0]);
                e.printStackTrace();
                return;
            }
        }

        Connection con = null;
        Statement stmt = null;

        try {
            String createManagers =
                "CREATE TABLE " +
                "MANAGERS OF MANAGER " +
                "(OID REF(MANAGER) " +
                "VALUES ARE SYSTEM " +
                "GENERATED)";

            String insertManager1 =
                "INSERT INTO MANAGERS " +
                "(MGR_ID, LAST_NAME, " +
                "FIRST_NAME, PHONE) " +
                "VALUES " +
                "(000001, 'MONTOYA', " +
                "'ALFREDO', " +
                "'8317225600')";

            String insertManager2 =
                "INSERT INTO MANAGERS " +
                "(MGR_ID, LAST_NAME, " +
                "FIRST_NAME, PHONE) " +
                "VALUES " +
                "(000002, 'HASKINS', " +
                "'MARGARET', " +
                "'4084355600')";

            String insertManager3 =
                "INSERT INTO MANAGERS " +
                "(MGR_ID, LAST_NAME, " +
                "FIRST_NAME, PHONE) " +
                "VALUES " +
                "(000003, 'CHEN', 'HELEN', " +
                "'4153785600')";
  
            con = myJDBCTutorialUtilities.getConnection();
            con.setAutoCommit(false);

            stmt = con.createStatement();
            stmt.executeUpdate(createManagers);

            stmt.addBatch(insertManager1);
            stmt.addBatch(insertManager2);
            stmt.addBatch(insertManager3);
            int [] updateCounts = stmt.executeBatch();

            con.commit();

            System.out.println("更新计数为:");
            for (int i = 0; i < updateCounts.length; i++) {
                System.out.print("    第" + (i + 1) + "个命令 = ");
                System.out.println(updateCounts[i]);
            }
        } catch(BatchUpdateException b) {
            System.err.println("-----BatchUpdateException-----");
            System.err.println("信息: " + b.getMessage());
            System.err.println("SQL状态: " + b.getSQLState());
            System.err.println("供应商: " + b.getErrorCode());
            System.err.print("成功命令的更新计数:");
            int [] rowsUpdated = b.getUpdateCounts();
            for (int i = 0; i < rowsUpdated.length; i++) {
                System.err.print(rowsUpdated[i] + "   ");
            }
            System.err.println("");
        } catch(SQLException ex) {
            System.err.println("------SQLException------");
            System.err.println("错误消息: " + ex.getMessage());
            System.err.println("SQL状态: " + ex.getSQLState());
            System.err.println("供应商: " + ex.getErrorCode());
        } finally {
            if (stmt != null) { stmt.close(); }
              JDBCTutorialUtilities.closeConnection(con);
        }
    }
}

使用用户定义类型作为列值

我们的创业者现在有了创建表STORES所需的UDT。结构化类型ADDRESS是列LOCATION的类型,类型REF(MANAGER)是列MGR的类型。

UDTCOF_TYPES基于SQL数据类型ARRAY,是列COF_TYPES的类型。以下代码行创建类型COF_ARRAY,作为带有10个元素的ARRAY值。基本类型COF_ARRAYVARCHAR(40)

  CREATE TYPE COF_ARRAY AS ARRAY(10) OF VARCHAR(40);

通过定义新的数据类型,以下SQL语句创建表STORES

  CREATE TABLE STORES
  (
    STORE_NO INTEGER,
    LOCATION ADDRESS,
    COF_TYPES COF_ARRAY,
    MGR REF(MANAGER)
  );

将用户定义类型插入表中

以下代码片段将一行数据插入STORES表中,按照顺序提供列STORE_NOLOCATIONCOF_TYPESMGR的值:

  INSERT INTO STORES VALUES
  (
    100001,
    ADDRESS(888, 'Main_Street',
      'Rancho_Alegre',
      'CA', '94049'),
    COF_ARRAY('Colombian', 'French_Roast',
      'Espresso', 'Colombian_Decaf',
      'French_Roast_Decaf'),
    SELECT OID FROM MANAGERS
      WHERE MGR_ID = 000001
  );

以下是对每个列及其插入值的说明:

  STORE_NO: 100001

此列的类型是INTEGER,数字100001INTEGER类型,类似于之前在COFFEESSUPPLIERS表中插入的条目。

  LOCATION: ADDRESS(888, 'Main_Street',
    'Rancho_Alegre', 'CA', '94049')

此列的类型是结构化类型ADDRESS,此值是ADDRESS实例的构造函数。当我们将ADDRESS的定义发送到数据库时,其中一件事是为新类型创建一个构造函数。括号中的逗号分隔值是ADDRESS类型的属性的初始化值,它们必须按照ADDRESS类型定义中属性的顺序出现。888是属性NUM的值,它是一个INTEGER值。"Main_Street"STREET的值,"Rancho_Alegre"CITY的值,这两个属性都是VARCHAR(40)类型。STATE属性的值是"CA",类型为CHAR(2)ZIP属性的值是"94049",类型为CHAR(5)

  COF_TYPES: COF_ARRAY(
    '哥伦比亚',
    '法式烘焙',
    '浓缩咖啡',
    '无咖啡因哥伦比亚',
    '无咖啡因法式烘焙'),

COF_TYPES的类型是COF_ARRAY,基本类型是VARCHAR(40),括号中逗号分隔的值是String对象,它们是数组的元素。所有者定义了类型COF_ARRAY最多有10个元素。这个数组有5个元素,因为创业者只提供了5个String对象。

  MGR: SELECT OID FROM MANAGERS
    WHERE MGR_ID = 000001

MGR的类型是REF(MANAGER),这意味着该列的值必须是指向结构类型MANAGER的引用。所有MANAGER的实例都存储在表MANAGERS中。所有REF(MANAGER)的实例也存储在该表中,存储在列OID中。这个表行描述的店铺的经理是Alfredo Montoya,他的信息存储在具有属性MGR_ID100001MANAGER实例中。要获取与Alfredo Montoya的MANAGER对象关联的REF(MANAGER)实例,请选择表MANAGERSMGR_ID100001的行中的列OID。将存储在STORES表的MGR列(REF(MANAGER)值)中的值是DBMS生成的用于唯一标识MANAGER结构类型实例的值。

使用以下代码片段将前面的SQL语句发送到数据库:

  String insertMgr =
    "INSERT INTO STORES VALUES " +
    "(100001, " +
    "ADDRESS(888, 'Main_Street', " +
      "'Rancho_Alegre', 'CA', " +
      "'94049'), " +
    "COF_ARRAY('Colombian', " +
      "'French_Roast', 'Espresso', " +
      "'Colombian_Decaf', " +
      "'French_Roast_Decaf'}, " +
    "SELECT OID FROM MANAGERS " +
    "WHERE MGR_ID = 000001)";

  stmt.executeUpdate(insertMgr);

然而,由于您要发送多个INSERT INTO语句,将它们作为批量更新一起发送将更有效,如以下代码示例所示:


    

上一页: 使用DISTINCT数据类型
下一页: 使用自定义类型映射