本教程是为 JDK 8 编写的。本页中描述的示例和实践不利用后续版本中引入的改进,可能使用不再可用的技术。
有关 Java SE 9 及后续版本中更新的语言功能的概述,请参阅 Java 语言变更。
有关所有 JDK 发布的新功能、增强功能以及已删除或已弃用选项的信息,请参阅 JDK 发布说明。
注意:MySQL当前不支持用户定义类型。MySQL和Java DB当前不支持结构化类型或DISTINCT SQL数据类型。没有可用的JDBC教程示例来演示本节中描述的功能。
随着业务的繁荣,The Coffee Break的业主经常增加新店铺并对数据库进行更改。业主决定为结构化类型ADDRESS使用自定义映射。这使得业主可以对映射ADDRESS类型的Java类进行更改。该Java类将为ADDRESS的每个属性设置一个字段。类的名称和字段的名称可以是任何有效的Java标识符。
以下主题涵盖:
自定义映射所需的第一步是创建一个实现SQLData接口的类。
结构化类型ADDRESS的SQL定义如下:
CREATE TYPE ADDRESS
(
NUM INTEGER,
STREET VARCHAR(40),
CITY VARCHAR(40),
STATE CHAR(2),
ZIP CHAR(5)
);
实现ADDRESS类型的自定义映射的类可能如下所示:
public class Address implements SQLData {
public int num;
public String street;
public String city;
public String state;
public String zip;
private String sql_type;
public String getSQLTypeName() {
return sql_type;
}
public void readSQL(SQLInput stream, String type)
throws SQLException {
sql_type = type;
num = stream.readInt();
street = stream.readString();
city = stream.readString();
state = stream.readString();
zip = stream.readString();
}
public void writeSQL(SQLOutput stream)
throws SQLException {
stream.writeInt(num);
stream.writeString(street);
stream.writeString(city);
stream.writeString(state);
stream.writeString(zip);
}
}
编写实现SQLData接口的类后,设置自定义映射的另一件事是在类型映射中添加条目。对于本示例,这意味着输入ADDRESS类型的完全限定SQL名称和类Address的Class对象。类型映射是java.util.Map接口的实例,与每个新建的连接关联在一起,因此您可以使用它。假设con是活动连接,以下代码片段向与con关联的类型映射中添加了UDTADDRESS的条目。
java.util.Map map = con.getTypeMap();
map.put("SchemaName.ADDRESS", Class.forName("Address"));
con.setTypeMap(map);
每当调用getObject方法检索ADDRESS类型的实例时,驱动程序将检查与连接关联的类型映射,并查看是否存在ADDRESS的条目。驱动程序将注意到Address类的Class对象,创建一个实例,并在后台执行许多其他操作,将ADDRESS映射到Address。您无需做任何事情,只需生成映射的类并在类型映射中进行条目输入,以让驱动程序知道存在自定义映射。驱动程序将完成所有其他操作。
对于存储具有自定义映射的结构化类型,情况类似。当调用setObject方法时,驱动程序将检查要设置的值是否是实现SQLData接口的类的实例。如果是(表示存在自定义映射),驱动程序将使用自定义映射将该值转换为其SQL对应项,然后将其返回到数据库。同样,驱动程序在后台执行自定义映射;您只需向setObject方法提供具有自定义映射的参数即可。稍后的本节中将给出一个示例。
看一下使用标准映射(Struct对象)和使用Java编程语言中的自定义映射(类)之间的区别。以下代码片段显示了对Struct对象的标准映射,当连接的类型映射中没有条目时,驱动程序使用此映射。
ResultSet rs = stmt.executeQuery(
"SELECT LOCATION " +
"WHERE STORE_NO = 100003");
rs.next();
Struct address = (Struct)rs.getObject("LOCATION");
变量address包含以下属性值:4344,"First_Street","Verona","CA","94545"。
以下代码片段显示了连接的类型映射中存在结构化类型ADDRESS时会发生的情况。请记住,列LOCATION存储了ADDRESS类型的值。
ResultSet rs = stmt.executeQuery(
"SELECT LOCATION " +
"WHERE STORE_NO = 100003");
rs.next();
Address store_3 = (Address)rs.getObject("LOCATION");
现在,变量store_3是Address类的一个实例,每个属性值都是Address的字段的当前值。请注意,在将getObject方法检索到的对象转换为Address对象之前,必须记得进行转换。还要注意,store_3必须是一个Address对象。
将使用Struct对象与使用Address类的实例进行比较。假设商店搬到了附近镇的更好位置,因此您必须更新数据库。通过自定义映射,重置store_3的字段,如下代码片段所示:
ResultSet rs = stmt.executeQuery(
"SELECT LOCATION " +
"WHERE STORE_NO = 100003");
rs.next();
Address store_3 = (Address)rs.getObject("LOCATION");
store_3.num = 1800;
store_3.street = "Artsy_Alley";
store_3.city = "Arden";
store_3.state = "CA";
store_3.zip = "94546";
PreparedStatement pstmt = con.prepareStatement(
"UPDATE STORES " +
"SET LOCATION = ? " +
"WHERE STORE_NO = 100003");
pstmt.setObject(1, store_3);
pstmt.executeUpdate();
LOCATION列中的值是ADDRESS类型的实例。驱动程序检查连接的类型映射,并看到有一个将ADDRESS与Address类链接起来的条目,并因此使用Address中指定的自定义映射。当代码调用setObject方法,并将变量store_3作为第二个参数时,驱动程序检查并看到store_3表示Address类的实例,该类实现了结构类型ADDRESS的SQLData接口,并自动使用自定义映射。
如果没有为ADDRESS自定义映射,更新将如下所示:
PreparedStatement pstmt = con.prepareStatement(
"UPDATE STORES " +
"SET LOCATION.NUM = 1800, " +
"LOCATION.STREET = 'Artsy_Alley', " +
"LOCATION.CITY = 'Arden', " +
"LOCATION.STATE = 'CA', " +
"LOCATION.ZIP = '94546' " +
"WHERE STORE_NO = 100003");
pstmt.executeUpdate;
到目前为止,您只使用了与连接关联的类型映射进行自定义映射。通常,这是大多数程序员将使用的唯一类型映射。然而,也可以创建一个类型映射并将其传递给某些方法,以便驱动程序将使用该类型映射而不是与连接关联的类型映射。这允许同一个用户定义类型(UDT)有两个不同的映射。实际上,可以为同一个UDT拥有多个自定义映射,只要每个映射都使用实现SQLData接口的类和类型映射中的条目设置。如果在可以接受类型映射的方法中不传递类型映射,则驱动程序默认使用与连接关联的类型映射。
除了与连接关联的类型映射之外,几乎没有什么情况需要使用其他类型映射。如果多个程序员在一个JDBC应用程序上工作并将他们的组件放在一起,并且正在使用同一个连接,可能需要向方法提供类型映射。如果两个或更多程序员为相同的SQL UDT创建了自定义映射,则每个人都需要提供自己的类型映射,从而覆盖连接的类型映射。