Hibernate一对多(双向)及多对多

  1. Hibernate:

  2. ///

  3. 双向关联就是有“一对多”和“多对一”两个关联组合而成德,在双向关联的两端都知道对方是谁。

  4. 下面就开始演示这种关联。

  5. 首先定义我们需要使用的POJO对象。

  6. public class Member

  7. {

  8. private String id;

  9. private String name;

  10. private Integer age;

  11. private Set<Order> orders = new HashSet<Order>();//我知道Order对象

  12. ……

  13. }

  14. public class Order

  15. {

  16. private Integer id;

  17. private String name;

  18. private String num;

  19. private Member member;//我知道Member对象

  20. ……

  21. }

  22. 两个POJO对应的映射文件分别为Member.hbm.xml和Order.hbm.xml。

  23. Member.hbm.xml

  24. <hibernate-mapping package="org.louis.domain">

  25. <class name="Member" table="TEST_MEMBER">

  26. <id name="id" column="ID">

  27. <generator class="uuid.hex"></generator>

  28. </id>

  29. <property name="age" column="AGE"></property>

  30. <property name="name" column="NAME"></property>

  31. <set name="orders" cascade="all" inverse="true">

  32. <!--设置inverse\=”true”表示Member不在作为主控方,就是说不再由Member来维持关联关系,
  33. 而有Order方来维持关联关系,这样Order在持久化的时候会主动去获取Member的主键值来作为自己

  34. 的外键值。 在one-to-manay关联关系中,一般将manay端设置为主控方(inverse=”false”),

  35. 这样将有助于改善性能。默认情况下inverse=”false” -->

  36. <key column="MEMBER_ID"></key>

  37. <one-to-many class="Order"/>

  38. </set>

  39. </class>

  40. </hibernate-mapping>

  41. Order.hbm.xml

  42. <hibernate-mapping package="org.louis.domain">

  43. <class name="Order" table="TEST_ORDER">

  44. <id name="id" column="ID">

  45. <generator class="native"></generator>

  46. </id>

  47. <property name="name" column="NAME"></property>

  48. <property name="num" column="NUM"></property>

  49. <many-to-one name="member" class="Member" column="MEMBER_ID"></many-to-one>

  50. </hibernate-mapping>

  51. 下面我们来进行测试。

  52. a、插入数据

  53. public void insert()

  54. {

  55. Session session = HibernateSessionFactory.getSessionFactory().getCurrentSession();

  56. session.beginTransaction();

  57. Member m = new Member();

  58. m.setAge(24);

  59. m.setName("Louis");

  60. Order order = new Order();

  61. order.setName("order 1");

  62. order.setNum("order num 1");

  63. order.setMember(m);

  64. m.getOrders().add(order);

  65. session.save(m);

  66. session.getTransaction().commit();

  67. }

  68. 查看Hibernate在后台使用的SQL语句如下:

  69. Hibernate:

  70. insert

  71. into

  72. TEST_MEMBER

  73. (AGE, NAME, ID)

  74. values

  75. (?, ?, ?)

  76. Hibernate:

  77. insert

  78. into

  79. TEST_ORDER

  80. (NAME, NUM, MEMBER_ID)

  81. values

  82. (?, ?, ?)

  83. 仅仅就两条语句,明显比单向关联的时候少了一条更新语句(把Order的外键进行更新为Menber的主键值)。

  84. b、加载数据

  85. 和单向关联的区别在于,如果加载的是Order对象,那么它也知道和它关联的对象。

  86. c、删除数据

  87. 删除数据和单向关联时的删除数据的区别在于,对Member及它的关联对象Order加载后,不需要在更新Order的外键为NULL,而是紧跟着逐条删除Order,最后删除Member。

  88. ///

  89. Hibernate:

  90. ///

  91. 多对多关联是Hibernate中一种比较特殊的关联,它需要借助中间表来完成多对多信息的保存。多对多关联只有双向关联。

  92. 对于一个订单(Order)可以包含多种产品(Products),而对于每种产品可以存在于多个订单之中,是一个典型的多对多关系。

  93. 本次演示用到的两个POJO类如下:

  94. public class Order

  95. {

  96. private Integer id;

  97. private String name;

  98. private String num;

  99. private Set<Product> products = new HashSet<Product>();//

  100. ......

  101. }

  102. public class Product

  103. {

  104. private Integer id;

  105. private String name;

  106. private Set<Order> orders = new HashSet<Order>();//

  107. ......

  108. }

  109. 它们对应的映射文件分别为:

  110. Order.hbm.xml

  111. <hibernate-mapping package="org.louis.domain">

  112. <class name="Order" table="TEST_ORDER">

  113. <id name="id" column="ID">

  114. <generator class="native"></generator>

  115. </id>

  116. <property name="name" column="NAME"></property>

  117. <property name="num" column="NUM"></property>

  118. <set name="products" table="TEST_ORDER_PRODUCT" cascade="save-update" fetch="join">

  119. <key column="ORDER_ID"></key>

  120. <many-to-many class="Product" column="PRODUCT_ID"></many-to-many>

  121. </set>

  122. </class>

  123. </hibernate-mapping>

  124. Product.hbm.xml

  125. <hibernate-mapping package="org.louis.domain">

  126. <class name="Product" table="TEST_PRODUCT">

  127. <id name="id" column="ID">

  128. <generator class="native"></generator>

  129. </id>

  130. <property name="name" column="NAME"></property>

  131. <set name="orders" table="TEST_ORDER_PRODUCT" cascade="save-update" fetch="join">

  132. <key column="PRODUCT_ID"></key>

  133. <many-to-many class="Order" column="ORDER_ID"></many-to-many>

  134. </set>

  135. </class>

  136. </hibernate-mapping>

  137. 我们注意到,在两个文件中都有<set>节点,同时都指定了其table属性,这是两个对象的的中间表,在多对多关联中,

  138. 很多操作都是通过的中间表来完成的;<set>元素的子元素<key>指定了中间表中的外键,它对应于当前文件的对象的

  139. 主键,如Order.hbm.xml中,对应于Order的主键;<many-to-many>指定了关联的另一方和中间表的对应于另一方主键

  140. 的外键。

  141. 下面来做一些测试。

  142. a、插入数据

  143. public void insert()

  144. {

  145. Session session = HibernateSessionFactory.getSessionFactory().getCurrentSession();

  146. session.beginTransaction();

  147. Product p1 = new Product();

  148. p1.setName("p1");

  149. Product p2 = new Product();

  150. p2.setName("p2");

  151. Order order = (Order)session.load(Order.class, 3);

  152. //      order.getProducts().add(p1);

  153. //      order.getProducts().add(p2);

  154. p1.getOrders().add(order);

  155. p2.getOrders().add(order);

  156. session.save(p1);

  157. session.save(p2);

  158. session.getTransaction().commit();

  159. }

  160. 查看Hibernate在后台执行的SQL语句如下:

  161. Hibernate:

  162. select

  163. order0_.ID as ID6_1_,

  164. order0_.NAME as NAME6_1_,

  165. order0_.NUM as NUM6_1_,

  166. order0_.MEMBER_ID as MEMBER4_6_1_,

  167. products1_.ORDER_ID as ORDER1_3_,

  168. product2_.ID as PRODUCT2_3_,

  169. product2_.ID as ID8_0_,

  170. product2_.NAME as NAME8_0_

  171. from

  172. TEST_ORDER order0_

  173. left outer join

  174. TEST_ORDER_PRODUCT products1_

  175. on order0_.ID=products1_.ORDER_ID

  176. left outer join

  177. TEST_PRODUCT product2_

  178. on products1_.PRODUCT_ID=product2_.ID

  179. where

  180. order0_.ID=?

  181. Hibernate:

  182. insert

  183. into

  184. TEST_PRODUCT

  185. (NAME)

  186. values

  187. (?)

  188. Hibernate:

  189. insert

  190. into

  191. TEST_PRODUCT

  192. (NAME)

  193. values

  194. (?)

  195. Hibernate:

  196. insert

  197. into

  198. TEST_ORDER_PRODUCT

  199. (PRODUCT_ID, ORDER_ID)

  200. values

  201. (?, ?)

  202. Hibernate:

  203. insert

  204. into

  205. TEST_ORDER_PRODUCT

  206. (PRODUCT_ID, ORDER_ID)

  207. values

  208. (?, ?)

  209. 第一条是更加ID加载Order对象,同时将其关联对象也加载了。接着插入两个Product,然后用两条SQL语句向中间表里面插入反映关联关系的数据。

  210. 如果将Java代码修改为下面这样:

  211. public void insert()

  212. {

  213. Session session = HibernateSessionFactory.getSessionFactory().getCurrentSession();

  214. session.beginTransaction();

  215. Product p1 = new Product();

  216. p1.setName("p1");

  217. Product p2 = new Product();

  218. p2.setName("p2");

  219. Order order = (Order)session.load(Order.class, 3);

  220. order.getProducts().add(p1);

  221. order.getProducts().add(p2);

  222. session.save(order);

  223. session.getTransaction().commit();

  224. }

  225. 我们发现Hibernate执行的SQL和上面的一样,由此看来,在这种多对多情况下,无论让谁来维护关系都是可以的,因为默认

  226. 情况下inverse="false";

  227. 我们再来看当设置Product.hbm.xml中的<set>节点属性inverse="true", 这表示对于两个对象的关联关系的维护有Order来做。

  228. 我们使用下面的代码来测试一下:

  229. public void insert()

  230. {

  231. Session session = HibernateSessionFactory.getSessionFactory().getCurrentSession();

  232. session.beginTransaction();

  233. Product p1 = new Product();

  234. p1.setName("p1");

  235. Product p2 = new Product();

  236. p2.setName("p2");

  237. Order order = (Order)session.load(Order.class, 3);

  238. //      order.getProducts().add(p1);

  239. //      order.getProducts().add(p2);

  240. p1.getOrders().add(order);

  241. p2.getOrders().add(order);

  242. session.save(p1);

  243. session.save(p2);

  244. session.getTransaction().commit();

  245. }

  246. 在这段代码中,明显可以看出关联关系的维护交给了Product来做了,这个Product.hbm.xml中配置的方式相矛盾,因此

  247. 必然会在保存数据的时候出现问题。运行这段代码,查看后台HIbernate使用的SQL语句为:

  248. Hibernate:

  249. select

  250. order0_.ID as ID6_1_,

  251. order0_.NAME as NAME6_1_,

  252. order0_.NUM as NUM6_1_,

  253. order0_.MEMBER_ID as MEMBER4_6_1_,

  254. products1_.ORDER_ID as ORDER1_3_,

  255. product2_.ID as PRODUCT2_3_,

  256. product2_.ID as ID8_0_,

  257. product2_.NAME as NAME8_0_

  258. from

  259. TEST_ORDER order0_

  260. left outer join

  261. TEST_ORDER_PRODUCT products1_

  262. on order0_.ID=products1_.ORDER_ID

  263. left outer join

  264. TEST_PRODUCT product2_

  265. on products1_.PRODUCT_ID=product2_.ID

  266. where

  267. order0_.ID=?

  268. Hibernate:

  269. insert

  270. into

  271. TEST_PRODUCT

  272. (NAME)

  273. values

  274. (?)

  275. Hibernate:

  276. insert

  277. into

  278. TEST_PRODUCT

  279. (NAME)

  280. values

  281. (?)

  282. 从这段SQL代码可以看出,只是进行了Product数据的插入,并没有对反映Order和Product关系的中间表进行数据

  283. 插入,看来配置文件中的设定由Order来维护关联关系起了一定的作用(为什么是一定的作用而不是完全的呢?这

  284. 是因为还没有进行必要的测试,只是在这里反映出Product已经不能维护关联关系了)。

  285. 下面我们将Java代码改动一下如下:

  286. public void insert()

  287. {

  288. Session session = HibernateSessionFactory.getSessionFactory().getCurrentSession();

  289. session.beginTransaction();

  290. Product p1 = new Product();

  291. p1.setName("p1");

  292. Product p2 = new Product();

  293. p2.setName("p2");

  294. Order order = (Order)session.load(Order.class, 3);

  295. order.getProducts().add(p1);

  296. order.getProducts().add(p2);

  297. session.save(order);

  298. session.getTransaction().commit();

  299. }

  300. 再次运行,查看后台SQL语句如下:

  301. Hibernate:

  302. select

  303. order0_.ID as ID6_1_,

  304. order0_.NAME as NAME6_1_,

  305. order0_.NUM as NUM6_1_,

  306. order0_.MEMBER_ID as MEMBER4_6_1_,

  307. products1_.ORDER_ID as ORDER1_3_,

  308. product2_.ID as PRODUCT2_3_,

  309. product2_.ID as ID8_0_,

  310. product2_.NAME as NAME8_0_

  311. from

  312. TEST_ORDER order0_

  313. left outer join

  314. TEST_ORDER_PRODUCT products1_

  315. on order0_.ID=products1_.ORDER_ID

  316. left outer join

  317. TEST_PRODUCT product2_

  318. on products1_.PRODUCT_ID=product2_.ID

  319. where

  320. order0_.ID=?

  321. Hibernate:

  322. insert

  323. into

  324. TEST_PRODUCT

  325. (NAME)

  326. values

  327. (?)

  328. Hibernate:

  329. insert

  330. into

  331. TEST_PRODUCT

  332. (NAME)

  333. values

  334. (?)

  335. Hibernate:

  336. insert

  337. into

  338. TEST_ORDER_PRODUCT

  339. (ORDER_ID, PRODUCT_ID)

  340. values

  341. (?, ?)

  342. Hibernate:

  343. insert

  344. into

  345. TEST_ORDER_PRODUCT

  346. (ORDER_ID, PRODUCT_ID)

  347. values

  348. (?, ?)

  349. 显然,和没有使用任何inverse="true"时的情况一摸一样,是正确的,也就是说现在是由Order来维护两个关联对象的关联关系的。

  350. 对于多对多关联来说,可以在任何一端来设置为主控方,也可都不设,那么两端都可以维护关联关系(并且,我们发现和设定一方

  351. 为主控方在性能上没有什么变化)。

  352. 如果两端都设定为inverse="true"的话呢?

  353. 我们来做个测试,将两个映射文件的<set>节点的inverse属性都设置为true。对于Order来说,是让Product作为主控方;对于

  354. Product来说让Order作为主控方。

  355. 先用下面代码测试:

  356. public void insert()

  357. {

  358. Session session = HibernateSessionFactory.getSessionFactory().getCurrentSession();

  359. session.beginTransaction();

  360. Product p1 = new Product();

  361. p1.setName("p1");

  362. Product p2 = new Product();

  363. p2.setName("p2");

  364. Order order = (Order)session.load(Order.class, 3);

  365. //  order.getProducts().add(p1);

  366. //  order.getProducts().add(p2);

  367. p1.getOrders().add(order);

  368. p2.getOrders().add(order);

  369. session.save(p1);

  370. session.save(p2);

  371. session.getTransaction().commit();

  372. }

  373. 查看后台SQL语句如下:

  374. Hibernate:

  375. select

  376. order0_.ID as ID6_1_,

  377. order0_.NAME as NAME6_1_,

  378. order0_.NUM as NUM6_1_,

  379. order0_.MEMBER_ID as MEMBER4_6_1_,

  380. products1_.ORDER_ID as ORDER1_3_,

  381. product2_.ID as PRODUCT2_3_,

  382. product2_.ID as ID8_0_,

  383. product2_.NAME as NAME8_0_

  384. from

  385. TEST_ORDER order0_

  386. left outer join

  387. TEST_ORDER_PRODUCT products1_

  388. on order0_.ID=products1_.ORDER_ID

  389. left outer join

  390. TEST_PRODUCT product2_

  391. on products1_.PRODUCT_ID=product2_.ID

  392. where

  393. order0_.ID=?

  394. Hibernate:

  395. insert

  396. into

  397. TEST_PRODUCT

  398. (NAME)

  399. values

  400. (?)

  401. Hibernate:

  402. insert

  403. into

  404. TEST_PRODUCT

  405. (NAME)

  406. values

  407. (?)

  408. 看来没有对中间表进行数据插入。

  409. 使用下面代码来测试:

  410. public void insert()

  411. {

  412. Session session = HibernateSessionFactory.getSessionFactory().getCurrentSession();

  413. session.beginTransaction();

  414. Product p1 = new Product();

  415. p1.setName("p1");

  416. Product p2 = new Product();

  417. p2.setName("p2");

  418. Order order = (Order)session.load(Order.class, 3);

  419. order.getProducts().add(p1);

  420. order.getProducts().add(p2);

  421. session.save(order);

  422. session.getTransaction().commit();

  423. }

  424. 查看后台的SQL语句如下:

  425. Hibernate:

  426. select

  427. order0_.ID as ID6_1_,

  428. order0_.NAME as NAME6_1_,

  429. order0_.NUM as NUM6_1_,

  430. order0_.MEMBER_ID as MEMBER4_6_1_,

  431. products1_.ORDER_ID as ORDER1_3_,

  432. product2_.ID as PRODUCT2_3_,

  433. product2_.ID as ID8_0_,

  434. product2_.NAME as NAME8_0_

  435. from

  436. TEST_ORDER order0_

  437. left outer join

  438. TEST_ORDER_PRODUCT products1_

  439. on order0_.ID=products1_.ORDER_ID

  440. left outer join

  441. TEST_PRODUCT product2_

  442. on products1_.PRODUCT_ID=product2_.ID

  443. where

  444. order0_.ID=?

  445. Hibernate:

  446. insert

  447. into

  448. TEST_PRODUCT

  449. (NAME)

  450. values

  451. (?)

  452. Hibernate:

  453. insert

  454. into

  455. TEST_PRODUCT

  456. (NAME)

  457. values

  458. (?)

  459. 比较两次的结果,发现一摸一样。看来如果两端都设置为主控方,是不能对中间表进行数据保存的。

  460. b、数据加载

  461. 数据加载就不再演示了,也就是默认采用左外连接的方式加载数据,能够将Order和它的关联对象

  462. Product都加载,当然是通过中间表;对于Product来说也是这样。

  463. c、删除数据

  464. 删除数据也不再演示,无论删除Order还是Product,都是简单的删除自己,而不会删除关联的对象

  465. ,这是因为设定了cascade="save-update"

点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

2020年前端实用代码段,为你的工作保驾护航

有空的时候,自己总结了几个代码段,在开发中也经常使用,谢谢。1、使用解构获取json数据let jsonData  id: 1,status: "OK",data: 'a', 'b';let  id, status, data: number   jsonData;console.log(id, status, number )