Hibernate 笔记二

对象持久化过程

  • 持久化作用

    持久化封装了数据访问的细节,为业务逻辑层提供了面向对象的API.完善的持久化层应该达到
    代码重用性好,可完成所有数据访问工作,具有独立性,当持久层发生变化时,不影响上层实现
    
  • Hibernate的持久化对象具体作用

    对象与数据库同步,使用对象操作数据库。
    
    实体域对象在内存中创建后,不能永久存在。将实体域对象永久保存起来,就是持久化的过程。通常只有实体域对象需要持久化,过程域对象和事件域对象一般不需要持久化。广义持久化指增、删、改、查。
    
  • 在持久化类的过程

    实际就是操作get set方法
    

Session缓存管理

一级缓存:

    一级缓存中的数据使用oid用来区分对象,当查询数据库的时候,
    如果一级缓存中存在oid的数据,则不会产生select语句,
    Hibernate对查询数据库实现了性能优化让缓存中的数据一直存在呢(即使Session关闭也存在),大家可以期待Hibernate的二级缓存。

快照

    Hibernate除了在内存中定义一级缓存之外,
    还存在一个快照区域(即对数据库数据的一份复制),
    在提交事务(清理缓存)的时候,会用缓存的数据和快照的数据进行比对,
    如果没有发生变化不执行update语句,如果发生了变化,则执行update语句

主键生成策略

Hibernate的唯一性标识
(1)Java按地址区分同一个类的不同对象.
(2)关系数据库用主键区分同一条记录.
(3)Hibernate使用OID来建立内存中的对象和数据库中记录的对应关系。对象的OID和数据库的表的主键对应。为保证OID的唯一性,应该让Hibernate来为OID赋值。

  • 自然主键和代理主键

    自然主键:具有业务含义的字段,如name
    代理主键:不具有业务含义的字段,如id
    
  • increment

    先获取当前的id的最大值,再对添加的条目id做+1操作,时hibernate维护自增的一种方式
    
    特点:跨数据库,不适合多进程并发更新数据库,适合单一进程访问数据库,不能用于群集环境。
    
  • identity

    identity是由数据库自己生成的,但这个主键必须设置为自增长,
    
    相当于再创表时设置 autoincrement 只能用在支持自动增长的字段数据库中使用,如MySQL。不支持orical
    
  • native

    特点:根据数据库自动选择,项目中如果用到多个数据库时,可以使用这种方式,使用时需要设置表的自增字段或建立序列,建立表等。
    
    例如MySQL使用identity,Oracle使用sequence
    
  • uuid

    UUID:Universally Unique Identifier,是指在一台机器上生成的数字,它保证对在同一时空中的所有机器都是唯一的。按照开放软件基金会(OSF)制定的标准计算,用到了以太网卡地址、纳秒级时间、芯片ID码和许多可能的数字,标准的UUID格式为:
    
    特点:uuid长度大,占用空间大,跨数据库,不用访问数据库就生成主键值
    

集合映射

