为什么JDBC使用原语而不是包装类?

时间:2013-08-12 11:07:04

标签: java jdbc prepared-statement resultset

这不是我现在遇到的具体问题,只是我无法解决的问题。我在这里找不到任何问题或在网上找到关于此的信息。

诸如java.lang.Integer之类的包装类(对于原始int)从一开始就存在于Java中。但 JDBC 的开发人员选择使用Java原语实现getIntgetLong等方法。原语的问题在于它们不能代表SQL NULL

要解决此问题,JDBC开发人员编写了许多方法,以便在列0时返回默认原始值,例如falseNULL,并引入一个wasNull()函数,供用户检查它是否应该是null。更糟糕的是,当许多其他方法(如 PreparedStatement的 setInt接受原语而不是包装器对象时,用户必须有条件地使用setNull将相应列设置为{{1} }。

我发现这为其用户如何使用 JDBC API增加了不必要的复杂性。如果JDBC的 ResultSet 对象具有NULL,当列具有SQL getInteger时,将null作为整数对象返回,那么它本来就是没有检查NULL的麻烦非常方便。 Java的拆箱会自动处理必须将 Integer 对象分配给wasNull原始变量并将其转换为int的情况。如果 PreparedStatement 具有0方法,则用户可以使用此方法设置合适列的值 - 包括SQL setInteger值 - 无需检查是否有麻烦应为null并恢复为NULL

即使到现在为止,还没有对JDBC进行任何更改来纠正这种异常现象。即使在我们已经处于Java 1.7或1.8 (我是对的吗?)之后,JDBC仍继续使用原始类型。 Java的内置装箱和拆箱将确保假定原始类型的旧应用程序将继续正确使用更新的JDBC版本以使用包装器。 为什么没有人费心去升级JDBC以使用包装器对象代替原语?

PS:很高兴听到推荐的替代解决方案来解决这个无意义的JDBC问题。

2 个答案:

答案 0 :(得分:5)

虽然它没有完全回答您的问题,但JDBC 1.2规范包含以下'rejected' design choice

  

A.8支持GetObject与getXXX
  由于各种get / set方法和通用getObject / setObject方法之间存在重叠,我们考虑丢弃get / set方法并简单地使用getObject / setObject。但是对于程序员知道SQL类型的简单常见情况,生成的转换和提取非常繁琐:
  int i = ((Integer)r.getObject(1, java.sql.Types.INTEGER)).intValue()
  因此,我们决定在这种情况下稍微改变我们的极简主义原则,并将各种get / set方法保留为大多数应用程序编程人员的首选接口,同时还为工具构建器和复杂应用程序添加getObject / setObject方法

该规范的

Chapter 14也说:

  

例如,SQL INTEGER通常映射到Java int。这支持一个简单的接口,用于读取和写入简单的Java类型的SQL值。

Chapter 2说:

  

提供与Java系统其余部分一致的Java接口

据我所知,大多数Java API使用原始类型而不是包装类型。

同样在规范的时间(1.2是1997年1月10日),没有装箱和拆箱,使用Integer等是相当麻烦的。例如,您根本无法直接使用Integer进行数学运算,首先需要调用intValue()来获取int,然后才能实际执行任何基本操作,如加法,乘法等,以及那么你需要将结果转换回Integer来存储它。对于基元来说,这听起来是一个非常好的理由。

请记住,规范是在1995年到1997年之间的某个地方设计的,这个时代的内存相对更加昂贵,而原语的包装对象只需要花费更多的内存。我相信一个简单的对象是8字节+原始字段的大小。对于int,成本为12个字节而不是4个。

答案 1 :(得分:3)

总之 - 兼容性。 JDBC API在自动装箱之前很久就已发布,现在更改它需要所有第三方供应商更新和发布他们的JDBC实现 - 这是一个后勤噩梦,如果甚至可能,那么使用JDBC API的那些必须适应行为的任何变化。

改变这种类型的显而易见的方法是发布一个新的API,如果你愿意,也可以使用JDBC2,尽管IMHO JDBC如此破坏以保证这一点,特别是考虑到JPA / Hibernate隐藏了低级JDBC API最常见的用法。