JDBC和Ibatis中的Date,Time,Timestamp处理

JDBC和Ibatis中的Date,Time,Timestamp处理

November 25th, 2010西坪 Leave a commentGo to comments

在此前,遇到过使用Ibatis操作Oracle时时间精度丢失的问题,昨天又遇到JDBC操作MySQL时间字段的问题,从网上看到各种式样的解释这些问题的博文/帖子,但多是雾里看花,不得要领。

  1. 理解JDBC中的时间类型
  2. MySQL与JDBC之间的类型映射
  3. Oracle与JDBC之间的类型映射
  4. Ibatis是怎么处理日期时间类型的
  5. 注释
  6. 参考资料

理解JDBC中的时间类型

java.sql包中包括三个类,Date, Time, 和 Timestamp,分别用来表示日期(无时间信息,eg: YYYY-MM-DD),时间(只处理时间,无日期部分, eg: HH:MM:SS)和时间戳(精确到纳秒级别)。在 它们都继承自java.util.Date。java.sql.Date和java.sql.Time都存有一个时间域,该时间域是精确到秒级别的,但是,他们只处理日期或者时间部分。在MySQL Connector/J(v5.1.13)的实现中可以看到,PrepareStatement#setDate 时会将时间重新format成”yyyy-MM-dd”格式。

因为历史遗留以及各种数据库本身的不同,各种JDBC实现中留下了格式各样的花招, 某些特殊场景下不遵照此情况可能也能工作,但不推荐这样做。

你必须根据你的实际需要来选择不同的类型,通常应该选用精确度与相应的数据库字段类型相比相同或者更高的JDBC类型

除此以外,还可以使用java.uitl.Date类型来处理时间。java.util.Date类型是上面各个类型的父类型,JDBC的API大都可以使用。

除此以外,在JDK1.6之前版本的构造java.sql.{Date|Time|TimeStamp}对象时存在性能问题,尤其是在多线程环境下更严重。这个Bug在这里。其原因是java.util.Date的相关方法调用了TimeZone.getDefaultRef(),而此方法是同步方法注1

MySQL的JDBC类型映射

DATEjava.sql.Date
DATETIMEjava.sql.Timestamp
TIMESTAMP[(M)]java.sql.Timestamp
TIMEjava.sql.Time
YEAR[(2|4)]If yearIsDateType configuration property is set to false, then
the returned object type is java.sql.Short. If set to true (the
default) then an object of type java.sql.Date (with the date
set to January 1st, at midnight).

MySQL的DATETIME、TIMESTAMP两种字段类型的显著区别在于TIMESTAMP的取值在’1970-01-01 00:00:01′ UTC 和 ‘2038-01-19 03:14:07′ UTC之间。

MySQL在时间处理方面也有一个问题,当Datetimes类型的字段的值为0000-00-00时取值方法会得到下面的异常:

Cannot convert value '0000-00-00 00:00:00' from column xx to TIMESTAMP

这个问题的原因在于,MySQL中默认使用0000-00-00等来表示时间的特殊值(参见文档)。而在Java中,并没有一个合适的方式来表示这个时间(因为Java中时间轴上0是1970-01-01 00:00:00),早于这个时间的用负数表示,这个最小的负数在时间轴上是表示不出来的。Connector/J提供了一个属性zeroDateTimeBehavior来解决此问题。

  • exception (the default), which throws an SQLException with an SQLState of S1009.(默认行为)
  • convertToNull, which returns NULL instead of the date.
  • round, which rounds the date to the nearest closest value which is 0001-01-01.

如下所示的jdbc连接将指定该行为转化为null值。

jdbc:mysql://localhost/myDatabase?zeroDateTimeBehavior=convertToNull

Oracle与JDBC之间的类型映射

DATEjava.sql.Date
DATEjava.sql.Time
TIMESTAMPjava.sql.Timestamp

Oracle数据库字段类型主要有DATE、TIMESTAMP。

在9i以后、11g以前的Oracle JDBC驱动中存在一个会丢失DATE类型字段的时间信息的bug,原因是其JDBC驱动将Oracle的Date类型处理为java.sql.Date类型,这就丢失了时间部分(看来对java.sql包下三种时间类型认识不足的问题还很普遍的)。关于这个问题,这篇帖子给出了不错的解释(墙外),此文中的方法适用于11g JDBC以前的各版本驱动。在11g JDBC驱动中,此问题已经解决了,Oracle 11g JDBC驱动的手册中也给出了解释注2