一对多关联关系

  单项关联:仅仅建立从Order到Customer的多对一关联,
    即仅仅在Order类中定义customer属性。
    或者仅仅建立从Customer到Order的一对多关联,
    即仅仅在Customer类中定义orders集合。
  • 第一步:配置两个持久化对象

    //顾客类
    public class Customer implements Serializable {
    private Integer id;
    private String name;
    
    //订单类
    public class Order implements Serializable {
    private Integer id;
    private String name;
    private String orderNO;
    private Customer customer;
    
  • 第二步:建立2个持久化类对应的映射文件Customer.hbm.xml和Order.hbm.xml:

    <?xml version="1.0" encoding="UTF-8"?>
    <hibernate-mapping>
    <class name="com.exampe.domain.Customer" table="t_customer">
    <id name="id" type="integer">
        <column name="id"></column>
        <generator class="identity"></generator>
    </id>
    <property name="name" type="string">
        <column name="name"></column>
    </property>
    </class>
    </hibernate-mapping>
    
    
    <class name="com.exampe.domain.Order" table="t_order">
    <id name="id" type="integer">
        <column name="id"></column>
        <generator class="identity"></generator>
    </id>
    <property name="name" type="string">
        <column name="name"></column>
    </property>
    <!-- 创建一对多关联 name代表属性值 class 关联类的全路径名称 customer_id指的是外键 -->
    <many-to-one name="customer" class="com.exampe.domain.Customer"
        column="cid" >
    
    </many-to-one>
    

  • 第三步 存储两个表的数据信息

    //配置两个类的关联
    configuration.configure();
    configuration.addClass(Customer.class);
    configuration.addClass(Order.class);
    
    
    //获取session
    Session session = sf.openSession();
    Transaction transaction = session.beginTransaction();
    //创建customer对象,并设置内容
    Customer customer = new Customer();
    customer.setName("李四");
    //创建订单对象
    Order order = new Order();
    order.setName("滑板鞋2");
    order.setOrderNO("N02");
    //进行对象关联
    order.setCustomer(customer);
    //存储到session中
    session.save(order);
    session.save(customer);
    //提交事务
    transaction.commit();
    session.close();
    

双项关联:既建立从Order到Customer的多对一关联,
又建立从Customer到Order的一对多关联。

  • 第一步 定义双向关联的持久化对象
    public class Customer implements Serializable {
    private Integer id;
    private String name;
    private Set orders = new HashSet();

    public class Order implements Serializable {
        private Integer id;
        private String name;
        private String orderNO;
        private Customer customer;
    
  • 第二步 配置对应的xml信息

    <class name="com.exampe.domain.Customer" table="t_customer">
    <id name="id" type="integer">
        <column name="id"></column>
        <generator class="identity"></generator>
    </id>
    <property name="name" type="string">
        <column name="name"></column>
    </property>
    <--! 这里创建一个set集合,定义名称以及关联的表明,指定对应的时t_order中哪一个键-->
    <set name="orders" table="t_order">
        <key>
            <column name="cid"></column>
        </key>
        <one-to-many class="com.exampe.domain.Order" />
    </set>
    

  • 第三步 代码测试

    // 获取session
    Session session = sf.openSession();
    Transaction transaction = session.beginTransaction();
    Order order = new Order();
    order.setName("电脑");
    order.setOrderNO("N03");
    
    Customer customer = new Customer();
    customer.setName("李四");
    // 顾客关联订单
    customer.getOrders().add(order);
    // 订单关联顾客
    order.setCustomer(customer);
    session.save(order);
    session.save(customer);
    // 提交事务
    transaction.commit();
    session.close();
    

一对多关系中的属性配置

  • cascade:级联:

    save-update:级联保存更新

  • inverse

    在hibernate中通过对 inverse 属性的值决定是由双向关联的哪一方来维护表和表之间的关系. inverse=false 的为主动方,inverse=true 的为被动方, 由主动方负责维护关联关系
    在没有设置 inverse=true 的情况下,父子两边都维护父子关系 
    在 1-n 关系中,将 n 方设为主控方将有助于性能改善(如果要国家元首记住全国人民的名字,不是太可能,但要让全国人民知道国家元首,就容易的多)
    在 1-N 关系中,若将 1 方设为主控方 会额外多出 update 语句
    结论:
    1.在映射一对多的双向关联关系时,应该在one方把inverse属性设为true,这可以提高性能。
    2.在建立两个对象的关联时,应该同时修改关联两端的相应属性:
       customer.getOrders().add(order);
       order.setCustomer(customer);
    这样才会使程序更加健壮,提高业务逻辑层的独立性,使业务逻辑层的程序代码不受Hibernate实现类的影响。同理,当删除双向关联的关系时,也应该修改关联两端的对象的相应属性:
    Customer.getOrders().remove(order);
    Order.setCustomer(null);
    

注解配置

对象状态

持久化状态

save方法能够将临时状态转化程持久化状态
使用get或者load方法获取的对象肯定是持久化状态
位于session缓存中

oid 不为空
位于session缓存中
当执行查询时,持久化对象和数据库中的相关记录
session在清理缓存时,会根据持久化对象的属性变化,来同步更新数据库中的内容
在同一个session实例的缓存中,数据库表中的每条记录只对应唯一的持久化对象。

临时状态

new 对象时为临时状态

在使用代理主键的情况下,oid通常为null,
不处于session的缓存中
在数据库中没有对应的记录

删除状态

当调用session的delete方法时,对象状态就变成了删除状态

    OID 不为 null
    从一个 Session实例的缓存中删除
    Session 已经计划将其从数据库删除, Session 在清理缓存时, 会执行 SQL delete 语句, 删除数据库中的对应记录
    一般情况下, 应用程序不该再使用被删除的对象

游离状态

当调用session.close();session的缓存被清空,缓存中所有对象都会变成游离对象,生命周期结束

session的evict()方法能够从缓存中删除一个持久化对象,从而时该持久化对象变成游离对象,当session的缓存中保存了大量的持久化对象,会消耗很多内存空间,为了提高性能,调用evict方法,从缓存中删除一些对象

oid不为null
不处于session缓存中
一般有持久化对象转化成游离对象

代码示例

@Test
public void add() {
    // 获取session
    Session session = sf.openSession();
    Transaction transaction = session.beginTransaction();
    // 创建customer对象,并设置内容
    // 1 刚刚创建对象---临时状态---
    Customer customer = new Customer();
    customer.setName("赵六");
    // 存储到session中
    // 2将对象保存到session中----持久化状态----,
    session.save(customer);
    // 3 删除 对象  删除状态
    session.delete(customer);
    // 提交事务
    transaction.commit();
    session.close();
    //4 当session关闭之后,对象变成游离状态
    System.out.print(customer.getId()+"-----");
}

对象状态之间转换

  • 持久化对象和游离状态转换

    //持久化状态和游离状态之间转换(了解)
    Session s = sf.openSession();
    Transaction tr = s.beginTransaction();
    
    Customer c1 = (Customer)s.get(Customer.class, 1);//产生select语句
    s.evict(c1);//将持久对象转换成游离对象
    s.update(c1);//将游离对象转换成持久对象
    
    Customer c2 = (Customer)s.get(Customer.class, 1);       
    tr.commit();//先执行s.flush,然后再提交事务
    s.close();
    

Session操作数据库的方法总结

  • save()方法:

    将临时对象转换持久对象
    
  • update()方法:将游离对象转换持久对象

    注意:当 update() 方法关联一个游离对象时, 
    如果在 Session 的缓存中已经存在相同 OID 的持久化对象, 会抛出异常
    
  • evict()方法:

    将持久对象转换成游离对象
    
  • saveOrUpdate()方法:

    存在save方法和update方法的特征
    
  • get() load()

    都可以根据给定的 OID 从数据库中加载一个持久化对象
    区别:
    1:当数据库中不存在与 OID 对应的记录时, load() 方法抛出 ObjectNotFoundException 异常, 而 get() 方法返回 null
    2:两者采用不同的延迟检索策略
    

Session缓存管理

lazy 懒加载介绍

  • 概念介绍

立即检索: 立即加载检索方法指定的对象,对应配置文件中lazy=false

延迟检索: 延迟加载检索方法指定的对象,对应配置文件中lazy=true

测试立即检索和延迟检索的代码,注意只有load()方法进行查询的时候,会产生延迟检索。
模型类设置为final修饰将无法生成代理对象

 get()方法:立即检索,只要调用get方法查询数据库,马上执行sql语句,查询对应的结果

 load()方法:会产生延迟检索,调用load()方法的时候,不会马上查询数据库,而是产生一个代理对象,


 代理对象中只初始化对象的OID,不会初始化其他的属性值
 而是调用其他属性值的时候,例如c.getName(),此时才会组织sql语句,查询数据库,返回对应的结果


 load()方法如何立即检索呢?
 只需要更改 class name="xxx" table="xxx" lazy="false">
  使用load方法可以对性能进行优化,如果只想获取oid的值(比如删除),此时会采用load()方法比get()更适合,因为不需要查询数据库,就可以获取oid
  • load方式访问游离状态的对象

    org.hibernate.LazyInitializationException: could not initialize proxy - no Session
    
    Customer c = (Customer)s.load(Customer.class, 1);
        if(!Hibernate.isInitialized(c)){
            System.out.println(c.getClass());
            System.out.println(c.getId());
            System.out.println(c.getName());
            调用Hibernate.initialize(),将代理对象放置到方法中,此时就会查询数据库,返回对应的真实对象
            Hibernate.initialize(c);
            System.out.println(c.getName());
        }
    
  • 检索策略

    • 立即检索

      优点:不管对象处于持久化状态还是游离状态,都方便使用
      
      缺点:select 会直接将所有字段都查询出来
           关联查询时,可能会查出没必要查询出的对象,浪费内存
      
      优先使用场景:应用程序需要立即访问到对象
      
    • 延迟检索

      优点:可以自行决定查询哪些对象,降低内存消耗
      
      缺点:应用程序使用游离状态的对象时,必须保证其已经被初始化
      
      优先使用场景:应用程序不需要立即访问到对象的属性,比如只想获取到对象的oid,做删除操作。
                  一对多或者多对多关联时
      

管理Session(Session与本地线程绑定):

Hibernate 3 自身提供了三种管理 Session 对象的方法
    Session 对象的生命周期与本地线程绑定   thread
    Session 对象的生命周期与 JTA 事务绑定  jta*
    Hibernate 委托程序管理 Session 对象的生命周期  managed
  • 在hibernate的配置文件中,设置session与本地线程绑定的代码

    <property name="hibernate.current_session_context_class">thread</property>
    
    
    测试Session与本地线程绑定
        public void testSessionThread(){
    //      Session s1 = sf.openSession();
    //      Session s2 = sf.openSession();
    
            Session s1 = sf.getCurrentSession();
            Session s2 = sf.getCurrentSession();
    
            System.out.println(s1==s2);//false:此时说明2个Session不是一个对象;true:2个Session是一个对象(与本地线程绑定)
    
    //      s1.close();  
    //      s2.close();  
        }
    这里:不再调用sessionFactory.openSession().而是调用sessionFactory. getCurrentSession().获取session对象.从当前的线程提取session,
    * 当前线程如果存在session对象,取出直接使用
    * 当前线程如果不存在session对象,获取一个新的session对象和当前的线程绑定
    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值