前段时间把一个在Linux 平台运行的应用程序部署一套到windows平台上,是java开发的基于oracle10g的程序,部署完成后,发现有个写入操作会报错,而后台跟踪到的SQL是插入日期时侯出现的问题。
insert into t ( fromtime ) values ( '2009-4-15' )
其中的fromtime是date 类型,而SQL中并没有对字符串做to_date转化。程序不大好修改,只能在环境变量等参数配置上下手。而奇怪的是这样的SQL在原来的Linux 平台上能运行的很好,说明是和环境问题是有关的。
首先修改了oracle的nls_date_format参数,原来是空的,改为yyyy-mm-dd hh24:mi:Ss,用sqlplus连到服务器测试SQL能执行,但是java程序还是报错,看来得改客户端的nls_date_format设置,老熊说有些jdbc驱动可以采用设置数据库的nls_data_format方式来解决,因为这种驱动连接上后自动将会话的这个设为跟数据库一样的。修改windows系统的环境变量仍然不行,看来java程序可以不从操作系统来读变量。以下是测试过程:
SQL> create table t1 (mydate varchar2(20));
Table created.
SQL> insert into t1 mydate values (2010-10-10);
1 row created.
SQL> alter table t1 modify mydate date;
Table altered.
SQL>
SQL> desc t1;
Name Null? Type
----------------------------------------- -------- ----------------------------
MYDATE DATESQL> insert into t1 mydate values ('2010-10-10');
insert into t1 mydate values ('2010-10-10')
*
ERROR at line 1:
ORA-01861: literal does not match format stringSQL> show parameter nls_date
NAME TYPE VALUE
------------------------------------ ----------- ------------------------------
nls_date_format string
nls_date_language stringSQL> alter system set nls_date_format='yyyy-mm-dd hh24:mi:ss';
alter system set nls_date_format='yyyy-mm-dd hh24:mi:ss'
*
ERROR at line 1:
ORA-02096: specified initialization parameter is not modifiable with this
optionSQL> alter system set nls_date_format='yyyy-mm-dd hh24:mi:ss' scope=spfile;
System altered.
SQL>
SQL> shutdown immediate
Database closed.
Database dismounted.
ORACLE instance shut down.
SQL> startup
ORACLE instance started.Total System Global Area 2147483648 bytes
Fixed Size 2022144 bytes
Variable Size 285213952 bytes
Database Buffers 1845493760 bytes
Redo Buffers 14753792 bytes
Database mounted.
Database opened.
SQL> show parameter nls_date_formatNAME TYPE VALUE
------------------------------------ ----------- ------------------------------
nls_date_format string yyyy-mm-dd hh24:mi:ssSQL> insert into t1 mydate values ('2010-10-10');
1 row created.
SQL> commit;
Commit complete.
最后尝试在程序使用的oracle用户登录的时侯修改当前session的nls_date_format,通过触发器来实现
create or replace trigger tri_logon_nlsformat
after logon
ON DATABASE
when (USER='BANPING')
begin
execute immediate 'alter session set nls_date_format = ''YYYY-MM-DD'' ';
end;
这样用户通过应用程序每次连接到数据库的时侯,就修改当前session的nls_date_format,应用程序不再报错,不过这只是一个丑陋的解决办法,根本的还需要从修改程序入手。
Language and Territory
The Thin driver obtains language and territory settings (NLS_LANGUAGE and NLS_TERRITORY) from the Java locale in the JVM user.language property. The date format (NLS_DATE_FORMAT) is set according to the territory setting.
谢谢weblogicfans指点,有空测试一下。
我一直看你的BLOG,非常不错。
我也是根据文档推测,我数据库水平很一般也没条件做测试,你可以认为是另外一个解决的途径,成不成功我也不敢保证。
上面的E文来自oracle官方的JDBC guide。
简单的说,如果不指定java的user.language和user.region变量,则java使用系统的LOCALE来算出当前的user.language、user.region。
中文windows,可以简单的认为是zh_CN
user.language、user.region可以自己指定,比如linux下面locale是en_US的时候java启动参数加上:
-Duser.language=zh -Duser.region=CN
从系统看,locale是en_US,但是java内部认为locale是zh_CN
而NLS_DATE_FORMAT的格式与user.language有直接关系。