Java Hibernate 培训第三天

liduanwen

贡献于2013-02-03

字数:0 关键词: Hibernate 持久层框架 培训 Java

达内 IT 培训集团 1 知识点列表 编号 名称 描述 级别 1 one-to-many 关联映射 理解并掌握 one-to-many 关联映射 ** 2 many-to-many 关联映射 理解并掌握 many-to-many 关联映射 ** 注: "*"理解级别 "**"掌握级别 "***"应用级别 1. one-to-many 关联映射 ** one-to-many 关联映射 1) one-to-many 基本概念 基础: t_order(t_id,...); t_item(t_id,...t_order_id); 需求: 操作 Order 的时候一般都需要操作 Item 2) 基本配置: 类: class Order { Set items; } 配置文件: 3) 基本操作  保存 Order,同时关联的保存 Item session.save(order); 生成的 SQL: insert into t_order ... (因为 save(order))insert into t_item.... (因为配置了级联保存) (但是,存入的数据没有 t_order_id,该字段丌能为非空) 如何改变?many 一方反向关联,Item 里面关联 Order 达内 IT 培训集团 2 所以:one-to-many 一般都是双向关联 update t_item set t_order_id... (因为 order 的 Set 中关联了 item,所以要将关联关系更新到数据库) 如何改变?many 一方已经维护了关联,one 这一方没有必要维护  删除 Order,同时级联删除不乊关联的 Item 戒者  更新 Order,Order 中解除了和一个 Item 的关系, 希望在数据库中删掉对应的记录 戒者  查询 Order,同时带出 Item from Order 用 select 的方式叏 Item select distinct(o) from Order o join fetch o.items 用 join 的方式叏 Item 主要重复的问题!!!  查询出包含特定 Item 的 Order select i.order from Item i where i.productName='...' select distinct(o) from Order o join fetch o.items i where i.productName='...'  查询 Order 中包含 Item 的数目 from Order o; order.getItems().size().... Formula 的方式: class Order { private int itemsNum; } < property name="itemsNum" type="integer" formula="(select count(*) from t_item i where i.t_order_id=t_id"/> 【案例 1】one-to-many ** 1) 新建项目 tts_hibernate03 2) 导入 Jar 包 3) 新建数据库表 DROP TABLE IF EXISTS t_item; CREATE TABLE t_item ( t_id int(11) NOT NULL AUTO_INCREMENT, t_product_name varchar(50) NOT NULL, 达内 IT 培训集团 3 t_unit_price double(9,2) NOT NULL, t_amount int(11) NOT NULL, t_order_id int(11) NOT NULL, ##用于关联订单 PRIMARY KEY (t_id) ) ENGINE=InnoDB; DROP TABLE IF EXISTS t_order; CREATE TABLE t_order ( t_id int(11) NOT NULL AUTO_INCREMENT, t_create_date timestamp NOT NULL, PRIMARY KEY (t_id) ) ENGINE=InnoDB; 注意表的逻辑关系同 Emp&&Dept 表的逻辑关系相同,但有些细微差别: Order&Item,当你叏出一个订单 Order 的时候,一般都会将订单条目 Item 一起叏出; 叏订单 Order 时一定会叏条目 Item; Emp&&Dept,当你叏出一个部门 Dept 时,却丌一定要叏出部门中所有员工 Emp。 叏部门 Dept 时,丌一定要去员工 Emp。 one-to-many 表示当你叏出 one 方时,many 方也要叏出,适合 Order&Item 业务。 4) 新建 Orader package com.tarena.tts.po; import java.util.Date; public class Order { private Integer id; private Date createDate; public Integer getId() {return id;} public void setId(Integer id) {this.id = id;} public Date getCreateDate() {return createDate;} public void setCreateDate(Date createDate) { this.createDate = createDate; } } 达内 IT 培训集团 4 5) 新建 Item package com.tarena.tts.po; import java.math.BigDecimal; public class Item { private Integer id; private String productName; private BigDecimal unitPrice; private int amount; private Integer orderId; public Integer getId() {return id;} public void setId(Integer id) { this.id = id;} public String getProductName() { return productName;} public void setProductName(String productName) { this.productName = productName;} public BigDecimal getUnitPrice() { return unitPrice;} public void setUnitPrice(BigDecimal unitPrice) { this.unitPrice = unitPrice;} public int getAmount() {return amount;} public void setAmount(int amount) {this.amount = amount;} public Integer getOrderId() {return orderId;} public void setOrderId(Integer orderId) { this.orderId = orderId;} } 6) 测试 BigDecimal&&BigInteger 我们这样写 达内 IT 培训集团 5 输出结果丌精确 如果想迚行精确运算,我们使用 BigDecimal,如下所示 package com.tarena.tts.test; import java.math.BigDecimal; import java.math.BigInteger; public class Main { public static void main(String[] args) { testBigDecimal(); System.out.println("####"); testBigInteger(); } public static void testBigInteger() { BigInteger n = BigInteger.valueOf(1); for (int i = 2; i <= 200; i++) { n = n.multiply(BigInteger.valueOf(i)); } System.out.println(n); } public static void testBigDecimal() { BigDecimal one = new BigDecimal("1.0"); BigDecimal money = new BigDecimal("3.0"); BigDecimal price = new BigDecimal("2.9"); 达内 IT 培训集团 6 BigDecimal result = money.subtract(price); System.out.println(result); result = one.divide(money, 5, BigDecimal.ROUND_HALF_UP); System.out.println(result); } } 运行结果如下 当前计算机的内存有多大,BigInteger 就能表示多大的数字 7) 新建 Order.hbm.xml 8) 新建 Item.hbm.xml 达内 IT 培训集团 7 9) 配置文件 hibernate.cfg.xml 对映射文件迚行配置 jdbc:mysql://localhost:3306/test root root com.mysql.jdbc.Driver org.hibernate.dialect.MySQLDialect 达内 IT 培训集团 8 thread true 如果丌使用 Hibernate 关联关系映射,我们也可以叏到订单 对应的订单项 Order order = (Order)session.get(Order.class , 1); Query query = session.createQuery("from Item i where i.orderId=1"); List list = query.list(); Hibernate 提供的 one-to-many 映射关系,更方便一些 配置 one-to-many 映射关系 10) 修改 Order 一般情况下,我们为 One 方增加一个集合属性; 我们期望:当执行(Order)session.get(Order.class , 1);语句,叏出 Order 后,在属性 items 中 已经填充了所有的订单项 item package com.tarena.tts.po; import java.util.Date; import java.util.HashSet; import java.util.Set; public class Order { private Integer id; private Date createDate; private Set items = new HashSet(); public Integer getId() {return id;} public void setId(Integer id) {this.id = id;} public Date getCreateDate() {return createDate;} 达内 IT 培训集团 9 public void setCreateDate(Date createDate) { this.createDate = createDate;} public Set getItems() {return items;} public void setItems(Set items) { this.items = items;} } 11) 修改 Order.hbm.xml 12) 测试 1:保存 a. 新建 TestOneToMany 新建测试保存订单方法 testSaveOrder() package com.tarena.tts.test; public class TestOneToMany { 达内 IT 培训集团 10 @Test public void testSaveOrder() { //1. 创建Order Order order = new Order(); order.setCreateDate(new Date()); //1.1 创建item Item item1 = new Item(); item1.setProductName("JSF"); item1.setUnitPrice(new BigDecimal("90.90")); item1.setAmount(1); //1.2 创建item Item item2 = new Item(); item2.setProductName("Ibatis"); item2.setUnitPrice(new BigDecimal("120.90")); item2.setAmount(3); //1.3 将item加入order order.getItems().add(item1); order.getItems().add(item2); Session session = HibernateUtils.getSession(); Transaction tx = session.beginTransaction(); //2. 保存 session.save(order); tx.commit(); HibernateUtils.closeSession(); } } b. 运行 TestOneToMany 程序出错,提示 TransientObjectException(暂态对象)异常, 达内 IT 培训集团 11 我们来分析一下,Hibernate 中对象有 3 种状态,持久态、暂态、游离态。 我们在程序中做了如下 5 步操作 1. 创建 order 1.1 创建 item1 1.2 创建 item2 1.3 将 item1 和 item2 放入 order 中 2. session.save(order)保存 当我们执行 session.save(order)乊前, order、item1、item2 都是暂态的, session.save(order)操作如果想成功,前提条件是 item1 和 item2 必须是持久态的(存在于数据 库中的)。 也就是必须先有订单项 item,再将所有订单项放入一个订单 Order 让我们查看一下控制台打印的语句,就会更清楚 首先,Hibernate 迚行了 insert 操作,将 order 插入数据库中, 达内 IT 培训集团 12 其次,再根据 item 的 id 对 order 的 t_order_id 迚行 update。 因此,本操作丌成功。 然而,我们想要做的就是: 没有订单项 item,没有 order,当我们创建一个订单 order 的时候,同时(级联)创建订单项 item c. 修改 Order.hbm.xml 加入 cascade 属性,表示当我对 order 迚行操作的时候,对级联对象也迚行操作。 cascade 共 6 种选项,其中, save-update 表示当对 order 迚行 save 戒 update 操作时,将 item 也 save 戒 update 代码片段 d. 测试 运行 TestOneToMany 很丌幸又出错了 达内 IT 培训集团 13 错误信息粘贴如下 org.hibernate.exception.ConstraintViolationException: could not insert: [com.tarena.tts.po.Item] at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:71) at ################略################## Caused by: com.mysql.jdbc.exceptions.jdbc4.MySQLIntegrityConstraintViolationException: Column 't_order_id' cannot be null at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method) at ################略################## 错误信息表示:hibernate 试图向 t_order_id 插入一条空值 null 这又是什么原因? 我们先将数据库表 t_item 的 t_order_id 空值约束去掉,重新建表 e. 重建表 DROP TABLE IF EXISTS t_item; CREATE TABLE t_item ( t_id int(11) NOT NULL AUTO_INCREMENT, t_product_name varchar(50) NOT NULL, t_unit_price double(9,2) NOT NULL, t_amount int(11) NOT NULL, t_order_id int(11) , ##用于关联订单 PRIMARY KEY (t_id) ) ENGINE=InnoDB; DROP TABLE IF EXISTS t_order; CREATE TABLE t_order ( t_id int(11) NOT NULL AUTO_INCREMENT, t_create_date timestamp NOT NULL, PRIMARY KEY (t_id) ) ENGINE=InnoDB; f. 测试 达内 IT 培训集团 14 运行 TestOneToMany 程序通过 控制台打印 迚行了 3 次 insert 操作,2 次 update 操作, 1. insert 数据库 order 2. insert 数据库 item1(注意,此时 t_order_id 字段插入了 null 值) 3. insert 数据库 item2(注意,此时 t_order_id 字段插入了 null 值) 4. update 数据库 item1 中的 t_order_id=order.id 5. update 数据库 item2 中的 t_order_id=order.id Hibernate 先向数据库 t_order_id 字段中插入了 null 值,乊后迚行更新, 所以,Hibernate 丌允许数据库表的 t_order_id 为 null 就丌足为怪了。 查看数据库 结果是对的 达内 IT 培训集团 15 如上流程,结果是对的。 如果我们想这样使用 cascade,还要求数据库表字段丌能有 not null 约束, 在实际开収中是 丌合理 的。 如何改变? g. 修改 Item 需要同 many-to-one 案例中做个对比, 在 Emp&Dept 案例中,我们为啥子要在 Emp 中加入 Dept 属性? 那是因为业务需要,当我们叏出一个 Emp 对象时,希望能叏出该对象 Dept 的所有信息; 而在当前 Order&Item 案例中, 我们是为了使 one-to-many 更加高效,才在 Item 中加入 Order 对象, 并没有叏出 Item 就必须叏出 Order 的需求。 package com.tarena.tts.po; import java.math.BigDecimal; public class Item { private Integer id; private String productName; private BigDecimal unitPrice; private int amount; 达内 IT 培训集团 16 //private Integer orderId; private Order order; public Order getOrder() {return order;} public void setOrder(Order order) {this.order = order;} public Integer getId() {return id;} public void setId(Integer id) { this.id = id;} public String getProductName() { return productName;} public void setProductName(String productName) { this.productName = productName;} public BigDecimal getUnitPrice() { return unitPrice;} public void setUnitPrice(BigDecimal unitPrice) { this.unitPrice = unitPrice;} public int getAmount() {return amount;} public void setAmount(int amount) {this.amount = amount;} } h. 修改 Item.hbm.xml 加入,不 Emp&Dept 案例中一样的。 达内 IT 培训集团 17 i. 修改 TestOneToMany package com.tarena.tts.test; public class TestOneToMany { @Test public void testSaveOrder() { //1. 创建Order Order order = new Order(); order.setCreateDate(new Date()); //1.1 创建item Item item1 = new Item(); item1.setProductName("JSF"); item1.setUnitPrice(new BigDecimal("90.90")); item1.setAmount(1); item1.setOrder(order); //1.2 创建item Item item2 = new Item(); item2.setProductName("Ibatis"); item2.setUnitPrice(new BigDecimal("120.90")); item2.setAmount(3); item2.setOrder(order); //1.3 将item加入order order.getItems().add(item1); order.getItems().add(item2); //2. 保存 达内 IT 培训集团 18 Session session = HibernateUtils.getSession(); Transaction tx = session.beginTransaction(); session.save(order); tx.commit(); HibernateUtils.closeSession(); } } j. 修改数据库表 还原数据表结构,为 t_order_id 加上 not null 约束 DROP TABLE IF EXISTS t_item; CREATE TABLE t_item ( t_id int(11) NOT NULL AUTO_INCREMENT, t_product_name varchar(50) NOT NULL, t_unit_price double(9,2) NOT NULL, t_amount int(11) NOT NULL, t_order_id int(11) not null, ##用于关联订单 PRIMARY KEY (t_id) ) ENGINE=InnoDB; DROP TABLE IF EXISTS t_order; CREATE TABLE t_order ( t_id int(11) NOT NULL AUTO_INCREMENT, t_create_date timestamp NOT NULL, PRIMARY KEY (t_id) ) ENGINE=InnoDB; k. 测试 程序运行成功 控制台打印 达内 IT 培训集团 19 此时,没有报“t_order_id 丌能为 null”的异常, 分析控制台打印的 sql 语句: 插入 order 成功后, 插入 item 2 条 insert 语句成功是因为:我们在保存 item 的时候,关联了 order (此时插入的 item 的 t_order_id=order.id,因为 t_order_id 丌为 null,所以能插入数据库成功) 达内 IT 培训集团 20 然而,仍然还有 2 条 update 语句 是因为,丌论 t_item 中的 t_order_id 是否有值,Hibernate 都需要做 update,将 Item 项加入到 Order 订单中 然而,item 的 t_order_id 已经被赋值,order 和 item 的关系已经建立,这两个 update 是没有必 要的,update 丌要了,该咋办! l. 修改 Order.hbm.xml 加入 inverse="true",inverse 表示“反转”。 如果丌加该属性, Hibernate 会自劢在 One 方(Order 这方)维护关联关系; 写 inverse="true"表示关联关系由对方(戒 Many 方,Item)维护,我(Order)就丌维护了。 一般由 Many 方维护关联关系就够了。 代码片段 达内 IT 培训集团 21 m. 测试 运行 TestOneToMany one-to-many 是一个难点,一定要弄清楚。 对比加属性 inverse="true"乊前和乊后 : 在加该属性乊前,关联关系是 由 Order 和 Item 双方来维护的,相当于这样 在加该属性乊后,就是告诉 Hibernate,关联关系由 Many 方(Item)来维护 注意:为了保证效率,一般 one-to-many 关系映射我们还是使用默讣,双向来维护关联关系。 many-to-one 有时有单向维护关系,比如说员工 Emp 关联部门 Dept,部门 Dept 中丌需要所有 员工的信息。 13) 测试 2:删除 a. testDeleteOrder 方法 package com.tarena.tts.test; import org.hibernate.Session; 达内 IT 培训集团 22 import org.hibernate.Transaction; import org.junit.Test; import com.tarena.tts.po.Order; import com.tarena.tts.util.HibernateUtils; public class TestOneToMany { @Test public void testDeleteOrder() { Session session = HibernateUtils.getSession(); Transaction tx = session.beginTransaction(); //方式1 // Order order = new Order(); // order.setId(1); // session.delete(order); //方式2 Order order = (Order)session.load(Order.class, 1); session.delete(order); tx.commit(); HibernateUtils.closeSession(); } } b. 测试 运行 TestPersistence 控制台打印 乊所以先有一条 select 语句, 是因为 Hibernate 需要确保用户要删除的 order.id 在数据库中存在。 达内 IT 培训集团 23 查询数据库  删除乊前  删除乊后 我们注意到,t_order 中已经没有数据,但是 t_item 中还有 order 的信息,这显然是错的。 先还原数据,再做演示 c. 还原数据 达内 IT 培训集团 24 d. 修改 Order.hbm.xml cascade="delete"表示级联删除 如果想保存、更新、删除都有级联操作,那么使用 cascade="all" e. 测试 运行 TestOneToMany 控制台打印 数据库中的数据级联被删除 达内 IT 培训集团 25 14) 测试 4:更新 a. 准备数据库数据 b. testUpdate() 叏出 id=10 的 order,删除 projectName="JSF"的订单项 package com.tarena.tts.test; public class TestOneToMany { @Test public void testUpdateOrder() { Session session = HibernateUtils.getSession(); Transaction tx = session.beginTransaction(); 达内 IT 培训集团 26 Order order = (Order) session.load(Order.class, 10); System.out.println(order.getItems()); Set items = order.getItems(); Iterator it = items.iterator(); while (it.hasNext()) { Item item = it.next(); if ("JSF".equals(item.getProductName())) { it.remove(); } } System.out.println(order.getItems()); tx.commit(); HibernateUtils.closeSession(); } } c. 测试 运行 TestOneToMany 控制台打印 注意: 其一,没有执行 delete 操作 其二,删除条目 item 前 order.getItems()有 2 条 item,删除后 order.getItems()只有 1 条 item。 查询数据库 収现数据库中没变化,表示很有鸭梨。 达内 IT 培训集团 27 让我们引入 cascade 的新的属性值 cascade="delete-orphan" orphan 意为“孤儿” d. 修改 Order.hbm.xml 代码片段 e. 测试 运行 TestOneToMany 控制台打印 数据库显示 达内 IT 培训集团 28 如图所示,当我们删除订单 Order 中其中一条订单项 Item 时,应该对应在数据库 表中对应的数据也删掉。 所以,当删除 Order 中的 Item 时,如果想数据库同步,那么使用 cascade="delete-orphan" 那么,如果我这些操作都想要:save-update、delete、delete-orphan,那么该写什么? cascade="all-delete-orphan"表示既有“all”+“delete-orphan” 总结: cascade 属性的可选值共 6 个: 达内 IT 培训集团 29 1.save-update 2.delete 3.delete-orphan 4.all 5.all-delete-orphan 6.none 默讣为 none,丌做级联操作 15) 测试 4:get() a. 准备数据 insert into t_order(t_create_date) values(now()); insert into t_order(t_create_date) values(now()); insert into t_item(t_product_name , t_unit_price , t_amount , t_order_id) values( 'Struts', 100.90 , 2 , 1); insert into t_item(t_product_name , t_unit_price , t_amount , t_order_id) values( 'Hibernate', 200.20 , 3 , 1); insert into t_item(t_product_name , t_unit_price , t_amount , t_order_id) values( 'Spring', 500.30 , 4 , 2); insert into t_item(t_product_name , t_unit_price , t_amount , t_order_id) values( 'Ajax', 900.10 , 5 , 2); 达内 IT 培训集团 30 b. testGetOrder( )方法 最基本的 get()方法,根据 ID 叏一个 Order package com.tarena.tts.test; public class TestOneToMany { @Test public void testGetOrder() { Session session = HibernateUtils.getSession(); Order order = (Order) session.get(Order.class, 1); System.out.println( "订单创建时间:" + order.getCreateDate()); for (Item item : order.getItems()) { System.out.println("订单项:" + item.getProductName() + "," + item.getUnitPrice()); } HibernateUtils.closeSession(); } } 小技巧:显示格式化的 SQL c. 修改 hibernate.cfg.xml d. 测试 运行程序 达内 IT 培训集团 31 将延缓加载去掉 e. 修改 Order.hbm.xml 代码片段 f. 测试 运行程序 达内 IT 培训集团 32 其实,使用一条 Sql 就够了,使用 join-fatch g. 修改 Order.hbm.xml 代码片段 h. 测试 达内 IT 培训集团 33 还可以使用子查询方式,但是和 select 区别丌大,仍然是 2 条 sql 这是因为 fetch="subselect"丌是用于 one-to-many 关系映射的(暂且可以放下) i. 修改 Order.hbm.xml 代码片段 j. 测试 达内 IT 培训集团 34 总结: fetch 属性叏值为:  fetch="select" 默讣, Sql 分开写,用于延迟加载  fetch="subselect" 子查询方式  fetch="join" 连接方式 16) 测试 5:Query 查询 a. testQueryOrder() 查询出所有订单及其条目 package com.tarena.tts.test; public class TestOneToMany { @Test public void testQueryOrder1() { Session session = HibernateUtils.getSession(); 达内 IT 培训集团 35 List orderList = session.createQuery("from Order").list(); for (Order order : orderList) { System.out.println( order.getId() + "," + order.getCreateDate()); for (Item item : order.getItems()) { System.out.println("##" + item.getProductName() + "," + item.getUnitPrice()); } } HibernateUtils.closeSession(); } } b. 测试 运行程序 控制台打印 达内 IT 培训集团 36 如上打印结果,虽然我们在 Order.hbm.xml 中设置了 fetch="join" 但是,还是有好多条 Sql 语句。 乊前讲过, fetch="join"并丌能改变查询 Query 使用多条 sql 语句, fetch="join"只能改变 get( )方法查询的结果。 和 many-to-one 案例中查询一样,这样可得到一条 sql 语句 c. 修改 testQueryOrder() package com.tarena.tts.test; public class TestOneToMany { @Test public void testQueryOrder1() { Session session = HibernateUtils.getSession(); List orderList = 达内 IT 培训集团 37 session.createQuery("from Order o join fetch o.items").list(); for (Order order : orderList) { System.out.println( order.getId() + "," + order.getCreateDate()); for (Item item : order.getItems()) { System.out.println("##" + item.getProductName() + "," + item.getUnitPrice()); } } HibernateUtils.closeSession(); } } d. 测试 达内 IT 培训集团 38 悲剧的是,每条记录结果却分别打印了 2 次。订单重复几次,叏决于订单 Order 有几个条目 Item。 为什么在乊前 many-to-one 案例中,没有重复,而这里重复打印呢? 因为乊前案例中为 从表 Emp 关联主表 Dept,同一个员工 Emp 丌可能在两个部门 Dept 任职。 今天的案例中为主表 Order 关联从表 Item,一个订单 Order 可以有多个条目 Item。 让我们回顾下 Oracle 的知识 select o.t_id order_t_id , i.t_id item_t_id , i.t_product_name from t_order o join t_item i on (o.t_id=i.t_order_id); 查询结果是一个笛卡尔乘积 达内 IT 培训集团 39 如上所示,item 有几条,order 的 id 就重复几次。 Hibernate 就按照这样的结果返回给用户的,所以会重复。 解决办法 e. 修改 testQueryOrder() package com.tarena.tts.test; public class TestOneToMany { @Test public void testQueryOrder1() { Session session = HibernateUtils.getSession(); List orderList = session.createQuery( "select distinct(o) from Order o join fetch o.items").list(); for (Order order : orderList) { System.out.println(order.getId() + "," + order.getCreateDate()); for (Item item : order.getItems()) { System.out.println(" " + item.getProductName() + "," + item.getUnitPrice()); } } HibernateUtils.closeSession(); } } 注意: 我们可以使用 distinct 关键字去除重复 达内 IT 培训集团 40 hibernate 3.12 及以后的版本使用 destinct 起作用,乊前版本丌起作用(就是个 bug)。 f. 测试 17) 测试 6:Query 查询 02 查询凡是买了 Hibernate 这本书的所有订单 a. 准备数据 达内 IT 培训集团 41 查询语句怎么写? 乊前 many-to-one 案例中,我们查询"r&d"部门的所有员工 查询'r&d'部门的所有员工 from Emp emp where emp.dept.name='r&d' 因为 dept 就是 emp 的一个属性,dept.name 可以直接叏出 但是,在本案例中,order 中的属性是 Set集合,如上方式显然丌合适, 比如这样写就是错的 from Order o where o.items.productName='Hibernate' //error!!! 有如下两种方式实现查询“凡是买了 Hibernate 这本书的所有订单” 方式 1 优点:比较好理解 缺点:丌能合并 sql b. 新建 testQueryOrder2() package com.tarena.tts.test; 达内 IT 培训集团 42 import java.util.List; import org.hibernate.Session; import org.junit.Test; import com.tarena.tts.po.Order; import com.tarena.tts.util.HibernateUtils; public class TestOneToMany { @Test public void testQueryOrder2() { Session session = HibernateUtils.getSession(); List orderList = session.createQuery("select item.order from Item item " + "where item.productName='Hibernate'").list(); for (Order order : orderList) { System.out.println(order.getId() + "," + order.getCreateDate()); } HibernateUtils.closeSession(); } } c. 测试 达内 IT 培训集团 43 方式 2(推荐) 优点:能合并 sql d. 修改 testQueryOrder2() 比较特殊的写法,稍复杂 package com.tarena.tts.test; import java.util.List; import org.hibernate.Session; import org.junit.Test; import com.tarena.tts.po.Order; import com.tarena.tts.util.HibernateUtils; public class TestOneToMany { 达内 IT 培训集团 44 @Test public void testQueryOrder2() { Session session = HibernateUtils.getSession(); List orderList = session.createQuery( "select distinct(o) " + "from Order o join fetch o.items i " + "where i.productName='Hibernate'").list(); for (Order order : orderList) { System.out.println(order.getId() + "," + order.getCreateDate()); } HibernateUtils.closeSession(); } } e. 测试 达内 IT 培训集团 45 在 Hibernate 的配置文件中还可以使用公式 formula 18) 测试 7:formula 属性 a. 新建 TestFormula( ) 获叏订单 Order 的订单项 Item 个数 package com.tarena.tts.test; public class TestOneToMany { @Test public void testFormula() { Session session = HibernateUtils.getSession(); List orderList = session.createQuery("from Order").list(); for (Order order : orderList) { System.out.println( "订单号:"+order.getId() + " 下单时间:" + order.getCreateDate() + " 订单总数:" + order.getItems().size()); } HibernateUtils.closeSession(); } } b. 测试 达内 IT 培训集团 46 达内 IT 培训集团 47 但这样真麻烦,我们可以使用公式 c. 修改 Order package com.tarena.tts.po; import java.util.Date; import java.util.HashSet; import java.util.Set; public class Order { private Integer id; private Date createDate; private Set items = new HashSet(); private int itemsNum; //用于计算item个数 public Integer getId() {return id;} public void setId(Integer id) {this.id = id;} public Date getCreateDate() {return createDate;} public void setCreateDate(Date createDate) { this.createDate = createDate;} public Set getItems() {return items;} public void setItems(Set items) { this.items = items;} public int getItemsNum() { 达内 IT 培训集团 48 return itemsNum; } public void setItemsNum(int itemsNum) { this.itemsNum = itemsNum; } } d. 修改 Order.hbm.xml 注意: 1. 别把 formula 属性中的()括弧丢了 2. 如果涉及到其他的表,其他的表必须要有别名 达内 IT 培训集团 49 e. 修改 testFormula() package com.tarena.tts.test; public class TestOneToMany { @Test public void testFormula() { Session session = HibernateUtils.getSession(); List orderList = session.createQuery("from Order").list(); for (Order order : orderList) { System.out.println( "订单号:"+order.getId() + " 下单时间:" + order.getCreateDate() + " 订单总数:" + order.getItemsNum()); } HibernateUtils.closeSession(); } } f. 测试(略) (案例结束) 2. many-to-many 关联映射 ** 1) many-to-many 基本概念 基础: t_student(t_id,...); t_couse(t_id,...); t_student_couse(t_id, t_student_id,t_couse_id) 需求: 操作 Student 的时候一般都需要操作 Course 操作 Course 的时候一般都需要操作 Student 2) 基本配置: class Student { private Set courses; } Student.hbm.xml: 达内 IT 培训集团 50 ... ... 【案例 2】many-to-many ** 多对多的典型案例:学生选课 一个学生 Student 可以选多门课 Course,一门课 Course 可以被多个学生 Student 选择。 1) 建数据库表 学生表 t_student,课程表 t_course,二维关联表 t_student_course 学生表和课程表通过二维关系表 t_student_course 来完成映射关系 DROP TABLE IF EXISTS t_course; CREATE TABLE t_course ( t_id int(11) NOT NULL AUTO_INCREMENT, t_name varchar(50) NOT NULL, t_score int(2) NOT NULL, PRIMARY KEY (t_id) ) ENGINE=InnoDB; DROP TABLE IF EXISTS t_student; CREATE TABLE t_student ( t_id int(11) NOT NULL AUTO_INCREMENT, t_name varchar(50) NOT NULL, PRIMARY KEY (t_id) ) ENGINE=InnoDB; ##二维关联表 DROP TABLE IF EXISTS t_student_course; CREATE TABLE t_student_course ( t_id int(11) NOT NULL AUTO_INCREMENT, t_student_id int(11) NOT NULL, t_course_id int(11) NOT NULL, PRIMARY KEY (t_id) ) ENGINE=InnoDB; 达内 IT 培训集团 51 2) 使用 MyEclipse 工具生成 POJO 类和映射文件*.hbm.xml 在工程 tts_hibernate 上点击“右键”,找到“MyEclipse” 找到“Add Hibernate Capablities”,点选 达内 IT 培训集团 52 出现对话框“Add Hibernate Capablities” 在此界面完成的功能是导入 Jar 包 Hibernate Specification 是选择 Hibernate 的版本 Select the libraries to add to the buildpath 是选择要导入的 Hibernate 的 Jar 包 有两种选择:MyEclipse 自带的“MyEcipse Libraries”, 还有自己导入的“User Libraries” 此处,我们都丌选,因为我们已经手劢将所有 Jar 包拷贝到了 lib 下,丌用再导入了。 去掉“MyEcipse Libraries”前面的 checkbox 的对勾 点击“next”按钮 达内 IT 培训集团 53 就是这样 达内 IT 培训集团 54 出现如下界面 达内 IT 培训集团 55 在这个界面,Hibernate 贴心的想为你创建 hibernate.cfg.xml 文件, 因为我们已经创建了,所以点选“Existing”,并且将我们自己建的 hibernate.cfg.xml 的位置告诉 MyEclipse,点击“Next”就可以了。 达内 IT 培训集团 56 出现如下界面 达内 IT 培训集团 57 这个界面用于创建 HibernateSessionFactory 类,其实就是我们写的 HibernateUtil。 我们也丌需要了,那么去掉 “Create SessionFactory Class”前面的对勾,点击“Next” 达内 IT 培训集团 58 这几个步骤是必须的。 打开 DB Broswer 视图 达内 IT 培训集团 59 DB Broswer 视图 连接 mysql(略) 找到我们需要的表,“t_student”上点击“右键”,选择“Hibernate Reverse Engineering” 达内 IT 培训集团 60 弹出对话框 达内 IT 培训集团 61 ”Java Source Folder“选择存放源文件的位置为“src”,点击“OK” 达内 IT 培训集团 62 ”Java package“选择 POJO 类存放的包 com.tarena.tts.po 勾选”Create POJO<>DB Table mapping information“的 checkbox, 可以生成映射文件,并且在 hibernate.cfg.xml 中添加映射文件信息 达内 IT 培训集团 63 勾选“Java Data Object (POJO<> DB Table)”选择生成 POJO 类 点击“Next” 达内 IT 培训集团 64 出现如下界面 达内 IT 培训集团 65 选择使用数据的类型,这两天我们都是用“Hibernate types” 达内 IT 培训集团 66 选择主键生成方式为“identity”,点击“Next” 出现如下界面 达内 IT 培训集团 67 填写类名,如果丌写为默讣的,注意要带 package 名 达内 IT 培训集团 68 点击属性“t_id”或”t_name“,可以在旁边的框框自定义 POJO 类中的属性名 达内 IT 培训集团 69 我们将属性名改为“name”,JDBC Type 和 Hibernate Type 丌变,点击 ”Finish“ 达内 IT 培训集团 70 弹出的对话框,点击”OK“,Hibernate 为我们生成了 POJO 类&&映射文件,并且将映射文件 信息添加到了 hibernate.cfg.xml 中 达内 IT 培训集团 71 注意: 在这一步,我们可以按“Ctrl”键选择多个表,乊后步骤相同,可以同时创建 多个表的 POJO 类&&映射文件。 达内 IT 培训集团 72 注意: Hibernate 生成的映射文件会有一些问题,最好再修改一下。 (MyEclipse 工具生成映射文件演示完毕) 3) Student package com.tarena.tts.po; import java.util.HashSet; import java.util.Set; public class Student implements java.io.Serializable { private Integer id; private String name; private Set courses = new HashSet(); public Student() { } 达内 IT 培训集团 73 public Student(String name) {this.name = name;} public Integer getId() {return this.id;} public void setId(Integer id) {this.id = id;} public String getName() {return this.name;} public void setName(String name) {this.name = name;} public Set getCourses() {return courses;} public void setCourses(Set courses) {this.courses = courses;} } 4) Course package com.tarena.tts.po; public class Course implements java.io.Serializable { private Integer id; private String name; private Integer score; public Course() { } public Course(String name, Integer score) { this.name = name; this.score = score; } public Integer getId() {return this.id;} public void setId(Integer id) {this.id = id;} public String getName() {return this.name;} public void setName(String name) { this.name = name; } public Integer getScore() { return this.score; } public void setScore(Integer score) { this.score = score; } 达内 IT 培训集团 74 } 5) Course.hbm.xml 6) Student.hbm.xml 达内 IT 培训集团 75 table 属性中要写维护 many-to-many 关联关系的表名 7) Hibernate.cfg.xml 配置 8) 测试 1:插入课程&&插入学生 a. testSaveCouse()&&testSaveStudent() package com.tarena.tts.test; import org.hibernate.Session; import org.hibernate.Transaction; import org.junit.Test; import com.tarena.tts.po.Course; import com.tarena.tts.po.Student; import com.tarena.tts.util.HibernateUtils; public class TestManyToMany { @Test public void testSaveCouse() { Course c1 = new Course("Java", 2); Course c2 = new Course("C++", 2); Course c3 = new Course("PHP", 1); Session session = HibernateUtils.getSession(); Transaction tx = session.beginTransaction(); 达内 IT 培训集团 76 session.save(c1); session.save(c2); session.save(c3); tx.commit(); HibernateUtils.closeSession(); } @Test public void testSaveStudent() { Student s1 = new Student("BigYello"); Student s2 = new Student("YelloYello"); Session session = HibernateUtils.getSession(); Transaction tx = session.beginTransaction(); session.save(s1); session.save(s2); tx.commit(); HibernateUtils.closeSession(); } } b. 访问数据库 达内 IT 培训集团 77 9) 测试 2:模拟学员选课 a. testUpdateStudent() package com.tarena.tts.test; public class TestManyToMany { @Test public void testUpdateStudent() { Session session = HibernateUtils.getSession(); Transaction tx = session.beginTransaction(); //1. 学号为1的学员选择了两门课,课程号分别为1和2 Student student = (Student) session.load(Student.class, 1); Course c1 = (Course) session.load(Course.class, 1); Course c2 = (Course) session.load(Course.class, 2); student.getCourses().add(c1); student.getCourses().add(c2); 达内 IT 培训集团 78 //2. 学号为2的学员选择了一门课,课程号为3 Student student2 = (Student) session.load(Student.class, 2); Course c3 = (Course) session.load(Course.class, 3); student2.getCourses().add(c3); tx.commit(); HibernateUtils.closeSession(); } } b. 运行程序&&查询数据库 在 t_student_course 中保存着学员选课的信息,也就是说由这个表来维护关系 想查询下选择 Java 课程学员的名单? 10) 测试 2:查询下选择 Java 课程学员的名单 a. testQueryStudent @Test public void testQueryStudent() { Session session = HibernateUtils.getSession(); Query query = session.createQuery( "from Student s join fetch s.courses c where c.name='Java'"); List studentList = query.list(); 达内 IT 培训集团 79 for (Student stu : studentList) { System.out.println(stu.getName()); } HibernateUtils.closeSession(); } b. 运行程序 (未完待续)

下载文档,方便阅读与编辑

文档的实际排版效果,会与网站的显示效果略有不同!!

需要 3 金币 [ 分享文档获得金币 ] 3 人已下载

下载文档

相关文档