`
guoyong123
  • 浏览: 56471 次
  • 性别: Icon_minigender_1
  • 来自: 河南
社区版块
存档分类
最新评论
  • jateide: 还有个TRACE级别。另外还有两个特殊级别:ALL 和 OFF ...
    Log4j级别

iBatis Date 类型 时分秒丢失(精度丢失)问题

    博客分类:
  • J2EE
 
阅读更多

初转自http://blog.feihoo.com/2010/11/jdbc-ibatis-datetimes.html

在此前,遇到过使用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类型映射

DATE java.sql.Date
DATETIME java.sql.Timestamp
TIMESTAMP[(M)] java.sql.Timestamp
TIME java.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之间的类型映射

DATE java.sql.Date
DATE java.sql.Time
TIMESTAMP java.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类型):

Xml代码 复制代码 收藏代码
  1. <sqlMap namespace="Info" >  
  2.   <resultMap id="Info" class="pojo.Info" >  
  3.     <result column="INFO_BEGINTIME" property="begin" jdbcType="DATE" />  
  4.     <result column="INFO_ENDTIME" property="end" jdbcType="DATE" />  
  5.   </resultMap  
<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”

补充一下:当我按照本文所说修改以后,发现仍然存在这个问题,经过仔细排叉,发现我在保存数据的时候,参数类型为java.sql.Date我又看了一下我的insert片断语句为:

Xml代码 复制代码 收藏代码
  1. <isNotNull prepend="," property="record.signDate" >  
  2.           <![CDATA[ SIGN_DATE = #record.signDate:DATE# ]]>  
  3. </isNotNull>  
<isNotNull prepend="," property="record.signDate" >
          <![CDATA[ SIGN_DATE = #record.signDate:DATE# ]]>
</isNotNull>

 我试着将代码修改为:

Xml代码 复制代码 收藏代码
  1. <isNotNull prepend="," property="record.signDate" >  
  2.           <![CDATA[ SIGN_DATE = #record.signDate# ]]>  
  3.         </isNotNull>  
<isNotNull prepend="," property="record.signDate" >
          <![CDATA[ SIGN_DATE = #record.signDate# ]]>
        </isNotNull>

 此时再查看debug日志,发现参数类型变为 java.sql.Timestamp问题到此解决,看来不光是resultMap,在insert与update等语句中也要修改。Ps:我的配置文件是由ibator生成

分享到:
评论

相关推荐

    使用iBatis的类型处理器TypeHandlerCallback

    `TypeHandlerCallback`是iBatis(或MyBatis)中的一个重要接口,用于处理Java类型与数据库类型之间的转换。在处理数据库操作时,数据的类型转换是必不可少的步骤,因为Java类型和数据库存储的数据类型往往不完全匹配...

    ibatis 支持枚举类型

    Ibatis对枚举类型的原生支持可能不如实体类那样直观,但通过一些策略,我们可以实现枚举与数据库字段之间的映射。以下将详细解释如何在Ibatis中处理枚举类型。 首先,我们需要定义枚举类。枚举类通常包含若干枚举...

    ibatis 读取oracle clob类型

    ibatis 读取oracle clob类型

    ibatis中输入输出各种类型的参数分析及#与$区别

    ### ibatis中输入输出各种类型的参数分析及#与$区别 #### iBatis简介与特点 iBatis作为一款轻量级的Java持久层框架,以其简洁、灵活的特点,在众多框架中占据一席之地。相较于知名的Hibernate框架,iBatis在实现上...

    ibatis总结 ibatis ibatis ibatis ibatis

    Ibatis 是一款轻量级的Java持久层框架,它允许开发者将SQL语句与Java代码分离,从而使得数据库访问更加灵活、易于维护。本篇文章将深入探讨Ibatis的核心概念、最佳实践以及与其他框架如Struts和Spring的整合。 1. ...

    ibatis demo,ibatis例子,ibatis示例

    Ibatis提供了多种方式来实现映射,如自动类型匹配、自定义类型处理器、复杂关联映射等。 7. **缓存机制**:Ibatis内置了本地缓存和二级缓存,可以提高数据读取速度。本地缓存作用于单个SqlSession,而二级缓存则...

    ibatis类型

    标题 "ibatis类型" 暗示我们讨论的是关于iBATIS这个持久层框架的一些特定类型或组件。iBATIS是Java开发中的一个流行数据库访问框架,它允许开发者将SQL语句直接集成到XML配置文件中,实现了SQL与Java代码的分离,...

    解决IBatis缓存动态字段问题

    ### 解决IBatis缓存动态字段问题 #### 背景与问题描述 在使用IBatis框架处理数据库操作时,可能会遇到动态数据表名、动态字段名的情况。这种情况下,由于IBatis的缓存机制,可能导致字段找不到的问题。具体表现为...

    ibatis源码,ibatis源码 ibatis源码 ibatis源码

    《深入解析iBatis源码》 iBatis,一个优秀的Java持久层框架,以其轻量级、灵活的特性在众多ORM(Object-Relational Mapping)框架中独树一帜。iBatis的核心在于它的SQL映射机制,它将数据库操作与业务逻辑解耦,...

    Ibatis调用Oracle存储过程返回自定义类型

    ### Ibatis调用Oracle存储过程返回自定义类型 在企业级应用开发中,尤其是在金融、保险等业务场景中,往往需要处理复杂的数据结构与逻辑。本文将深入探讨如何使用Ibatis框架来调用Oracle数据库中的存储过程,并实现...

    ibatis开发过程取值问题

    本篇文章将深入探讨Ibatis在开发过程中遇到的取值问题,以及“##”符号在其中的作用。 首先,让我们了解Ibatis的核心概念。Ibatis的主要目标是解决对象关系映射(ORM)的问题,它允许开发者编写XML或注解形式的SQL...

    ibatis日期格式.doc

    文档“ibatis日期格式.doc”主要讨论了在使用iBatis框架时,关于日期格式处理以及数据库查询优化的问题。iBatis是一个轻量级的持久层框架,它允许开发者将SQL语句直接写在XML配置文件中,方便数据库操作。 在小组...

    ibatis api,ibatis文档,ibatis说明文档

    Ibatis文档是学习和解决问题的重要资源,它包含了详细的API参考、配置指南、最佳实践和常见问题解答。通过阅读文档,你可以了解如何配置Ibatis、创建Mapper接口和XML映射文件,以及如何处理复杂的查询和关联。 在...

    iBATIS内置别名列表

    本文将详细介绍iBATIS内置别名列表中所包含的各种类型及其具体含义。 #### 二、事务管理器别名 事务管理器是iBATIS中用于控制事务行为的重要组件。通过设置不同的事务管理器,可以灵活地调整事务的行为模式。...

    Ibatis3手册 Ibatis3参考手册

    ### Ibatis3手册知识点概述 Ibatis3作为一款流行的持久层框架,在软件开发领域具有重要的地位。本篇文章基于“Ibatis3手册 Ibatis3参考手册”的标题及描述,深入解析Ibatis3的核心概念、架构特点以及如何进行实际...

    ibatis api 帮助文档+IBATIS 开发文档

    **IBATIS API 帮助文档与IBATIS开发文档详解** IBATIS,一个由iBATIS公司开发的开源持久层框架,是Java世界中广泛使用的数据访问接口(DAO)工具。它允许开发者将SQL语句直接写在XML配置文件中,实现了SQL与Java...

    Ibatis入门例子,Ibatis教程

    Ibatis,全称为MyBatis,是一个优秀的Java持久层框架,它主要负责SQL映射,使得开发者能够将注意力集中在编写SQL语句上,而无需关注JDBC代码的编写。Ibatis消除了几乎所有的JDBC代码和手动设置参数以及获取结果集的...

    Ibatis

    1.3 参数映射:通过 `&lt;parameterMap&gt;` 和 `&lt;parameter&gt;` 元素,可以指定 SQL 语句中的参数类型和值,支持简单的类型和复杂的对象映射。 1.4 结果集映射:通过 `&lt;resultMap&gt;` 元素,定义了 SQL 查询结果如何映射到 ...

    ibatis_with_memcached

    本项目"ibatis_with_memcached"就是关于如何将Ibatis与Memcached集成,实现高效的数据库缓存策略的实例。 Ibatis是一个基于Java的SQL映射框架,它允许开发者编写SQL语句并与Java对象进行绑定,从而避免了传统的JDBC...

    ibatis教程 输入参数详解

    关于从文档复制代码的问题,以及ibatis的概述、如何开始使用ibatis、构建SqlSessionFactory的方法(包括通过XML和不使用XML的方式)、获取SqlSession的过程、探索映射SQL语句的方法、命名空间的注意事项、作用域与...

Global site tag (gtag.js) - Google Analytics