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

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

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

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

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

2、使用Annotation

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

下面具体来看一个例子。

先看第一种实现方式。

第一种:使用映射文件XXX.bhm.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;
    }
    }

上面的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();
    }
    }

c、联合主键的映射文件 Student.bhm.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>

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

<?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>

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>

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();
    }
    }

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

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

第二种:使用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();
    }
    }

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

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

package com.hibernate.model;
    import javax.persistence.Entity;
    import javax.persistence.Id;
    @Entity
    public 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;
    }

    }

测试类

配置文件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();
    }

    }

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

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

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

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

package com.hibernate.model;
    import javax.persistence.EmbeddedId;
    import javax.persistence.Entity;
    @Entity
    public 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;
    }

    }

主键类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();
    }
    }

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;
    }

    }

主键类代码如下:

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

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();
    }
    }

测试类代码如下:

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();
    }

    }

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

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

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

时间: 2024-07-29 19:28:25

《Hibernate学习笔记之三》:联合主键的映射的相关文章

Hibernate学习笔记_联合主键

复合主键(联合主键):多个字段构成唯一性. 一,xml方式 1. 将联合主键的属性提取出来,重新编写一个StudentPK类(原Student类中的id,name要删除 并新加入属性“StudentPK”) //StudentPK .javapackage com.bjsxt.hibernate; public class StudentPK implements java.io.Serializable{ private int id; private String name; public

联合主键的映射运用

1.联合主键的映射规则 1) 类中的每个主键属性都对应到数据表中的每个主键列. Hibernate要求具有联合主键的实体类实现Serializable接口,并且重写hashCode与equals方法,重写这两个方法的原因在于Hibernate要根据数据库的联合主键来判断某两行记录是否是一样的,如果一样那么就认为是同一个对象,如果不一样,那么就认为是不同的对象.这反映到程序领域中就是根据hashCode与equals方法来判断某两个对象是否能够放到诸如Set这样的集合当中.联合主键的实体类实现Se

hibernate里联合主键composite-id映射,查询单个主键的问题(转)

今天项目中遇到这个问题,搞了大半天,我郁闷...hibernate里联合主键配置(多个字段一起作为主键) <class name="com.cskj.hibernate.map.BbWjjc" table="bb_wjjc" schema="dbo" catalog="wjgl">        <composite-id name="id" class="com.cskj.hi

hibernate里联合主键composite-id映射,查询单个主键的问题

今天项目中遇到这个问题,搞了大半天,现在记录下来hibernate里联合主键配置(多个字段一起作为主键) <class name="com.cskj.hibernate.map.BbWjjc" table="bb_wjjc" schema="dbo" catalog="wjgl"> <composite-id name="id" class="com.cskj.hibernate

Hibernate 查询排序与联合主键映射

1.查询排序 (1)数据库排序(推荐) <map order-by="name ase" > <!--name的升序,降序desc--> session.createQuery(" ").uniqueResult() //返回唯一的对象,前台对象只有一个 <set order-by="name asc"> (2)内存排序 <set sort="natural" > sort属性值

Hibernate(5)—— 联合主键 、一对一关联关系映射(xml和注解) 和 领域驱动设计

俗话说,自己写的代码,6个月后也是别人的代码……复习!复习!复习!涉及的知识点总结如下: One to One 映射关系 一对一单向外键(XML/Annotation) 一对一双向外键关联(XML/Annotation) 联合主键 一对一单向外键联合主键(Xml/Annotation) 一对一组件关联(XML/Annotation) 理解组件 领域驱动设计——自动生成数据库脚本 一对一关系的小结 一些出错问题的总结 自动生成数据库脚本 一般在项目开发过程中,我们的习惯是先建好数据库和表,然后在进

hibernate学习笔记之三(一级缓存的使用和管理)

(hibernate学习笔记系列来自于 "疯狂Java" Hibernate视频学习) 一级缓存 一级缓存生命周期很短,与session生命周期一致,所以一级缓存也叫session级缓存或事务级缓存.位于缓存中的对象处于持久化状态,它与表中的相关记录对应,session能够在某些时间点,按照缓存中持久化对象的属性来同步数据库中表的记录,这一过程称为清理缓存. 一级缓存实现原理. session缓存是由它的实现类sessionImpl中定义的一些集合属性构成的,原理是保证有一个引用在关联

hibernate学习笔记之三(单条记录的增删改查)

(hibernate学习笔记系列来自于 "疯狂Java" Hibernate视频学习) 保存用户 session.save(person); session.persist(person); 查询用户 load(Class theClass,Serializable id) get(Class theClass,Serializable id) 修改用户 session.update(person) session.merger(person); 保存或修改用户 session.sav

hibernate框架学习笔记4:主键生成策略、对象状态

创建一个实体类: package domain; public class Customer { private Long cust_id; private String cust_name; private String cust_source; private String cust_industry; private String cust_level; private String cust_linkman; private String cust_phone; private Stri