[原创]java WEB学习笔记83:Hibernate学习之路---双向 1-n介绍,关键点解释,代码实现,set属性介绍(inverse,cascade ,order-by )
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[原创]java WEB学习笔记83:Hibernate学习之路---双向 1-n介绍,关键点解释,代码实现,set属性介绍(inverse,cascade ,order-by )相关的知识,希望对你有一定的参考价值。
本博客的目的:①总结自己的学习过程,相当于学习笔记 ②将自己的经验分享给大家,相互学习,互相交流,不可商用
内容难免出现问题,欢迎指正,交流,探讨,可以留言,也可以通过以下方式联系。
本人互联网技术爱好者,互联网技术发烧友
微博:伊直都在0221
QQ:951226918
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
1.双向 1-n
1)域模型:从 Order 到 Customer 的多对一双向关联需要在Order 类中定义一个 Customer 属性, 而在 Customer 类中需定义存放 Order 对象的集合属性
2)关系数据模型:ORDERS 表中的 CUSTOMER_ID 参照 CUSTOMER 表的主键
2.单向 n-1 关键点解释
1)当 Session 从数据库中加载 Java 集合时, 创建的是 Hibernate 内置集合类的实例, 因此在持久化类中定义集合属性时必须把属性声明为 Java 接口类型
> Hibernate 的内置集合类具有集合代理功能, 支持延迟检索策略
> 事实上, Hibernate 的内置集合类封装了 JDK 中的集合类, 这使得 Hibernate 能够对缓存中的集合对象进行脏检查, 按照集合对象的状态来同步更新数据库。
2)在定义集合属性时, 通常把它初始化为集合实现类的一个实例. 这样可以提高程序的健壮性, 避免应用程序访问取值为 null 的集合的方法抛出 NullPointerException
3)Hibernate 使用 <set> 元素来映射 set 类型的属性
1 <!-- 在 1 端 -->
2 <!-- 映射 1对 n 的那个集合属性 -->
3 <!-- set:映射set类型的属性,
4 table:set 中的元素中的记录放在哪一个数据表中,该值 需要 n 端的表的名字一致
5 key:指定n 端 表中的外键列的名字
6 -->
7 <set name="orders" table="ORDERS">
8 <key column="CUSTOMER_ID"></key>
9 <!-- 指定映射类型 1 - n -->
10 <one-to-many class="Order"/>
11 </set>
3.代码
1 <?xml version="1.0" encoding="UTF-8"?> 2 <!DOCTYPE hibernate-configuration PUBLIC 3 "-//Hibernate/Hibernate Configuration DTD 3.0//EN" 4 "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> 5 <hibernate-configuration> 6 <session-factory> 7 <!-- hibernate 连接数据库的基本信息 --> 8 <property name="connection.username">root</property> 9 <property name="connection.password">zhangzhen</property> 10 <property name="connection.driver_class">com.mysql.jdbc.Driver</property> 11 <property name="connection.url">jdbc:mysql:///hibernate</property> 12 13 14 <!-- 配置hibernate 的节本信息 --> 15 <!-- hibernate 所使用的数据库方言 --> 16 <!--<property name="dialect">org.hibernate.dialect.MySQLInnoDBDialect</property>--> 17 <property name="dialect">org.hibernate.dialect.MySQLDialect</property> 18 <!-- 执行操作时是否在控制台打印SQL --> 19 <property name="show_sql">true</property> 20 21 <!-- 是否都SQL 进行格式化 --> 22 <property name="format_sql">true</property> 23 24 25 <!-- 指定自动生成数据表的策略 --> 26 <property name="hbm2ddl.auto">update</property> 27 28 <!-- 设置hibernate 的事务隔离级别 --> 29 <property name="connection.isolation">2</property> 30 31 32 <!-- 配置c3p0 --> 33 <property name="hibernate.c3p0.max_size">10</property> 34 <property name="hibernate.c3p0.min_size">5</property> 35 <property name="c3p0.acquire_increment">2</property> 36 <property name="c3p0.idle_test_period">2000</property> 37 <property name="c3p0.timeout">2000</property> 38 <property name="c3p0.max_statements">10</property> 39 40 41 <!-- 对于mysql 无效,对于oracle 有效 --> 42 <!-- 设定JDBC 的Statement 读取数据的时候每次从数据库中取出的记录的条数 --> 43 <property name="hibernate.jdbc.fetch_size">100</property> 44 45 <!-- 设置数据库进行批量删除,批量更新和批量插入的时候的大小 --> 46 <property name="hibernate.jdbc.batch_size">30</property> 47 48 <!-- 指定关联的 .hbm.xml 文件 --> 49 <!-- 50 <mapping resource="hibernate/helloworld/News.hbm.xml"/> 51 <mapping resource="hibernate/helloworld/Worker.hbm.xml"/> 52 53 <mapping resource="com/jason/hibernate/entities/n21/Customer.hbm.xml"/> 54 <mapping resource="com/jason/hibernate/entities/n21/Order.hbm.xml"/> 55 --> 56 57 <mapping resource="com/jason/hibernate/entities/n21/both/Customer.hbm.xml"/> 58 <mapping resource="com/jason/hibernate/entities/n21/both/Order.hbm.xml"/> 59 60 61 62 63 </session-factory> 64 65 </hibernate-configuration>
Customer.hbm.xml
1 <?xml version="1.0"?>
2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4 <!-- Generated 2016-10-5 17:43:02 by Hibernate Tools 3.4.0.CR1 -->
5 <hibernate-mapping package="com.jason.hibernate.entities.n21.both">
6
7 <class name="Customer" table="CUSTOMERS">
8
9 <id name="customerId" type="java.lang.Integer">
10 <column name="CUSTOMER_ID" />
11 <generator class="native" />
12 </id>
13
14 <property name="customerName" type="java.lang.String">
15 <column name="CUSTOMER_NAME" />
16 </property>
17
18
19
20 <!-- 在 1 端 -->
21 <!-- 映射 1对 n 的那个集合属性 -->
22 <!-- set:映射set类型的属性,
23 table:set 中的元素中的记录放在哪一个数据表中,该值 需要 n 端的表的名字一致
24 key:指定n 端 表中的外键列的名字
25 -->
26 <set name="orders" table="ORDERS">
27 <key column="CUSTOMER_ID"></key>
28 <!-- 指定映射类型 1 - n -->
29 <one-to-many class="Order"/>
30 </set>
31
32
33 </class>
34
35 </hibernate-mapping>
Customer
1 package com.jason.hibernate.entities.n21.both;
2
3 import java.util.HashSet;
4 import java.util.Set;
5
6 public class Customer {
7
8 private Integer customerId;
9 private String customerName;
10
11 /*
12 * 1.orders 初始化后,防止发生空指针异常
13 * 2.声明集合类型时,需使用接口类型,因为hibernate 在获取集合类型时,返回的是hibernate 内置的集合类型,而不是javaSE 的实现
14 */
15 private Set<Order> orders = new HashSet<>();
16 public Set<Order> getOrders() {
17 return orders;
18 }
19
20 public void setOrders(Set<Order> orders) {
21 this.orders = orders;
22 }
23
24 public Integer getCustomerId() {
25 return customerId;
26 }
27
28 public void setCustomerId(Integer customerId) {
29 this.customerId = customerId;
30 }
31
32 public String getCustomerName() {
33 return customerName;
34 }
35
36 public void setCustomerName(String customerName) {
37 this.customerName = customerName;
38 }
39
40 @Override
41 public String toString() {
42 return "Customer [customerId=" + customerId + ", customerName="
43 + customerName + "]";
44 }
45
46
47 }
Order.hbm.xml
1 <?xml version="1.0"?>
2 <!DOCTYPE hibernate-mapping PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
3 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
4 <!-- Generated 2016-10-5 17:43:02 by Hibernate Tools 3.4.0.CR1 -->
5 <hibernate-mapping package="com.jason.hibernate.entities.n21.both">
6 <class name="Order" table="ORDERS">
7
8 <id name="orderId" type="java.lang.Integer">
9 <column name="ORDER_ID" />
10 <generator class="native" />
11 </id>
12
13 <property name="orderName" type="java.lang.String">
14 <column name="ORDER_NAME" />
15 </property>
16
17 <!-- 映射 多对一 关联关系 -->
18 <!--
19 name: ‘n‘端 关联 ‘1‘端的属性的名字
20 class: ‘1‘端 属性对应的类名
21 colum: ‘1‘端 在 ‘n‘端 对应的数据表中的外键的名字
22 -->
23 <many-to-one name="customer" class="Customer">
24 <column name="CUSTOMER_ID" />
25 </many-to-one>
26
27
28
29 </class>
30
31 </hibernate-mapping>
Order
1 package com.jason.hibernate.entities.n21.both;
2
3 public class Order {
4
5 private Integer orderId;
6 private String orderName;
7
8 private Customer customer;
9
10 public Integer getOrderId() {
11 return orderId;
12 }
13
14 public void setOrderId(Integer orderId) {
15 this.orderId = orderId;
16 }
17
18 public String getOrderName() {
19 return orderName;
20 }
21
22 public void setOrderName(String orderName) {
23 this.orderName = orderName;
24 }
25
26 public Customer getCustomer() {
27 return customer;
28 }
29
30 public void setCustomer(Customer customer) {
31 this.customer = customer;
32 }
33
34 @Override
35 public String toString() {
36 return "Order [orderId=" + orderId + ", orderName=" + orderName
37 + ", customer=" + customer + "]";
38 }
39
40
41 }
HibernateTest.java
1 package com.jason.hibernate.entities.n21.both;
2
3 import hibernate.helloworld.News;
4 import hibernate.helloworld.Pay;
5 import hibernate.helloworld.Worker;
6
7 import java.io.FileInputStream;
8 import java.io.FileNotFoundException;
9 import java.io.InputStream;
10 import java.sql.Blob;
11 import java.sql.Connection;
12 import java.sql.Date;
13 import java.sql.SQLException;
14
15 import org.hibernate.Hibernate;
16 import org.hibernate.LazyInitializationException;
17 import org.hibernate.Session;
18 import org.hibernate.SessionFactory;
19 import org.hibernate.Transaction;
20 import org.hibernate.cfg.Configuration;
21 import org.hibernate.jdbc.Work;
22 import org.hibernate.service.ServiceRegistry;
23 import org.hibernate.service.ServiceRegistryBuilder;
24 import org.junit.After;
25 import org.junit.Before;
26 import org.junit.Test;
27 import org.omg.CORBA.ORB;
28
29 public class HibernateTest {
30
31 private SessionFactory sessionFactory;
32 private Session session;
33 private Transaction transaction;
34
35 @Test
36 public void test() {
37
38 // 1. 创建一个SessionFatory 对象
39 SessionFactory sessionFactory = null;
40
41 // 1) 创建Configuration 对象:对应hibernate 的基本配置信息 和 对象关系映射信息
42 Configuration configuration = new Configuration().configure();
43
44 // 2) 创建一个ServiceRegistry 对象:hibernate 4.x 新天添加的对象。
45 // hibernate 的任何配置 和 服务都需要在该对象中注册后才有效
46 ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
47 .applySettings(configuration.getProperties())
48 .buildServiceRegistry();
49
50 // sessionFactory = configuration.buildSessionFactory();
51 sessionFactory = configuration.buildSessionFactory(serviceRegistry);
52
53 // 2. 创建一个session 对象
54 Session session = sessionFactory.openSession();
55
56 // 3. 开启事物
57 Transaction transaction = session.beginTransaction();
58
59 // 4.执行保存操作
60 News news = new News("java", "jason", new Date(
61 new java.util.Date().getTime()));
62 session.save(news);
63
64 // 5.提交事物
65 transaction.commit();
66 // 6.关闭session
67 session.close();
68 // 7.关闭SessionFactory 对象
69 sessionFactory.close();
70 }
71
72 // 创建上述三个对象
73 @Before
74 public void init() {
75 Configuration configuration = new Configuration().configure();
76 ServiceRegistry serviceRegistry = new ServiceRegistryBuilder()
77 .applySettings(configuration.getProperties())
78 .buildServiceRegistry();
79
80 sessionFactory = configuration.buildSessionFactory(serviceRegistry);
81
82 session = sessionFactory.openSession();
83
84 transaction = session.beginTransaction();
85 }
86
87 // 关闭上述三个对象
88 @After
89 public void destroy() {
90 transaction.commit();
91 session.close();
92 sessionFactory.close();
93 }
94
95 @Test
96 public void testDelete() {
97 // 在不设定级联关系的情况下,且 1 端的对象 有 n端的对象在引用,不能直接删除1 端的对象
98 Customer customer = (Customer) session.get(Customer.class, 1);
99 session.delete(customer);
100 }
101
102
103 @Test
104 public void testUpdate2(){
105 Customer customer = (Customer) session.get(Customer.class, 1);
106 customer.getOrders().iterator().next().setOrderName("BBBB");
107 }
108 @Test
109 public void testUpdate() {
110 Order order = (Order) session.get(Order.class, 1);
111 order.getCustomer().setCustomerName("tom2");
112
113 }
114
115 @Test
116 public void testOne2ManyGet() {
117
118 //1.对n 的一端的集合使用延迟加载
119 Customer customer = (Customer) session.get(Customer.class, 1);
120 System.out.println(customer.getCustomerName());
121
122 //2.返回n端的集合是hibernate 的内置集合类型。该类型具有延迟加载和存放代理对象的功能
123 System.out.println(customer.getOrders().getClass());
124
125 //3.可能抛出 LazyInitializationException 异常
126
127 //4.再需要使用集合中元素的时候进行初始化
128
129
130
131 }
132
133
134 @Test
135 public void testManyToOneGet() {
136 // 1.若查询n 的一端的对象,则默认情况下,只查询了n 的一端的对象,而没有查询关联的1 端的对象
137 // 延迟加载
138 Order order = (Order) session.get(Order.class, 1);
139 System.out.println(order);
140
141 // 2.在需要使用到关联的对象,才发送对应的sql 语句
142 Customer customer = order.getCustomer();
143 System.out.println(customer);
144
145 // 3.获取order对象,默认情况,其关联的Customer 对象是一个代理对象
146 }
147
148 @Test
149 public void testManyToOneSave() {
150 Customer customer = new Customer();
151 customer.setCustomerName("AA");
152
153 Order order1 = new Order();
154 order1.setOrderName("order-1");
155
156 Order order2 = new Order();
157 order2.setOrderName("order-2");
158
159 // 设定关联关系
160 order1.setCustomer(customer);
161 order2.setCustomer(customer);
162
163 customer.getOrders().add(order1);
164 customer.getOrders().add(order2);
165
166 // 执行svae操作:先插入 Customer,再插入Order,3条insert,2条update
167 // 因为1端 和 n端 都维护关联关系,所以多出四条
168 // 可以在1 端的set节点,指定inverse=true ,来使1端放弃维护关联关系
169 // 建议先插入1 的一端,后插入 n端
170 //
171 session.save(customer);
172 session.save(order1);
173 session.save(order2);
174
175 // 先插入Order,再插入Customer,3条insert,4条update
176 // session.save(order1);
177 // session.save(order2);
178 //
179 // session.save(customer);
180
181 }
182
183 }
关于set标签
1) <set> 元素来映射持久化类的 set 类型的属性 name: 设定待映射的持久化类的属性的
2)<key> 元素设定与所关联的持久化类对应的表的外键 column: 指定关联表的外键名
3)<one-to-many> 元素设定集合属性中所关联的持久化类 class: 指定关联的持久化类的类名
4)inverse 属性
① 在hibernate中通过对 inverse 属性的来决定是由双向关联的哪一方来维护表和表之间的关系. inverse = false 的为主动方,inverse = true 的为被动方, 由主动方负责维护关联关系
② 在没有设置 inverse=true 的情况下,父子两边都维护父子 关系
③ 在 1-n 关系中,将 n 方设为主控方将有助于性能改善(如果要国家元首记住全国人民的名字,不是太可能,但要让全国人民知道国家元首,就容易的多)
④ 在 1-N 关系中,若将 1 方设为主控方
>会额外多出 update 语句。
>插入数据时无法同时插入外键列,因而无法为外键列添加非空约束
5)cascade 属性(了解)
① 在对象 – 关系映射文件中, 用于映射持久化类之间关联关系的元素, <set>, <many-to-one> 和 <one-to-one> 都有一个 cascade 属性, 它用于指定如何操纵与当前对象关联的其他对象.
6)order-by 属性
① <set> 元素有一个 order-by 属性, 如果设置了该属性, 当 Hibernate 通过 select 语句到数据库中检索集合对象时, 利用 order by 子句进行排序。设置的是表的字段名,而不是持久化类的属性名
② order-by 属性中还可以加入 SQL 函数
说明:
1.在 n 端的 .hbm.xml 中映射关联关系
1 <!-- 映射 多对一 关联关系 -->
2 <!--
3 name: ‘n‘端 关联 ‘1‘端的属性的名字
4 class: ‘1‘端 属性对应的类名
5 colum: ‘1‘端 在 ‘n‘端 对应的数据表中的外键的名字
6 -->
7 <many-to-one name="customer" class="Customer">
8 <column name="CUSTOMER_ID" />
9 </many-to-one>
以上是关于[原创]java WEB学习笔记83:Hibernate学习之路---双向 1-n介绍,关键点解释,代码实现,set属性介绍(inverse,cascade ,order-by )的主要内容,如果未能解决你的问题,请参考以下文章
[原创]java WEB学习笔记10:GenericServlet
[原创]java WEB学习笔记18:java EE 中的MVC 设计模式
[原创]java WEB学习笔记08:ServletResponse & HttpServletResponse
[原创]java WEB学习笔记20:案例完整实践(part 1)---MVC架构分析