hibernate映射的 关联关系:有 一对多关联关系,一对一关联关系,多对多关联关系,继承关系

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了hibernate映射的 关联关系:有 一对多关联关系,一对一关联关系,多对多关联关系,继承关系相关的知识,希望对你有一定的参考价值。

hibernate环境配置:导包。。。。

 

单向n-1:单向 n-1 关联只需从 n 的一端可以访问 1 的一端

  <many-to-one> 元素来映射组成关系:

name: 设定待映射的持久化类的属性的名字

column: 设定和持久化类的属性对应的表的外键

class:设定待映射的持久化类的属性的类型

 

建立1所对应的封装类,这儿只写属性,setter和getter就不写了;

private Integer customerId;
private String customerName;

建立n所对应的封装类,这儿只写属性,setter和getter就不写了;

private Integer orderId;
private String orderName;
private Customer customer;

 

在该包下建立上面 封装类对应的 hibernate映射文件Hibernate XML Mapping file(hbm.xml):Customer.hbm.xml,Order.hbm.xml;

<hibernate-mapping>
            <!-- 一般在表名后边加一个S,组成表名,利于order引用时,便于识别 -->
    <class name="com.atguigu.hibernate.one2many.Customer" table="CUSTOMER">
    
        <id name="customerId" type="java.lang.Integer">
            <column name="CUSTOMER_ID" />
            <generator class="native" />
        </id>
        
        <property name="customerName" type="java.lang.String">
            <column name="CUSTOMER_NAME" />
        </property>
        
    </class>
</hibernate-mapping>
<hibernate-mapping package="com.atguigu.hibernate.one2many">
    <class name="Order" table="ORDERS">
    
        <id name="orderId" type="java.lang.Integer">
            <column name="ORDER_ID" />
            <generator class="native" />
        </id>
        
        <property name="orderName" type="java.lang.String">
            <column name="ORDER_NAME" />
        </property>
        
        <!-- 
            映射多对一的关联关系。 使用 many-to-one 来映射多对一的关联关系 
            name: 多这一端关联的一那一端的属性的名字
            class: 一那一端的属性对应的类名
            column: 一那一端在多的一端对应的数据表中的外键的名字
        -->
        <!-- class=》Customer类的全类名,上边package中写了包名,所以这就不用写了 -->
        <many-to-one name="customer" class="Customer" column="CUSTOMER_ID"></many-to-one>
        
    </class>
    
</hibernate-mapping>

 

在src目录下,建立 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>
    
    <!-- Hibernate连接数据库的基本信息 -->
    <property name="connection.username">root</property>
    <property name="connection.password">lxn123</property>
    <property name="connection.driver_class">com.mysql.jdbc.Driver</property>
    <property name="connection.url">jdbc:mysql:///hibernate4</property>
    
    <!-- Hibernate的基本配置 -->
    <!-- Hibernate使用的数据库方言 -->
    <property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>
    
    <!-- 运行时是否执行sql语言 -->
    <property name="show_sql">true</property>
    
    <!-- 运行时是否格式化 -->
    <property name="format_sql">true</property>
    
    <!-- 生成数据表的策略,有四种,一般使用update -->
    <property name="hbm2ddl.auto">update</property>
    
    <!-- 设置hibernate事物的隔离级别 ,2为可读,可提交-->
    <property name="connection.isolation">2</property>
    
    <!-- 删除对象后,使其OID为null -->
    <property name="use_identifier_rollback">true</property>
    
    <!-- 配置c3p0数据源 -->
    <!-- 数据库连接池的最大连接数 -->
    <property name="hibernate.c3p0.max_size">10</property>
    
    <!-- 数据库连接池的最小连接数 -->
    <property name="hibernate.c3p0.min_size">5</property>
    
     <!-- 当数据库连接池中的连接耗尽时, 同一时刻获取多少个数据库连接 -->
    <property name="c3p0.acquire_increment">2</property>
    
    <!-- 表示连接池检测线程多长时间检测一次池内的所有链接对象是否超时. -->
    <property name="c3p0.idle_test_period">2000</property>
        
    <!-- 数据库连接池中连接对象在多长时间没有使用过后,就应该被销毁 -->
    <property name="c3p0.timeout">2000</property>
    
    <property name="c3p0.max_statements">10</property>
    
    <!-- 设定 JDBC 的 Statement 读取数据的时候每次从数据库中取出的记录条数 -->
    <property name="hibernate.jdbc.fetch_size">100</property>
    
    <!-- 设定 JDBC 的 Statement 读取数据的时候每次从数据库中取出的记录条数 -->
    <property name="jdbc.batch_size">30</property>
    
    <!-- 需要关联的hibernate关联文件 -->
    <!--  
    <mapping resource="com/atguigu/hibernate/one2many/Order.hbm.xml"/>
    <mapping resource="com/atguigu/hibernate/one2many/Customer.hbm.xml"/>
    <mapping resource="com/atguigu/hibernate/one2many/both/Customer.hbm.xml"/>
    <mapping resource="com/atguigu/hibernate/one2many/both/Order.hbm.xml"/>
    <mapping resource="com/atguigu/hibernate/many2many/Category.hbm.xml"/>
    <mapping resource="com/atguigu/hibernate/many2many/Item.hbm.xml"/>
    -->
    <mapping resource="com/atguigu/hibernate/subclass/Person.hbm.xml"/>
    
    </session-factory>
