- Hibernate:
- ///
- 双向关联就是有“一对多”和“多对一”两个关联组合而成德,在双向关联的两端都知道对方是谁。
- 下面就开始演示这种关联。
- 首先定义我们需要使用的POJO对象。
- public class Member
- {
- private String id;
- private String name;
- private Integer age;
- private Set<Order> orders = new HashSet<Order>();//我知道Order对象
- ……
- }
- public class Order
- {
- private Integer id;
- private String name;
- private String num;
- private Member member;//我知道Member对象
- ……
- }
- 两个POJO对应的映射文件分别为Member.hbm.xml和Order.hbm.xml。
- Member.hbm.xml
- <hibernate-mapping package="org.louis.domain">
- <class name="Member" table="TEST_MEMBER">
- <id name="id" column="ID">
- <generator class="uuid.hex"></generator>
- </id>
- <property name="age" column="AGE"></property>
- <property name="name" column="NAME"></property>
- <set name="orders" cascade="all" inverse="true">
- <!--设置inverse=”true”表示Member不在作为主控方,就是说不再由Member来维持关联关系,
- 而有Order方来维持关联关系,这样Order在持久化的时候会主动去获取Member的主键值来作为自己
- 的外键值。 在one-to-manay关联关系中,一般将manay端设置为主控方(inverse=”false”),
- 这样将有助于改善性能。默认情况下inverse=”false” -->
- <key column="MEMBER_ID"></key>
- <one-to-many class="Order"/>
- </set>
- </class>
- </hibernate-mapping>
- Order.hbm.xml
- <hibernate-mapping package="org.louis.domain">
- <class name="Order" table="TEST_ORDER">
- <id name="id" column="ID">
- <generator class="native"></generator>
- </id>
- <property name="name" column="NAME"></property>
- <property name="num" column="NUM"></property>
- <many-to-one name="member" class="Member" column="MEMBER_ID"></many-to-one>
- </hibernate-mapping>
- 下面我们来进行测试。
- a、插入数据
- public void insert()
- {
- Session session = HibernateSessionFactory.getSessionFactory().getCurrentSession();
- session.beginTransaction();
- Member m = new Member();
- m.setAge(24);
- m.setName("Louis");
- Order order = new Order();
- order.setName("order 1");
- order.setNum("order num 1");
- order.setMember(m);
- m.getOrders().add(order);
- session.save(m);
- session.getTransaction().commit();
- }
- 查看Hibernate在后台使用的SQL语句如下:
- Hibernate:
- insert
- into
- TEST_MEMBER
- (AGE, NAME, ID)
- values
- (?, ?, ?)
- Hibernate:
- insert
- into
- TEST_ORDER
- (NAME, NUM, MEMBER_ID)
- values
- (?, ?, ?)
- 仅仅就两条语句,明显比单向关联的时候少了一条更新语句(把Order的外键进行更新为Menber的主键值)。
- b、加载数据
- 和单向关联的区别在于,如果加载的是Order对象,那么它也知道和它关联的对象。
- c、删除数据
- 删除数据和单向关联时的删除数据的区别在于,对Member及它的关联对象Order加载后,不需要在更新Order的外键为NULL,而是紧跟着逐条删除Order,最后删除Member。
- ///
- Hibernate:
- ///
- 多对多关联是Hibernate中一种比较特殊的关联,它需要借助中间表来完成多对多信息的保存。多对多关联只有双向关联。
- 对于一个订单(Order)可以包含多种产品(Products),而对于每种产品可以存在于多个订单之中,是一个典型的多对多关系。
- 本次演示用到的两个POJO类如下:
- public class Order
- {
- private Integer id;
- private String name;
- private String num;
- private Set<Product> products = new HashSet<Product>();//
- ......
- }
- public class Product
- {
- private Integer id;
- private String name;
- private Set<Order> orders = new HashSet<Order>();//
- ......
- }
- 它们对应的映射文件分别为:
- Order.hbm.xml
- <hibernate-mapping package="org.louis.domain">
- <class name="Order" table="TEST_ORDER">
- <id name="id" column="ID">
- <generator class="native"></generator>
- </id>
- <property name="name" column="NAME"></property>
- <property name="num" column="NUM"></property>
- <set name="products" table="TEST_ORDER_PRODUCT" cascade="save-update" fetch="join">
- <key column="ORDER_ID"></key>
- <many-to-many class="Product" column="PRODUCT_ID"></many-to-many>
- </set>
- </class>
- </hibernate-mapping>
- Product.hbm.xml
- <hibernate-mapping package="org.louis.domain">
- <class name="Product" table="TEST_PRODUCT">
- <id name="id" column="ID">
- <generator class="native"></generator>
- </id>
- <property name="name" column="NAME"></property>
- <set name="orders" table="TEST_ORDER_PRODUCT" cascade="save-update" fetch="join">
- <key column="PRODUCT_ID"></key>
- <many-to-many class="Order" column="ORDER_ID"></many-to-many>
- </set>
- </class>
- </hibernate-mapping>
- 我们注意到,在两个文件中都有<set>节点,同时都指定了其table属性,这是两个对象的的中间表,在多对多关联中,
- 很多操作都是通过的中间表来完成的;<set>元素的子元素<key>指定了中间表中的外键,它对应于当前文件的对象的
- 主键,如Order.hbm.xml中,对应于Order的主键;<many-to-many>指定了关联的另一方和中间表的对应于另一方主键
- 的外键。
- 下面来做一些测试。
- a、插入数据
- public void insert()
- {
- Session session = HibernateSessionFactory.getSessionFactory().getCurrentSession();
- session.beginTransaction();
- Product p1 = new Product();
- p1.setName("p1");
- Product p2 = new Product();
- p2.setName("p2");
- Order order = (Order)session.load(Order.class, 3);
- // order.getProducts().add(p1);
- // order.getProducts().add(p2);
- p1.getOrders().add(order);
- p2.getOrders().add(order);
- session.save(p1);
- session.save(p2);
- session.getTransaction().commit();
- }
- 查看Hibernate在后台执行的SQL语句如下:
- Hibernate:
- select
- order0_.ID as ID6_1_,
- order0_.NAME as NAME6_1_,
- order0_.NUM as NUM6_1_,
- order0_.MEMBER_ID as MEMBER4_6_1_,
- products1_.ORDER_ID as ORDER1_3_,
- product2_.ID as PRODUCT2_3_,
- product2_.ID as ID8_0_,
- product2_.NAME as NAME8_0_
- from
- TEST_ORDER order0_
- left outer join
- TEST_ORDER_PRODUCT products1_
- on order0_.ID=products1_.ORDER_ID
- left outer join
- TEST_PRODUCT product2_
- on products1_.PRODUCT_ID=product2_.ID
- where
- order0_.ID=?
- Hibernate:
- insert
- into
- TEST_PRODUCT
- (NAME)
- values
- (?)
- Hibernate:
- insert
- into
- TEST_PRODUCT
- (NAME)
- values
- (?)
- Hibernate:
- insert
- into
- TEST_ORDER_PRODUCT
- (PRODUCT_ID, ORDER_ID)
- values
- (?, ?)
- Hibernate:
- insert
- into
- TEST_ORDER_PRODUCT
- (PRODUCT_ID, ORDER_ID)
- values
- (?, ?)
- 第一条是更加ID加载Order对象,同时将其关联对象也加载了。接着插入两个Product,然后用两条SQL语句向中间表里面插入反映关联关系的数据。
- 如果将Java代码修改为下面这样:
- public void insert()
- {
- Session session = HibernateSessionFactory.getSessionFactory().getCurrentSession();
- session.beginTransaction();
- Product p1 = new Product();
- p1.setName("p1");
- Product p2 = new Product();
- p2.setName("p2");
- Order order = (Order)session.load(Order.class, 3);
- order.getProducts().add(p1);
- order.getProducts().add(p2);
- session.save(order);
- session.getTransaction().commit();
- }
- 我们发现Hibernate执行的SQL和上面的一样,由此看来,在这种多对多情况下,无论让谁来维护关系都是可以的,因为默认
- 情况下inverse="false";
- 我们再来看当设置Product.hbm.xml中的<set>节点属性inverse="true", 这表示对于两个对象的关联关系的维护有Order来做。
- 我们使用下面的代码来测试一下:
- public void insert()
- {
- Session session = HibernateSessionFactory.getSessionFactory().getCurrentSession();
- session.beginTransaction();
- Product p1 = new Product();
- p1.setName("p1");
- Product p2 = new Product();
- p2.setName("p2");
- Order order = (Order)session.load(Order.class, 3);
- // order.getProducts().add(p1);
- // order.getProducts().add(p2);
- p1.getOrders().add(order);
- p2.getOrders().add(order);
- session.save(p1);
- session.save(p2);
- session.getTransaction().commit();
- }
- 在这段代码中,明显可以看出关联关系的维护交给了Product来做了,这个Product.hbm.xml中配置的方式相矛盾,因此
- 必然会在保存数据的时候出现问题。运行这段代码,查看后台HIbernate使用的SQL语句为:
- Hibernate:
- select
- order0_.ID as ID6_1_,
- order0_.NAME as NAME6_1_,
- order0_.NUM as NUM6_1_,
- order0_.MEMBER_ID as MEMBER4_6_1_,
- products1_.ORDER_ID as ORDER1_3_,
- product2_.ID as PRODUCT2_3_,
- product2_.ID as ID8_0_,
- product2_.NAME as NAME8_0_
- from
- TEST_ORDER order0_
- left outer join
- TEST_ORDER_PRODUCT products1_
- on order0_.ID=products1_.ORDER_ID
- left outer join
- TEST_PRODUCT product2_
- on products1_.PRODUCT_ID=product2_.ID
- where
- order0_.ID=?
- Hibernate:
- insert
- into
- TEST_PRODUCT
- (NAME)
- values
- (?)
- Hibernate:
- insert
- into
- TEST_PRODUCT
- (NAME)
- values
- (?)
- 从这段SQL代码可以看出,只是进行了Product数据的插入,并没有对反映Order和Product关系的中间表进行数据
- 插入,看来配置文件中的设定由Order来维护关联关系起了一定的作用(为什么是一定的作用而不是完全的呢?这
- 是因为还没有进行必要的测试,只是在这里反映出Product已经不能维护关联关系了)。
- 下面我们将Java代码改动一下如下:
- public void insert()
- {
- Session session = HibernateSessionFactory.getSessionFactory().getCurrentSession();
- session.beginTransaction();
- Product p1 = new Product();
- p1.setName("p1");
- Product p2 = new Product();
- p2.setName("p2");
- Order order = (Order)session.load(Order.class, 3);
- order.getProducts().add(p1);
- order.getProducts().add(p2);
- session.save(order);
- session.getTransaction().commit();
- }
- 再次运行,查看后台SQL语句如下:
- Hibernate:
- select
- order0_.ID as ID6_1_,
- order0_.NAME as NAME6_1_,
- order0_.NUM as NUM6_1_,
- order0_.MEMBER_ID as MEMBER4_6_1_,
- products1_.ORDER_ID as ORDER1_3_,
- product2_.ID as PRODUCT2_3_,
- product2_.ID as ID8_0_,
- product2_.NAME as NAME8_0_
- from
- TEST_ORDER order0_
- left outer join
- TEST_ORDER_PRODUCT products1_
- on order0_.ID=products1_.ORDER_ID
- left outer join
- TEST_PRODUCT product2_
- on products1_.PRODUCT_ID=product2_.ID
- where
- order0_.ID=?
- Hibernate:
- insert
- into
- TEST_PRODUCT
- (NAME)
- values
- (?)
- Hibernate:
- insert
- into
- TEST_PRODUCT
- (NAME)
- values
- (?)
- Hibernate:
- insert
- into
- TEST_ORDER_PRODUCT
- (ORDER_ID, PRODUCT_ID)
- values
- (?, ?)
- Hibernate:
- insert
- into
- TEST_ORDER_PRODUCT
- (ORDER_ID, PRODUCT_ID)
- values
- (?, ?)
- 显然,和没有使用任何inverse="true"时的情况一摸一样,是正确的,也就是说现在是由Order来维护两个关联对象的关联关系的。
- 对于多对多关联来说,可以在任何一端来设置为主控方,也可都不设,那么两端都可以维护关联关系(并且,我们发现和设定一方
- 为主控方在性能上没有什么变化)。
- 如果两端都设定为inverse="true"的话呢?
- 我们来做个测试,将两个映射文件的<set>节点的inverse属性都设置为true。对于Order来说,是让Product作为主控方;对于
- Product来说让Order作为主控方。
- 先用下面代码测试:
- public void insert()
- {
- Session session = HibernateSessionFactory.getSessionFactory().getCurrentSession();
- session.beginTransaction();
- Product p1 = new Product();
- p1.setName("p1");
- Product p2 = new Product();
- p2.setName("p2");
- Order order = (Order)session.load(Order.class, 3);
- // order.getProducts().add(p1);
- // order.getProducts().add(p2);
- p1.getOrders().add(order);
- p2.getOrders().add(order);
- session.save(p1);
- session.save(p2);
- session.getTransaction().commit();
- }
- 查看后台SQL语句如下:
- Hibernate:
- select
- order0_.ID as ID6_1_,
- order0_.NAME as NAME6_1_,
- order0_.NUM as NUM6_1_,
- order0_.MEMBER_ID as MEMBER4_6_1_,
- products1_.ORDER_ID as ORDER1_3_,
- product2_.ID as PRODUCT2_3_,
- product2_.ID as ID8_0_,
- product2_.NAME as NAME8_0_
- from
- TEST_ORDER order0_
- left outer join
- TEST_ORDER_PRODUCT products1_
- on order0_.ID=products1_.ORDER_ID
- left outer join
- TEST_PRODUCT product2_
- on products1_.PRODUCT_ID=product2_.ID
- where
- order0_.ID=?
- Hibernate:
- insert
- into
- TEST_PRODUCT
- (NAME)
- values
- (?)
- Hibernate:
- insert
- into
- TEST_PRODUCT
- (NAME)
- values
- (?)
- 看来没有对中间表进行数据插入。
- 使用下面代码来测试:
- public void insert()
- {
- Session session = HibernateSessionFactory.getSessionFactory().getCurrentSession();
- session.beginTransaction();
- Product p1 = new Product();
- p1.setName("p1");
- Product p2 = new Product();
- p2.setName("p2");
- Order order = (Order)session.load(Order.class, 3);
- order.getProducts().add(p1);
- order.getProducts().add(p2);
- session.save(order);
- session.getTransaction().commit();
- }
- 查看后台的SQL语句如下:
- Hibernate:
- select
- order0_.ID as ID6_1_,
- order0_.NAME as NAME6_1_,
- order0_.NUM as NUM6_1_,
- order0_.MEMBER_ID as MEMBER4_6_1_,
- products1_.ORDER_ID as ORDER1_3_,
- product2_.ID as PRODUCT2_3_,
- product2_.ID as ID8_0_,
- product2_.NAME as NAME8_0_
- from
- TEST_ORDER order0_
- left outer join
- TEST_ORDER_PRODUCT products1_
- on order0_.ID=products1_.ORDER_ID
- left outer join
- TEST_PRODUCT product2_
- on products1_.PRODUCT_ID=product2_.ID
- where
- order0_.ID=?
- Hibernate:
- insert
- into
- TEST_PRODUCT
- (NAME)
- values
- (?)
- Hibernate:
- insert
- into
- TEST_PRODUCT
- (NAME)
- values
- (?)
- 比较两次的结果,发现一摸一样。看来如果两端都设置为主控方,是不能对中间表进行数据保存的。
- b、数据加载
- 数据加载就不再演示了,也就是默认采用左外连接的方式加载数据,能够将Order和它的关联对象
- Product都加载,当然是通过中间表;对于Product来说也是这样。
- c、删除数据
- 删除数据也不再演示,无论删除Order还是Product,都是简单的删除自己,而不会删除关联的对象
- ,这是因为设定了cascade="save-update"
Hibernate一对多(双向)及多对多
最新推荐文章于 2021-10-19 11:47:50 发布