hibernate 两个外键作为主键(注解方式)

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hibernate 两个外键作为主键(注解方式)相关的知识,希望对你有一定的参考价值。

@ManyToOne(targetEntity=Role.class)
private Role role;
@ManyToOne(targetEntity=Module.class)
private Module module;
现在我想让role和module作为主键,怎么实现呢?用hibernate注解的方式

参考技术A 你将role 和module封装成一个类,将这个类作为主键,在这个类上加上@Embeddable,再在联合主键上加@EmbeddedId 就行了 参考技术B 可以试试将两个外键封装为一个类,把这个类当做主键追问

我试过了,用了@IdClass(RMPK.class)
RMPK.java里面是
private Role role;
private Module module;
不过还是不行,你有解决办法吗?

追答

我这书上倒是有例子,可是没源码,你在主键类里重写equals方法和hashCode方法了吗,还有映射文件的配置

本回答被提问者采纳
参考技术C 配置文件里面 或者annotation里面设置他们的关系
参考技术D 这个应该是多对多的,直接在中间配置两个多对多的map映射啊

hibernate表关系

1.一对一

  用户表可以查分成两个表,一个userInfo、一个userLogin表

 

  实现方式:

(1)使用外键:外键+唯一性约束+非空约束

(2)公用主键:公用主键,从表的主键同时也是外键,来源于主表的主键。

 

2. 一对多

2.1. 概念

 使用外键,实现一对多关系,外键可以为null

  主从关系:一:主表,多:从表  ,从表的外键必须是主表的主键或者null。

 代码在:day29

2.2.实现方法:Student     Clazz    

step1:在一个一方创建对象引用集合  Set<>

private Set<Student> students = new HashSet<>(0);

step2:在多的一方创建对象引用  

private Clazz clazz;

step3:在多方 实体中建立多对一关系-<many-to-one>【由多的一方维护关系】。

step4:在一方配置<set>标签【可选】   配置一对多

public class Student implements Serializable {
    private Integer id;
    private String stuName;
    private Integer age;
    private Clazz clazz;
    //set,get省略
}
Student.java

 

 

public class Clazz implements Serializable {
    private Integer id;
    private String className;

    //实例化,可以防止空指针异常
    private Set<Student> students = new HashSet<>(0); //长度设为0
}
Clazz.java

 

 

<hibernate-mapping package="cn.getword.domain">
    <class name="Student" table="tb_student">
        <id name="id" column="id" type="int">
            <generator class="native"></generator>
        </id>
        <property name="stuName" column="username" />
        <property name="age" column="age" />
        <!-- name:用于指定引用对象在类中属性名称
             class:一方的类的路径
             column:外键名称
         -->
        <many-to-one name="clazz" class="Clazz" column="clazz_id" />
    </class>
</hibernate-mapping>
Student.hbm.xml

 

 

<hibernate-mapping package="cn.getword.domain">
    <class name="Clazz" table="tb_clazz">
        <id name="id" column="id" type="int">
            <generator class="native"></generator>
        </id>
        <property name="className" column="class_name" />
        <!-- name:引用集合的名称
             table:多方表明
         -->
        <set name="students" table="tb_student">
            <!-- key:多方表的外键名称 -->
            <key column="clazz_id"></key>
            <one-to-many class="Student"></one-to-many>
        </set>
    </class>
</hibernate-mapping>
Clazz.hbm.xml

 

 

 

2.3. 一对多的CRUD操作

