本教程是为 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创建了自定义映射,则每个人都需要提供自己的类型映射,从而覆盖连接的类型映射。