欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

05. 关联关系映射(多对多)

程序员文章站 2022-04-22 22:20:34
...

关联关系映射

多对多关联关系映射(Engineer和Technology)

这里我们学习Hibernate中多对多的关联关系映射,我们使用的例子是Engineer(工程师)和Technology(技术),一个Engineer可以掌握多门技术,一门Technology也可以被多个工程师掌握。

多对多双向关联关系的表结构

Engineer表

CREATE TABLE t_engineer(
    id NUMBER(10) PRIMARY KEY,
    name VARCHAR2(20) NOT NULL,
    gender VARCHAR(10),
    age NUMBER(3) CHECK(age BETWEEN 1 AND 100),
    company VARCHAR2(30)
);

Technology表

CREATE TABLE t_engineer(
    id NUMBER(10) PRIMARY KEY,
    name VARCHAR2(20) NOT NULL,
    category VARCHAR2(20) NOT NULL
);

桥表(用于连接两张表,并描述两张表的多对多关系)。
该表存储上面两张表的各自主键,把那两列作为外键,对应外表的主键,并使用两个外表的主键作为该表的联合主键。

CREATE TABLE t_engineer_technology(
    engineer_id NUMBER(10),
    technology_id NUMBER(10),
    PRIMARY KEY (engineer_id, technology_id),
    FOREIGN KEY(engineer_id) REFERENCES t_engineer(id),
    FOREIGN KEY(technology_id) REFERENCES t_technology(id)
);

多对多双向关联关系的类结构

Engineer类

package com.li.association.many2many.pojo;

import java.util.HashSet;
import java.util.Set;

public class Engineer {

    private Long id;
    private String name;
    private String gender;
    private Integer age;
    private String company;
    private Set<Technology> technologies=new HashSet<Technology>();

    public Engineer() {
        super();
    }

    public Engineer(Long id, String name, String gender, Integer age, String company, Set<Technology> technologies) {
        super();
        this.id = id;
        this.name = name;
        this.gender = gender;
        this.age = age;
        this.company = company;
        this.technologies = technologies;
    }

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public Integer getAge() {
        return age;
    }

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

    public String getCompany() {
        return company;
    }

    public void setCompany(String company) {
        this.company = company;
    }

    public Set<Technology> getTechnologies() {
        return technologies;
    }

    public void setTechnologies(Set<Technology> technologies) {
        this.technologies = technologies;
    }


}

Technology类

package com.li.association.many2many.pojo;

import java.util.HashSet;
import java.util.Set;

public class Technology {
    private Long id;
    private String name;
    private String category;
    private Set<Engineer> engineers=new HashSet<Engineer>();

    public Technology() {
        super();
    }

    public Technology(Long id, String name, String category, Set<Engineer> engineers) {
        super();
        this.id = id;
        this.name = name;
        this.category = category;
        this.engineers = engineers;
    }

    public Long getId() {
        return id;
    }

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

    public String getName() {
        return name;
    }

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

    public String getCategory() {
        return category;
    }

    public void setCategory(String category) {
        this.category = category;
    }

    public Set<Engineer> getEngineers() {
        return engineers;
    }

    public void setEngineers(Set<Engineer> engineers) {
        this.engineers = engineers;
    }

}

多对多双向关联关系的映射文件

Engineer.hbm.xml

  • set标签中name=”technology”,表示Engineer有一个集合属性为technology

  • table=”T_ENGINEER_TECHNOLOGY”:指示桥表

  • cascade=”all”:级联所有操作,只需在一侧设置即可

  • inverse=”true”:取消插入语句之后的更新操作,在多对多关系中必须设置,也只需在一侧设置即可

  • key标签中的column属性设置的是该表主键对应到桥表的外键列

  • many-to-many标签中,指定另一个对应的表的主键列和该表对应的类

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.li.association.many2many.pojo">
    <class name="Engineer" table="ENGINEER">
        <id name="id" type="long" column="ID"></id>

        <property name="name" type="string" column="NAME"></property>
        <property name="gender" type="string" column="GENDER"></property>
        <property name="age" type="integer" column="AGE"></property>
        <property name="company" type="string" column="COMPANY"></property>

        <set name="technologies" table="T_ENGINEER_TECHNOLOGY" cascade="all" inverse="true">
            <key column="ENGINEER_ID"></key>
            <many-to-many column="TECHNOLOGY_ID" class="Technology"/>
        </set>

    </class>