(1)添加

    /**
     *  查询一个班级
     *  创建一个学生
     *  建立两者关系:
     *  执行保存操作
     *      这里由于Clazz没有级联操作,所以即使Clazz【持久态】发生了改变【添加了Student】,也不会进行更新操作
     *      解决方法:
     *          (一) 添加Student【维护多对一的关系,并且可以级联操作】
     *          (二) 让Clazz【主表】也参与维护关系(set一对多),并且给他添加级联操作【cascade=true】,但是student没有外键?
     *                    此时需要建立双向关系【student1.setClazz(clazz);】,在更新Clazz的时候发现有关联的Student对象,因此执行
     *                    添加Student操作,由于我们让Student参加维护关系的工作,同时又给student设置了Clazz,因此在添加Student的时候
     *                    会自动添加外键约束【等价于方法一】
     *   在实际开发中:一般有多的一方维护关系,并设置级联操作。一的一方将关系维护交给对方,并设置级联操作。配置如下
     *   <many-to-one name="clazz" class="Clazz" column="clazz_id" cascade="save-update" />
     *   <set name="students" table="tb_student" cascade="save-update,delete" inverse="true">
     *       <key column="clazz_id"></key>
     *       <one-to-many class="Student"></one-to-many>
     *   </set>
     */
    @Test
    public void save(){
        Student student1 = new Student();
        student1.setStuName("admin2");
        student1.setAge(21);

        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();
        Clazz clazz = session.get(Clazz.class, 1);
        clazz.getStudents().add(student1); // 给Clazz添加一个学生,在级联操作时,会执行insert student
        student1.setClazz(clazz);
        session.update(clazz);
        transaction.commit();
    }
    /**
     *  创建一个班级
     *  创建一个学生
     *  建立两者关系:建立双向关系,原理同上,建立双向关系能保证外键的生成
     *  执行保存操作
     *      先保存主表,在保存从表
     *   此时发现一个问题:多出一个update语句
     *      跟一级缓存有关、由于双方建立双向关系。
     *     解决办法:让一方放弃维护关系的权利。在多方配置文件中【set】,添加属性inverse=true
     */
    @Test
    public void save2(){
        Student student1 = new Student();
        student1.setStuName("张三");
        student1.setAge(21);

        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();
        Clazz clazz = new Clazz();
        clazz.setClassName("软件8班");
        //建立关系
        clazz.getStudents().add(student1);
        student1.setClazz(clazz);//如果Clazz参与了维护关系,则这行代码就不需要了,在添加student1后悔自动update它的外键【这里需要外键可以为空】

        //保存
        session.save(clazz);
//        session.save(student1);  根据级联操作,student会自动添加,并且添加student时,会自动添加外键【student维护了多对一 的关系】
        transaction.commit();
    }

    /**
     * 级联保存
     *      <many-to-one name="clazz" class="Clazz" column="clazz_id" cascade="save-update" />
     *          save-update:对保存和更新操作进行级联操作,
     *          如果不设置级联操作,则会报错【持久状态对象不能包含瞬时状态变量】
     *       级联操作配置在哪方,哪方就可以进行级联操作,这里配置在从属【Student】一方,
     *       因此只有Student可以进行级联添加,添加Clazz没有就不会有级联效果
     *
     */
    @Test
    public void save3(){
        Student student1 = new Student();
        student1.setStuName("张三");
        student1.setAge(21);

        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();
        Clazz clazz = new Clazz();
        clazz.setClassName("软件6班");
        //建立关系
        student1.setClazz(clazz);

        //保存
        session.save(student1);
        transaction.commit();
    }
View Code

 

 

 

(2)更新

    /**
     * 级联更新:只要Clazz的属性发生了变化,就会自动更新
     *  查询一个Clazz
     *  创建一个学生
     *  建立二者关系
     *  更新Clazz
     *  
     *  和save1类似
     */
    @Test
    public void update(){
        Student student1 = new Student();
        student1.setStuName("admin3");
        student1.setAge(21);

        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();
        Clazz clazz = session.get(Clazz.class, 1);
        clazz.getStudents().add(student1); // 给Clazz添加一个学生,在级联操作时,会执行insert student
        student1.setClazz(clazz);
        session.update(clazz);
        transaction.commit();
    }
View Code

 

 

(3)删除

   /**
     * 删除从表:直接删除
     * 删除主表:
     *      有引用:
     *          级联级联【在主表中配置级联删除,从表不需要配置】
     *              如果没有将关系维护权交给从表【set inverse=true】,那么在删除主表之前会先把从表的外键设为null,外键为非空,则抛出异常
     *              因此在级联删除时推荐将关系维护交给从表【Student】来维护
     *          没有设置级联删除:报错
     *      没有引用:直接删除
     */
    @Test
    public void delete(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();
        //建立关系
        transaction.commit();
    }
View Code

 

 

(4)查询

    /**
     * 对象导航查询
     *
     */
    @Test
    public void select(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();
        Clazz clazz = session.get(Clazz.class, 1);
        System.out.println(clazz);
        //建立关系
        transaction.commit();
    }
View Code

 

获取Clazz对象是,对于Student对象,默认采用延迟加载的方式加载。

获取Student对象,默认对于Clazz对象也是立即加载。【这种情况可以使用立即加载,因为数据量小】

  更改默认配置:

<many-to-one name="clazz" class="Clazz" column="clazz_id" cascade="save-update" lazy="false" />    //立即加载

<set name="students" table="tb_student" cascade="save-update,delete" inverse="true" lazy="false">
      <!-- key:多方表的外键名称 -->
      <key column="clazz_id"></key>
      <one-to-many class="Student"></one-to-many>
</set>

 

  • many-to-one:  Student中配置

false:立即加载Clazz,默认就是立即加载,可以不用修改