</hibernate-configuration>

 

建立测试类,进行测试:

package com.atguigu.hibernate.test;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.Transaction;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.atguigu.hibernate.one2many.Customer;
import com.atguigu.hibernate.one2many.Order;
import com.mysql.jdbc.Connection;

public class HibernateTest {
    
    private SessionFactory sessionFactory;
    private Session session;
    private Transaction transaction;
    
    @Before
    public void init(){
        Configuration configuration=new Configuration().configure();
        ServiceRegistry serviceRegistry=
                new ServiceRegistryBuilder().applySettings(configuration.getProperties())
                                            .buildServiceRegistry();
        sessionFactory=configuration.buildSessionFactory(serviceRegistry);
        session=sessionFactory.openSession();
        transaction=session.beginTransaction();
    }
    
    @After
    public void destroy(){
        transaction.commit();
        session.close();
        sessionFactory.close();
    }
    
    @Test
    public void testDelect(){
        //在不设定级联关系的情况下, 且 1 这一端的对象有 n 的对象在引用, 不能直接删除 1 这一端的对象
        Customer customer=(Customer) session.get(Customer.class, 1);
        session.delete(customer);
    }
    
    public void testUpdate(){
        Order order=(Order) session.get(Order.class, 3);
        order.getCustomer().setCustomerName("AAAAAA");
        
    }
    
    public void many2oneTget(){
        //1. 若查询多的一端的一个对象, 则默认情况下, 只查询了多的一端的对象. 而没有查询关联的
        //1 的那一端的对象!
        Order order=(Order) session.get(Order.class, 3);
        System.out.println(order.getOrderName());
        
        System.out.println(order.getCustomer().getClass().getName());
        
        Customer customer=(Customer) session.get(Customer.class, 1);
        System.out.println(customer.getCustomerName());
    }
    
    public void many2OneTsave(){
        
        Customer customer = new Customer();
        customer.setCustomerName("panpan");
        
        Order order= new Order();
        order.setOrderName("ggg");
        
        Order order2=new Order();
        order2.setOrderName("drrr");
        
        //设定关联关系
        order.setCustomer(customer);
        order2.setCustomer(customer);
        
        session.save(order);
        session.save(order2);
        session.save(customer);
    }
}

-------------------------------------------------------------------------------------

双向 1-n:

双向 1-n 与 双向 n-1 是完全相同的两种情形

双向 1-n 需要在 1 的一端可以访问 n 的一端, 反之依然

<set> 元素来映射持久化类的 set 类型的属性: name: 设定待映射的持久化类的属性

<key> 元素设定与所关联的持久化类对应的表的外键 :column: 指定关联表的外键名

<one-to-many> 元素设定集合属性中所关联的持久化类: class: 指定关联的持久化类的类名

 

建立1所对应的封装类,这儿只写属性,setter和getter就不写了;

private Integer customerId;
private String customerName;
    
/*
* 1. 声明集合类型时, 需使用接口类型, 因为 hibernate 在获取
* 集合类型时, 返回的是 Hibernate 内置的集合类型, 而不是 JavaSE 一个标准的
* 集合实现. 
* 2. 需要把集合进行初始化, 可以防止发生空指针异常
*/
private Set<Order> orders=new HashSet<Order>();

 

建立n所对应的封装类,这儿只写属性,setter和getter就不写了;

private Integer orderId;
private String orderName;
private Customer customer;

 

在该包下建立上面 封装类对应的 hibernate映射文件Hibernate XML Mapping file(hbm.xml):Customer.hbm.xml,Order.hbm.xml;