</hibernate-mapping>

Technology.hbm.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

<hibernate-mapping package="com.li.association.many2many.pojo">
    <class name="Technology" table="T_TECHNOLOGY">
        <id name="id" type="long" column="ID"></id>
        <property name="name" type="string" column="NAME"></property>
        <property name="category" type="string" column="CATEGORY"></property>

        <set name="engineers" table="T_ENGINEER_TECHNOLOGY">
            <key column="TECHNOLOGY_ID"/>
            <many-to-many column="ENGINEER_ID" class="Engineer"/>
        </set>
    </class>
</hibernate-mapping>

配置 核心配置文件 hibernate.cfg.xml

将两个映射文件添加到配置文件中

<mapping resource="com/li/association/many2many/pojo/Engineer.hbm.xml"/>
<mapping resource="com/li/association/many2many/pojo/Technology.hbm.xml"/>

测试

package com.li.test;

import org.hibernate.Session;
import org.hibernate.Transaction;

import com.li.association.many2many.pojo.Engineer;
import com.li.association.many2many.pojo.Technology;
import com.li.common.HibernateSessionFactory;

public class Association4 {

    public static void main(String[] args) {
        Engineer engineer1=new Engineer();
        engineer1.setId(12L);
        engineer1.setName("Wang");
        engineer1.setGender("male");
        engineer1.setCompany("alibaba");
        engineer1.setAge(20);

        Engineer engineer2=new Engineer();
        engineer2.setId(34L);
        engineer2.setName("Lisa");
        engineer2.setGender("female");
        engineer2.setCompany("alibaba");
        engineer2.setAge(20);

        Technology t1=new Technology();
        t1.setId(56L);
        t1.setName("Hibernate");
        t1.setCategory("Java");

        Technology t2=new Technology();
        t2.setId(78L);
        t2.setName("Spring");
        t2.setCategory("Java");

        /*建立双向关联关系*/
        engineer1.getTechnologies().add(t1);
        engineer1.getTechnologies().add(t2);
        engineer2.getTechnologies().add(t1); 
        engineer2.getTechnologies().add(t2); 
        t1.getEngineers().add(engineer1);
        t1.getEngineers().add(engineer2);
        t2.getEngineers().add(engineer1);
        t2.getEngineers().add(engineer2);

        Session session=HibernateSessionFactory.getSession();
        Transaction trans=null;

        try {
            trans=session.beginTransaction();

            /*级联保存,因为我们的级联设置在Engineer一侧,所以我们保存Engineer对象会级联保存Technology,我们也可以将级联同时设置在两侧,这样我们不管保存哪一侧都会级联保存*/
            session.save(engineer1);
            session.save(engineer2);

            /*级联检索*/
            Engineer engineer = session.get(Engineer.class, 12L);
            System.out.println(engineer);

            /*级联删除,连同两个Technology对象都会在数据库中删除*/
            session.delete(engineer);

            /*提交事务*/
            trans.commit();
        } catch (Exception e) {
            e.printStackTrace();
            trans.rollback();
        }
    }

}

级联删除设置 cascade=”all”时,如果我们删除一个Engineer对象记录(如上面的engineer1),将会删除所有跟此记录的记录,如T_TECHNOLOGY表中对应t1,t2的记录,此时T_TECHNOLOGY和桥表中的记录都空了。当然,有时我们不希望这么做,我们希望,在删除engineer1的时候,只删除t_engineer表中的该记录以及桥表中对应的该记录,那么我们可以设置只在保存和更新的时候记录,而删除的时候不记录,更改为 cascade=”save-update”。

相关标签: 关联关系