no-proxy:不管

proxy:加载时机取决于Clazz对象的load加载时机。

    public void select2(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();
        Student student = session.get(Student.class, 1);
        Clazz clazz = student.getClazz();
        System.out.println(clazz);

        //建立关系
        transaction.commit();
    }
View Code

 

 

  • set:查询Clazz,默认懒加载Students,也可以不用修改

 

    public void select(){
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();
        Clazz clazz = session.get(Clazz.class, 1);
        Set<Student> students = clazz.getStudents();
        System.out.println(students);
        //建立关系
        transaction.commit();
    }
View Code

 

 

 

 

 

 

3. 多对多

  使用中间表实现多对多关系。中间表只有两个字段,即两个表的主键,中间表的主键就是两个外键的联合主键。

 

3.1 实现

多对多可以看成各自相对于中间表一对多的关系。中间表引用他们的主键--构造中间表

多方都是配置set:

  注意:必须有一方放弃维护关系,否则在插入中间表信息时会产生冲突

 实体类:

public class Teacher implements Serializable {
    private Integer id;
    private String name;

    private Set<Student> students = new HashSet<Student>(0);
    set/get...    
}
Teacher.java

 

 

public class Student implements Serializable {
    private Integer id;
    private String stuName;
    private Integer age;
    private Clazz clazz;
    private Set<Teacher> teachers = new HashSet<Teacher>(0);

}
Student.java

 

 

配置文件:

<hibernate-mapping package="cn.getword.domain">
    <class name="Teacher" table="tb_teacher">
        <id name="id" column="id" type="int">
            <generator class="native"></generator>
        </id>
        <property name="name" column="name" />
        <!--
            name:
            table:中间表名【如果把teacher和中间表看做一对多,那么就是指的是多方表名】
         -->
        <set name="students" table="tb_teacher_student">
            <!--key
                column: 指定中间表的外键名称
            -->
            <key column="tid"></key>
            <!--配置多对多的关系
                class:指定对方实体类的类名
                column:对方实体类主键对应的外键
            -->
            <many-to-many class="Student" column="sid"></many-to-many>
        </set>
    </class>
</hibernate-mapping>
Teacher.hbm.xml

 

 

<hibernate-mapping package="cn.getword.domain">
    <class name="Student" table="tb_student">
        <id name="id" column="id" type="int">
            <generator class="native"></generator>
        </id>
        <property name="stuName" column="username" />
        <property name="age" column="age" />
        <!-- name:用于指定引用对象在类中属性名称
             class:一方的类的路径
             column:外键名称
         -->
        <many-to-one name="clazz" class="Clazz" column="clazz_id" cascade="save-update" />

        <set name="teachers" table="tb_teacher_student" inverse="true">
            <key column="sid"></key>
            <many-to-many class="Teacher" column="tid"></many-to-many>
        </set>
    </class>
</hibernate-mapping>
Student.hbm.xml

 

 

 

 

3.2 CRUD操作

 (1)添加

   /**
     *  创建两个老师
     *  创建三个学生
     *  给一号老师分配1,2学生
     *  给二号老师分配2,3学生
     *  建立双向关系
     *  保存
     */
    @Test
    public void save(){
        Teacher t1 = new Teacher();
        t1.setName("张三");
        Teacher t2 = new Teacher();
        t2.setName("李四");

        Student s1 = new Student();
        s1.setStuName("赵云");
        s1.setAge(12);
        Student s2 = new Student();
        s2.setStuName("周瑜");
        s2.setAge(21);
        Student s3 = new Student();
        s3.setStuName("诸葛");
        s3.setAge(23);

        //建立关系
        t1.getStudents().add(s1);
        t1.getStudents().add(s2);
        t2.getStudents().add(s2);
        t2.getStudents().add(s3);

//        s1.getTeachers().add(t1);
//        s2.getTeachers().add(t1);
//        s2.getTeachers().add(t2);
//        s3.getTeachers().add(t2);
        //保存
        Session session = HibernateUtils.getCurrentSession();
        Transaction transaction = session.beginTransaction();
        session.save(t1);
        session.save(t2);
        transaction.commit();
    }
View Code

 

 

 

(2)删除

先删除中间表相关记录,在删除实体类的数据

注意:千万慎重使用。一不小心删光所有数据

 

 

 

 

 

end

以上是关于hibernate 两个外键作为主键(注解方式)的主要内容,如果未能解决你的问题,请参考以下文章

如何使用主键作为JPA和Hibernate的外键引用?

两个外键作为主键

hibernate系列之四

hibernate5(12)注解映射[4]一对一外键关联

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

hibernate表关系