<hibernate-mapping package="com.atguigu.hibernate.one2many.both">

    <class name="Customer" table="CUSTOMERS">
    
        <id name="customerId" type="java.lang.Integer">
            <column name="CUSTOMER_ID" />
            <generator class="native" />
        </id>
        
        <property name="customerName" type="java.lang.String">
            <column name="CUSTOMER_NAME" />
        </property>
        
        <!-- 映射 1 对多的那个集合属性 -->
        <!-- set: 映射 set 类型的属性, table: set 中的元素对应的记录放在哪一个数据表中. 该值需要和多对一的多的那个表的名字一致 -->
        <!-- inverse: 指定由哪一方来维护关联关系. 通常设置为 true, 以指定由多的一端来维护关联关系 -->
        <!-- cascade 设定级联操作. 开发时不建议设定该属性. 建议使用手工的方式来处理 -->
        <!-- order-by 在查询时对集合中的元素进行排序, order-by 中使用的是表的字段名, 而不是持久化类的属性名  -->
        <set name="orders" table="ORDERS" inverse="true" order-by="ORDER_NAME DESC">
            <!-- 执行多的表中的外键列的名字 -->
            <key><column name="CUSTOMER_ID" /></key>
            <!-- 指定映射类型 ,即映射类的全类名,上边package中已经添加了包,这儿只写映射的类名-->
            <one-to-many class="Order" />
        </set>
        
    </class>
</hibernate-mapping>
<hibernate-mapping package="com.atguigu.hibernate.one2many.both">

    <class name="Order" table="ORDERS">
    
        <id name="orderId" type="java.lang.Integer">
            <column name="ORDER_ID" />
            <generator class="native" />
        </id>
        
        <property name="orderName" type="java.lang.String">
            <column name="ORDER_NAME" />
        </property>
        
        <!-- 
            映射多对一的关联关系。 使用 many-to-one 来映射多对一的关联关系 
            name: 多这一端关联的一那一端的属性的名字
            class: 一那一端的属性对应的类名
            column: 一那一端在多的一端对应的数据表中的外键的名字
        -->
        <many-to-one name="customer" class="Customer" column="CUSTOMER_ID"></many-to-one>
        
    </class>
</hibernate-mapping>

 

配置文件上边有,必须配置 需要关联的hibernate关联文件;

建立测试类,进行测试:这儿只写测试方法,sessionFactory等,就不写了,上边的测试类中有;

    @Test
    public void testDelete(){
        //在不设定级联关系的情况下, 且 1 这一端的对象有 n 的对象在引用, 不能直接删除 1 这一端的对象
        Customer customer = (Customer) session.get(Customer.class, 1);
        session.delete(customer); 
    }
    
    @Test
    public void testUpdat2(){
        Customer customer = (Customer) session.get(Customer.class, 1);
        customer.getOrders().iterator().next().setOrderName("GGG"); 
    }
    
    @Test
    public void testUpdate(){
        Order order = (Order) session.get(Order.class, 1);
        order.getCustomer().setCustomerName("AAA");
    }
    
    @Test
    public void testOne2ManyGet(){
        //1. 对 n 的一端的集合使用延迟加载
        Customer customer = (Customer) session.get(Customer.class, 7);
        System.out.println(customer.getCustomerName()); 
        //2. 返回的多的一端的集合时 Hibernate 内置的集合类型. 
        //该类型具有延迟加载和存放代理对象的功能. 
        System.out.println(customer.getOrders().getClass()); 
        
        //session.close();
        //3. 可能会抛出 LazyInitializationException 异常 
        
        System.out.println(customer.getOrders().size()); 
        
        //4. 再需要使用集合中元素的时候进行初始化. 
    }
    
    @Test
    public void testMany2OneGet(){
        //1. 若查询多的一端的一个对象, 则默认情况下, 只查询了多的一端的对象. 而没有查询关联的
        //1 的那一端的对象!
        Order order = (Order) session.get(Order.class, 1);
        System.out.println(order.getOrderName()); 
        
        System.out.println(order.getCustomer().getClass().getName());
        
        session.close();
        
        //2. 在需要使用到关联的对象时, 才发送对应的 SQL 语句. 
        Customer customer = order.getCustomer();
        System.out.println(customer.getCustomerName()); 
        
        //3. 在查询 Customer 对象时, 由多的一端导航到 1 的一端时, 
        //若此时 session 已被关闭, 则默认情况下
        //会发生 LazyInitializationException 异常
        
        //4. 获取 Order 对象时, 默认情况下, 其关联的 Customer 对象是一个代理对象!
        
    }
    
    public void testBoth(){
        Customer customer=new Customer();
        Order order=new Order();
        Order order2=new Order(); 
        
        customer.setCustomerName("AAAAA");
        order.setOrderName("aaaaa");
        order2.setOrderName("bbbbb");
        
        //设定关联关系
        order.setCustomer(customer);
        order2.setCustomer(customer);
        
        customer.getOrders().add(order);
        customer.getOrders().add(order2);
        
        session.save(customer);
        session.save(order);
        session.save(order2);
        
    }