事实上,如果是使用Ibatis,pojo属性的类型设置为java.util.Date,确保 jdbcType不为 DATE或者TIME,则避免了这个bug。因为此时Ibatis会以java.sql.Timestamp来处理该字段。我专门对此做了验证,点此看测试项目源码。

Ibatis是怎么处理日期类型的

注意:本文皆基于ibatis 2.3.4.726源码分析。不过根据我初略观察,Ibatis3也适用,但请遇到问题时有所留意。

在此前工作中碰到Oracle中的Date字段会只剩下日期部分的数据,丢失了,Google发现一些人的解决方法是将JDBCType指定为datetime。有人甚至自己编写一个自定义的TypeHandler来解决这个问题。其实这完全是瞎猫撞上死耗子,那个datetime根本没意义,却歪打正着。一般的错误都是如下的配置(或者是pojo的属性为java.sql.Date类型):

<sqlMap namespace="Info" ><resultMap id="Info" class="pojo.Info" ><result column="INFO_BEGINTIME" property="begin" jdbcType="DATE" /><result column="INFO_ENDTIME" property="end" jdbcType="DATE" /></resultMap>

此时不论你pojo.Info中的字段类型(或者的javaType属性)是java.util.Date还是java.sql.Date,最终都会丢失数据。实际上,在pojo.Info中的字段类型(或者javaType属性)为java.util.Date时,如果指定jdbcType为DATE或TIME(区分大小写),则将分别得到DateOnlyTypeHandler或TimeOnlyTypeHandler。如果你不指定jdbcType,或者指定一个错误的值,例如datetime,或者xxxx等,都会得到DateTypeHandler(日期时间都会处理)。

如果pojo.Info中的属性类型(或者配置中的javaType属性)是java.sql.Date,则选择处理handler类时不会使用jdbcType,而是根据属性类型得到SqlDateTypeHandler(只包含日期)。

在Ibatis的手册中,指出来jdbcType一般情况下是不用于判断处理方式的注3。大部分情况下是依据javaType或者pojo属性类型来判断处理方式,在少部分无法根据pojo属性类型判断的情况下才使用。如果你用java.util.Date对应到MySQL,则就是这种少数情况之一。因为MySQL可以将多个数据库字段类型映射到java.util.Date,所以需要指定是DATE还是TIME(必须是大写),如果不指定则默认是完整的日期时间(此时按JDBC的timestamp操作)。

针对ibatis对时间的处理,我写了个测试,点此看测试代码。

对于Ibatis操作Date/Time/DateTime,总结如下:

  • 将pojo的属性类型设置为java.sql.Date(或java.sql.Time, java.sql.Timestamp),此时会严格遵循这三种类型的语义。但此方法因存在前文中提到的性能问题,在JDK1.6以前的JDK版本中能少使用就少使用。
  • 如果你想在pojo中使用java.util.Date, 则要注意:
    • 完整的日期时间,要确保jdbcType为空,或为DATE,TIME以外的值
    • 只需要时间,要指定jdbcType=”TIME”
    • 只需要日期,要指定jdbcType=”DATE”

注释

注释1:Use oracle.sql.DATE or oracle.sql.TIMESTAMP rather than
java.sql.Date or java.sql.Timestamp if you are using JDK 1.5 or earlier versions or require maximum performance. You may also use the oracle.sql data type if you want to read many date values or compute or display only a small percentage. Due to a bug in all versions of Java prior to JDK 1.6, construction of java.lang.Date and java.lang.Timestamp objects is slow, especially in multithreaded environments. This bug is fixed in JDK 1.6.

注释2:Mapping SQL DATE Data type to Java Oracle Database 8i and earlier versions did not support TIMESTAMP data, but Oracle DATE data used to have a time component as an extension to the SQL standard. So, Oracle Database 8i and earlier versions of JDBC drivers mapped oracle.sql.DATE to java.sql.Timestamp to preserve the time component. Starting with Oracle Database 9.0.1, TIMESTAMP support was included and 9i JDBC drivers started mapping oracle.sql.DATE to java.sql.Date. This mapping was incorrect as it truncated the time component of Oracle DATE data. To overcome this problem, Oracle Database 11.1 introduces a new flag mapDateToTimestamp. The default value of this flag is true, which means that by default the drivers will correctly map oracle.sql.DATE to java.sql.Timestamp, retaining the time information. If you still want the incorrect but 10g compatible oracle.sql.DATE to java.sql.Date mapping, then you can get it by setting the value of mapDateToTimestamp flag to false.

