最近工作中用到了这三种数据库对日期字段的操作,现做如下总结。
1.数据库字段与java类型的对应关系。
明白这对应关系,我们才能正确的使用PreparedStatement设置参数
2.关于日期字段的几点说明
首先任何的日期字段设置的时候都应尽量保持类型对应设置。
比如:oracle的date字段,如果你想保留年月日时分秒,那么就应该用
setTime(java.sql.Time)或者setTimestamp(java.sql.Timestamp)来设置值,这两个还是有区别的尽管执行结果会一样
再比如:如果是mysql的time字段设置值那么应该是
setTime(java.sql.Time)
虽然,其他的设置也可以执行成功,注意这里说的是成功,并不是说合理
比如:如果是mysql的time字段设置值,你通过
setTimestamp(java.sql.Timestamp)设置值,那么其实到数据库里只会用到时分秒字段。这中方式还可以理解,而且还算合理(因为毕竟有时分秒并且这个时分秒是有效的)
如果通过setDate(java.sql.Date)来设置值,那么问题就大了,因为你设置了java.sql.Date,而这么设置,数据库只会拿到时分秒,而这个Date类型是没有时分秒的,也就是说我取时分秒字段,而你没有时分秒字段,那还了得,肯定不合理。
所以,为啥说,不同的数据库,一定要根据不同数据库的不同字段对应的字段关系来合理使用setDate,setTime,setTimestamp方法,请参照第一点说的
3.oracle的日期字段,这个相当特别
从第一点java与数据库的对应关系上看,发现一个不可思议的问题。那就是java.sql.Time对应了oracle中的date类型。别人都是java.sql.Timestamp类型对应日期字段(包含年月日时分秒的),而且别人java.sql.Time对应的都是时分秒字段(没有年月日)
我们都知道oracle的date类型包含了年月日时分秒(当然也可以存储没有时分秒的),而java.sql.Time只是存储了时间信息(至少对于mysql,sqlserver都是这么用的,只用到了时分秒。但实际上java.sql.Time可定会存储年月日信息,否则oracle怎么能准确获取到年月日信息)
没错,oracle通过setTime字段设置的参数,到数据库中仍然会准确的保存到年月日信息(前提是你的数据库字段得是date或者timestamp类型)
你以为这么就完了吗?
错,oracle还有个令人恶心的,你说你java.sql.Time对应数据的date字段就对应吧,获取的时候如果通过getTime()获取日期date字段,那么恭喜你你上当了,获取到的日期变成了1970-01-01 xx:xx:xx 时分秒是对的,但是日期是什么啊。
正确的获取方式是通过getTimestamp()方法来获取
举个oracle的例子,t_1表中有四个字段
id number类型
tdate1 date类型
tdate2 timestamp类型
tdate3 date类型
注意记录的样子,
tdate1我是通过setDate(java.sql.Date)设置的
tdate2我是通过setTimestamp(java.sql.Timestamp)设置的
tdate3我是通过setTime(java.sql.Time)设置的(当然也可以通过setTimestamp(java.sql.Timestamp)设置值)
但是查询这条记录的时候:
tdate1字段值的获取通过getDate()
tdate2字段值的获取通过getTimestamp()
tdate3字段值的获取通过getTimestamp(),注意这里千万不能使用getTime()获取
3.说一说oracle中,setTimestamp和setTime的区别(mysql,sqlserver没有这种区别,因为他们的java.sql.Time不会对应日期字段(包含年月日时分秒的日期字段))
要说区别,对于结果可能没什么区别或者说区别不大(无论是更新还是插入,结果都是一样的,都年月日时分秒),但是执行效率确实不一样的,怎样确定执行效率,那就需要查询oracle的执行计划才能明白。
实验:还是第二点的表,我们现在更新一下id字段,where条件是tdate3=?
String sql="update t_1 set id=12 where tdate3=?"; PreparedStatement ps= conn.prepareStatement(sql); java.util.Date d=new java.util.Date(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-07-23 21:31:19").getTime()); ps.setTimestamp(1, new Timestamp(d.getTime())); System.out.println(ps.executeUpdate());
查看该条语句执行计划:查看方法不在这里提了
关键点在于filter(INTERNAL_FUNCTION("TDATA3")),这句话的意思是内部函数进行了类型转换,然后匹配传过来的参数,注意这里转换的是字段,而不是传过来的值。
同样,我们通过setTime试试
String sql="update t_1 set id=13 where tdate3=?"; System.out.println(conn); PreparedStatement ps= conn.prepareStatement(sql); java.util.Date d=new java.util.Date(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse("2016-07-23 21:31:19").getTime()); ps.setTime(1, new Time(d.getTime())); System.out.println(ps.executeUpdate());
查询执行计划:filter("TDATE3"),也就是说没有进行任何的类型转换,直接赋值了
再来说说,类型转换与不转换的区别。
类型转换,oracle会调用内部的函数对字段进行转换,然后匹配,这种访问速度肯定没有不进行任何类型转换的速度快,这就涉及到了执行效率问题。
特别注意,如果时间字段是索引字段,那么这两种访问速度差距会很大。
同样,也可以进行试验。
那就是将tdate3字段作为主键,id作为普通字段。
然后执行上述试验过程,会发现:
通过setTimestamp设置的参数,走的是全表扫描 TABLE ACCESS FULL|
而通过setTime设置的参数,走的是INDEX UNIQUE SCAN 唯一键索引,这速度肯定要比全表扫描快得多。
4.mysql,sqlserver说明
mysql,sqlserver没有oracle这种变态的问题
对应什么类型就是什么类型,setTimestamp设置的参数,就通过getTimestamp获取,
setDate设置的参数就通过getDate获取,setTime设置的参数,就通过getTime获取
5.总结
不同字段的类型参数设置一定要按照对应的java类型来设置参数,尽管不同的类型也可能设置成功,但是大部分都是不合理的,或者说即便是合理的,执行效率也会有一定的影响。尤为注意的是oracle
设置的时候setTime(java.sql.Time)设置参数,getTimestamp取得字段值。