------------------------------------------------------------------------------------

  基于外键映射的 1-1:

  对于基于外键的1-1关联,其外键可以存放在任意一边,在需要存放外键一端,增加many-to-one元素。为many-to-one元素增加unique=“true” 属性来表示为1-1关联

  另一端需要使用one-to-one元素,该元素使用 property-ref 属性指定使用被关联实体主键以外的字段作为关联字段

 

建立一个 1所对应的封装类,这儿只写属性,setter和getter就不写了;

private Integer manId;
private String manName;
    
private Department dep;

 

建立另外一个 1所对应的封装类,这儿只写属性,setter和getter就不写了;

private Integer depId;
private String depName;
    
private Manager man;

 

在该包下建立上面 封装类对应的 hibernate映射文件Hibernate XML Mapping file(hbm.xml):

<hibernate-mapping package="com.atguigu.hibernate.one2one.foreign">

    <class name="Manager" table="MANAGERS">
    
        <id name="manId" type="java.lang.Integer">
            <column name="MAN_ID" />
            <generator class="native" />
        </id>
        
        <property name="manName" type="java.lang.String">
            <column name="MAN_NAME" />
        </property>
        
        <!-- 映射 1-1 的关联关系: 在对应的数据表中已经有外键了, 当前持久化类使用 one-to-one 进行映射 -->
        <!-- 
        没有外键的一端需要使用one-to-one元素,该元素使用 property-ref 属性指定使用被关联实体主键以外的字段作为关联字段
         -->
        <one-to-one name="dep" class="Department"></one-to-one>
    
    </class>
</hibernate-mapping>
<hibernate-mapping package="com.atguigu.hibernate.one2one.foreign">

    <class name="Department" table="DEPARTMENTS">
    
        <id name="depId" type="java.lang.Integer">
            <column name="DEP_ID" />
            <generator class="native" />
        </id>
        
        <property name="depName" type="java.lang.String">
            <column name="DEP_NAME" />
        </property>
        
        <!-- 使用 many-to-one 的方式来映射 1-1 关联关系 ,使用外键的方式,且外键唯一-->
        <many-to-one name="man" class="Manager" column="MAN_ID" unique="true"></many-to-one>
    </class>
</hibernate-mapping>

 

配置文件上边有,必须配置 需要关联的hibernate关联文件;

建立测试类,进行测试:这儿只写测试方法,sessionFactory等,就不写了,上边的测试类中有;

    @Test
    public void testGet2(){
        //在查询没有外键的实体对象时, 使用的左外连接查询, 一并查询出其关联的对象
        //并已经进行初始化. 
        Manager mgr = (Manager) session.get(Manager.class, 1);
        System.out.println(mgr.getManName()); 
        System.out.println(mgr.getDep().getDepId()); 
    }
    
    @Test
    public void testGet(){
        //1. 默认情况下对关联属性使用懒加载
        Department dept = (Department) session.get(Department.class, 1);
        System.out.println(dept.getDepName()); 
        
        //2. 所以会出现懒加载异常的问题. 
//        session.close();
//        Manager mgr = dept.getMgr();
//        System.out.println(mgr.getClass()); 
//        System.out.println(mgr.getMgrName()); 
        
        //3. 查询 Manager 对象的连接条件应该是 dept.manager_id = mgr.manager_id
        //而不应该是 dept.dept_id = mgr.manager_id
        Manager mgr = dept.getMan();
        System.out.println(mgr.getManName()); 
        
        
    }
    
    @Test
    public void testone2onesave(){
        Department department=new Department();
        Manager manager=new Manager();
        
        department.setDepName("AAAAA");
        manager.setManName("aaaaaa");
        
        //设定关联关系
        department.setMan(manager);
        manager.setDep(department);
        
        //保存操作
        //建议先保存没有外键列的那个对象. 这样会减少 UPDATE 语句
        session.save(manager);
        session.save(department);
    }

------------------------------------------------------------------------------------

 基于主键映射的 1-1:

  基于主键的映射策略:指一端的主键生成器使用 foreign 策略,表明根据”对方”的主键来生成自己的主键,自己并不能独立生成主键. <param> 子元素指定使用当前持久化类的哪个属性作为 “对方”;

  采用foreign主键生成器策略的一端增加 one-to-one 元素映射关联属性,其one-to-one属性还应增加 constrained=“true” 属性;另一端增加one-to-one元素映射关联属性。 constrained(约束):指定为当前持久化类对应的数据库表的主键添加一个外键约束,引用被关联的对象(“对方”)所对应的数据库表主键

 

