在领域模型中,类与类之间最普遍的关系是关联关系,而在UML中关联是有方向的。以Customer类和Order类为例,从Order到Customer的关联是多对一关联,而从Customer到Order的关联则是一对多关联,两者均属于单向关联。


1. 单向n-1关联

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

java 不使用关联查询列表_关联关系

1.1 对象关系映射

  显然,无法直接使用property元素来映射Order类的customer属性,Hibernate提供many-to-one元素来映射多对一关联关系。示例如下:

<!-- 
    映射单向n-1映射关系:CUSTOMER_ID为ORDERS表中的外键
    实现:使用<many-to-one>元素来映射关联关系,其具体属性说明如下:
        name: 设定待映射的持久化类的属性的名字
        class:设定待映射的持久化类的属性的类型
        column: 设定和持久化类的属性对应的表的外键
 -->
<many-to-one name="customer" class="Customer" column="CUSTOMER_ID" not-null="true"></many-to-one>
1.2 数据库操作
public class HibernateTest {

    private SessionFactory sessionFactory; // 单实例的
    private Session session; // 项目中不能声明为成员变量,可能会有并发问题
    private Transaction transaction; // 项目中不能声明为成员变量,可能会有并发问题

    @Before
    public void init() {
        // 创建Session对象并开启事务
        Configuration configuration = new Configuration().configure();

        ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
                .applySettings(configuration.getProperties())
                .buildServiceRegistry();

        sessionFactory = configuration.buildSessionFactory(serviceRegistry);

        session = sessionFactory.openSession();

        transaction = session.beginTransaction();
    }

    @Test
    public void testManyToOneSave() {

        Customer customer = new Customer();
        customer.setCustomerName("qiaobc");

        Order order1 = new Order();
        order1.setOrderName("Order-1");

        Order order2 = new Order();
        order2.setOrderName("Order-2");

        // 设定关联关系
        order1.setCustomer(customer);
        order2.setCustomer(customer);

        // 1. 先1后n:只有3条INSERT语句;
//      session.save(customer);
//      session.save(order1);
//      session.save(order2);

        // 2. 先n后1:3条INSERT语句后有2条更新语句;
        session.save(order1);
        session.save(order2);
        session.save(customer);
    }

    @Test
    public void testManyToOneGet() {
        // 1. 查询n的一端的对象时,默认不会查询与其关联的1的一端的对象
        Order order = (Order) session.get(Order.class, 1);
        System.out.println(order.getOrderName());

        // 4. 仅获取n的一端的对象时,默认情况下其关联的1的一端的对象是一个代理对象
        // com.qiaobc.hibernate.entities.n21.Customer_$$_javassist_3
        System.out.println(order.getCustomer().getClass().getName());   // 不会查询关联对象

        // 3. 在查询关联对象时,若session关闭,则默认情况下会抛出org.hibernate.LazyInitializationException异常
        // session.close();

        // 2. 若需要使用关联对象时,才发送SELECT语句
        System.out.println(order);
    }

    @Test
    public void testManyToOneUpdate() {
        // 2条SELECT语句,1条UPDATE语句
        Order order = (Order) session.get(Order.class, 1);
        order.getCustomer().setCustomerName("qiaob");
    }

    @Test
    public void testManyToOneDelete() {
        // 在未设定级联关系的情况下,1的一端的对象有n的一端对象在引用,故其不能删除
        Customer customer = (Customer) session.get(Customer.class, 1);
        session.delete(customer);
    }

    @After
    public void destory() {
        transaction.commit();
        session.close();
        sessionFactory.close();
    }
}

2. 双向1-n关联

  概念:双向1-n与双向 n-1是完全相同的两种情形,即需要在1的一端可以访问n的一端,反之依然。

java 不使用关联查询列表_Test_02

2.1 对象关系映射

  在双向1-n关联关系中,既需要在1的一端配置一对多映射关系(set元素),又需要在n的一端配置多对一映射关系(many-to-one元素)。示例如下:

<!-- 1. 在Order.hbm.xml中:配置n-1映射关系 -->
<many-to-one name="customer" class="Customer" column="CUSTOMER_ID"></many-to-one>

<!-- 2. 在Customer.hbm.xml中:配置1-n映射关系 -->
<set name="orders" table="ORDERS">
    <key column="CUSTOMER_ID"></key>
    <one-to-many class="Order"/>
</set>
2.2 1-n集合声明

java 不使用关联查询列表_java 不使用关联查询列表_03

2.3 数据库操作 
public class HibernateTest {

