java.util.date 与 java.sql.date
System.out.println(new java.util.Date()); // Thu Aug 02 09:33:02 CST 2018
System.out.println(new java.sql.Date()); // 2018-08-02
在查询数据库时,根据JDBC API规范,厂商驱动返回的ResultSet中日期类型就是 java.sql.Date(继承自java.util.Date),时间截类型就是 java.sql.Timestamp。因此当mybatis的mapper中没有指定jdbcType时返回的时间类型就是上面的情况。
java.sql.Date和java.util.Date 最大的不同在于java.sql.Date只记录日期,而没有具体这一天的时间。所以举例来说,如果当前是2009-12-24 23:20,你创建一个java.sql.Date 将只记下2009-12-24这个信息。若你需要保留时间进行JDBC操作,请使用java.sql.Timestamp代替。
java.sql.Timestamp和java.util.Date 的一个不同点在于,它可以保存纳秒(一秒中的第几纳秒)。你可以get或set这个纳秒值:
long time = System.currentTimeMillis();
java.sql.Timestamp timestamp = new java.sql.Timestamp(time);
timestamp.setNanos(123456);
int nanos = timestamp.getNanos(); // nanos = 123456
mysql与java的时间类型
mysql所支持的日期时间类型有:DATETIME、TIMESTAMP、DATE、TIME、YEAR,如下所示:
JDBC规范所支持的数据库时间类型为:纯日期java.sql.Date、纯时间java.sql.Time、java.sql.Timestamp。 当我们在代码中使用java.util.Date作为实体的日期类型时,它既包含了日期又包含了时间信息,实际上是能够表示MySQL的三种字段类型:date、datetime、timestamp。
使用java.util.Date作为参数传递给Mapper时,不管MySQL的日期字段类型是date、datetime或者timestamp中的哪一种,默认缺省情况下,MyBatis都能够自动做出类型转换,可以直接使用 =、>、<、>=、<=符号来进行筛选。
但是,当我们手动指定jdbcType=DATE的时候,MyBatis会自动截取掉时间,只保留日期。如果MySQL的日期字段类型是datetime或者timestamp一定不要这么写。
jdbc类型为date时只有年月日
当jdbcType="DATE"类型时,返回的时间只有年月日(yyyy-MM-dd)而时分秒为0的;当jdbcType=“TIMESTAMP”的时候,返回的时间是年月日和时分秒(yyyy-MM-dd HH:mm:ss)
最终查询到的数据类型是DATE的数据只有年月日(yyyy-MM-dd),而TIMESTAMP的年月日和时分秒都有(yyyy-MM-dd HH:mm:ss),如下展示
同样的,当插入数据时,如果指定jdbcType=DATE 则bean就算有时分秒也不会被插入数据库,如果指定jdbcType=TIMESTAMP 则可以将年月日和时分秒插入数据库。
Mysql的TIMESTAMP和DATETIME的相同点:
两者都可用来表示YYYY-MM-DD HH:MM:SS[.fraction]类型的日期。
TIMESTAMP和DATETIME的不同点:
两者的存储方式不一样
对于TIMESTAMP,它把客户端插入的时间从当前时区转化为UTC(世界标准时间)进行存储。查询时,将其又转化为客户端当前时区进行返回。
而对于DATETIME,不做任何改变,基本上是原样输入和输出。
对于跨时区的业务,TIMESTAMP更为合适。
mapper中日期类型踩坑
如果实体类中的时间为Date类型,mapper中按时间查询时不指定javaType,Mybatis会视参数为Timestamp类型,时间参数被格式化为yyyy-MM-dd HH:mm:ss,如下案例: 定义用户在线实体,所有时间类型都定义为Date类型;数据表中online_date类型是date
public class UserOnline implements Serializable {
private Long userId;
// 在线时间,格式为yyyy-MM-dd HH:mm:ss
private Date onlineTime;
// 在线日期,格式为 yyyy-MM-dd
private Date onlineDate;
// 起始日期查询条件,格式为 yyyy-MM-dd
private Date onlineDateStart;
// 终止日期查询条件,格式为 yyyy-MM-dd
private Date onlineDateEnd;
}
<where>
<if test="userId != null">
and user_id = #{userId}
</if>
<if test="onlineDateStart != null">
and online_date <![CDATA[ >= ]]> #{onlineDateStart,jdbcType=DATE}
</if>
<if test="onlineDateEnd != null">
and online_date <![CDATA[ <= ]]> #{onlineDateEnd,jdbcType=DATE}
</if>
</where>
mapper查询条件如果不指定onlineDateStart的jdbcType=DATE,那么Mybatis会默认jdbcType=TIMESTAMP类型,查询的结果不会是预期的结果。 注意:如果使用between的话,指定了jdbcType=DATE也无效,mybatis会把数据当成Timestamp类型。解决办法是使用时指定格式化参数,或者转成字符串传进去直接用占位符替换。
一些关于JdbcType的总结
- 在mybatis的mapper文件中jdbcType属性底层对应的是一个JdbcType枚举类,所以jdbcType属性的值对应的都是大写
- jdbcType属性并不是在任何时候都需要设置的,一般情况下是在值可能为空时设置,确定值不为空时,可以不设置
- 在mybatis源码中JdbcType枚举类的每一个值都有对应的处理类,如ARRAY对应的处理类为ARRAYTypeHandler。所有的处理类都在org.apache.ibatis.type包下
- 有必要熟悉mybatis的mapper文件中JdbcType和JavaType的对应关系,详细情况可上网查阅
参考: