修改 Oracle XE 的CHARACTERSET


从Oracle 11g上导出一些资料至 oracle xe上时,会出现长度不足的情况。经查发现是NLS_CHARACTERSET设置不同导致的。
Oracle XE安装后,默认为AL32UTF8,这样一个中文字符会占用三个字节,而原来的数据库设置为:ZHS16GBK。以下为将Oracle XE上的AL32UTF8改为ZHS16GBK的方法:

以 sysdba 角色 进入sqlplus 之后执行以下命令:

D:\oraclexe\app\oracle\product\10.2.0\server\BIN\sqlplus.exe / as sysdbaSHUTDOWN IMMEDIATE;
STARTUP MOUNT;
ALTER SESSION SET SQL_TRACE=TRUE;
ALTER SYSTEM ENABLE RESTRICTED SESSION;
ALTER SYSTEM SET JOB_QUEUE_PROCESSES=0;
ALTER SYSTEM SET AQ_TM_PROCESSES=0;
ALTER DATABASE OPEN;
ALTER DATABASE CHARACTER SET INTERNAL_USE ZHS16GBK;
ALTER SESSION SET SQL_TRACE=FALSE;

查看Oracle 參數

select * from v$NLS_PARAMETERS

重启数据库

SHUTDOWN IMMEDIATE;
STARTUP;

修改完成后管理界面会乱码,不用管它。

参考:修改 Oracle XE 的CHARACTERSET

apache-derby-数据库使用注意事项


Apache Derby数据库是一个小巧的、支持内嵌模式的数据库,他实现了SQL-99 和 SQL-2003 标准。最近由于项目的原因,开始使用Derby,以下是一些使用心得:

  1. SQL语句不支持rownum关键字

    虽然不直接支持rownum关键字,但是可以通过ROW_NUMBER函数实现。用法如下:

    SELECT * FROM (
      SELECT
        ROW_NUMBER() OVER () AS rownum,
        columns
      FROM tablename
    ) AS foo
    WHERE rownum

    参考:OLAPRowNumber

  2. 修改derby.log文件存储目录

    derby.log记录了数据库启动、运行、结束的日志信息,这个日志文件默认并不会跟随着数据库存储,而是存放在了工程根目录下。通过设置derby.system.home属性,我们可以解决derby.log日志文件存储路径的问题。在Java应用启动的时候增加一下参数:

    -Dderby.system.home=/home/leo/derby

    系统启动之后,derby.log文件就会存放在/home/leo/derby目录下

  3. 关闭Derby数据库

    Apache Derby在作为内嵌数据库运行的时候, 我们需要在系统退出之前停止Derby数据库。方法如下:

    DriverManager.getConnection("jdbc:derby:;shutdown=true");

    可以直接用以上语句停止数据库。执行停止命令时,数据库名称不是必须的。Apache Derby数据库在停止成功后,会抛出java.sql.SQLException,这是正常的。 更详细的描述请参考:Shutting down the system

  4. 同一个JVM实例中如何重启Derby

    首先按照第三步关闭数据库,然后执行下面的代码,需要特别注意的是newInstance()方法。重新注册驱动,必须增加newInstance()方法。

    Class.forName(org.apache.derby.jdbc.EmbeddedDriver).newInstance();

    或者增加 deregister=false属性,然后关闭数据库。通过设置deregister=false属性后,可以避免重新注册驱动。

    DriverManager.getConnection("jdbc:derby:;shutdown=true;deregister=false");

JDBC编程中如何判断表是否存在


在java.sql包中有一个这样的接口 DatabaseMetaData接口,通过Connection.getMetaData()我们可以获得这个接口的实现。

接口中有一个getTables方法,返回ResultSet。通过getTables方法我们即可获得指定名称的表。例如:

DatabaseMetaData dmd = conn.getMetaData();
ResultSet rs = dmd.getTables(null, null, “TEST”, null);
if (rs.next) return true;// 指定的表存在

关于getTables参数的含义,大家可以参考javadoc,说得很明白。这里需要提醒的是:在Oracle中,我们使用sql的时候,表名是不区分大小写的,但是getTables中的表名必须使用大写,因为Oracle默认使用大写存储表名,否则会找不到指定的表。

java.sql.Date.valueOf 使用注意事项


最近在帮同事解决了一个日期格式化的问题,总结如下,以飨读者。

