1. 程式人生 > >Hibernate中多屬性構成複合主鍵對映

Hibernate中多屬性構成複合主鍵對映

目錄:

1. 實現方式一將複合主鍵對應的屬性與實體其他普通屬性放在一起

2. 實現方式二將主鍵屬性提取到一個主鍵類中,實體類只需包含主鍵類的一個引用

在日常開發中會遇到這樣一種情況,資料庫中的某張表需要多個欄位列才能唯一確定一行記錄,這時表需要使用複合主鍵。面對這樣的情況Hibernate為我們提供了兩種方式來解決複合主鍵問題。

方式一:將複合主鍵對應的屬性與實體其他普通屬性放在一起

例如實體類People中"id"和"name"屬性對應複合主鍵:

/*實體類,使用複合主鍵必須實現Serializable介面*/
public class People implements Serializable
{
    private static final long serialVersionUID = -4888836126783955019L;
    
    private String id;
    private String name;
    private int age;
    
    public People()
    {
        
    }

    public String getId()
    {
        return id;
    }

    public void setId(String id)
    {
        this.id = id;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    public int getAge()
    {
        return age;
    }

    public void setAge(int age)
    {
        this.age = age;
    }

    @Override
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj)
    {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        People other = (People) obj;
        if (id == null)
        {
            if (other.id != null)
                return false;
        }
        else if (!id.equals(other.id))
            return false;
        if (name == null)
        {
            if (other.name != null)
                return false;
        }
        else if (!name.equals(other.name))
            return false;
        return true;
    }
}


People.hbm.xml:

<?xml version="1.0" encoding="utf-8"?>
<!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.suxiaolei.hibernate.pojos.People" table="people">
        <!-- 複合主鍵使用composite-id標籤 -->
        <composite-id>
            <!-- key-property標籤表示哪一些屬性對應複合主鍵 -->
            <key-property name="id" column="id" type="string"></key-property>
            <key-property name="name" column="name" type="string"></key-property>
        </composite-id>

        <property name="age" column="age" type="integer"></property>
    </class>
</hibernate-mapping>


Hibernate中使用複合主鍵時需要注意一些規則:

  1. 使用複合主鍵的實體類必須實現Serializable介面。必須實現Serializable介面的原因很簡單,我們查詢資料的時候是根據主鍵查詢的。開啟Hibernate的幫助文件我們可以找到get與load方法的宣告形式如下:
    Object load(Class theClass,Serializable id)
    Object get(Class theClass,Serializable id)
    當我們查詢複合主鍵類的物件時,需要傳遞主鍵值給get()或load()方法的id引數,而id引數只能接收一個實現了Serializable介面的物件。而複合主鍵類的主鍵不是一個屬性可以表示的,所以只能先new出複合主鍵類的例項(例如:new People()),然後使用主鍵屬性的set方法將主鍵值賦值給主鍵屬性,然後將整個物件傳遞給get()或load()方法的id引數,實現主鍵值的傳遞,所以
    複合主鍵的實體類必須實現Serializable介面。

  2. 使用複合主鍵的實體類必須重寫equals和hashCode方法。必須重寫equals和hashCode方法也很好理解。這兩個方法使用於判斷兩個物件(兩條記錄)是否相等的。為什麼要判斷兩個物件是否相等呢?因為資料庫中的任意兩條記錄中的主鍵值是不能相同的,所以我們在程式中只要確保了兩個物件的主鍵值不同就可以防止主鍵約束違例的錯誤出現。也許這裡你會奇怪為什麼不使用複合主鍵的實體類不重寫這兩個方法也沒有主鍵違例的情況出現,這是因為使用單一主鍵方式,主鍵值是Hibernate來維護的,它會確保主鍵不會重複,而複合主鍵方式,主鍵值是程式設計人員自己維護的,所以必須重寫equals和hashCode方法用於判斷兩個物件的主鍵是否相同。

  3. 重寫的equals和hashCode方法,只與主鍵屬性有關,普通屬性不要影響這兩個方法進行判斷。這個原因很簡單,主鍵才能決定一條記錄,其他屬性不能決定一條記錄。

儲存測試:

public class Client
{
    public static void main(String[] args)
    {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction tx = null;
        
        try
        {
            tx = session.beginTransaction();
            
            People people = new People();
            /*主鍵值由我們自己維護*/
            people.setId("123456");
            people.setName("zhangsan");
            people.setAge(40);
            
            session.save(people);
            
            tx.commit();
        }
        catch (Exception e)
        {
            if(tx != null)
            {
                tx.rollback();
            }
            
            e.printStackTrace();
        }
        finally
        {
            session.close();
        }
    }
}


看看資料庫:

資料被正確的插入到資料庫中了。

讀取資料測試:

public class Client
{
    public static void main(String[] args)
    {
        Session session = HibernateUtil.getSessionFactory().openSession();
        Transaction tx = null;
        
        try
        {
            tx = session.beginTransaction();
            
            /*查詢複合主鍵物件,需要先構建主鍵*/
            People peoplePrimaryKey = new People();
            peoplePrimaryKey.setId("123456");
            peoplePrimaryKey.setName("zhangsan");
            
            /*然後將構建的主鍵值傳入get方法中獲取對應的People物件*/
            People people = (People)session.get(People.class, peoplePrimaryKey);
            
            System.out.println("people age is:"+people.getAge());
            
            tx.commit();
        }
        catch (Exception e)
        {
            if(tx != null)
            {
                tx.rollback();
            }
            
            e.printStackTrace();
        }
        finally
        {
            session.close();
        }
    }
}


控制檯輸出:

people age is:40

可以看到資料成功的取出了。

方式二:將主鍵屬性提取到一個主鍵類中,實體類只需包含主鍵類的一個引用。

主鍵類:

/*必須實現Serializable介面*/
public class PeoplePrimaryKey implements Serializable
{
    private static final long serialVersionUID = -1190986010439330142L;
    
    /*複合主鍵值*/
    private String id;
    private String name;
    
    public PeoplePrimaryKey()
    {
        
    }
    
    /*複合主鍵值的get和set方法*/
    public String getId()
    {
        return id;
    }

    public void setId(String id)
    {
        this.id = id;
    }

    public String getName()
    {
        return name;
    }

    public void setName(String name)
    {
        this.name = name;
    }

    @Override
    public int hashCode()
    {
        final int prime = 31;
        int result = 1;
        result = prime * result + ((id == null) ? 0 : id.hashCode());
        result = prime * result + ((name == null) ? 0 : name.hashCode());
        return result;
    }

    @Override
    public boolean equals(Object obj)
    {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        PeoplePrimaryKey other = (PeoplePrimaryKey) obj;
        if (id == null)
        {
            if (other.id != null)
                return false;
        }
        else if (!id.equals(other.id))
            return false;
        if (name == null)
        {
            if (other.name != null)
                return false;
        }
        else if (!name.equals(other.name))
            return false;
        return true;
    }
}


實體類:

public class People
{
    /*持有主鍵類的一個引用,使用該引用作為這個類的OID*/
    private PeoplePrimaryKey peoplePrimaryKey;
    private int age;
    
    public People()
    {
        
    }
    
    public PeoplePrimaryKey getPeoplePrimaryKey()
    {
        return peoplePrimaryKey;
    }

    public void setPeoplePrimaryKey(PeoplePrimaryKey peoplePrimaryKey)
    {
        this.peoplePrimaryKey = peoplePrimaryKey;
    }

    public int getAge()
    {
        return age;
    }

    public void setAge(int age)
    {
        this.age = age;
    }
}


People.hbm.xml檔案稍有一點變動:

<?xml version="1.0" encoding="utf-8"?>
<!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.suxiaolei.hibernate.pojos.People" table="people">
        <!-- 複合主鍵使用composite-id標籤 -->
        <!--
        name - 指定了複合主鍵對應哪一個屬性
        class - 指定了複合主鍵屬性的型別
        -->
        <composite-id name="peoplePrimaryKey" class="com.suxiaolei.hibernate.pojos.PeoplePrimaryKey">
            <!-- key-property指定了複合主鍵由哪些屬性組成 -->
            <key-property name="id" column="id" type="string"></key-property>
            <key-property name="name" column="name" type="string"></key-property>
        </composite-id>

        <property name="age" column="age" type="integer"></property>
    </class>
</hibernate-mapping>


場景測試與方式一大同小異這裡不再舉例了。主鍵類為什麼實現Serializable介面和為什麼重寫equals和hashCode方法上面已經解釋的很清楚了。