注释3:Ibatis 2的手册中给出的jdbcType属性的解释:属性jdbcType用于显式地指定给本属性(property)赋值的数据库字段的数据类型。对于某些特定的操作,如果不指定字段的数据类型,某些JDBC Driver无法识别字段的数据类型。一个很好的例子是PreparedStatement.setNull(int parameterIndex, int sqlType)方法,要求指定数据类型。如果不指定数据类型,某些Driver可能指定为Types.Other或Types.Null。但是,不能保证所有的Driver都表现一致。对于这种情况,SQL Map API允许使用parameterMap子元素parameter的jdbcType属性指定数据类型。
正常情况下,只有当字段可以为NULL时才需要jdbcType属性。另一需要指定jdbcType属性的情况是字段类型为日期时间类型的情况。因为Java只有一个Date类型(java.util.Date),而大多数SQL数据库有多个-通常至少有3种。因此,需要指定字段类型是DATE还是DATETIME。
属性jdbcType可以是JDBC Types类中定义的任意参数的字符串值。虽然如此,还是有某些类型不支持(即BLOB)。本节的稍后部分会说明架构支持的数据类型。
注意!大多数JDBC Driver只有在字段可以为NULL时需要指定jdbcType属性。因此,对于这些Driver,只是在字段可以为NULL时才需要指定type属性。
注意!当使用Oracle Driver时,如果没有给可以为NULL的字段指定jdbcType属性,当试图给这些字段赋值NULL时,会出现“Invalid column type”错误。

参考资料

  • JSR-000221 JDBC 4.0
  • Ibatis Developer Guide
  • MySQL Connector/J

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/353824.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

每日算法之抽签

X星球要派出一个5人组成的观察团前往W星。其中&#xff1a;A国最多可以派出4人。B国最多可以派出2人。C国最多可以派出2人。....那么最终派往W星的观察团会有多少种国别的不同组合呢&#xff1f;下面的程序解决了这个问题。数组a[] 中既是每个国家可以派出的最多的名额。程序执…

如何开始Java机器学习

什么是开始使用Java机器学习的最佳工具&#xff1f; 他们已经存在了一段时间&#xff0c;但如今看来&#xff0c;每个人都在谈论人工智能和机器学习。 对于科学家和研究人员而言&#xff0c;它已不再是秘密&#xff0c;几乎可以在任何新兴技术中实现。 在下面的文章中&#x…

keil中关于使用_at_绝对地址定位问题

keil中关于使用_at_绝对地址定位问题 2008-01-07 13:46:26| 分类&#xff1a; MCU51 | 标签&#xff1a; |字号大中小 订阅 在网上看到有人提到在keil中使用_at_进行绝对地址定位问题&#xff0c;我简单介绍一下它的用法。 使用_at_关键字对存储器进行绝对地址定位程序…

ztree树

