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

前言
最近公司跟另外一个公司在合作一个项目,不幸被卷入其中。这几天做了一个变态的工作,就是客户死活就要复合主键能够支持null值,甚至扬言不惜修改hibernate,于是他如愿了。
命题
数据表中设置为 主键的两个字段其中一个可以为空。
分析
经过分析,Hibernate3.0的beta1~3支持可以为空的 主键,但是之后就不再支持。同时,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的代码。
参考文献
 
步骤
建立库表结构
以下是用于实验的表结构代码:
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文件内容如下:
<?xmlversion="1.0"?>
<!DOCTYPEhibernate-mappingPUBLIC"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
    "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
<hibernate-mapping>
    <classname="com.chinainsurance.platform.service.impl.test.Class1"table="class1"lazy="false">
       <composite-idname="id"class="com.chinainsurance.platform.service.impl.test.Class1ID">
           <key-propertyname="field1"column="field1"type="java.lang.String"length="10"/>
           <key-propertyname="field2"column="field2"type="java.lang.String"length="30" />
       </composite-id>
       <propertyname="field3"column="f3"type="java.lang.Integer"length="20"not-null="true"/>
       <propertyname="field4"column="f4"type="java.lang.String"length="10"not-null="true"/>
      
       <loaderquery-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-queryname="selectClass1">
       <returnclass="com.chinainsurance.platform.service.impl.test.Class1">
           <return-propertyname="id">
              <return-columnname="field1"/>
              <return-columnname="field2"/>
           </return-property>
           <return-propertyname="field3"column="f3"/>
           <return-propertyname="field4"column="f4"/>
       </return>
      
       select * from class1 where field1=? and nvl(field2,'*')=nvl(?, '*')
    </sql-query>            
</hibernate-mapping>
 
修改的Hibernate源代码
经过调试跟踪,发现不支持null 主键的代码在org. hibernate.type.ComponentType的hydrate函数中,代码如下:
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();
    }     
   
}
 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值