对象状态 瞬时态|临时态:Transient 1、没有与Hibernate产生关联(没有与Session有关联)。 2、与数据库中的记录没有产生关联,即对象没有ID(有关联:就是与数据库中的ID有对应)。 持久态:Persistent 1、与Hibernate有关联(与session有关联)。 2、对象有ID。 游离态|脱管态:Detached 1、没有与Hibernate产生关联。 2、对象有ID。 三种状态转换如下图所示:
示例代码如下:
package com.itheima.a_state;
import org.hibernate.Session;
import org.junit.Test;
import com.itheima.domain.User;
import com.itheima.utils.HibernateUtils;
// 对象的三种状态
public class Demo1 {
@Test
// 演示对象三种状态
public void fun1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
User u = new User(); // 瞬时状态
u.setName("tom"); // 瞬时状态
u.setPassword("1234"); // 瞬时状态
session.save(u); // 持久状态
// 问题: 调用完save方法后,数据库中有没有对应记录?
// 答:数据库中没有对应记录,但是最终(未来)会被同步到数据库中,所以仍然是持久状态。
session.getTransaction().commit(); // 持久状态,在事务提交时,会把持久化状态对象同步(更新)到数据库中
session.close(); // 游离状态
}
@Test
// 三种状态的转换
// 瞬时 => 持久
public void fun2() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
User u = new User(); // 瞬时状态
u.setName("tom"); // 瞬时状态
u.setPassword("1234"); // 瞬时状态
session.save(u); // 持久状态
// save方法会使用主键生成策略,只是为User对象指定id =>
// native => 主键自增,会打印 insert into 语句
// increment => 数据库自己生成主键,先从数据库中查询最大的ID值,将ID值加1作为新的主键,不建议使用,存在线程并发问题
// assigned => 需要手动指定主键,不手动指定将会报错
session.getTransaction().commit(); // 持久状态,在事务提交时,会把持久化状态对象同步(更新)到数据库中
session.close(); // 游离状态
}
// 瞬时 => 游离
// 瞬时: 没有关联,没有id
// 游离: 没有关联,有id(与数据库中对应的id)
@Test
public void fun3() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
User u = new User(); // 瞬时状态
u.setId(1); // 游离状态
session.getTransaction().commit(); // 游离状态
session.close(); // 游离状态
}
@Test
// 持久 => 瞬时
// 持久: 有关联,有id
// 瞬时: 无关联,无id
public void fun4() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 通过get方法,得到持久状态对象
User u = (User) session.get(User.class, 1); // 持久状态
session.getTransaction().commit(); // 持久状态,在事务提交时,会把持久化状态对象同步(更新)到数据库中
session.close(); // 游离状态
u.setId(null); // 瞬时状态
}
@Test
// 持久 => 瞬时
// 持久: 有关联,有id
// 瞬时: 无关联,无id
public void fun5() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 通过get方法,得到持久状态对象
User u = (User) session.get(User.class, 1); // 持久状态
session.evict(u); // 游离状态,该方法将User对象与session的关联移除
u.setId(null); // 瞬时状态
session.save(u); // 持久状态
session.getTransaction().commit(); // 持久状态,在事务提交时,会把持久化状态对象同步(更新)到数据库中
session.close(); // 游离状态
}
@Test
// 持久 => 游离
// 只需要将session的关联取消
public void fun6() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 通过get方法,得到持久状态对象
User u = (User) session.get(User.class, 1); // 持久状态
session.evict(u); // 游离状态
session.getTransaction().commit(); // 游离状态
session.close(); // 游离状态
}
@Test
// 游离 => 瞬时
// 移除ID
public void fun7() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 通过get方法,得到持久状态对象
User u = (User) session.get(User.class, 1); // 持久状态
session.evict(u); // 游离状态
u.setId(null); // 瞬时状态
session.getTransaction().commit(); // 瞬时状态
session.close(); // 瞬时状态
}
@Test
// 游离 => 持久
// 是否与Session关联
public void fun8() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 通过get方法,得到持久状态对象
User u = (User) session.get(User.class, 1); // 持久状态
session.evict(u); // 游离状态
session.update(u); // 持久状态
session.getTransaction().commit(); // 持久状态 ,在事务提交时,会把持久化状态对象同步(更新)到数据库中
session.close(); // 瞬时状态
}
// 三种状态有什么用?
// 答: 持久状态,我们使用Hibernate主要是为了持久化我们的数据。
// 对于对象的状态,我们期望我们需要同步到数据库的数据,都被转换成持久状态。
// 持久化状态的特点:Hibernate会自动将持久化状态对象的变化同步到数据库中。
@Test
public void fun9() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 通过get方法,得到持久状态对象
User u = (User) session.get(User.class, 1); // 持久状态
// u.setName("jerry"); // 依然是持久状态
u.setId(3); // 会报错。因为Hibernate中规定:与session建立关联的对象的ID,不允许修改!
session.update(u); // 多余代码 => 因为Hibernate会自动将持久化状态对象的变化同步(更新)到数据库中。
session.getTransaction().commit(); // 持久状态 => 打印update语句
session.close(); // 瞬时状态
}
}
一级缓存(更深层次理解Hibernate中对象的操作) 缓存:Hibernate中也存在缓存。Hibernate中存在的缓存也是用来提高效率的。 Hibernate中存在两种缓存: 1、线程级别的缓存。也叫Session缓存、Hibernate一级缓存(今天学) 2、进程级别的缓存。也叫Hibernate二级缓存(最后一天学) Session缓存:就是Session对象中存在的缓存,缓存中存在的是(持久化)对象。
一级缓存:又称为session级别的缓存。当获得一次会话(session),hibernate在session中创建多个集合(map),用于存放操作数据(PO对象),为程序优化服务,如果之后需要相应的数据,hibernate优先从session缓存中获取,如果有就使用;如果没有再查询数据库。当session关闭时,一级缓存销毁。
示例代码如下:
package com.itheima.b_cache;
import java.util.List;
import org.hibernate.Session;
import org.junit.Test;
import com.itheima.domain.User;
import com.itheima.utils.HibernateUtils;
// session缓存 的细节问题
public class Demo2 {
@Test
// 1、
// 保存对象时使用 save方法
// 保存对象时使用 persist方法
// 有区别吗?答:没有区别,名称不同,功能一样。
// persist(持久)方法 来自于JPA接口
// save(保存)方法来自于Hibernate
public void fun1() {
Session session = HibernateUtils.openSession();
// session.beginTransaction();
User u = new User();
u.setName("张三");
// session.save(u); // insert into 语句被打印 => 目的:获得id
session.persist(u);
// session.getTransaction().commit();
session.close();
}
// 2.1、HQL语句查询是否会使用一级缓存?答:HQL语句查询不会使用一级缓存。
@Test
public void fun2() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
List<User> list1 = session.createQuery("from User").list(); // 会打印一条select语句
List<User> list2 = session.createQuery("from User").list(); // 又会打印一条select语句
List<User> list3 = session.createQuery("from User").list(); // 又又会打印一条select语句
session.getTransaction().commit();
session.close();
}
// 2.2、HQL语句批量查询时,查询结果是否会进入一级缓存?答:HQL语句查询的结果会放入一级缓存中。
@Test
public void fun3() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
List<User> list1 = session.createQuery("from User").list(); // 会打印一条select语句
User u = (User) session.get(User.class, 1); // 不会再打印一条select语句了,因为使用了一级缓存
session.getTransaction().commit();
session.close();
}
@Test
// 3.1、原生SQL语句查询的结果会不会放入一级缓存中?答:如果把查询结果封装到对象中,对象会放入一级缓存中。
public void fun4() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
List<User> list1 = session.createSQLQuery("select * from t_user").addEntity(User.class).list(); // 会打印一条select语句
User u = (User) session.get(User.class, 1); // 不会再打印一条select语句了,因为使用了一级缓存
System.out.println(u);
session.getTransaction().commit();
session.close();
}
@Test
// 3.2、原生SQL语句查询的结果会不会放入一级缓存中?答:如果没有把查询结果封装到对象中,对象不会放入一级缓存中。
public void fun5() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
List list1 = session.createSQLQuery("select * from t_user").list(); // 会打印一条select语句
User u = (User) session.get(User.class, 1); // 又会打印一条select语句
System.out.println(u);
session.getTransaction().commit();
session.close();
}
// 同理:Hibernate中的criteria查询 => 也会将查询结果放入一级缓存. 但是查询不会使用一级缓存,与HQL查询结论一致。
}
快照:与一级缓存一样的存放位置,对一级缓存数据备份。保证数据库的数据与一级缓存的数据必须一致。 如果一级缓存修改了,在执行commit提交时,将自动刷新一级缓存,执行update语句,将一级缓存的数据更新到数据库。 示例代码如下:
package com.itheima.b_cache;
import org.hibernate.Session;
import org.junit.Test;
import com.itheima.domain.User;
import com.itheima.utils.HibernateUtils;
// session缓存
public class Demo1 {
@Test
// 证明session缓存的存在
public void fun1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
// 把持久化状态的对象 => 存到缓存中
User u1 = (User) session.get(User.class, 1); // 发送select语句,从数据库取出记录,并封装成对象
User u2 = (User) session.get(User.class, 1); // 再次查询时,会从缓存中查找,不会再发送select
User u3 = (User) session.get(User.class, 1); // 再次查询时,会从缓存中查找,不会再发送select
System.out.println(u1 == u2); // true
System.out.println(u1 == u3); // true
session.getTransaction().commit();
session.close();
}
@Test
// session缓存中的快照
public void fun2() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
User u1 = (User) session.get(User.class, 1); // 持久状态,会打印select
session.update(u1); // 持久状态,不会打印select语句
session.getTransaction().commit(); // 不会打印select语句
session.close();
}
@Test
// session缓存中的快照
public void fun3() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
User u1 = new User(); // 瞬时状态
u1.setId(1); // 游离状态
u1.setName("jerry"); // 游离状态
u1.setPassword("1234"); // 游离状态
session.update(u1); // 持久状态,不会打印select语句
session.getTransaction().commit(); // 会打印select语句
session.close();
}
@Test
// 感受一级缓存效率的提高
public void fun4() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
User u1 = (User) session.get(User.class, 1); // 持久化状态
u1.setName("tom");
session.update(u1);
u1.setName("jack");
session.update(u1);
u1.setName("rose");
session.update(u1); // 持久化状态: 本质就是存在缓存中的对象。
session.getTransaction().commit();
session.close();
}
}
一级缓存中对象和快照图解如下图所示:
示例代码如下_01:
package com.itheima.c_question;
import java.util.List;
import org.hibernate.Session;
import org.junit.Test;
import com.itheima.domain.User;
import com.itheima.utils.HibernateUtils;
// 学生问题
// 1、save方法 和 persist方法 对比之后,没有区别?
public class Demo1 {
@Test
public void fun1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
User u = new User();
u.setId(99);
session.persist(u); // 体现的是持久化,persist提供的理念是将对象完整的持久化,持久化也包括对象的ID。
// 如果在持久化之前设置了ID,那么就会将设置的ID进行insert,但是主键策略是由数据库来维护的。所以会产生矛盾,抛出异常。
session.getTransaction().commit();
session.close();
}
@Test
public void fun2() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
User u = new User();
u.setId(99);
session.save(u); // save方法,如果保存的对象在保存之前设置了ID,那么该ID也被认为是无效的ID。
System.out.println(u.getId()); // ID是根据主键策略自动生成的,不是99
session.getTransaction().commit();
session.close();
}
}
示例代码如下_02:
package com.itheima.c_question;
import java.util.List;
import org.hibernate.Session;
import org.junit.Test;
import com.itheima.domain.User;
import com.itheima.utils.HibernateUtils;
// 学生问题
// 2、Hql查询,查询的结果会放入Session一级缓存中,但是为什么每次调用Hql查询都会生成Sql语句呢?
// 并不代表 Hql没有使用一级缓存。
public class Demo2 {
@Test
public void fun1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
List<User> list1 = session.createQuery("from User").list(); // 发送sql
List<User> list2 = session.createQuery("from User").list(); // 发送sql
System.out.println(list1.get(0) == list2.get(0)); // true =>
session.getTransaction().commit();
session.close();
}
// 3、缓存中的数据如果与数据库中的数据不同步,会怎么样?
// 答:会优先使用缓存中的。
// 如何解决不同步问题呢?
// 答:使用JDBC
// 在一级缓存中出现该问题的几率比较小。因为一级缓存的生命周期比较短。一级缓存的生命周期类似于request对象。
// openSession(); => 一级缓存生命周期开始
// session.close(); => 一级缓存销毁
@Test
public void fun2() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
User u1 = (User) session.get(User.class, 1);
User u2 = (User) session.get(User.class, 1);
session.getTransaction().commit();
session.close();
}
}
createQuery语句查询图解如下:
示例代码如下:
package com.itheima.d_api;
import org.hibernate.Session;
import org.junit.Test;
import com.itheima.domain.User;
import com.itheima.utils.HibernateUtils;
// 其他API(大部分都是了解即可)
public class Demo1 {
@Test
// 1、evict(); 将缓存中的对象移除
// 2、clear(); 清空一级缓存
public void fun1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
User u1 = (User) session.get(User.class, 1);
session.clear(); // 清空一级缓存
User u2 = (User) session.get(User.class, 1);
session.getTransaction().commit();
session.close();
}
@Test
// 3、refresh(Object); 刷新 => 强制刷新缓存中的对象 => (可以用来解决缓存与数据库数据不同步的问题(局部解决))
public void fun2() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
User u1 = (User) session.get(User.class, 1);
session.refresh(u1); // 将缓存中的对象立刻与数据库同步,会再发送一个sql语句
session.getTransaction().commit();
session.close();
}
@Test
// 4、flush(); 对比快照,并提交缓存对象
public void fun3() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
User u1 = (User) session.get(User.class, 1);
// u1.setName("zhangsan"); // 使得缓存和快照不一致
session.flush(); // 立刻提交session缓存中的对象到数据库
session.getTransaction().commit();
session.close();
}
@Test
// 代理主键 => native
// 5.1、saveOrUpdate(Object); 方法
// saveOrUpdate 可以同时完成保存或更新操作
// 主键为空 时 => 执行save语句
// 主键有值 时 => 执行update语句
public void fun4() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
User u = new User();
u.setId(99);
u.setName("jack");
u.setPassword("1234");
session.saveOrUpdate(u);
session.getTransaction().commit();
session.close();
}
// 自然主键 => assigned
// 5.2、update 与 saveOrUpdate方法
// saveOrUpdate 可以同时完成保存或更新操作
// 主键为空时 => 报错,因为无论是save还是update,都必须指定id
// 主键有值时 => 先会根据主键查询数据库,看数据库是什么情况:
// 数据库中存在这条数据时 => 执行update语句
// 数据库中不存在这条数据时 => 执行insert语句
@Test
public void fun5() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
User u = new User();
u.setId(88);
u.setName("jack01");
u.setPassword("1234");
session.saveOrUpdate(u);
session.getTransaction().commit();
session.close();
}
@Test
// 在我们使用Hibernate的时候,注意避免出现:两个相同的ID对象,放入一级缓存的情况。因为一级缓存本质上是一个Map结合。键是ID不能相同。
public void fun6() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
User u1 = (User) session.get(User.class, 1); // 持久状态,缓存中存在
session.evict(u1); // 游离状态,缓存中不存在
User u2 = (User) session.get(User.class, 1); // 持久状态,缓存中存在,id一样,但是对象不是同一个
session.update(u1); // 将u1重新转换为持久状态,缓存中存在,报错
session.getTransaction().commit();
session.close();
}
}
一对多:1表(主表)必须主键和多表(从表)必须外键,主表主键与从表外键形成主外键关系。 多对多:提供中间表(从表),提供2个字段(外键)分别对应两个主表。 一对一:非常少见。
----------------------------------
一对多:客户Customer和订单Order
private class Customer {
// 一对多:一个客户拥有多个订单
private Set<Order> orderSet;
......
}
private class Order {
// 多对一:多个订单属于一个客户
private Customer customer;
......
}
----------------------------------
多对多:Student学生和Course课程
private class Student {
// 多对多:多个学生学习不同课程
private Set<Course> courseSet;
......
}
private class Course {
// 多对多:多个课程被不同学生学习
private Set<Student> student;
......
}
----------------------------------
一对一:公司Company 和地址Address
private class Company {
private Address address;
......
}
private class Address {
private Company company;
......
}
----------------------------------
Customer.java
package com.itheima.domain;
import java.util.HashSet;
import java.util.Set;
public class Customer {
private Integer cid;
private String cname;
// 一对多:一个客户拥有多个订单
// 在一的一方,使用集合表达持有多的一方引用,建议使用Set(不重复、无序)
private Set<Order> orders = new HashSet<Order>(); // 建议实例化,使用方便
// getter和setter方法
}
Order.java
package com.itheima.domain;
public class Order {
private Integer oid;
private String oname;
// 多对一:多个订单 属于 一个客户
private Customer customer;
// getter和setter方法
}
Customer.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itheima.domain">
<class name="Customer" table="t_customer">
<id name="cid" column="cid">
<generator class="native"></generator>
</id>
<property name="cname" column="cname" type="String"></property>
<!-- 一对多:一个客户当前客户 拥有 多个订单
1 、确定容器set <set>
2、name确定对象属性名
3 、确定从表外键的名称 <key>
4 、确定关系,及关联的另一方的完整类名
注意:
在hibernate中可以只进行单向配置。
每一个配置项都可以完整的描述彼此关系。
一般情况采用双向配置,双方都可以完成描述表与表之间关系。
-->
<set name="orders">
<key column="customer_id"></key>
<one-to-many class="Order"/>
</set>
</class>
</hibernate-mapping>
Order.hbm.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itheima.domain">
<class name="Order" table="t_order">
<id name="oid" column="oid">
<generator class="native"></generator>
</id>
<property name="oname" column="oname" type="String"></property>
<!-- 多对一:多个订单属于一个客户
1、name 确定属性名称
2、column 确定从表的外键名称
3、class 关联的另一方的完整类名
-->
<many-to-one name="customer" column="customer_id" class="Customer"></many-to-one>
</class>
</hibernate-mapping>
hibernate.cfg.xml
......
<!-- 添加ORM映射文件 ,填写src之后的路径-->
<mapping resource="com/itheima/domain/Customer.hbm.xml"/>
<mapping resource="com/itheima/domain/Order.hbm.xml"/>
......
测试一对多代码:
package com.itheima.e_one2many;
import java.util.Set;
import org.hibernate.Session;
import org.junit.Test;
import com.itheima.domain.Customer;
import com.itheima.domain.Order;
import com.itheima.utils.HibernateUtils;
// 测试:一对多实现
public class Demo1 {
@Test
// 1 、测试一对多关系中:保存操作
// 共打印5条语句
// 前3条打印insert => 保存对象,维护外键
// 后两条打印update => 维护外键
// 维护了两次外键,有些多余,该如何解决呢?
// 解决 => 单纯指定关系由其中一方来维护,另一方不维护关系(放弃维护)。
// 注意 => 外键维护的放弃,只能由非外键所在对象来放弃。
// => 需要在非外键所在对象的配置文件中进行配置。
// 本例中:配置Customer的inverse属性为:true
// 只打印3条语句 => 外键由Order自己来维护
public void fun1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Customer c = new Customer();
c.setCname("tom");
Order o1 = new Order();
o1.setOname("肥皂");
Order o2 = new Order();
o2.setOname("蜡烛");
// 外键维护的放弃,只能由非外键所在对象来放弃。需要在非外键所在对象的配置文件中进行配置。配置好后,下面两句代码可以省略了。
// c.getOrders().add(o1); // 维护关系
// c.getOrders().add(o2); // 维护关系
o1.setCustomer(c); // 维护关系
o2.setCustomer(c); // 维护关系
session.save(c); // 保存对象
session.save(o1); // 保存对象
session.save(o2); // 保存对象
session.getTransaction().commit();
session.close();
}
// 多表关系 => 删除
// 直接删除 Customer时 ,会先移除 Customer中引用的外键,然后再删除Customer。
// 结论: 在维护一方的对象时,会自动维护这一方对象与另一方对象的关系。
// 当把Customer的 inverse属性设置为:true 后,
// 再次演示删除会报错 => 因为Customer不负责维护外键了,直接删除Customer会导致,Order引用了无效的id,违反了外键约束。
// 那么该如何解决呢?
// 答:因为现在是通过Order来维护外键的,所以应该先单独设置订单不属于任何Customer后,再删除Customer。
// 什么时候配置inverse属性呢?
// 答:主要看业务即(看实际需求)。
// 如果一的一方经常需要自己来维护外键,那么在一的一方就不要配置inverse属性了。
// 如果一的一方经常要通过多的一方来维护外键,那么在一的一方要配置inverse属性为true。
@Test
public void fun2() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Customer c = (Customer) session.get(Customer.class, 3);
// 答:因为现在是通过Order来维护外键的,所以应该先单独设置订单不属于任何Customer后,再删除Customer。
Set<Order> set = c.getOrders(); // 得到的是持久状态,修改后不需要更新,在提交的时候会自动同步数据库
for (Order o : set) {
o.setCustomer(null); // 先单独设置订单不属于任何Customer
}
session.delete(c);
session.getTransaction().commit();
session.close();
}
}
示例代码同下:
示例代码如下所示:
package com.itheima.e_one2many;
import org.hibernate.Session;
import org.junit.Test;
import com.itheima.domain.Customer;
import com.itheima.domain.Order;
import com.itheima.utils.HibernateUtils;
// 测试:一对多操作
public class Demo2 {
@Test
// 增 => 级联保存
// 我们希望在保存Customer时,自动将Customer集合中未保存的瞬时状态对象Order进行保存。并不会维护关系哦!
// 配置 Customer 的 cascade 属性为: save-update 级联保存和级联修改。
// 级联保存和反转的对比:
// 首先在配置级联保存,Customer的cascade属性为: save-update 后
// 接着配置Customer的inverse属性为:true 后,
// 会出现一个问题,Order的外键的值为null了。我们怎么使得外键有值呢?
// 答: 法一:我们可以手动维护关系。如下,此时运行代码,打印3条insert语句。
// 法二:将Customer的inverse属性改为默认值:false 后,此时运行代码,打印5条sql语句:3条insert,2条update。
public void fun1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Customer c = new Customer();
c.setCname("tom");
Order o1 = new Order();
o1.setOname("肥皂");
Order o2 = new Order();
o2.setOname("蜡烛");
c.getOrders().add(o1); // 维护关系
c.getOrders().add(o2); // 维护关系
// 配置Customer的inverse属性为:true 后,配置好后,下面两句代码可以省略了。即可以不用多维护一次外键了。
// c.getOrders().add(o1); // 维护关系
// c.getOrders().add(o2); // 维护关系
// 在配置Customer的inverse属性为:true 后,
// 但是会出现一个问题,Order的外键的值为null了。我们怎么使得外键有值呢?答:法一:我们可以手动维护关系。
// o1.setCustomer(c); // 手动维护关系
// o2.setCustomer(c); // 手动维护关系
session.save(c); // 保存对象
// 配置Customer的cascade属性为:save-update 后,配置好后,下面两句代码可以省略了。
// session.save(o1); // 保存对象
// session.save(o2); // 保存对象
session.getTransaction().commit();
session.close();
}
@Test
// 增 => 级联修改
// 我们希望在保存Customer时,自动将Customer集合中未保存的瞬时状态对象Order进行保存。并不会维护关系哦!
// 配置 Customer 的 cascade 属性为: save-update 级联保存和级联修改。
public void fun2() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Customer c = (Customer) session.get(Customer.class, 4); // 1条 select 语句
for (Order o : c.getOrders()) { // 1条 select 语句,根据外键查到的
o.setOname("哇哈哈"); // 修改订单
}
session.getTransaction().commit(); // 2条update 语句,因为该用户有2个订单,又因为设置级联修改,会自动将订单的修改同步(更新)到数据库
session.close();
}
@Test
// 删除 => 级联删除
// 配置 Customer 的 cascade 属性为: delete 级联删除。
// 在删除Customer时 ,会将Customer下的订单一并删除。
// 前提:该用户有2个订单
// 此时若再配置Customer的 inverse 属性为:
// false 时,共6条sql语句
// true 时,共5条sql语句, 比上面少一条维护外键语句
public void fun3() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Customer c = (Customer) session.get(Customer.class, 5); // 1条 select 语句
session.delete(c); // 删除Customer,并删除两个Order,1条 select 语句
session.getTransaction().commit(); // 之后,1条 update 语句 + 3条 delete 语句
session.close();
}
@Test
// 删除 => 级联删除
// 配置 Customer 的 cascade 属性为: delete 级联删除。
// 操作的两方 cascade 值都为 delete 时
// 需要注意: 千万不要在两方都配置级联删除,如果这样配置的话,删除任何一方,都会导致整个关系链对象全部删除。
public void fun4() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Order o = (Order) session.get(Order.class, 9); // 1条 select 语句
session.delete(o);
// 找到所有关联的 Customer,1条 select 语句
// 此时 Customer 也配置了级联删除 => 1条 select 语句, 找下面的 Order
// 删除所有 Order
// 再删除 Customer
session.getTransaction().commit();
session.close();
}
}
一对多关系中存在父子关系。1表(主表)可以称为父表,多表(从表)称为子表。 总结: 主表不能删除从表已经引用(关联)的数据。 从表不能添加主表不存在的数据。
package com.itheima.e_one2many;
import java.util.HashSet;
import java.util.Iterator;
import org.hibernate.Session;
import org.junit.Test;
import com.itheima.domain.Customer;
import com.itheima.domain.Order;
import com.itheima.utils.HibernateUtils;
// 测试:一对多操作:孤儿删除
public class Demo3 {
@Test
// 配置 Customer 的 inverse 属性为: false
// 配置 Customer 的 cascade 属性为: delete-orphan 孤儿删除 => 当没有任何外键引用Order时,Order会被删除
// 示例前提:该用户有2个订单
public void fun1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Customer c = (Customer) session.get(Customer.class, 3); // 1条 select 语句
Iterator<Order> it = c.getOrders().iterator(); // 1条 select 语句,1条 update 语句,2条 delete 语句
while (it.hasNext()) { // 遍历Customer下的订单,解决客户和订单的关系
it.next(); // 默认:客户和订单解除关系后,外键被设置成null,此时订单就是孤儿。客户和订单都存在。
it.remove(); // 孤儿删除(孤子删除),当订单为孤儿时,一并删除。客户仍存在。
}
// 注意: 删除Customer下的订单时,不能使用 下面两句代码:
// c.setOrders(null);
// c.setOrders(new HashSet<Order>());
session.getTransaction().commit();
session.close();
}
}
示例代码如下:
package com.itheima.e_one2many;
import java.util.Iterator;
import org.hibernate.Session;
import org.junit.Test;
import com.itheima.domain.Customer;
import com.itheima.domain.Order;
import com.itheima.utils.HibernateUtils;
//测试:一对多关系:all 和 all-delete-orphan
public class Demo4 {
@Test
// 配置 Customer 的 cascade 属性为: all-delete-orphan => 相当于配置了 save-update,delete,delete-orphan
public void fun1() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Customer c = new Customer();
c.setCname("tom");
Order o1 = new Order();
o1.setOname("肥皂");
Order o2 = new Order();
o2.setOname("蜡烛");
c.getOrders().add(o1); // 维护关系
c.getOrders().add(o2); // 维护关系
session.save(c);
session.getTransaction().commit();
session.close();
}
@Test
// 配置 Customer 的 cascade 属性为: all-delete-orphan => 相当于配置了 save-update,delete,delete-orphan
public void fun2() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Customer c = (Customer) session.get(Customer.class, 10);
session.delete(c);
session.getTransaction().commit();
session.close();
}
@Test
// 配置 Customer 的 cascade 属性为: all-delete-orphan => 相当于配置了 save-update,delete,delete-orphan
public void fun3() {
Session session = HibernateUtils.openSession();
session.beginTransaction();
Customer c = (Customer) session.get(Customer.class, 12);
Iterator<Order> it = c.getOrders().iterator(); // 1条 select 语句,1条 update 语句,2条 delete 语句
while (it.hasNext()) { // 遍历Customer下的订单,解决客户和订单的关系
it.next(); // 默认:客户和订单解除关系后,外键被设置成null,此时订单就是孤儿。客户和订单都存在。
it.remove(); // 孤儿删除(孤子删除),当订单为孤儿时,一并删除。客户仍存在。
}
session.getTransaction().commit();
session.close();
}
}
核心配置文件hibernate.cfg.xml内容如下:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-mapping PUBLIC
"-//Hibernate/Hibernate Mapping DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
<hibernate-mapping package="com.itheima.domain">
<class name="Customer" table="t_customer">
<id name="cid" column="cid">
<generator class="native"></generator>
</id>
<property name="cname" column="cname" type="string"></property>
<!-- 一对多:一个客户当前客户 拥有 多个订单
1 、确定容器set <set>
2、name确定对象属性名
3 、确定从表外键的名称 <key>
4 、确定关系,及关联的另一方的完整类名
注意:
在hibernate中可以只进行单向配置。
每一个配置项都可以完整的描述彼此关系。
一般情况采用双向配置,双方都可以完成描述表与表之间关系。
inverse属性:反转
是否将关系的维护反转给对方,默认值是:false,
设置成true表示放弃维护,将维护关系反转。
cascade属性:级联操作
save-update:级联保存和级联修改。保存A时,同时保存B
delete:删除A时,同时删除B,A、B都不存在了。
delete-orphan:孤儿删除,解除关系,同时将B删除,A存在的。
all:save-update 和 delete 整合
all-delete-orphan:三个整合 在一起
如果需要配置多项,使用逗号分隔。<set cascade="save-update,delete">
-->
<set name="orders" inverse="false" cascade="all-delete-orphan">
<key column="customer_id"></key>
<one-to-many class="Order"/>
</set>
</class>
</hibernate-mapping>