此Java教程是针对JDK 8编写的。本页面中描述的示例和实践不利用后续版本中引入的改进,并可能使用不再可用的技术。
有关Java SE 9及后续版本中更新的语言特性的摘要,请参见Java语言更改。
有关所有JDK版本的新功能、增强功能以及已删除或已弃用选项的信息,请参见JDK发行说明。
注意: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
结构化类型已经在数据库中注册为一个数据类型,所有者可以将其作为表列或结构化类型的属性的数据类型。
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
的方法。
假设rs
是ResultSet
对象的当前行的第四列中的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的所有者创建了三个新的数据类型,这些类型在数据库中用作列类型或属性类型:结构化类型LOCATION
和MANAGER
,以及PHONE_NO
的DISTINCT
类型。企业家在新类型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的列值。
以下代码示例创建了表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_ARRAY
是VARCHAR(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_NO
、LOCATION
、COF_TYPES
和MGR
的值:
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
,数字100001
是INTEGER
类型,类似于之前在COFFEES
和SUPPLIERS
表中插入的条目。
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_ID
为100001
的MANAGER
实例中。要获取与Alfredo Montoya的MANAGER
对象关联的REF(MANAGER)
实例,请选择表MANAGERS
中MGR_ID
为100001
的行中的列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
语句,将它们作为批量更新一起发送将更有效,如以下代码示例所示: