Hibernate框架2

回顾

Hibernate:

1.Hibernate框架的概述:

* 就是一个持久层的ORM框架.

* ORM:对象关系映射.将Java中实体对象与关系型数据库中表建立一种关系.Hibernate中可以通过操作对象的方式,从而操作数据库的表.

* 常见的持久层框架:

        * Hibernate

        * MyBatis

        * JPA

        * DBUtils

* Hibernate3.x和Hibernate4.x

2.Hibernate快速入门:

        2.1下载Hibernate开发包.

        2.2Hibernate开发包目录结构:

                * 文档:

                * 开发包:

                        * 操作字节码:

                            * 必须的

                        * 可选的

                         * jpa

                * 项目:

        2.3创建一个java项目 导入相应jar包.

        2.4创建数据库表:

        2.5创建一个实体类:

        2.6建立映射:

        2.7创建Hibernate核心配置文件:

        2.8编写一个测试类:

3.Hibenrate的CRUD的操作:

        * save();(增)

        * update();

        * delete();

        * get()/load();

                * 区别:

                      * get    :采用是立即检索,马上发送SQL查询.返回的是真实对象.检索一个找不到的对象的时候NullPointException

                        * load   :采用延迟加载技术,当真正使用这个对象的时候,才会发送SQL.返回代理对象.检索一个找不到的对象的时候ObjectNotFoundException

4.Hibernate的常见配置及API:

        * 4.1核心配置:

                * 两种方式;

                    * 属性文件的方式:

                            * 在src下创建一个hibernate.properties.

                                    ***** 手动加载映射文件.

                    * XML格式的配置:

                            * 在src下创建一个hibernate.cfg.xml(可以更改名字)

                    * 必须的配置:

                            * 数据库连接信息.

                            * Hibernate的方言.(针对不同的sql选择不同的sql语句)

                    * 可选配置:

                                * 显示SQL

                                * 格式化SQL

                                * hbm2ddl.auto

                                        * create/create-drop/update/validate

                     * 映射文件:

                                * <mapping resource=””/>

* 4.2映射配置:

* <class>建立类与表映射

        * name   :类的全路径

        * table  :表名称

* <id>

        * name

        * column

        * type

        * length

* <property>

        * name

        * column

        * type

        * length

5.Hibernate的API

        * Configuration:管理配置信息.(加载配置文件)

                    * 属性文件: 直接创建.

                    * XML: new Configuration().configure(); 

        * SessionFactory:(获得连接对象,维护二级缓存,线程安全,重量级对象)

                * 维护二级缓存,线程安全的对象.

                * 抽取工具类.

        * Session:(hibernate和应用程序的桥梁,增删改查)

                * 维护一级缓存,线程不安全的对象. 

        * Transaction:

                    ***** 事务是默认不自动提交,手动提交事务.(不开事务,每个session就是一个独立的事务)

        * Query:(hql查询)

        * Criteria:(qbc查询) 

6.持久化类编写:

        * 持久化类:就是一个实体类 + XML映射.

        * 编写原则:

                * 无参数构造:

                * 属性提供set/get方法

                * 提供为一个标识:

                * 尽量使用包装类型:

                * 这个类尽量不要使用final修饰. 

         * 自然主键和代理主键:

        * Hibernate提供主键生成策略:

                * increment :(不能应用于集群)

                * identity  :(数据库要求支持数据自动增长)

                * sequence  :(数据库支持序列化接口)

                * uuid      :(随机字符串)

                * native     :(本地策略,自动选择)

                * assgined  : (必须手动分配id)

1.2  Hibernate的持久化类状态:

1.2.1    Hibernate的持久化类状态

持久化类:就是一个实体类 与 数据库表建立了映射.

* Hibernate为了方便管理持久化类,将持久化类分成了三种状态.

持久化对象的状态

    瞬时态 transient  :(临时态)

    持久态 persistent :

    脱管态 detached   :(离线态)

1.2.2    三种持久化对象的状态:

Transient瞬时态:

特点:持久化对象没有唯一标识OID.没有纳入Session的管理. 

瞬时态transient    不存在持久化标识 OID ,尚未与 Hibernate Session 关联对象,被认为处于瞬时态,失去引用将被 JVM 回收

  无持久化标识OID,未与Session关联

Persistent持久态:

特点:持久化对象有唯一标识OID.已经纳入到Session的管理.

持久态  persistent 存在持久化标识 OID ,与当前 session 有关联,并且相关联的 session 没有关闭 , 并且事务未提交

  存在持久化标识OID,与Session关联

*****结论:持久化持久态对象有自动更新数据库的能力. 自动更新数据库的能力依赖了Hibernate的一级缓存.

Detached脱管态:

特点:持久化对象有唯一标识OID,没有纳入到Session管理. 

脱管态  detached 存在持久化标识 OID ,但没有与当前 session 关联,脱管状态改变 hibernate 不能检测到

  存在持久化标识OID,未与Session关联

1.2.3    区分三种持久化对象的状态:

(需要创建数据库,数据表可以自动创建(运行创建))

@Test

// 区分持久化对象的三种状态:

public void demo1(){

    // 1.创建Session

    Session session =HibernateUtils.openSession();

    // 2.开启事务

    Transaction tx =session.beginTransaction();   

    // 向数据库中保存一本图书:

    Book book = new Book(); // 瞬时态:没有唯一标识OID,没有与session关联.

    book.setName("Hiernate开发");//设置书名

    book.setAuthor("孙XX");//设置作者

    book.setPrice(65d);//设置价格

    session.save(book); // 持久态:有唯一标识OID,与session关联.   

    // 3.事务提交

    tx.commit();

    // 4.释放资源

    session.close();   

    book.setName("Struts2开发"); // 脱管态:有唯一的标识,没有与session关联.(session已经关闭)



看事务是否提交,事务提交,数据库有数据,没提交,没有数据

1.2.4    三种状态对象转换:

瞬时态:

获得:

        Book book = new Book(); 

瞬时--->持久

        * save(book);

        * save()/saveOrUpdate();

瞬时--->脱管

        * book.setId(1);

持久态:

获得:

        Book book = (Book)session.get(Book.class,1);

        * get()/load()/find()/iterate(); 

持久--->瞬时:

        * delete(book);

            * 特殊状态:删除态.(被删除的对象,不建议去使用.) 

持久--->脱管:

        * session.close();

        * close()/clear()/evict();

脱管态:

获得:(不建议直接获得)

        Book book = new Book();

        book.setId(1); 

脱管--->持久:

        * session.update();

        * update()/saveOrUpdate()/lock() 

脱管--->瞬时:

        * book.setId(null);


1.2.5    持久态对象有自动更新数据库的能力;

@Test

// 测试持久态的对象自动更新数据库

public void demo2(){

    // 1.创建Session

    Session session =HibernateUtils.openSession();

    // 2.开启事务

    Transaction tx =session.beginTransaction();

    // 获得一个持久态的对象.

    Book book = (Book)session.get(Book.class, 1);

    book.setName("Struts2开发");   //重新设置书名

    // session.update(book);   

    // 3.提交事务

    tx.commit();

    // 4.关闭资源

    session.close();

****** 自动更新数据库的能力依赖了Hibernate的一级缓存(一级缓存中快照区).

1.3  Hibernate的一级缓存:(重要)

1.3.1    Hibernate的一级缓存:

什么是缓存:

        * 缓存将数据库/硬盘上文件中数据,放入到缓存中(就是内存中一块空间).当再次使用的使用,可以直接从内存中获取.

缓存的好处:

        * 提升程序运行的效率.缓存技术是Hibernate的一个优化的手段. 

Hibernate分成两个基本的缓存:

        * 一级缓存:Session级别的缓存.一级缓存与session的生命周期一致.自带的.不可卸载.

        * 二级缓存:SessionFactory级别的缓存.不是自带的.

在Session 接口的实现中包含一系列的 Java 集合, 这些 Java 集合构成了 Session 缓存. 只要 Session 实例没有结束生命周期, 存放在它缓存中的对象也不会结束生命周期.

一级缓存:

Session 接口的实现中包含一系列的 Java 集合 , 这些 Java 集合构成了 Session 缓存 . 只要 Session 实例没有结束生命周期 , 存放在它缓存中的对象也不会结束生命周期
l session save() 方法持久化一个对象时,该对象被载入缓存,以后即使程序中不再引用该对象,只要缓存不清空,该对象仍然处于生命周期中。当试图 get() load() 对象时,会判断缓存中是否存在该对象,有则返回, 此时不查询数据库。没有再查询数据库
Session 能够在某些时间点 , 按照缓存中对象的变化来执行相关的 SQL 语句 , 来同步更新数据库 , 这一过程被称为 刷出缓存(flush)
默认情况下Session 在以下时间点刷出缓存:
        • 当应用程序调用 Transaction 的commit() 方法的时 , 该方法先刷出缓存 ( session.flush ()) ,然后在向数据库提交事务 ( tx.commit ())
        • 当应用程序执行一些 查询操作 时,如果缓存中持久化对象的属性已经发生了变化,会先刷出缓存,以保证查询结果能够反映持久化对象的最新状态
        • 调用 Session flush() 方法

1.3.2    证明Hibernate的一级缓存的存在:

@Test

// 证明一级缓存的存在

public void demo3(){

    // 1.创建Session

    Session session =HibernateUtils.openSession();

    // 2.开启事务

    Transaction tx =session.beginTransaction();         

    // save方法可以向一级缓存中存放数据的.

    /*Book book = new Book();

    book.setName("JQuery开发");

    book.setAuthor("张XX");

    book.setPrice(45d);

    Integer id = (Integer)session.save(book);   

    Book book2 = (Book)session.get(Book.class, id);   //(原来get一执行,马上就会发送sql语句,现在不会了)save方法可以向一级缓存中存放数据的.

    System.out.println(book2);*/   

    // 分别用get执行两次查询.

    Book book1 = (Book)session.get(Book.class, 1);// 马上发生SQL去查询

    System.out.println(book1);   

    Book book2 = (Book)session.get(Book.class, 1);// 不发生SQL,因为使用一级缓存的数据

    System.out.println(book2);   //两次打印的地址一样

    // 3.提交事务

    tx.commit();

    // 4.关闭资源

    session.close();

1.3.3    深入理解一级缓存中快照区:

当session加载了customer对象后,会为customer对象的值类型的属性复制一份快照。当刷出缓存时,通过比较对象的当前属性和快照,来判断对象的哪些属性发生了变化

@Test

// 深入理解一级缓存结构:快照区:

public void demo4(){

    // 1.创建Session

    Session session =HibernateUtils.openSession();

    // 2.开启事务

    Transaction tx =session.beginTransaction();  

    // 获得一个持久态的对象.

    Book book = (Book)session.get(Book.class, 1);

    book.setName("Spring3开发");//改变的是一级缓存区(key),不改变快照区(value)。不一致,就会更新数据库

    // 3.提交事务

    tx.commit();

    // 4.关闭资源

    session.close();

结论:向一级缓存存入数据的时候,放入一级缓存区和一级缓存快照区,当更新了一级缓存的数据的时候,事务一旦提交,比对一级缓存和快照区,如果数据一致,不更新,如果数据不一致,自动更新数据库.



1.3.4    Hibernate管理一级缓存:

一级缓存是与session的生命周期相关的.session生命周期结束,一级缓存销毁了.

*clear()/evict()/flush()/refresh()管理一级缓存.

        * clear()           :清空一级缓存中所有的对象.(get后也会发送sql语句)

        * evict(Object obj) :清空一级缓存中某个对象.使对象变为离线

        * flush()           :刷出缓存.(让缓存的数据刷出到数据库)(一执行,发送update等语句发送,但数据库的数据事务提交后才有)

        * refresh(Object obj):快照区的数据重新覆盖了一级缓存的数据.(不更新了,跟数据库原来数据保持一致)

1.3.5    Hibernate一级缓存的刷出时机:(了解.)

清理session的缓存(设置缓存的flush模式)

   session.setFlushMode ( FlushMode.AUTO );

FlushMode:(一般采用默认值,不去修改)

        * 常量:

                * ALWAYS     ,1,每次查询的时候都会刷出,2,手动调用flush,3,事务提交的时候.(刷出:提交update语句,更新数据库)

                * AUTO       :默认值.1,有些查询会刷出,2,手动调用flush,3,事务提交的时候.

                * COMMIT     ,1,在事务提交的时候,2,手动调用flush的时候.

                * MANUAL     ,1,只有在手动调用flush才会刷出.

                严格程度:MANUAL > COMMIT > AUTO >ALWAYS


ALWAYSAUTO的区别:当hibernate缓存中的对象被改动之后,会被标记为脏数据(即与数据库不同步了)。当session设置为FlushMode.AUTO时,hibernate在进行查询的时候会判断缓存中的数据是否为脏数据,是则刷数据库,不是则不刷,而always是直接刷新,不进行任何判断。很显然auto比always要高效得多 

可以控制sql语句输出时机

1.4  操作持久化对象的方法:

save():

        1,保存一条记录:

        2,将瞬时态对象转成持久态对象.

Session save() 方法使一个瞬时对象转变为持久化对象
Session save() 方法完成以下操作 :
        • 把瞬时对象加入到 Session 缓存中 , 使它进入持久化状态
        • 选用映射文件指定的标识符生成器 , 为持久化对象分配唯一的 OID . 在使用代理主键的情况下 , setId () 方法为瞬时对象设置 OID 是无效的 .
        • 计划执行一条 insert 语句 , Customer 对象当前的属性值组装到 insert 语句中
Hibernate 通过持久化对象的 OID 来维持它和数据库相关记录的对应关系 . 当 Customer对象处于持久化状态时, 不允许程序随意修改它的 ID

update()

更新一条记录:将脱管态对象转成持久态对象.并且计划执行一条 update 语句.


若希望  Session 仅当修改了 Customer 对象的属性时 才执行  update() 语句 可以把映射文件中 <class>  元素的  select-before-update( 更新之前先查询 ) 设为  true. 该属性的默认值为  false

在<class>标签上设置select-before-update="true"在更新之前先去查询

update() 方法关联一个脱管对象时 , 如果在 Session 的缓存中已经存在相同 OID 的持久化对象 , 会抛出异常

update() 方法关联一个脱管对象时 , 如果在数据库中不存在相应的记录 , 也会抛出异常 .

saveOrUpdate():

根据对象状态的不同执行不同的save获得update方法.

        * 如果对象是一个瞬时态对象:执行save操作.


        * 如果对象是一个脱管态对象:执行update操作.


        * 如果是持久化对象就直接返回,不执行操作


        * 设置id不存在,就会报错,可以在<id>上设置一个unsaved-value=”-1”,执行保存的操作.

判定对象为临时对象的标准

        Java 对象的 OID null

        映射文件中为<id>设置了unsaved-value属性, 并且 Java 对象的 OID 取值与这个unsaved-value属性值匹配,执行插入操作

delete():

将持久态对象转成瞬时态.

   Session delete()方法既可以删除一个脱管对象,

  可以删除一个持久化对象

        • 如果参数是持久化对象,就执行一个 delete 语句, 若为脱管对象,先使游离对象被 session 关联
        • 计划执行一条 delete 语句
        • 把对象从 Session 缓存中删除 , 该对象进入脱管状态 . 刷新缓存后,该对象将被从数据库中删除

get()/load():

获得一个持久态对象.

1.5  Hibernate关联关系的映射:

1.5.1    实体之间的关系:

实体之间有三种关系:

        * 一对多:

                * 一个用户,生成多个订单,每一个订单只能属于一个用户.

                * 建表原则:

                        * 在多的一方创建一个字段,作为外键,指向一的一方的主键.

        * 多对多:

                * 一个学生可以选择多门课程,一个课程可以被多个学生选择.

                *建表原则:

                        * 创建第三张表,中间表至少有两个字段,分别作为外键指向多对多双方主键.

        * 一对一:(特殊.最少.)

                    * 一个公司只能有一个注册地址,一个注册地址,只能被一个公司使用.(否则将两个表建到一个表.)

                    *建表原则:

                        * 唯一外键:

                            * 一对一的双方,假设一方是多的关系.需要在多的一方创建一个字段,作为外键.指向一的一方的主键.但是在外键添加一个unique.

                        * 主键对应:

                            * 一对一的双方,通过主键进行关联.

Hibernate是采用Java对象关系来描述数据表实体之间的关系


1.5.2    Hibernate中一对多的配置:(*****)

第一步:* 创建两个实体:

* 客户实体:

public class Customer {

private Integer cid;

private String cname;

// 一个客户有多个订单.

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

public Integer getCid() {

    return cid;

}

public void setCid(Integercid) {

    this.cid = cid;

}

public String getCname() {

    return cname;

}

public void setCname(Stringcname) {

    this.cname = cname;

}

public Set<Order>getOrders() {

    return orders;

}

public voidsetOrders(Set<Order> orders) {

    this.orders = orders;

}

}

* 订单实体:

public class Order {

private Integer oid;

private String addr;

// 订单属于某一个客户.放置一个客户的对象.

private Customer customer;

public Integer getOid() {

    return oid;

}

public void setOid(Integeroid) {

    this.oid = oid;

}

public String getAddr() {

    return addr;

}

public void setAddr(Stringaddr) {

    this.addr = addr;

}

public Customer getCustomer(){

    return customer;

}

public voidsetCustomer(Customer customer) {

    this.customer = customer;

}

}

第二步:建立映射:

Customer.hbm.xml

<hibernate-mapping>

<class name="cn.itcast.hibernate3.demo2.Customer"table="customer">(customer对应表

    <!-- 配置唯一标识 -->

    <id name="cid"column="cid">

        <generator class="native"/>

    </id>

    <!-- 配置普通属性 -->

    <property name="cname" column="cname" length="20"/>

    <!-- 建立映射 -->

    <!-- 配置一个集合 <set>的name Customer对象中的关联对象的属性名称. -->

    <set name="orders">(类属性中集合的对象名)

        <!-- <key>标签中column:用来描述一对多 多的一方的外键的名称. -->

        <key column="cno"></key>

        <!-- 配置一个<one-to-many>标签中class属性:订单的类的全路径-->

        <one-to-many class="cn.itcast.hibernate3.demo2.Order"/>

    </set>(订单集合)

</class>

</hibernate-mapping>

Order.hbm.xml

<hibernate-mapping>

<class name="cn.itcast.hibernate3.demo2.Ordertable="orders">(表名orders

    <!-- 配置唯一标识 -->

    <id name="oid"column="oid">

        <generator class="native"/>

    </id>

    <!-- 配置普通属性 -->

    <property name="addr"column="addr" length="50"/>

    <!-- 配置映射 -->

    <!--

    <many-to-one>标签(谁是多订单就在前面)

        name    :关联对象的属性的名称.

        column  :表中的外键名称.

        class   :关联对象类的全路径

    -->

    <many-to-one name="customer" column="cno"class="cn.itcast.hibernate3.demo2.Customer"/>(name是类属性名称

</class>

</hibernate-mapping>

第三步:将映射放到核心配置文件中.

1.5.3    Hibernate中级联保存的效果:

级联:操作当前对象的时候,关联的对象如何处理.

cascade=”save-update”     

保存客户和订单的时候,是否可以只保存其中的一方?

        不行的,报一个异常: 一个持久态对象关联一个瞬时的对象.        

级联方向性:

        * 保存客户的时候,选择级联订单.

set>集合是客户的关联订单对象的集合.所以在<set>上配置一个属性:cascade="save-update" 

                <!-- 建立映射 -->

<!-- 配置一个集合 <set>的name Customer对象中的关联对象的属性名称. -->
<set name="orders"  cascade="save-update" inverse="true">
<!-- <key>标签中column:用来描述一对多多的一方的外键的名称. -->
<key column="cno"></key>
<!-- 配置一个<one-to-many>标签中class属性:订单的类的全路径 -->
<one-to-many class="cn.itcast.hibernate3.demo2.Order"/>
</set>

        * 保存订单的时候,选择级联客户.

在Order.hbm.xml中<many-to-one>配置cascade属性:级联保存

<many-to-one 

name="customer"cascade="save-update"  column="cno" class="cn.itcast.hibernate3.demo2.Customer"/>

对象导航


session.save(order1) 插入几条记录----4

session.save(customer) 插入几条记录----3

session.save(order2) 插入几条记录-----1条

@Test
// 测试对象的导航关系:
public void demo5(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// 定义一个客户:
Customer customer = new Customer();
customer.setCname("金刚");
// 定义三个订单
Order order1 = new Order();
order1.setAddr("西三旗");
Order order2 = new Order();
order2.setAddr("上地");
Order order3 = new Order();
order3.setAddr("五道口");
order1.setCustomer(customer);
customer.getOrders().add(order2);
customer.getOrders().add(order3);
// session.save(order1); // 共发送4条insert语句:
// session.save(customer);// 共发送3条insert语句:
session.save(order2); // 共发送1条insert语句:
tx.commit();
session.close();
}

1.5.4    Hibernate中级联删除的效果:

cascade=”delete”

@Test
// 删除一个客户:
// 默认的情况下,将外键置为null,删除数据记录.(订单还有
public void demo6(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// 删除的时候有两种:
// 先查询在删除的情况:(还有一种new一个设置一个id再删除)
Customer customer = (Customer) session.get(Customer.class, 1);
session.delete(customer);
tx.commit();
session.close();
}

@Test

// 级联删除: 删除客户的时候级联删除订单.
// 在Customer.hbm.xml的<set>标签上配置cascade="delete"
public void demo7(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// 级联删除:先查询,在删除的方式.
Customer custoemr = (Customer) session. get(Customer.class, 1);
session. delete( custoemr);
tx.commit();
session.close();

}

@Test   订单删除了,客户信息也不存在
// 级联删除:删除订单的时候,级联删除客户.
public void demo8(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Order order = (Order) session.get(Order.class, 1);
session.delete(order);
tx.commit();
session.close();

}

@Test
// 孤儿删除:
// 在Customer.hbm.xml中<set>上配置cascade="delete-orhpan"
public void demo9(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
// 让1号客户与1号订单解除关系:
Customer customer = (Customer) session.get(Customer.class, 1);
Order order = (Order) session.get(Order.class, 1);
customer.getOrders().remove(order); customer.getOrders().拿到集合,remove(order)将集合中的order删除)
tx.commit();
session.close();
}

1.5.5    Hibernate中的级联取值:

在对象 关系映射文件中 , 用于映射持久化类之间关联关系的元素 ,<set>, <many-to-one> <one-to-one> 都有一个 cascade 属性 , 它用于指定如何操纵与当前对象关联的其他对象 .

none            :不使用级联

dave-update     :保存或更新的时候级联

delete          :删除的时候级联

all             :除了孤儿删除以外的所有级联.

delete-orphan   :孤儿删除(孤子删除).

        * 仅限于一对多.只有一对多时候,才有父子存在.认为一的一方是父亲,多的一方是子方.

        * 当一个客户与某个订单解除了关系.将外键置为null.订单没有了所属客户,相当于一个孩子没有了父亲.将这种记录就删除了.

        所谓父子关系:

                是指父方来控制子方的持久化生命周期,子方对象必须和一个父方对象关联

all-delete-orphan   :包含了孤儿删除的所有的级联.(最全的)

customer.hbm.xml<set>元素的cascade属性取值为all-delete-orphan,

  Hibernate会按照如下方式处理customer对象:

        1.当保存或更新customer对象时,级联保存或更新所有关联的order对象,

              相当于save-update.

        2.当删除customer对象时,级联删除所有的order对象,相当于delete

        3.删除不再和customer对象关联的所有order对象。

               当关联双方存在父子关系时,就可以把父方的cascade属性设为all-delete-orphan.

1.5.6    双向维护产生多余的SQL:

配置inverse=”true”:在那一端配置.那么那一端放弃了外键的维护权.

* 一般情况下,一的一方去放弃. 

cascade:操作关联对象.

inverse:控制外键的维护.

@Test
// 双向维护:自动更新数据库,产生多余的SQL.
// 双方都有外键的维护能力.必须让其中一方放弃外键的维护权.(一般情况下都是一的放弃.)
public void demo10(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Customer customer = (Customer) session.get(Customer.class, 1);//查一号客户
Order order = (Order) session.get(Order.class, 2);//找到二号订单
customer.getOrders().add(order);//二号订单放进来
order.setCustomer(customer); //都是持久态对象,发生变化会自动更新数据库,但是自动更新数据库会产生多余的sql语句
tx.commit();
session.close();

}


Hibernate会自动清理缓存中的所有持久化对象,按照持久化对象的改变来同步更新数据库,因此执行了上述的两条更新语句所以会产生两条update语句

@Test

// 区分cascade和inverse
// 在Customer.hbm.xml中的<set>上配置 cascade="save-update" inverse="true"
public void demo11(){
Session session = HibernateUtils.openSession();
Transaction tx = session.beginTransaction();
Customer customer = new Customer();
customer.setCname("张三"); //设置客户名称
Order order = new Order();
order.setAddr("西三旗");
customer.getOrders().add(order);//客户关联订单
// 客户是否存到数据库:存
// 订单是否存到数据库:存 原因:由cascade="save-update"决定,把关联对象保存起来.外键是null.,保存订单的时候,才会维护外键。(cascade看关键对象, inverse看外键
session.save( customer);
tx.commit();
session.close();

}

1.5.7    Hibernate的多对多的配置:

多对多的实体关系模型也是很常见的,比如学生和课程的关系。一个学生可以选修多门课程,一个课程可以被多名学生选修。在关系型数据库中对于多对多关联关系的处理一般采用中间表的形式,将多对多的关系转化成两个一对多的关系。 

第一步:创建实体类:

学生的实体:

public class Student {

private Integer sid;

private String sname;

// 一个学生选择多门课程:

private Set<Course>courses = new HashSet<Course>();

public Integer getSid() {

    return sid;

}

public void setSid(Integersid) {

    this.sid = sid;

}

public String getSname() {

    return sname;

}

public void setSname(Stringsname) {

    this.sname = sname;

}

public Set<Course>getCourses() {

    return courses;

}

public voidsetCourses(Set<Course> courses) {

    this.courses = courses;

}

}

课程的实体:

public class Course {

private Integer cid;

private String cname;

// 一个课程被多个学生选择:

private Set<Student>students = new HashSet<Student>();

public Integer getCid() {

    return cid;

}

public void setCid(Integercid) {

    this.cid = cid;

}

public String getCname() {

    return cname;

}

public void setCname(Stringcname) {

    this.cname = cname;

}

public Set<Student>getStudents() {

    return students;

}

public voidsetStudents(Set<Student> students) {

    this.students = students;

}

第二步建立映射:

Student.hbm.xml

<hibernate-mapping>

<class name="cn.itcast.hibernate3.demo3.Student"table="student">

    <!-- 配置唯一标识 -->

    <id name="sid" column="sid">

        <generator class="native"/>

    </id>

    <!-- 配置普通属性 -->

    <property name="sname" column="sname"length="20"/>

    <!-- 配置关联映射 -->

    <!-- <set>标签name:对应学生中的课程集合的名称   table:中间表名称. -->

    <set name="courses" table="stu_cour">

        <!-- <key>中column写当前类在中间表的外键.-->

        <key column="sno"></key>

        <!--<many-to-many>中class:另一方类的全路径. column:另一方在中间表中外键名称-->

        <many-to-many class="cn.itcast.hibernate3.demo3.Course" column="cno"/>

    </set>

</class>

</hibernate-mapping> 

Course.hbm.xml

<hibernate-mapping>

<class name="cn.itcast.hibernate3.demo3.Course" table="course">

    <!-- 配置唯一标识 -->

    <id name="cid" column="cid">

        <generator class="native"/>

    </id>

    <!-- 配置普通属性 -->

    <property name="cname" column="cname"length="20"/>

    <!-- 配置与学生关联映射 -->

    <!-- <set>中name:对应当前类中的学生的集合的名称  table:中间表的名称-->

    <set name="students" table="stu_courinverse="true">

        <!-- <key>中column:当前类在中间表中外键 -->

        <key column="cno"></key>

        <!--<many-to-many>中class:另一方的类全路径. column:另一方在中间表中外键名称 -->

        <many-to-manyclass="cn.itcast.hibernate3.demo3.Student"column="sno"/>

    </set>

</class>

</hibernate-mapping> 

双向关联报错,

第三步:将映射文件加入到核心配置文件中:


改变1号学生和2号课程的关联关系,改为1号学生和1号课程


一对一关联指两个表之间的记录是一一对应的关系。分为两种:外键关联和主键关联。

        对于基于外键的 1-1 关联,其外键可以存放在任意一边, 在需要存放外键一端,增加 many-to-one 元素 。为 many-to-one 元素增加 unique=“true” 属性来表示为 1-1 关联,并用 name 属性来指定关联属性的属性名

另一端需要使用 one-to-one 元素,该元素使用 property-ref 属性指定使用被关联实体主键以外的字段作为关联字段



        一对一的另一种解决方式就是主键关联,在这种关联关系中,要求两个对象的主键必须保持一致,通过两个表的主键建立关联关系,无须外键参与。

基于主键的映射策略 : 指一端的主键生成器使用 foreign 策略 , 表明根据”对方”的主键来生成自己的主键,自己并不能独立生成主键 .< param > 子元素指定使用当前持久化类的那个属性作为“对方”

采用 foreign 主键生成器策略的一端增加 one-to-one 元素映射关联属性,其 one-to-one 属性还应增加 constrained=“true” 属性;另一端 (company) 增加 one-to-one 元素映射关联属性。
constrained( 约束 ): 指定为当前持久化类对应的数据库表的主键添加一个外键约束,引用被关联的对象 (“ 对方” ) 所对应的数据库表主键



内容总结:

Hibernate持久化对象的状态:

        * 瞬时态:

        * 持久态:

        * 脱管态:

        * 状态之间的转换: 

Hibernate一级缓存:

        * Hibernate的一级缓存的存在.

                * get/load/save

        * Hibernate的一级缓存的结构:

                * 快照区.

        * Hibernate一级缓存的管理:

                *clear()/evict()/flush()/refresh();

        * 刷出时机(了解)

Hibernate操作持久化对象常用方法:

        * save();

        * update();

        * saveOrUpdate();

        * 瞬时:执行save

        * 脱管:执行update

        * delete();

        * get()/load();直接获得持久对象.

Hibernate的关联关系:(*****)

        * 一对多的配置:(手动配置)

                * 级联.

        * 多对多的配置:(手动配置)

                * 级联: 

猜你喜欢

转载自blog.csdn.net/qq_24140237/article/details/79769860