建立一个 1所对应的封装类,这儿只写属性,setter和getter就不写了;

private Integer depId;
private String depName;
    
private Manager man;

 

建立另一个 1所对应的封装类,这儿只写属性,setter和getter就不写了;

private Integer manId;
private String manName;
    
private Department dep;

 

在该包下建立上面 封装类对应的 hibernate映射文件Hibernate XML Mapping file(hbm.xml):

<hibernate-mapping package="com.atguigu.hibernate.one2one.primary">

    <class name="Department" table="DEPARTMENTS">
    
        <id name="depId" type="java.lang.Integer">
            <column name="DEPID" />
            <!-- 使用外键的方式来生成当前的主键 -->
            <generator class="foreign">
                <!-- property 属性指定使用当前持久化类的哪一个属性的主键作为外键 -->
                <param name="property">man</param>
            </generator>
        </id>
        
        <property name="depName" type="java.lang.String">
            <column name="DEPNAME" />
        </property>
        
        <!--  
        采用 foreign 主键生成器策略的一端增加 one-to-one 元素映射关联属性,
        其 one-to-one 节点还应增加 constrained=true 属性, 以使当前的主键上添加外键约束
        -->
        <one-to-one name="man" class="Manager" constrained="true"></one-to-one>
    
    </class>
</hibernate-mapping>
<hibernate-mapping package="com.atguigu.hibernate.one2one.primary">

    <class name="Manager" table="MANAGERS">
    
        <id name="manId" type="java.lang.Integer">
            <column name="MAN_ID" />
            <generator class="native" />
        </id>
        
        <property name="manName" type="java.lang.String">
            <column name="MAN_NAME" />
        </property>
        
        <one-to-one name="dep" class="Department"></one-to-one>
    
    </class>
</hibernate-mapping>

 

配置文件上边有,必须配置 需要关联的hibernate关联文件;

建立测试类,进行测试:这儿只写测试方法,sessionFactory等,就不写了,上边的测试类中有;

    @Test
    public void testGet2(){
        //在查询没有外键的实体对象时, 使用的左外连接查询, 一并查询出其关联的对象
        //并已经进行初始化. 
        Manager mgr = (Manager) session.get(Manager.class, 1);
        System.out.println(mgr.getManName()); 
        System.out.println(mgr.getDep().getDepId()); 
    }
    
    @Test
    public void testGet(){
        //1. 默认情况下对关联属性使用懒加载
        Department dept = (Department) session.get(Department.class, 1);
        System.out.println(dept.getDepId()); 
        
        //2. 所以会出现懒加载异常的问题. 
        Manager mgr = dept.getMan();
        System.out.println(mgr.getManName()); 
    }
    
    @Test
    public void testSave(){
        
        Department department = new Department();
        department.setDepName("DEPT-DD");
        
        Manager manager = new Manager();
        manager.setManName("MGR-DD");
        
        //设定关联关系
        manager.setDep(department);
        department.setMan(manager);
        
        //保存操作
        //先插入哪一个都不会有多余的 UPDATE
        session.save(department);
        session.save(manager);
        
    }

------------------------------------------------------------------------------------

n-n:

  单向n-n 的关联必须使用连接表 与 1-n 映射类似,必须为 set 集合元素添加 key 子元素,指定 CATEGORIES_ITEMS 表中参照 CATEGORIES 表的外键为 CATEGORIY_ID. 与 1-n 关联映射不同的是,建立 n-n 关联时, 集合中的元素使用 many-to-many. many-to-many 子元素的 class 属性指定 items 集合中存放的是 Item 对象, column 属性指定 CATEGORIES_ITEMS 表中参照 ITEMS 表的外键为 ITEM_ID

  双向 n-n 关联需要两端都使用集合属性 双向n-n关联必须使用连接表 集合属性应增加 key 子元素用以映射外键列, 集合元素里还应增加many-to-many子元素关联实体类 在双向 n-n 关联的两边都需指定连接表的表名及外键列的列名. 两个集合元素 set 的 table 元素的值必须指定,而且必须相同。set元素的两个子元素:key 和 many-to-many 都必须指定 column 属性,其中,key 和 many-to-many 分别指定本持久化类和关联类在连接表中的外键列名,因此两边的 key 与 many-to-many 的column属性交叉相同。也就是说,一边的set元素的key的 cloumn值为a,many-to-many 的 column 为b;则另一边的 set 元素的 key 的 column 值 b,many-to-many的 column 值为 a. 对于双向 n-n 关联, 必须把其中一端的 inverse 设置为 true, 否则两端都维护关联关系可能会造成主键冲突

 

