《Hibernate学习笔记三》:联合主键的映射

来源:互联网 发布:java相关文献 编辑:程序博客网 时间:2024/06/02 04:55

《Hibernate学习笔记三》:联合主键的映射

就如在前面所举的例子一样,是使用的id作为唯一的主键,一般情况下我们也只使用唯一的一个属性作为主键,但是在实际中,我们可能会遇到几个属性作为主键的情况,因此,在本篇博文中,就来介绍下,联合主键的映射关系应该如何来做??

联合主键的映射有两种方式来进行实现。

1、使用映射文件 XXX.hbm.xml

2、使用Annotation

Hibernate首先需要使用联合主键的实体类必须实现Serializable接口,即为了使序列能够被序列化进行传输,并要求实体类重写equals、hashCode这两个方法,用这两个方法来确保联合主键的唯一性。

下面具体来看一个例子。 
先看第一种实现方式。

第一种:使用映射文件XXX.hbm.xml来实现联合主键的映射

a、实体类 Student

package com.hibernate.model;public class Student {    private StudentPK pk;//主键    public StudentPK getPk() {        return pk;    }    public void setPk(StudentPK pk) {        this.pk = pk;    }    private int age;    public int getAge() {        return age;    }    public void setAge(int age) {        this.age = age;    }   }
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

上面的private StudentPK pk;就是我们的联合主键,联合主键我们封装在了一个类StudentPK中,也可以之间将联合主键的属性不进行封装,直接写在实体类Student。

b、联合主键类 StudentPK

这个就是对联合主键id和name进行了封装,即使得StudentPK为联合主键类,这个类要实现Serializable接口,并需要重写equals和hashCode方法。来实现组合的标识符的相等判断

package com.hibernate.model;    import java.io.Serializable;public class StudentPK implements Serializable{    private int id;    private String name;    public int getId() {        return id;    }    public void setId(int id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public boolean equals(Object o){        if (o instanceof StudentPK) {            StudentPK pk = (StudentPK) o;            if(this.id==pk.getId()&&this.name.equals(pk.getName())){                return true;            }        }        return false ;    }    @Override    public int hashCode(){        return this.name.hashCode();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

c、联合主键的映射文件 Student.hbm.xml

在前面的例子中我们见到过一个属性作为主键的映射文件如何来写,下面就是几个属性作为主键的映射文件应该如何来写。

在官方文档上有这样一句话:

定义可以访问旧式的多主键数据。 我们强烈不建议使用这种方式。

文档上面给出的composite-id的一般格式如下:

<composite-id        name="propertyName"        class="ClassName"        mapped="true|false"        access="field|property|ClassName"        node="element-name|."        >        <key-property name="propertyName" type="typename" column="column_name"/>        <key-many-to-one name="propertyName class="ClassName" column="column_name"/>        ......    </composite-id>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

在我们的例子中完整的映射文件内容如下:

<?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 package="com.hibernate.model">    <class name="Student" table="Student"> <!-- 如果我们不写table,则默认表名与类名一致 -->        <!-- 联合主键 -->        <composite-id name="pk" class="com.hibernate.model.StudentPK">             <key-property name="id" column="id" />               <key-property name="name" column="name"  type="string"/>          </composite-id>        <!-- 其他属性,name对应实体类的属性,column对应关系型数据库表的列 -->        <property name="age"/>    </class>    </hibernate-mapping>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

d、配置文件 hibernate.cfg.xml

配置文件内容如下:

<?xml version='1.0' encoding='utf-8'?>    <!DOCTYPE hibernate-configuration PUBLIC        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"        "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">    <hibernate-configuration>    <session-factory>        <!-- Database connection settings -->        <property name="connection.driver_class">com.mysql.jdbc.Driver</property>        <property name="connection.url">jdbc:mysql://localhost/hibernate</property>  <!--  hibernate为数据库名称 -->        <!--  数据库的用户名和密码 -->        <property name="connection.username">root</property>        <property name="connection.password">123456</property>        <!-- JDBC connection pool (use the built-in) -->        <property name="connection.pool_size">1</property>        <!-- SQL dialect 主要看你使用的是那种数据库 -->        <property name="dialect">org.hibernate.dialect.MySQLDialect</property>        <!-- Enable Hibernate's automatic session context management -->        <property name="current_session_context_class">thread</property>        <!-- Disable the second-level cache  -->        <property name="cache.provider_class">org.hibernate.cache.NoCacheProvider</property>        <!-- Echo all executed SQL to stdout -->        <property name="show_sql">true</property>        <!--  按一定的格式来进行打印MySQL的SQL语句  -->        <property name="format_sql">true</property>        <!-- Drop and re-create the database schema on startup -->        <!--  属性hbm2ddl.auto 可选值有:create|update|drop-create|invalide         create  每次都会给你新建一个表,因此存在数据丢失        update  当你的实体类中添加或删除了其他的属性,即将导致相应的表结构发生变化,因此会更改表的结构        invalide  即对象关系映射的检查。        -->       <property name="hbm2ddl.auto">update</property>         <!-- Student类与表Student的映射关系 -->        <mapping resource="com/hibernate/model/Student.hbm.xml"/>    </session-factory></hibernate-configuration>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44

e、测试类 StudentTest

测试类代码如下:

package com.hibernate.model;    import org.hibernate.Session;    import org.hibernate.SessionFactory;    import org.hibernate.cfg.Configuration;    import org.junit.AfterClass;    import org.junit.BeforeClass;    import org.junit.Test;public class StudentTest {    private static SessionFactory sf=null;    @BeforeClass    public static void beforeClass(){        //读取配置 文件 hibernate.cfg.xml        Configuration cf= new Configuration().configure();        sf=cf.buildSessionFactory();    }    @Test    public void testSaveStudent() {        //获取 Session        Session session=sf.openSession();        //开启事务         session.beginTransaction();        StudentPK pk=new StudentPK();        pk.setId(2);        pk.setName("wuranghao");        Student student=new Student();        student.setPk(pk);        student.setAge(19);        //对持久化对象 进行 增删改查 操作         session.save(student);        session.getTransaction().commit();        session.close();    }    @AfterClass    public static void afterClass(){        sf.close();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

运行结果在数据库中的反应如下:

这样我们就完成了联合主键的映射。

第二种:使用Annotation来实现联合主键的映射

在文档中,说明了定义组合主键的几张语法如下:

1、将组件类注解为@Embeddable,并将组件的属性注解为@Id。

2、将组件的属性注解为@EmbeddedId

3、将类注解为@IdClass,并将该实体中所有属于主键的属性都注解为@Id。

下面对这三种方式分别举例来进行介绍。

1、将组件类注解为@Embeddable,并将组件的属性注解为@Id。

第一步:将组件类注解为@Embeddable

package com.hibernate.model;    import java.io.Serializable;    import javax.persistence.Embeddable;    /*第一步:将组件类注解为@Embeddable,而第二步并不是在此类中把id和name用注解@Id修饰,    而是在Teacher类中将TeacherPK组件*/@Embeddable  public class TeacherPK implements Serializable{    private int id;    private String name;        public int getId() {        return id;    }    public void setId(int id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public boolean equals(Object o){        if (o instanceof TeacherPK) {            TeacherPK pk = (TeacherPK) o;            if(this.id==pk.getId()&&this.name.equals(pk.getName())){                return true;            }        }        return false ;    }    @Override    public int hashCode(){        return this.name.hashCode();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37

第二步:将组件的属性注解为@Id

这里要特别注意的是:并不是在TeacherPK这个组合主键类中把id和name用注解@Id修饰,而是在实体类Teacher类中将TeacherPK组件用@Id来进行注释

package com.hibernate.model;    import javax.persistence.Entity;    import javax.persistence.Id;@Entitypublic class Teacher {    private TeacherPK pk;//主键引用    private String title;    @Id    public TeacherPK getPk() {        return pk;    }    public void setPk(TeacherPK pk) {        this.pk = pk;    }    public String getTitle() {        return title;    }    public void setTitle(String title) {        this.title = title;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

测试类

配置文件hibernate.cfg.xml和上面的一样,这里就不再贴代码,而测试类的代码如下:

package com.hibernate.model;    import org.hibernate.Session;    import org.hibernate.SessionFactory;    import org.hibernate.cfg.Configuration;    import org.junit.AfterClass;    import org.junit.BeforeClass;    import org.junit.Test;public class TeacherTest {    private static SessionFactory sf=null;    @BeforeClass    public static void beforeClass(){        //读取配置 文件 hibernate.cfg.xml        Configuration cf= new Configuration().configure();        sf=cf.buildSessionFactory();    }    @Test    public void testSaveTeacher() {        //获取 Session        Session session=sf.openSession();        //开启事务         session.beginTransaction();        TeacherPK pk=new TeacherPK();//联合主键对象        pk.setId(3);        pk.setName("xiaoming");        Teacher t=new Teacher();        t.setPk(pk);        t.setTitle("professior");        //对持久化对象 进行 增删改查 操作         session.save(t);        session.getTransaction().commit();        session.close();    }    @AfterClass    public static void afterClass(){        sf.close();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

在控制台输出的建表数据如下:

因此,利用将组件类注解为@Embeddable,并将组件的属性注解为@Id。这种方式我们就成功的对联合主键进行了映射。

2、将组件的属性注解为@EmbeddedId

这种方式比第一种方式更加简单,直接不需要将组件类TeacherPK用注解为@Embeddable,而是直接用@EmbeddedId来注解Teacher类中的主键属性,如下:

package com.hibernate.model;    import javax.persistence.EmbeddedId;    import javax.persistence.Entity;@Entitypublic class Teacher {    private TeacherPK pk;//主键引用    private String title;    //联合主键映射的第二种方式:直接将组件用注解@EmbeddedId来进行修饰    @EmbeddedId    public TeacherPK getPk() {        return pk;    }    public void setPk(TeacherPK pk) {        this.pk = pk;    }    public String getTitle() {        return title;    }    public void setTitle(String title) {        this.title = title;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

主键类TeacherPK不需要任何的注解。

package com.hibernate.model;import java.io.Serializable; public class TeacherPK implements Serializable{    private int id;    private String name;        public int getId() {        return id;    }    public void setId(int id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public boolean equals(Object o){        if (o instanceof TeacherPK) {            TeacherPK pk = (TeacherPK) o;            if(this.id==pk.getId()&&this.name.equals(pk.getName())){                return true;            }        }        return false ;    }    @Override    public int hashCode(){        return this.name.hashCode();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

3、将类注解为@IdClass,并将该实体中所有属于主键的属性都注解为@Id。

实体类Teacher

将实体类Teacher注解为@IDClass,并将实体中所有属性主键的属性都注解为@Id,而主键类TeacherPK则不用任何注解来进行修饰。 

package com.hibernate.model;import javax.persistence.Entity;import javax.persistence.Id;import javax.persistence.IdClass;@Entity@IdClass(value=TeacherPK.class)public class Teacher {    //private TeacherPK pk;//主键引用    private int id;    private String name;    private String title;    @Id    public int getId() {        return id;    }    public void setId(int id) {        this.id = id;    }    @Id    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }       //  public TeacherPK getPk() {    //      return pk;    //  }    //  public void setPk(TeacherPK pk) {    //      this.pk = pk;    //  }    public String getTitle() {        return title;    }    public void setTitle(String title) {        this.title = title;    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39

主键类代码如下:

不用任何注解来进行修饰。

package com.hibernate.model;import java.io.Serializable; public class TeacherPK implements Serializable{    private int id;    private String name;        public int getId() {        return id;    }    public void setId(int id) {        this.id = id;    }    public String getName() {        return name;    }    public void setName(String name) {        this.name = name;    }    @Override    public boolean equals(Object o){        if (o instanceof TeacherPK) {            TeacherPK pk = (TeacherPK) o;            if(this.id==pk.getId()&&this.name.equals(pk.getName())){                return true;            }        }        return false ;    }    @Override    public int hashCode(){        return this.name.hashCode();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33

测试类代码如下:

package com.hibernate.model;import org.hibernate.Session;import org.hibernate.SessionFactory;import org.hibernate.cfg.Configuration;import org.junit.AfterClass;import org.junit.BeforeClass;import org.junit.Test;public class TeacherTest {    private static SessionFactory sf=null;    @BeforeClass    public static void beforeClass(){        //读取配置 文件 hibernate.cfg.xml        Configuration cf= new Configuration().configure();        sf=cf.buildSessionFactory();    }    @Test    public void testSaveTeacher() {        //获取 Session        Session session=sf.openSession();        //开启事务         session.beginTransaction();    //      TeacherPK pk=new TeacherPK();//联合主键对象    //      pk.setId(3);    //      pk.setName("xiaoming");        Teacher t=new Teacher();        t.setId(2);        t.setName("zhangsan");        t.setTitle("professior");        //对持久化对象 进行 增删改查 操作         session.save(t);        session.getTransaction().commit();          session.close();    }    @AfterClass    public static void afterClass(){        sf.close();    }}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42

hibernate.cfg.xml文件内容与上面介绍的一样。

运行程序后在数据库中Teacher表结构如下:

以上就是第三种注解方式来完成联合主键的映射。

from: http://blog.csdn.net/u010412719/article/details/51275744