• hibernate(二)对象的三种状态、一级缓存、多对一、inverse、cascade


    录:

    1、对象的三种状态
    2、session.save()方法
    3、与session建立关联的对象的id,不允许修改
    4、Hibernate一级缓存(更深层次理解hibernate中对象的操作)
    5、Hibernate一级缓存细节问题   
    6、session.save()方法和persist()方法的区别
    7、session的其他API
    8、映射文件表达多对一(或一对多)关系
    9、多对一关系-多表操作
    10、多对一关系的维护--inverse 属性
    11、cascade级联操作

    1、对象的三种状态    <--返回目录

      对象的三种状态
        1)瞬时态(或临时态): 没有与Hibernate产生关联,与数据库中的记录没有产生关联(有关联就是与数据库中的 id 有关联)
        2)持久态: 与Hibernate有关联,与数据库有关联(对象有 id)
        3)游离态(或托管态): 与Hibernate没有关联,与数据库有关联(对象有 id)

      如何判断:

        1)对象有 id <==> 与数据库有关联

        2)使用 session 操作过 对象 <==> 与 Hibernate 有关联

      HibernateUtils

    package com.oy.helloworld;
    
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.cfg.Configuration;
    public class HibernateUtils {
        // 会话工厂,整个程序只有一份。
        private static SessionFactory sf;
    
        static {
            // 1 加载配置
            Configuration config = new Configuration().configure();
    
            // 2 获得工厂
            sf = config.buildSessionFactory();
            // 3 关闭虚拟机时,释放SessionFactory
            Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
                public void run() {
                    System.out.println("虚拟机关闭!释放资源");
                    sf.close();
                }
            }));
        }
    
        // 获得一个新的session
        public static Session openSession() {
            return sf.openSession();
        }
    
        // 获得当前线程中绑定session
        public static Session getCurrentSession() {
            return sf.getCurrentSession();
        }
    }
    View Code

      测试代码

    public class TestHelloWorld {
        @Test
        public void fun1() {
            Session session = HibernateUtils.openSession();
            session.beginTransaction();
            // ===========================================
            User u = new User();  // 临时态
            u.setUsername("tom"); // 临时态
            u.setPassword("333"); // 临时态
            // save 方法会使用主键生成策略,为 User 指定 id
            session.save(u); // 持久态: 与Hibernate有关联,与数据库有关联(对象有 id)
            // ===========================================
            session.getTransaction().commit(); // 持久态
            session.close(); // 游离态
        }
    }
    @Test
    public void fun2() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();
        // ===========================================
        User u = (User) session.get(User.class, 1); // 持久状态
        u.setUsername("jerry");
        session.update(u); // 多余代码,Hibernate会自动将持久化状态对象的变化同步到数据库中,无需该代码
        // ===========================================
        session.getTransaction().commit();// 持久状态,打印update语句
        session.close(); // 游离状态
    }

    2、session.save()方法    <--返回目录

      session.save()方法作用:获得 id
        1)当主键生成策略为 identity 主键自增长时,该方法会通过 insert 语句来获得 id
        2)当主键生成策略为 increment,该方法会使用语句 select max(id) from t_user; (使用断点查看,是否生成该 sql 语句),然后事务提交时,才生成 insert 语句
        3)当主键生成策略为 assigned 时,在 session.save() 之前没有手动设置id,报错。


      关于save()方法的测试

    package com.oy.test;
    
    import java.util.List;
    
    import org.hibernate.Criteria;
    import org.hibernate.Query;
    import org.hibernate.SQLQuery;
    import org.hibernate.Session;
    import org.hibernate.SessionFactory;
    import org.hibernate.Transaction;
    import org.hibernate.cfg.Configuration;
    import org.hibernate.criterion.Restrictions;
    import org.junit.Test;
    
    import com.oy.domain.User;
    
    public class Demo1 {
        @Test
        /**
         * 主键生成策略increment:数据库自己生成。先从数据库总查询最大的ID值,将ID值加1;
         * 当主键生成策略为increment,save()方法会使用语句select max(uid) from t_user来给user设置uid;
         * 由于这里没有使用事务,最后没有使用insert语句,数据库没有添加记录;
         */
        public void fun1() {
            // 1.读取配置文件
            Configuration conf = new Configuration().configure();
            // 2.根据配置创建Factory
            SessionFactory sessionFactory = conf.buildSessionFactory();
            // 3.获得操作数据库的session对象
            Session session = sessionFactory.openSession();
            // 4.操作数据库
    
            //
            User user = new User();
            user.setUname("小张1");
            user.setPassword("123");
            System.out.println("save前:" + user);//save前:User [uid=0, uname=小张1, password=123]
            session.save(user);//当主键生成策略为increment,该方法会使用语句select max(uid) from t_user
                                //由于这里没有insert语句,后面也没有
    
            System.out.println("save后:" + user);//save后:User [uid=7, uname=小张1, password=123]
    
            // 5.关闭资源
            session.close();
            System.out.println("session关闭后:" + user);//session关闭后:User [uid=7, uname=小张1, password=123]
            sessionFactory.close();
        }
        
        @Test
        /**
         * 主键生成策略increment:数据库自己生成。先从数据库总查询最大的ID值,将ID值加1;
         * 当主键生成策略为increment,save()方法会使用语句select max(uid) from t_user来给user设置uid;
         * 由于这里使用事务,事务提交时使用insert语句添加记录。
         */
        public void fun2() {
            // 1.读取配置文件
            Configuration conf = new Configuration().configure();
            // 2.根据配置创建Factory
            SessionFactory sessionFactory = conf.buildSessionFactory();
            // 3.获得操作数据库的session对象
            Session session = sessionFactory.openSession();
            // 4.开启事务
            Transaction ts = session.beginTransaction();
            
            //
            User user = new User();
            user.setUname("小张1");
            user.setPassword("123");
            System.out.println("save前:" + user);//save前:User [uid=0, uname=小张1, password=123]
            session.save(user);//当主键生成策略为increment,该方法会使用语句select max(uid) from t_user
                                //这里没有insert语句
    
            System.out.println("save后:" + user);//save后:User [uid=8, uname=小张1, password=123]
    
            //5.提交事务
            ts.commit();  //事务提交,这里执行insert into语句
            // 6.关闭资源
            session.close();
            System.out.println("session关闭后:" + user);//session关闭后:User [uid=8, uname=小张1, password=123]
            sessionFactory.close();
        }
        
        @Test
        /**
         * 主键生成策略是identity
         */
        public void fun3() {
            // 1.读取配置文件
            Configuration conf = new Configuration().configure();
            // 2.根据配置创建Factory
            SessionFactory sessionFactory = conf.buildSessionFactory();
            // 3.获得操作数据库的session对象
            Session session = sessionFactory.openSession();
            // 4.操作数据库
    
            //
            User user = new User();
            user.setUname("小张2");
            user.setPassword("123");
            System.out.println("save前:" + user);//save前:User [uid=0, uname=小张2, password=123]
            session.save(user);//由于主键生成策略是identity,使用insert语句获取uid;
                                //使用insert语句获取uid,然后给user设置uid;
                                //由于使用了insert语句,save()方法后数据已经持久化到数据库中了;
            System.out.println("save后:" + user);//save后:User [uid=7, uname=小张2, password=123]
    
            // 5.关闭资源
            session.close();
            System.out.println("session关闭后:" + user);//session关闭后:User [uid=7, uname=小张2, password=123]
            sessionFactory.close();
        }
    }

    3、与session建立关联的对象的id,不允许修改    <--返回目录

    Session session = HibernateUtils.openSession();
    session.beginTransaction();
    User u = (User)session.get(User.class, 1);
    u.setId(3); //报错,因为与session建立关联的对象的id,不允许修改
    session.commint();
    session.close();

    4、Hibernate一级缓存(更深层次理解hibernate中对象的操作)    <--返回目录

      缓存:Hibernate中也存在缓存. Hibernate中存在的缓存也是用来提高效率.
      Hibernate中存在两种缓存:
              - 线程级别的缓存. Session缓存   (现在要学习的)
              - 进程级别的缓存. Hibernate 二级缓存.
      session缓存: 就是session对象中存在的缓存.缓存中存在的是(持久化)对象.
        
      证明session缓存的存在

    @Test
    public void fun2() {
        Session session = HibernateUtils.openSession();
        session.beginTransaction();
        // ===========================================
        User u1 = (User) session.get(User.class, 1);// 发送select语句,从数据库取出记录封装成User对象
                                                    // 持久化状态对象=>存到缓存中
        User u2 = (User) session.get(User.class, 1);// 再次查询时,会从缓存中查找,不会发送select语句
        // ===========================================
        session.getTransaction().commit();// 持久状态,打印update语句
        session.close(); // 游离状态
    }

           
      问题:一级缓存以什么形式存在的?
        Map形式存在,Map<串行化Serializable, Object>

      缓存中的快照: 在从数据库取得数据时,会将数据一式两份,一份作为缓存中的对象,一份作为快照。 在session提交时作为对比。

      一级缓存执行原理

    5、Hibernate一级缓存细节问题       <--返回目录

      持久化状态本质:就是存在缓存中的对象,就是持久化状态
        
      保存对象时可以使用save()方法和persist()方法
              - 区别:有区别
              - save()方法来自hibernate
              - persist()方法来自JPA接口,persist就是持久化的意思
        
      HQL语句批量查询时,查询结果是否会进入缓存?
              - 会。

    List<User> userList = session.createQuery("from User").list();//查询所有,并把查询出的结果封装成对象放入缓存
    User u = (User)session.get(User.class, 1);//若缓存中有id=1的对象,直接从缓存中取User对象;缓存没有找到,使用select where id=1查询数据库

      HQL查询是否会使用一级缓存?
              - 批量查询时不会。

      案例1:

    // 批量查询,不使用一级缓存
    List<User> userList1 = session.createQuery("from User").list();//打印select语句
    List<User> userList2 = session.createQuery("from User").list();//也会打印select语句

      案例2:

    // 批量查询,不使用一级缓存
    Query query1 = session.createQuery("from com.oy.domain.User where username='张三'");
    User u1 = (User)query1.uniqueResult();//执行hql语句,返回一个User对象
    Query query2 = session.createQuery("from com.oy.domain.User where username='张三'");
    User u2 = (User)query2.uniqueResult();//执行hql语句,返回一个User对象

      案例3:

    // 非批量查询,使用一级缓存
    Query query3 = session.createQuery("from com.oy.domain.User where id=2"); User u3 = (User)query3.uniqueResult();//执行hql语句,返回一个User对象 Query query4 = session.createQuery("from com.oy.domain.User where id=2"); User u4 = (User)query4.uniqueResult();//不执行hql语句

      案例4:

    List<User> userList1 = session.createQuery("from User").list();//打印select语句
    Query query3 = session.createQuery("from com.oy.domain.User where uid=2");
    User u3 = (User)query3.uniqueResult();//执行hql语句,返回一个User对象
    Query query4 = session.createQuery("from com.oy.domain.User where uid='2");
    User u4 = (User)query4.uniqueResult();//不执行hql语句

      案例5:

    Query query3 = session.createQuery("from com.oy.domain.User where uid=2");
    User u3 = (User)query3.uniqueResult();//执行hql语句,返回一个User对象
    User u = (User)session.get(User.class, 2);//不打印sql语句,直接从缓存中取

      原生SQL语句查询时,查询结果是否会进入缓存?
            - 如果有这句query.addEntity(User.class),会。否则不会。
            
      criteria查询,会将查询结果放入一级缓存,但是查询不会使用一级查询。与HQL一样的。

    6、session.save()方法和persist()方法的区别    <--返回目录

      - 主键生成策略为native,数据库主键设置自增
            - persist()方法体现的是持久化;persist()的理念是将对象完整的持久化,也包括对象的id;
              在保存之前设置了id,那么将会使用设置的id进行insert,但是主键策略是由数据库来维护,所以产出矛盾,所以抛出异常。
                User user = new User();
                user.setId(99);
                session.persist(user);  //这里报错
            - session.save()方法:如果保存之前设置了id,那么该id也被认为是无效的id。
                User user = new User();
                user.setId(99);
                session.savet(user);  //这里不报错

    7、session的其他API    <--返回目录

        * session.evict(user): 将user对象从session缓存中移除
        * session.clear(): 将session缓存中所有对象移除
        * session.refresh(user): 强制刷新缓存中的对象,发送seclet语句,将数据库中数据更新到session缓存。
          该方法可以用来解决缓存与数据库数据不同步的问题,但不是一种好的解决方案。
        * session.flush():对比快照,立刻将session缓存中对象提交(执行update语句)
        
        * saveOrUpdate方法:
            - 数据库为代理主键,设置主键自增,主键生成策略为native:主键为空,save;主键非空,update
            - 数据库为自然主键,主键生成策略为assigned: 必须手动设置id,然后根据id查询,查不到save,查到update

    8、映射文件表达多对一(或一对多)关系    <--返回目录

      订单表 t_order(多)                客户表 t_customer(一)
           oid    oname    cid(外键)             cid     cname    

      mysql 建表 sql

    create database test default character set utf8;
    use test;
    
    create table t_order(
        oid    int primary key auto_increment,
        oname varchar(40),
        cid int
    )ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    create table t_customer(
        cid    int primary key auto_increment,
        cname varchar(40)
    )ENGINE=InnoDB DEFAULT CHARSET=utf8;
    View Code

       
      实体类Order.java

    public class Order{
        private Integer oid;//订单id
        private String oname;//订单名称
        private Customer customer;//外键
    }

       
      实体类Customer.java

    public class Customer{
        private Integer cid;//客户id
        private String cname;//客户名称
        private Set<Order> orders = new HashSet<Order>();
    }

      先配置Order==>t_order的映射  Order.hbm.xml文件

    <class name="com.oy.domain.Order" table="t_order">
        <id name="oid" column="oid">
            <generator class="native"></generator>
        </id>
        <property name="oname" column="oname"></property>
        <many-to-one name="customer" column="cid" class="com.oy.domain.Customer"></many-to-one>
    </class>

       
      然后配置Customer==>t_customer的映射  Customer.hbm.xml文件

    <class name="com.oy.domain.Order" table="t_order">
        <id name="cid" column="cid">
            <generator class="native"></generator>
        </id>
        <property name="cname" column="cname"></property>
        
        <set name="orders">
            <key column="cid"></key>
            <one-to-many class="com.oy.domain.Order"></one-to-many>
        </set> 
    </class>

       
      在hibernate.cfg.xml配置文件中引入映射文件

    <?xml version="1.0" encoding="UTF-8"?>
        <!DOCTYPE hibernate-configuration PUBLIC
        "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
        "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
        
    <hibernate-configuration>
        <session-factory>
            <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
            <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/test?useUnicode=true&amp;characterEncoding=UTF-8</property>
            <property name="hibernate.connection.username">root</property>
            <property name="hibernate.connection.password">123456</property>
            
            <property name="show_sql">true</property>  <!--操作数据库时,向控制台打印sql语句-->
            <property name="format_sql">true</property>  <!--打印sql语句前,优化sql语句-->
            <property name="hbm2ddl.auto">update</property>  <!--是否自动生成表结构-->
            <property name="hibernate.connection.autocommit">true</property>  <!--事务自动提交-->
            
            <!-- 添加映射文件 -->
            <mapping resource="com/oy/domain/Customer.hbm.xml"/>
            <mapping resource="com/oy/domain/Order.hbm.xml"/>
        </session-factory>
    </hibernate-configuration>
    View Code

    9、多对一关系-多表操作    <--返回目录

    demo 源码见:链接:https://pan.baidu.com/s/12nwIyodOhVCEemaAyPVPtQ 提取码:tp9w

      增(下面代码是按照如下顺序:先持久化无外键所在对象,后持久化有外键的对象)

    Customer customer = new Customer();
    customer.setCname("张三1");
    session.save(customer);//没这句报错
                        //org.hibernate.TransientObjectException: object references an unsaved transient 
                        //instance - save the transient instance before flushing: com.oy.domain.Customer
    Order o1= new Order();
    o1.setOname("牙膏1");
    o1.setCustomer(customer);
    session.save(o1);

      增:session.save(customer); 移到后面也是可以的

    Customer customer = new Customer();
    customer.setCname("张三1");
    //session.save(customer);
    
    Order o1= new Order();
    o1.setOname("牙膏1");
    o1.setCustomer(customer);
    session.save(customer);//移到后面也是可以的
    session.save(o1);
    //session.save(customer);//移到这是也是可以的

       控制台打印结果:

    Hibernate: 
        insert 
        into
            t_customer
            (cname) 
        values
            (?)
    Hibernate: 
        insert 
        into
            t_order
            (oname, cid) 
        values
            (?, ?)
    虚拟机关闭!释放资源

      给已有的 Customer 新增 Order

    @Test
    public void fun2() {
        Session session = HibernateUtils.openSession();
        Transaction ts = session.beginTransaction();
        // ==============================================
        Customer customer = (Customer) session.get(Customer.class, 1);
    
        Order o1 = new Order();
        o1.setOname("手机");
        Order o2 = new Order();
        o2.setOname("电脑");
        
        customer.getOrders().add(o1);
        customer.getOrders().add(o2);
        
        session.save(o1);
        session.save(o2);
        session.save(customer);  // 如果 Customer.hbm.xml 配置了 inverse=true, 这句可以注释掉,并且不会打印 Update 语句
        // ==============================================
        ts.commit(); // 事务提交,这里使用insert into语句
        session.close();
    }

      打印结果:

    Hibernate: 
        select
            customer0_.cid as cid0_0_,
            customer0_.cname as cname0_0_ 
        from
            t_customer customer0_ 
        where
            customer0_.cid=?
    Hibernate: 
        select
            orders0_.cid as cid0_1_,
            orders0_.oid as oid1_,
            orders0_.oid as oid1_0_,
            orders0_.oname as oname1_0_,
            orders0_.cid as cid1_0_ 
        from
            t_order orders0_ 
        where
            orders0_.cid=?
    Hibernate: 
        insert 
        into
            t_order
            (oname, cid) 
        values
            (?, ?)
    Hibernate: 
        insert 
        into
            t_order
            (oname, cid) 
        values
            (?, ?)
    Hibernate: 
        update
            t_order 
        set
            cid=? 
        where
            oid=?
    Hibernate: 
        update
            t_order 
        set
            cid=? 
        where
            oid=?
    虚拟机关闭!释放资源

      

    10、多对一关系的维护--inverse 属性    <--返回目录

      外键维护的放弃,只能有非外键所在的的对象来放弃,即t_customer放弃维护关系


      在Customer.hbm.xml表中配置:t_customer放弃维护关系
        <set name="orders" inverse="true">
        inverse: 是否将关系的维护反转给对方,默认false;true表示放弃维护关系
        
      当inverse="false"时,删除t_customer表中记录,成功,但是会把引用该客户的外键cid设置为null;
      当inverse="true"时,删除t_customer表中记录,报错。这时候怎么办?

    List<Order> orders = customer.getOrders();
    for(Order o: orders) {
        o.setCustomer(null); // 设置订单不属于任何Customer
    }

    11、cascade级联操作    <--返回目录

      cascade: 级联操作

    <!-- 表达一对多关系中的集合
        name:集合的属性名称
        inverse: 是否将关系的维护反转给对方. 默认值: false
               true: 在Customer 中 放弃维护外键关系
               
        cascade: 级联操作
            save-update:级联保存,级联修改. 保存A时,同时保存B. 
            delete:删除A,同时删除B,AB都不存在
            delete-orphan:孤儿删除,解除关系,同时将B删除,A存在的。
            如果需要配置多项,使用逗号分隔。<set cascade="save-update,delete">
            
            all: save-update 和 delete 整合
            all-delete-orphan: 三个整合
            
     -->
    <set name="orders" inverse="false" cascade="all-delete-orphan"  >
        <!--
            key 用来描述外键
            column : 外键的值
          -->
        <key column="cid" ></key>
        <!-- one-to-many 表达, Customer 与orders 的关系是一对多
            class: 表达关联的另一方的完整类名
         -->
        <one-to-many class="Order" />
    </set>

      

      级联删除要注意:不要在两表之间都使用级联删除,否则会全部删除数据!!!

    ---

  • 相关阅读:
    Xcode修改新建项目注释模板(作者和公司名等)
    Xcode全局替换内容,一键Replace
    The type javax.ws.rs.core.MediaType cannot be resolved. It is indirectly referenced from required .class files
    IT公司的女流之辈
    论公司的核心竞争力
    人性
    如何使用yum 下载 一个 package ?如何使用 yum install package 但是保留 rpm 格式的 package ? 或者又 如何通过yum 中已经安装的package 导出它,即yum导出rpm?
    The type java.lang.CharSequence cannot be resolved. It is indirectly referenced from required .class files.
    maven repo plugin archiver
    Error connecting to database [Can't connect to local MySQL server through socket '/var/lib/mysql/mysql.sock' (13)]
  • 原文地址:https://www.cnblogs.com/xy-ouyang/p/13121488.html
Copyright © 2020-2023  润新知