在领域模型中,类与类之间最普遍的关系是关联关系,而在UML中关联是有方向的。以Customer类和Order类为例,从Order到Customer的关联是多对一关联,而从Customer到Order的关联则是一对多关联,两者均属于单向关联。
1. 单向n-1关联
概念:单向n-1关联只需从n的一端可以访问1的一端即可。
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的一端,反之依然。
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集合声明
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元素
3.1 inverse属性
Hibernate中通过inverse属性来决定是由双向关联的哪一方来维护表与表之间的级联关系,默认情况下父子双方均会维护父子关系(保存操作时会多出UPDATE语句)。
实现:通过设置inverse=true使当前方为被动方,即不维护级联关系。
注意:在双向1-n关系中,将n的一方设置为主动方将有助于性能改善。
3.2 cascade属性
了解:在对象关系映射文件中,用于映射持久化类之间关联关系的元素都有cascade属性,用于指定如何操纵与当前对象关联的其他对象.。
3.3 在数据库中对集合排序
order-by属性可使Hibernate在检索集合对象时,利用order by子句对集合进行排序,该属性中还可以加入SQL函数。