常规的ztree树 后台数据封装成list对象 public PageModel getTreeBuildingRegData(Map<String, String> params) {PageModel pageModelnew PageModel();String statusparams.get("status");String orgIdparams.get("org_id");List<OmsBuildingReg…

如何提高效率

如何提高效率 时间管理 April 28th, 2011 本文来自读者 桃雨 翻译投稿。 Aaron Swartz写过一篇很有名的文章&#xff0c;叫做《HOWTO: Be more productive》&#xff08;如何提高效率&#xff09;。这篇文章写的实在是太好了&#xff0c;我看了好多遍&#xff0c;很赞同作者的…

Andrew Ng - 深度学习工程师 - Part 2. 改善深层神经网络:超参数调试、正则化以及优化(Week 1. 机器学习的实用层面)...

第1周 机器学习的实用层面 1.1 训练/开发/测试 早期机器学习时代&#xff08;数据规模较小&#xff09;&#xff0c;如果不需要dev set&#xff0c;常见的划分有 70%/30% 的训练/测试 划分&#xff0c;如果需要验证集&#xff0c;常见的是 60%/20%/20%划分 在big data era&…

jms 如何测试_使用JMSTester对JMS层进行基准测试

jms 如何测试对于我去过的大多数客户端&#xff0c;使用ActiveMQ扩展JMS消息传递层是一个优先事项。 有多种方法可以实现这一目标&#xff0c;但毫无疑问&#xff0c;创建基准并分析实际硬件上的体系结构&#xff08;或者正如我的同事Gary Tully所说的“询问机器”&#xff09;…

为什么待办事项清单不管用

为什么待办事项清单不管用 时间管理 November 22nd, 2012 本文原文来自 Harvard Business Review&#xff0c;由 换装迷宫tayy 翻译。 停止制作你的待办事项清单吧。它们只会让你感觉失败和受挫。想想你正在管理的那些待办清单&#xff1a;有多少事项从年初起就已经在那儿了&…

charts 画饼图

统计某一天某类物体的百分比 新知识点&#xff1a;aggregate https://blog.csdn.net/congcong68/article/details/51619882 主要的 $group $match $sort $limit pipeline [{$group:{_id:$area,count:{$sum:1}}}, # count 是聚合之后新增的一个字段。{$sort:{count:1}}, # …

Apache Camel 2.18 –即将推出的功能的亮点

骆驼骑士正在忙于即将发布的Apache Camel 2.18版本。 当我们动态更新发行说明时 &#xff0c;这是一种快速查看即将发生的情况的方法。 我只是想在夏天在这里进行快速更新&#xff0c;到目前为止&#xff0c;我们已经完成了简短的重点介绍。 随着发行版的临近和文档的完善&…

实时多线程系统的日志实现

实时多线程系统的日志实现 2008-03-21 09:19 黄明/戴颖  软件世界 我要评论(0) 字号&#xff1a;T | T为了分析软件系统在测试和运行期产生的故障&#xff0c;目前大多数软件系统所广泛使用的一种方法就是日志记录。本文给出了利用循环缓冲区和单独的日志读写线程实现实时系统…

51NOD 1424 零树

Discription 有一棵以1为根的树&#xff0c;他有n个结点&#xff0c;用1到n编号。第i号点有一个值vi。 现在可以对树进行如下操作&#xff1a; 步骤1&#xff1a;在树中选一个连通块&#xff0c;这个连通块必须包含1这个结点。 步骤2&#xff1a;然后对这个连通块中所有结点的值…

nosql的数据服务_使用NoSQL实现实体服务–第1部分:概述

nosql的数据服务在过去的几周中&#xff0c;我一直在进行一些研发工作&#xff0c;以了解使用NoSQL数据库实现实体服务 &#xff08;也称为数据服务&#xff09;的优势。 实体服务是Thomas Erl的《服务技术》丛书中提出的服务分类。 它用于描述高度不可知和可重复使用的服务&am…

Java中获取当前函数名

Java中获取当前函数名 博客分类&#xff1a; Java JavathreadJDKIDEA 有时候我们需要在程序中获取当前运行的函数名&#xff0c;如何简单的做到这点呢&#xff1f;我们可以用getStackTrace轻松搞定。 一提到getStatckTrace多数人会联想到Thowable中的getStackTrace方法。的确&…

day19 复习,组合和继承

组合&#xff1a;当一个类的对象作为另一个类对象的属性就叫组合 表示的一种什么有什么的关系 面向对象的思想 不关心程序的执行过程 关心的事一个程序中的角色以及角色与角色的关系 python中一切皆对象 实例化的过程 创建一个对象 __init__给对象添加属性&#xff0c;对…

Spring Boot和数据库初始化

Spring Boot是一个很好的框架&#xff0c;可在开发Spring应用程序时为开发人员节省大量时间和精力。 它的主要功能之一是数据库初始化。 您可以使用spring boot来初始化您的sql数据库。 我们将从gradle文件开始 group com.gkatzioura version 1.0-SNAPSHOTapply plugin: java…

学习Java: Queue

15 08, 2007 学习Java: Queue Java — 作者 zybing 15:17Java提供了Quere&#xff0c;相当好用&#xff0c;在1.5版本中又有增强。Queue&#xff1a; 基本上&#xff0c;一个队列就是一个先入先出&#xff08;FIFO&#xff09;的数据结构 offer&#xff0c;add区别&#xff1…

建站手册-网站主机:电子商务主机

ylbtech-建站手册-网站主机&#xff1a;电子商务主机1.返回顶部 1、http://www.w3school.com.cn/hosting/host_ecommerce.asp2、2.返回顶部1、如果您的公司正在销售某种产品或服务&#xff0c;那么电子商务也许是做生意的一种好办法。 Internet 商业 电子商务就是在 Internet 上…

ConcurrentLinkedQueue

ConcurrentLinkedQueue 因为ConcurrentLinkedQueue是线程序安全的并且是针对并发的 主类 Java代码 public class conn{ public static void main(String[] args) throws Exception{ Queue<String> queuenew ConcurrentLinkedQueue<String>(); …

【大型网站技术实践】初级篇:借助Nginx搭建反向代理服务器

每天学习一点点 编程PDF电子书、视频教程免费下载&#xff1a;http://www.shitanlife.com/code一、反向代理&#xff1a;Web服务器的“经纪人” 1.1 反向代理初印象 反向代理&#xff08;Reverse Proxy&#xff09;方式是指以代理服务器来接受internet上的连接请求&#xff0c;…