这儿只写双向n-n;

建立一个n所对应的封装类,这儿只写属性,setter和getter就不写了;

private Integer id;
private String name;
    
private Set<Item> items=new HashSet<Item>();

 

建立另一个n所对应的封装类,这儿只写属性,setter和getter就不写了;

private Integer id;
private String name;
    
private Set<Category> categorys=new HashSet<Category>();

 

在该包下建立上面 封装类对应的 hibernate映射文件Hibernate XML Mapping file(hbm.xml):

<hibernate-mapping package="com.atguigu.hibernate.many2many">

    <class name="Category" table="CATEGORYS">
    
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
        
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        
        <!-- table: 指定中间表 -->
        <set name="items" table="CATEGORY_ITEM">
            <key>
                <column name="CATEGORY_ID" />
            </key>
             <!-- 使用 many-to-many 指定多对多的关联关系. 
             column 执行 Set 集合中的持久化类在中间表的外键列的名称  -->
            <many-to-many class="Item" column="ITEM_ID"></many-to-many>
        </set>
    </class>
</hibernate-mapping>
<hibernate-mapping package="com.atguigu.hibernate.many2many">

    <class name="Item" table="ITEMS">
    
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
        
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        
        <!-- table: 指定中间表 ,inverse="true"维护关联关系-->
        <set name="categorys" table="CATEGORY_ITEM" inverse="true">
            <key>
                <column name="ITEM_ID" />
            </key>
            <!-- 使用 many-to-many 指定多对多的关联关系. 
             column 执行 Set 集合中的持久化类在中间表的外键列的名称  -->
            <many-to-many class="Category" column="CATEGORY_ID"></many-to-many>
        </set>
        
    </class>
</hibernate-mapping>

 

配置文件上边有,必须配置 需要关联的hibernate关联文件;

建立测试类,进行测试:这儿只写测试方法,sessionFactory等,就不写了,上边的测试类中有;

    @Test
    public void testGet(){
        Category category = (Category) session.get(Category.class, 1);
        System.out.println(category.getName()); 
        
        //需要连接中间表
        Set<Item> items = category.getItems();
        System.out.println(items.size()); 
    }
    
    @Test
    public void testSave(){
        Category category1 = new Category();
        category1.setName("C-AA");

        Category category2 = new Category();
        category2.setName("C-BB");
        
        Item item1 = new Item();
        item1.setName("I-AA");
        
        Item item2 = new Item();
        item2.setName("I-BB");
        
        //设定关联关系
        category1.getItems().add(item1);
        category1.getItems().add(item2);
        
        category2.getItems().add(item1);
        category2.getItems().add(item2);
        
        item1.getCategorys().add(category1);
        item1.getCategorys().add(category2);
        
        item2.getCategorys().add(category1);
        item2.getCategorys().add(category2);
        
        //执行保存操作
        session.save(category1);
        session.save(category2);
        
        session.save(item1);
        session.save(item2);
    }

------------------------------------------------------------------------------------

映射继承关系:

  对于面向对象的程序设计语言而言,继承和多态是两个最基本的概念。Hibernate 的继承映射可以理解持久化类之间的继承关系。例如:人和学生之间的关系。学生继承了人,可以认为学生是一个特殊的人,如果对人进行查询,学生的实例也将被得到。

  

  Hibernate支持三种继承映射策略:

   使用 subclass 进行映射:将域模型中的每一个实体对象映射到一个独立的表中,也就是说不用在关系数据模型中考虑域模型中的继承关系和多态。

   使用 joined-subclass 进行映射: 对于继承关系中的子类使用同一个表,这就需要在数据库表中增加额外的区分子类类型的字段。

  使用 union-subclass 进行映射:域模型中的每个类映射到一个表,通过关系数据模型中的外键来描述表之间的继承关系。这也就相当于按照域模型的结构来建立数据库中的表,并通过外键来建立表之间的继承关系。

 

采用 subclass 元素的继承映射:

  采用 subclass 的继承映射可以实现对于继承关系中父类和子类使用同一张表;

  因为父类和子类的实例全部保存在同一个表中,因此需要在该表内增加一列,使用该列来区分每行记录到低是哪个类的实例----这个列被称为辨别者列(discriminator);

  使用 subclass 来映射子类,使用 class 或 subclass 的 discriminator-value 属性指定辨别者列的值 所有子类定义的字段都不能有非空约束。

 

建立父类所对应的封装类,这儿只写属性,setter和getter就不写了;

private Integer id;
private String name;
private int age;

 