java.sql.Date.valueOf()方法提供了把yyyy-mm-dd类型字符串转换成java.sql.Date类型的功能。但是在使用的时候有以下注意事项:

  1. valueOf参数必须是一个有效日期的yyyy-mm-dd格式类型的日期。
    在jdk1.4中,valueOf的参数可以设置为0000-00-00,jdk能够正确转换,不出现异常。但是在jdk 6中,使用该参数时提示IllegalArgumentException异常。修改为0001-01-01正常。
  2. valueOf参数必须严格符合yyyy-mm-dd格式。
    修正之前的代码通过Calendar类获得年月日,这时获得的年月日都是int类型的, 当月份和日小于10的时候是一位的,此时JDK源码中判断月、日位数的检查生效,不会再进行转换,直接抛出IllegalArgumentException异常。通过Calendar类获取年月日之后,要对不足位的字段进行补位,保证能够正常转换。

题外话:关于第二点问题,同事很疑惑为什么之前可以,现在不可以了。阅读jdk源代码并结合Bug发生的时间来看,没问题的时间2010年12月下旬,此时月、日都是两位;而出问题的时间是2011年1月上旬,月、日都不足位。通过这个经验教训来看系统边界测试有时候是很有必要的。

ORA-06553: PLS-307: too many declarations of ‘MD5’ match this call


最近因为项目需要做两个库之间的数据交换,但是因为一些客观原因导致不能使用数据库的高级复制功能,遂想到通过两个中间库 作为数据交换的节点进行数据交换,但是这样子一来,就需要自己处理数据的更新、插入、删除判断了。

插入的数据比较好判断,通过一个表记录数据的主键即可,但是更新的数据怎么判断出来呢?通过调查发现,可以采用记录数据主键以及创建记录数据体的MD5校验码来检查数据是否被更新了。

Oracle本身提供了一个md5函数可以生成文本的md5值,连接数据执行

select dbms_obfuscation_toolkit.md5(‘test’) from dual;

居然显示ORA-06553: PLS-307: too many declarations of ‘MD5’ match this call错误,上网一查,发现需要指明接收的参数名称。通过下面的方法可以解决该问题。

select dbms_obfuscation_toolkit.md5(input_string => ‘test’) from dual;

增加了input_string类型的参数,至此问题解决。

最近决定往分布式系统架构方向走,得努力了。在卓越买了两本书,一本Java 消息服务,还有一本Spring技术内幕:深入解析Spring架构与设计原理。关于分布式,还是先从消息服务入手,然后再搞定Hadoop的配置和基本开发。Spring方面的话虽然自己一直在用,也经常看一些源代码,并且模仿Spring的设计方法在自己的系统里面做一些实现。但是没有全面的了解,感觉还是不扎实,所以看一下Spring技术内幕全盘掌握Spring的一些技术。

——谨此勉励自己

Hibernate + c3p0 出现APPARENT DEADLOCK!!!错误


最近一个项目用到了SpringMVC+Hibernate,Hibernate的连接缓冲池用的是c3p0。项目快结束了,做了一个压力测试,发现c3p0报APPARENT DEADLOCK!!!错误。网上查了查,在hibernate官方网站发现如下内容:

Mark,

May the StatementCache be with you.

Regarding your deadlocks, they are interesting. The tasks that are failing to terminate are not the Statement cache tasks, but Connection close() tasks. Nevertheless, past deadlocks have occurred because of Statement close was happening simultaneous to Connection close(), and I’m suspicious that we are seeing more of the same here, since you are clearly churning though a lot of PreparedStatements. (You have a zillion pending statement close tasks, and three Connection close tasks that are failing.)

0.9.0 cleared up the most obvious bad interaction between Statement and Connection close(), but it occurs to me that the strategy does not yet guarantee that a PreparedStatement and Connection close cannot occur asynchronously and near simultaneously by happenstance, especially when Statements are being close()ed quite frequently. So, my job is to tighten that up. [Even though in theory it should be foreseen and handled cleanly, many drivers don’t like Statements to be used simultaneous to Connection ops or after Connection close().]

Your job is this: 1) take moonlight’s suggestion for just long enough to persuade yourself that the statement cache is implicated in the apparent deadlock, and write back to let me know the problem goes away when statement caching is turned off; 2) try making max_statements much, much larger, or set it to 0 and set c3p0.maxStatementsPerConnection in c3p0.properties to as many distinct PreparedStatements as your application ever uses. This will both improve the performance of your application (statement caching doesn’t much help if you are always churning through the statements), and make the deadlock you are seeing, presuming it is an artifact of statement close simultaneous with connection close, much less likely. The only likely downside of a large max_statements is the potential memory footprint (see how it works for you), but there may be many upsides. Do give it a shot.

Good luck!
Steve (c3p0 guy)

最后通过在c3p0的配置文件中指明maxStatements = 0以及maxStatementsPerConnection = 0解决了APPARENT DEADLOCK!!!的错误