变态工作之修改hibernate让其支持null主键

关于Hibernate主键包含null值的改动方案

前言

最近公司跟另外一个公司在合作一个项目,不幸被卷入其中。这几天做了一个变态的工作,就是客户死活就要复合主键能够支持null值,甚至扬言不惜修改hibernate,于是他如愿了。

命题

数据表中设置为主键的两个字段其中一个可以为空。

分析

经过分析,Hibernate3.0beta13支持可以为空的主键,但是之后就不再支持。同时,oracle也不支持可以为null的主键,在建表的过程中,只能建立unique索引。

由此,建立可以为null的数据表主键的方法是一种非标准化的做法。就像参考文献中Hibernate作者所说的:

Comment by Gavin King [04/Mar/05 02:04 PM]

Hibernate 3 treats any key with a null value as null. This is reasonable because nulls in primary keys are completely evil and not allowed on most dbs.

为了满足命题要求,我们需要修改Hibernate的代码。

参考文献

l         http://opensource.atlassian.com/projects/hibernate/browse/HHH-177

 

步骤

建立库表结构

以下是用于实验的表结构代码:

drop table CLASS1

create table class1
    (field1 varchar2(10 char) not null,
    field2 varchar2(30 char) ,
    f3 number(10,0) not null,
    f4 varchar2(10 char) not null)

create unique index uniq1 on CLASS1 (FIELD1 , FIELD2 );

 

代码

类定义的时候使用标准的Hibernate的复合主键结构。并且在映射配置文件中使用自定义的CRUD语句代替Hibernate的自动生成语句。

文件Class1.java

package com.chinainsurance.platform.service.impl.test;

 

import java.io.Serializable;

 

public class Class1 implements Serializable  {

    Class1ID id;

    Integer field3;

    String field4;

   

    public Integer getField3() {

       return field3;

    }

    public void setField3(Integer field3) {

       this.field3 = field3;

    }

    public String getField4() {

       return field4;

    }

    public void setField4(String field4) {

       this.field4 = field4;

    }

    public Class1ID getId() {

       return id;

    }

    public void setId(Class1ID id) {

       this.id = id;

    }

}

 

文件Class1ID.java

package com.chinainsurance.platform.service.impl.test;

 

import java.io.Serializable;

 

public class Class1ID implements Serializable {

    String field1;

    String field2;    // nullable

 

   

   

    public String getField1() {

       return field1;

    }

    public void setField1(String field1) {

       this.field1 = field1;

    }

    public String getField2() {

       return field2;

    }

    public void setField2(String field2) {

       this.field2 = field2;

    }

    public boolean equals(Object arg0) {

       return super.equals(arg0);

    }

    public int hashCode() {

       return field1.hashCode();

    }

   

   

}

定义的hbm文件内容如下:

<?xml version="1.0"?>

<!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">

<hibernate-mapping>

    <class name="com.chinainsurance.platform.service.impl.test.Class1" table="class1" lazy="false" >

       <composite-id name="id" class="com.chinainsurance.platform.service.impl.test.Class1ID" >

           <key-property name="field1" column="field1" type="java.lang.String" length="10" />

           <key-property name="field2" column="field2" type="java.lang.String" length="30"  />

       </composite-id>

       <property name="field3" column="f3" type="java.lang.Integer" length="20" not-null="true"/>

       <property name="field4" column="f4" type="java.lang.String" length="10" not-null="true"/>

      

       <loader query-ref="selectClass1"/>

 

       <sql-insert>INSERT INTO class1 (f3, f4, field1, field2) VALUES ( ?, ?, ?, ? )</sql-insert>

       <sql-update>UPDATE class1 SET f3=?, f4=? WHERE field1=? anD nvl(fielD2,'*')=nvl(?, '*')</sql-update>

       <sql-delete>DELETE FROM class1 where field1=? and nvl(fielD2,'*')=nvl(?, '*')</sql-delete>

 

    </class>

   

    <sql-query name="selectClass1">

       <return class="com.chinainsurance.platform.service.impl.test.Class1" >

           <return-property name="id">

              <return-column name="field1"/>

              <return-column name="field2"/>

           </return-property>

           <return-property name="field3" column="f3" />

           <return-property name="field4" column="f4" />

       </return>

      

       select * from class1 where field1=? and nvl(field2,'*')=nvl(?, '*')

    </sql-query>            