建立子类所对应的封装类,子类继承父类person

package com.atguigu.hibernate.subclass;

public class Student extends Person{
    
    private String school;

    public String getSchool() {
        return school;
    }

    public void setSchool(String school) {
        this.school = school;
    }
    
}

 

在该包下建立上面 封装类对应的 hibernate映射文件Hibernate XML Mapping file(hbm.xml):

<hibernate-mapping package="com.atguigu.hibernate.subclass">

    <class name="Person" table="PERSON_STUDENT" discriminator-value="PERSON">
    
        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
        
         <!-- 配置辨别者列 -->
         <discriminator column="TYPE" type="string"></discriminator>
        
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        
        <property name="age" type="int">
            <column name="AGE" />
        </property>
        
        <!-- 映射子类 Student, 使用 subclass 进行映射 -->
        <subclass name="Student" discriminator-value="STUDENT">
            <property name="school" type="string" column="SCHOOL"></property>
        </subclass>
    </class>
</hibernate-mapping>

 

配置文件上边有,必须配置 需要关联的hibernate关联文件;

建立测试类,进行测试:这儿只写测试方法,sessionFactory等,就不写了,上边的测试类中有;

    /**
     * 缺点:
     * 1. 使用了辨别者列.
     * 2. 子类独有的字段不能添加非空约束.
     * 3. 若继承层次较深, 则数据表的字段也会较多. 
     */
    
    /**
     * 查询:
     * 1. 查询父类记录, 只需要查询一张数据表
     * 2. 对于子类记录, 也只需要查询一张数据表
     */
    @Test
    public void testQuery(){
        List<Person> persons = session.createQuery("FROM Person").list();
        System.out.println(persons.size()); 
        
        List<Student> stus = session.createQuery("FROM Student").list();
        System.out.println(stus.size()); 
    }
    
    /**
     * 插入操作: 
     * 1. 对于子类对象只需把记录插入到一张数据表中.
     * 2. 辨别者列有 Hibernate 自动维护. 
     */
    @Test
    public void testSave(){
        
        Person person = new Person();
        person.setAge(11);
        person.setName("AA");
        
        session.save(person);
        
        Student stu = new Student();
        stu.setAge(22);
        stu.setName("BB");
        stu.setSchool("ATGUIGU");
        
        session.save(stu);
        
    }

 

采用 joined-subclass 元素的继承映射:

  采用 joined-subclass 元素的继承映射可以实现每个子类一张表 采用这种映射策略时,父类实例保存在父类表中,子类实例由父类表和子类表共同存储。因为子类实例也是一个特殊的父类实例,因此必然也包含了父类实例的属性。于是将子类和父类共有的属性保存在父类表中,子类增加的属性,则保存在子类表中。

  在这种映射策略下,无须使用鉴别者列,但需要为每个子类使用 key 元素映射共有主键。

  子类增加的属性可以添加非空约束。因为子类的属性和父类的属性没有保存在同一个表中;

 

建立父类所对应的封装类,这儿只写属性,setter和getter就不写了;

private Integer id;
private String name;
private int age;

 

建立子类所对应的封装类,子类继承父类person

package com.atguigu.hibernate.joined.subclass;

public class Student extends Person{

    private String school;
    public String getSchool() {
        return school;
    }
    public void setSchool(String school) {
        this.school = school;
    }
    
}

 

在该包下建立上面 封装类对应的 hibernate映射文件Hibernate XML Mapping file(hbm.xml):

<hibernate-mapping package="com.atguigu.hibernate.joined.subclass">

    <class name="Person" table="PERSONS">

        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="native" />
        </id>
        
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        
        <property name="age" type="int">
            <column name="AGE" />
        </property>
        
        <joined-subclass name="Student" table="STUDENTS">
            <key column="STUDENT_id"></key>
            <property name="school" type="string" column="SCHOOL"></property>
        </joined-subclass>
        
    </class>
</hibernate-mapping>

 

配置文件上边有,必须配置 需要关联的hibernate关联文件;

建立测试类,进行测试:这儿只写测试方法,sessionFactory等,就不写了,上边的测试类中有;

/**
     * 优点:
     * 1. 不需要使用了辨别者列.
     * 2. 子类独有的字段能添加非空约束.
     * 3. 没有冗余的字段. 
     */
    
    /**
     * 查询:
     * 1. 查询父类记录, 做一个左外连接查询
     * 2. 对于子类记录, 做一个内连接查询. 
     */
    @Test
    public void testQuery(){
        List<Person> persons = session.createQuery("FROM Person").list();
        System.out.println(persons.size()); 
        
        List<Student> stus = session.createQuery("FROM Student").list();
        System.out.println(stus.size()); 
    }
    
    /**
     * 插入操作: 
     * 1. 对于子类对象至少需要插入到两张数据表中. 
     */
    @Test
    public void testSave(){
        
        Person person = new Person();
        person.setAge(11);
        person.setName("AA");
        
        session.save(person);
        
        Student stu = new Student();
        stu.setAge(22);
        stu.setName("BB");
        stu.setSchool("ATGUIGU");
        
        session.save(stu);
        
    }

 