    private SessionFactory sessionFactory; // 单实例的
    private Session session; // 项目中不能声明为成员变量,可能会有并发问题
    private Transaction transaction; // 项目中不能声明为成员变量,可能会有并发问题

    @Before
    public void init() {
        // 创建Session对象并开启事务
        Configuration configuration = new Configuration().configure();

        ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
                .applySettings(configuration.getProperties())
                .buildServiceRegistry();

        sessionFactory = configuration.buildSessionFactory(serviceRegistry);

        session = sessionFactory.openSession();

        transaction = session.beginTransaction();
    }

    @Test
    public void testManyToOneSave() {

        Customer customer = new Customer();
        customer.setCustomerName("qiaobc");

        Order order1 = new Order();
        order1.setOrderName("Order-1");

        Order order2 = new Order();
        order2.setOrderName("Order-2");

        // 设定关联关系
        order1.setCustomer(customer);
        order2.setCustomer(customer);

        customer.getOrders().add(order1);   // 需要在持久化类中对集合属性进行初始化
        customer.getOrders().add(order2);

        // 1. 先1后n:3条INSERT语句,2条UPDATE语句,因为均需要维护关联关系
        // 注意:可以设置1的一端的set属性inverse="true",使其放弃维护关联关系
//      session.save(customer);
//      session.save(order1);
//      session.save(order2);

        // 2. 先n后1:3条INSERT语句,4条UPDATE语句,因为均需要维护关联关系
        session.save(order1);
        session.save(order2);
        session.save(customer);
    }

    @Test
    public void testManyToOneGet() {
        // 1. 查询n的一端的对象时,默认不会查询与其关联的1的一端的对象
        Order order = (Order) session.get(Order.class, 1);
        System.out.println(order.getOrderName());

        // 4. 仅获取n的一端的对象时,默认情况下其关联的1的一端的对象是一个代理对象
        // com.qiaobc.hibernate.entities.n21.Customer_$$_javassist_3
        System.out.println(order.getCustomer().getClass().getName());   // 不会查询关联对象

        // 3. 在查询关联对象时,若session关闭,则默认情况下会抛出org.hibernate.LazyInitializationException异常
        // session.close();

        // 2. 若需要使用关联对象时,才发送SELECT语句
        System.out.println(order);
    }

    @Test
    public void testOneToManyGet() {
        // 1. 对n的一端的集合延迟加载
        Customer customer = (Customer) session.get(Customer.class, 1);
        System.out.println(customer.getCustomerName());

        // 2. 需要在持久化类中定义集合类型为Java接口类型
        // org.hibernate.collection.internal.PersistentSet:
        //      Hibernate内置的集合类型,具有延迟加载和存放代理对象的功能
        System.out.println(customer.getOrders().getClass().getName());

        // 3. 可能会抛出org.hibernate.LazyInitializationException异常
//      session.close();
//      System.out.println(customer.getOrders().size());

        // 4. 在需要使用集合中元素的时候才进行初始化。
    }

    @Test
    public void testManyToOneUpdate() {
        // 2条SELECT语句,1条UPDATE语句
        Order order = (Order) session.get(Order.class, 1);
        order.getCustomer().setCustomerName("qiaob");
    }

    @Test
    public void testManyToOneUpdate2() {
        Customer customer = (Customer) session.get(Customer.class, 1);
        customer.getOrders().iterator().next().setOrderName("qiaobei");
    }

    @Test
    public void testManyToOneDelete() {
        // 在未设定级联关系的情况下,1的一端的对象有n的一端对象在引用,故其不能删除
        Customer customer = (Customer) session.get(Customer.class, 1);
        session.delete(customer);
    }

    @After
    public void destory() {
        transaction.commit();
        session.close();
        sessionFactory.close();
    }
}

3. set元素

java 不使用关联查询列表_Customer_04

3.1 inverse属性

  Hibernate中通过inverse属性来决定是由双向关联的哪一方来维护表与表之间的级联关系,默认情况下父子双方均会维护父子关系(保存操作时会多出UPDATE语句)。
  实现:通过设置inverse=true使当前方为被动方,即不维护级联关系。
  注意:在双向1-n关系中,将n的一方设置为主动方将有助于性能改善。

3.2 cascade属性

  了解:在对象关系映射文件中,用于映射持久化类之间关联关系的元素都有cascade属性,用于指定如何操纵与当前对象关联的其他对象.。

java 不使用关联查询列表_Test_05

3.3 在数据库中对集合排序

  order-by属性可使Hibernate在检索集合对象时,利用order by子句对集合进行排序,该属性中还可以加入SQL函数。

java 不使用关联查询列表_Test_06