</hibernate-mapping>

 

修改的Hibernate源代码

经过调试跟踪,发现不支持null主键的代码在org.hibernate.type.ComponentTypehydrate函数中,代码如下:

if ( val == null ) {

    if (isKey) return null; //different nullability rules for pk/fk

}

else {

    notNull = true;

}

为此,我们需要把这一段代码修改为如下:

if( val != null )

    notNull = true;

编译Hiberante

Hibernate的根目录下有build.bat,运行就可以编译生成hibernate3.jar,该文件也会在根目录下。

测试

使用如下的测试用例来测试以下结果:

 

package com.chinainsurance.platform.service.impl.test;

 

import java.util.List;

 

import org.springframework.orm.hibernate3.HibernateTemplate;

import org.springframework.orm.hibernate3.support.HibernateDaoSupport;

 

import com.chinainsurance.platform.service.helper.TaskServiceHelper;

 

import junit.framework.TestCase;

 

/*

drop table CIUSER.CLASS1

create table class1 (field1 varchar2(10 char) not null, field2 varchar2(30 char) , f3 number(10,0) not null, f4 varchar2(10 char) not null)

create unique index uniq1 on CIUSER.CLASS1 (FIELD1 , FIELD2 );

*/

public class TestClasses extends TestCase {

 

    HibernateTemplate dao;

   

    public TestClasses()

    {

       TaskServiceHelper.context = TestUtil.getContext();

       HibernateDaoSupport t = (HibernateDaoSupport) TaskServiceHelper.getDao("reportJobDao");

       dao = t.getHibernateTemplate();

    }

    protected void setUp() throws Exception {

       super.setUp();

    }

 

    protected void tearDown() throws Exception {

       super.tearDown();

       try{   clearClass1();    }catch(Exception e){}

    }

 

    private void clearClass1()

    {

       List list = dao.loadAll(Class1.class);

       for(int i = 0; i < list.size(); ++i)

       {

           dao.delete(list.get(i));

       }

    }

   

    private Class1 insertClass1()

    {

       Class1 obj1 = createClass1();

       try{

       dao.save(obj1);

       }

       catch(Exception e)

       {

           // System.out.println(e.toString());

       }

      

       return obj1;

    }

   

    private Class1 createClass1()

    {

       Class1 obj1 = new Class1();

       Class1ID id = new Class1ID();

       id.setField1("c1f1");

       id.setField2(null);

       obj1.setId(id);

      

       obj1.setField3(new Integer(1));

       obj1.setField4("c1f4");

 

       return obj1;

    }

   

    public void testClass1Add()

    {

       System.out.println("testClass1Add");

       dao.save(createClass1());

       clearClass1();

    }

 

    public void testClass1Delete()

    {

       System.out.println("testClass1Delete");

       Class1 obj1 = insertClass1();

      

       dao.delete(obj1);

    }

 

    public void testClass1Load()

    {

       System.out.println("testClass1Load");

       insertClass1();

      

       Class1ID id = new Class1ID();

       id.setField1("c1f1");

       id.setField2(null);

 

       Class1 obj1 = (Class1) dao.load(Class1.class, id);

       assertEquals(obj1.getId().getField1(), "c1f1");

       assertNull(obj1.getId().getField2());

       clearClass1();

    }

 

    public void testClass1Update()

    {

       System.out.println("testClass1Update");

      

       Class1 obj1 = insertClass1();

   

       obj1.setField4("ok");

       dao.update(obj1);

    }

 

    public void testClass1Find()

    {

       System.out.println("testClass1Find");

       insertClass1();

       List list = dao.find("from Class1 as c where c.id.field2 is null");

       assertTrue(list.size() > 0);

       clearClass1();

    }

   

    public void testClass1LoadAll()

    {

       System.out.println("testClass1LoadAll");

       insertClass1();

       List list = dao.loadAll(Class1.class);

       //assertTrue(list.size() > 0);

       //clearClass1();

       System.out.println("size = " + list.size());

       for(int i = 0; i < list.size(); ++i)

       {

           Class1 cls = (Class1)list.get(i);

           System.out.println("" + cls.getId().getField1() + cls.getId().getField2());

       }

      

       clearClass1();

    }     

   

}

posted on 2006-07-09 13:46  老翅寒暑  阅读(4012)  评论(9编辑  收藏  举报

导航