采用 union-subclass 元素的继承映射:

  采用 union-subclass 元素可以实现将每一个实体对象映射到一个独立的表中。

  子类增加的属性可以有非空约束 --- 即父类实例的数据保存在父表中,而子类实例的数据保存在子类表中。

  子类实例的数据仅保存在子类表中, 而在父类表中没有任何记录

  在这种映射策略下,子类表的字段会比父类表的映射字段要多,因为子类表的字段等于父类表的字段、加子类增加属性的总和;

  在这种映射策略下,既不需要使用鉴别者列,也无须使用 key 元素来映射共有主键.

使用 union-subclass 映射策略是不可使用 identity 的主键生成策略, 因为同一类继承层次中所有实体类都需要使用同一个主键种子, 即多个持久化实体对应的记录的主键应该是连续的. 受此影响, 也不该使用 native 主键生成策略, 因为 native 会根据数据库来选择使用 identity 或 sequence.

 

建立父类所对应的封装类,这儿只写属性,setter和getter就不写了;

private Integer id;
private String name;
private int age;

 

建立子类所对应的封装类,子类继承父类person

package com.atguigu.hibernate.joined.subclass;

public class Student extends Person{

    private String school;
    public String getSchool() {
        return school;
    }
    public void setSchool(String school) {
        this.school = school;
    }
    
}

 

在该包下建立上面 封装类对应的 hibernate映射文件Hibernate XML Mapping file(hbm.xml):

<hibernate-mapping package="com.atguigu.hibernate.union.subclass">

    <class name="Person" table="PERSONS">

        <id name="id" type="java.lang.Integer">
            <column name="ID" />
            <generator class="hilo" />
        </id>
        
        <property name="name" type="java.lang.String">
            <column name="NAME" />
        </property>
        
        <property name="age" type="int">
            <column name="AGE" />
        </property>
    
        <union-subclass name="Student" table="STUDENTS">
            <property name="school" column="SCHOOL" type="string"></property>
        </union-subclass>        
        
    </class>
</hibernate-mapping>

 

配置文件上边有,必须配置 需要关联的hibernate关联文件;

建立测试类,进行测试:这儿只写测试方法,sessionFactory等,就不写了,上边的测试类中有;

@Test
    public void testUpdate(){
        String hql = "UPDATE Person p SET p.age = 20";
        session.createQuery(hql).executeUpdate();
    }
    
    /**
     * 优点:
     * 1. 无需使用辨别者列.
     * 2. 子类独有的字段能添加非空约束.
     * 
     * 缺点:
     * 1. 存在冗余的字段
     * 2. 若更新父表的字段, 则更新的效率较低
     */
    
    /**
     * 查询:
     * 1. 查询父类记录, 需把父表和子表记录汇总到一起再做查询. 性能稍差. 
     * 2. 对于子类记录, 也只需要查询一张数据表
     */
    @Test
    public void testQuery(){
        List<Person> persons = session.createQuery("FROM Person").list();
        System.out.println(persons.size()); 
        
        List<Student> stus = session.createQuery("FROM Student").list();
        System.out.println(stus.size()); 
    }
    
    /**
     * 插入操作: 
     * 1. 对于子类对象只需把记录插入到一张数据表中.
     */
    @Test
    public void testSave(){
        
        Person person = new Person();
        person.setAge(11);
        person.setName("AA");
        
        session.save(person);
        
        Student stu = new Student();
        stu.setAge(22);
        stu.setName("BB");
        stu.setSchool("ATGUIGU");
        
        session.save(stu);
        
    }

 

以上是关于hibernate映射的 关联关系:有 一对多关联关系,一对一关联关系,多对多关联关系,继承关系的主要内容,如果未能解决你的问题,请参考以下文章

Hibernate,关系映射的多对一单向关联多对一双向关联一对一主键关联一对一外键关联多对多关系关联

1-7 hibernate关联关系映射

Hibernate的关联关系映射

Hibernate—— 一对多 和 多对多关联关系映射(xml和注解)总结(转载)

(转)Hibernate框架基础——一对多关联关系映射

Hibernate框架学习之注解配置关系映射