关于hibernate的见解

来源:互联网 发布:有哪些网络调查网站 编辑:程序博客网 时间:2024/05/22 11:35
Hibernate:
    什么是hibernate:
        Hibernate它是一个轻量级的jdbc封装,也就是说,我们可以使用hibernate来完成原来我们使用jdbc完成操作,就是与数据库的交互操作。它是在dao层去使用的。

        对象关系映射(英语:Object Relation Mapping,简称ORM,或O/RM,或O/R mapping)
        简单说,我们使用orm可以将我们的对象与我们的类去进行映射,使的我们可以去操作对象就完成对表的操作。
    
    hibernate的学习重点:
        掌握hibernate的基本配置-------搭建hibenate开发环境
        掌握hibernate常用API---------如何使用hibedrnate框架进行开
        掌握hibernate的关联映射--------解决表与表之间存在关系问题 1:n  1:1  m:n
        掌握hibernate的检索方式-------就是掌握hibernate的查询
        掌握hibernate的优化方式------提高hibernate效率

    hibernate的快速入门:
        下载hibernate;
        ***注意:高版本的需要jdk8才可以
        hibernate目录结构:
            documentation目录:存放hibernate的相关文件与API
            lib目录:存放hibernate编译和运行所依赖的jar包,其中required子目录下包含了运行hibernate项目必须的jar包
            project目录:存放hibernate各种相关的源代码与资源.
            在lib/required目录中,包含的jar包

        创建数据库跟表:
            CREATE DATABASE hibernateTest;

            USER hibernateTest;

            CREATE TABLE t_customer(
                id INT PRIMARY KEY AUTO_INCREMENT,
                NAME VARCHAR(20),
                address VARCHAR(50)
            )

        创建实体类:
            public class Customer
            
            private int id;
            private String name ;
            private String address;
            private String sex;
            
        导入hibernate框架相关依赖jar包
            导入lib/required下所有的jar
            导入数据库的驱动jar包
            日志相关jar包
            将hibernate/project/etc/log4j.properties文件导入到工程src下.
        
        hibernate的相关文件配置工作:
            有两种:
            xxx.hbm.xml 它主要是用于描述类与数据库中的表的映射关系.
            hibernate.cfg.xml 它是hibernate框架核心配置文件。
    
        映射配置文件:
            位置:它要与实体类在同一个包下.
            名称 :类名.hbm.xml
            约束:
            <!DOCTYPE hibernate-mapping PUBLIC
                "-//Hibernate/Hibernate Mapping DTD 3.0//EN"
                "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">
                        
            
            配置:
                <?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>
                    <!-- name是类的全名 table 是表名 catal是数据库名 -->
                    <class name="com.itheima.domain.Customer" table="t_customer"
                        catalog="hibernateTest">
                        <!-- 主键生成策略 -->
                        <id name="id" column="id">
                            <generator class="native"></generator>
                        </id>
                        <property name="name" column="name" length="20" type="string"></property>  <!-- hibernate数据类型 -->
                        <property name="address">
                            <column name="address" length="50" sql-type="varchar(50)"></column> <!-- sql数据类型 -->
                        </property>
                        <property name="sex" column="sex" length="20"></property>
                    </class>
                </hibernate-mapping>
                            
        核心配置文件:
            它主要是hibernate框架所使用的,它主要包含了连接数据库相关信息,hibernate相关配置等。
            位置:在src下创建一个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">

            配置文件:
                <?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:///hibernateTest</property>
                        <property name="hibernate.connection.username">root</property>
                        <property name="hibernate.connection.password">root</property>
                        <!-- c3p0的配置 -->
                        <property name="hibernate.c3p0.max_size">20</property>
                        <property name="hibernate.c3p0.min_size">5</property>
                        <property name="hibernate.c3p0.timeout">120</property>
                        <property name="hibernate.c3p0.idle_test_period">3000</property>
                        <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</property>
                        <!-- 事务自动提交的设置 -->
                        <property name="hibernate.connection.autocommit">false</property>
                            <!-- 自动创建表 -->
                        <property name="hibernate.hbm2ddl.auto">update</property>
                        <!-- 可以将数据库发送sql显示出来 -->
                        <property name="hibernate.show_sql">true</property>
                        <!-- 格式化sql -->
                        <property name="hibernate.format_sql">true</property>
                        <!-- hibernate方言 -->
                        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
                        <!-- 配合映射文件所在的位置 -->
                        <mapping resource="com/itheima/domain/Customer.hbm.xml"/>
                    </session-factory>
                </hibernate-configuration>
                        
            
        Hibernate快速 入门开发测试:
                @Test
                public void saveCustomerTest1(){
                    Customer customer = new Customer();
                    
                    customer.setName("马老师");
                    customer.setAddress("中山桥");
                    
                    //固定步骤  使用hibernate中的配置来保存数据到数据库中
                    Configuration configure = new Configuration().configure();
                    SessionFactory sessionFactory = configure.buildSessionFactory();
                    Session session = sessionFactory.openSession();
                    
                    session.beginTransaction();//开启事务
                    session.save(customer);//保存操作
                    session.getTransaction().commit();;//提交事务
                    
                    session.close();
                    sessionFactory.close();
                    
                }
            
            
        Hibernate执行原理总结
            hibernate工作原理:
            1,通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件。
            2,由hibernate.cfg.xml中的<mappingresource="com/xx/User.hbm.xml"/>读取解析映射信息。
            3,通过config.buildSessionFactory();//得到sessionFactory。
            4,sessionFactory.openSession();//得到session。
            5,session.beginTransaction();//开启事务。
            6,persistent operate;
            7,session.getTransaction().commit();//提交事务
            8,关闭session;
            9,关闭sessionFactory;
        
    Hibernate的配置详解        
        Hibernate中我们使用时主要有两种配置文件;
            核心配置文件  hibernate.cfg.xml:
                对于hibernate的核心配置文件它有两种方式:
                1.    hibernate.cfg.xml
                2.    hibernate.properties
                我们在开发中使用比较多的是hibernate.cfg.xml这种方式,原因它的配置能力更强,易于修改
                我们主要讲解的是hibernate.cfg.xml配置

                * 可以加载数据库相关信息:
                    <!-- 配置数据库链接的四个项 -->
                        <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
                        <property name="hibernate.connection.url">jdbc:mysql:///hibernateTest</property>
                        <property name="hibernate.connection.username">root</property>
                        <property name="hibernate.connection.password">root</property>
            
                * hibernate相关配置:
                    <!-- 可以将数据库发送sql显示出来 -->
                        <property name="hibernate.show_sql">true</property>
                        <!-- 格式化sql -->
                        <property name="hibernate.format_sql">true</property>
                        <!-- hibernate方言 -->
                        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>    
                
                * 加载映射配置文件:
                    <!-- 配合映射文件所在的位置 -->
                        <mapping resource="com/itheima/domain/Customer.hbm.xml"/>
            
                对于hibernate.cfg.xml配置文件中的内容可以参考hibernate/project/etc/hibernate.properties的配置
            
                *配置这个属性后,我们可以进行表的自动创建
                        <!-- 自动创建表 -->
                        <property name="hibernate.hbm2ddl.auto">update</property>
            
                    Create-drop 每次都会创建一个新的表,执行完成后删除。一般在测试中使用
                    Create   每次都会创建一个新的表,一般是在测试中使用
                    update   如果数据库中有表,不创建,没有表创建,如果映射不匹配,会自动更新表结构(只能添加)
                    validate  只会使用存在的表,并且会对映射关系进行校验.

                                
            映射配置文件   xxx.hbm.xml
                映射配置文件它的名称是类名.hbm.xml,它一般放置在实体类所在的包下。
                这个配置文件的主要作用是建立表与类的映射关系。

                统一声明包名,这样在<class>中就不需要写类的全名.
                    <hibernate-mapping package="com.itheima.domain">
            
                关于<class>标签配置
                    name属性:类的全名称
                    table 表的名称,可以省略,这时表的名称就与类名一致
                    catalog属性:数据库名称 可以省略.如果省略,参考核心配置文件中url路径中的库名称

                关于<id>标签
                    首先它必须存在。<id>是用于建立类中的属性与表中的主键映射。
                    name 类中的属性名称
                    column 表中的主键名称  column它也可以省略,这时列名就与类中属性名称一致
                    length 字段长度
                    type属性 指定类型

                    <generator>它主要是描述主键生成策略.

                关于<property>标签
                    它是描述类中属性与表中非主键的映射关系

                关于hibernate的映射文件中类型问题
                    对于type属性它的取值,可以有三种:
                    1.    java中的数据类型
                    2.    hibernate中的数据类型
                    3.    SQL的数据类型
                    
                    默认是hibernate中数据类型
            
            
    Hibernate常用API;
        Hibernate的核心类和接口一共有6个,分别为:Session,SessionFactory,
        Transaction,Query,Criteria和Configuration。这6个核心类和接口在任何开发中都会用到

        Configuration
            它主要是用于加载hibernate配置.
            Configuration config=new Configuration().config(); 主要加载src下的hibernate.cfg.xml
            Configuration config=new Configuration();主要加载的src下的hibernate.properties
            Configuration config=new Configuration().config(核心配置文件名称);加载指定的名称的配置文件
            问题:我们是在hibernate.cfg.xml文件中有xxx.hbm.xml文件的位置。
            如果我们使用的是hibernate.properties这种核心配置,它如何加载映射配置?
            //手动加载
            //config.addResource("com/itheima/domain/Customer.hbm.xml");直接加载配置文件
            //config.addClass(Customer.class);//这种方式会直接在实体类所在的包下查找规范映射配置文件
            
        SessionFactory
            首先SessionFactory它的获取是通过Configuration得到。    
            SessionFactory sessionFactory = configure.buildSessionFactory();
            SessionFactory接口负责初始化Hibernate。它充当数据存储源的代理,并负责创建Session对象。
            这里用到了工厂模式。
            需要注意的是SessionFactory并不是轻量级的,因为一般情况下,一个项目通常只需要一个SessionFactory就够,
            当需要操作多个数据库时,可以为每个数据库指定一个SessionFactory。
            
            通过SessionFactory可以得到Session.
            Session session = sessionFactory.openSession();
            是从连接池中获取一个连接。
            
            Session session = sessionFactory.getCurrentSession();
            获取一个与线程绑定的Session.
            
            SessionFactory它不是轻量级的,不要频繁创建关闭它。
            在一个项目中有一个SessionFactory就可以,通过SessionFactory来获取Session进行操作。
            问题:怎样可以保证在一个项目中所使用的SessionFactory是同一个哪?
                可以写一个工具类
                public class HibernateUtils {
    
                    private static Configuration config;
                    private static SessionFactory sessionFactory;
                    static{
                        config=new Configuration().configure();
                        sessionFactory=config.buildSessionFactory();
                    }
                    
                    public static Session openSession(){
                        return sessionFactory.openSession();
                    }

                }

            SessionFactory内部还维护了一个连接池,如果我们要想使用c3p0连接池,应该怎样处理?

                1.    我们要导入c3p0的相关jar包
                在hibernate/lib/options下有关于c3p0连接池jar包

                2.    在hibernate.cfg.xml文件中配置c3p0连接
                可以查看etc/hibernate.properties中关于c3p0的配置
                    <!-- c3p0的配置 -->
                        <property name="hibernate.c3p0.max_size">20</property>
                        <property name="hibernate.c3p0.min_size">5</property>
                        <property name="hibernate.c3p0.timeout">120</property>
                        <property name="hibernate.c3p0.idle_test_period">3000</property>
                        <property name="hibernate.connection.provider_class">
                        org.hibernate.connection.C3P0ConnectionProvider</property>
            
        Session
            Session接口负责执行被持久化对象的CRUD操作
            (CRUD的任务是完成与数据库的交流,包含了很多常见的SQL语句)。
            但需要注意的是Session对象是非线程安全的。
            问题:我们如何解决session的安全问题?
                我们只需要在方法内部来使用Session就可以。

            问题:Session如何获取到?
                SessionFactory.openSession()    ;  相当于直接通过SessionFactory创建一个新的Session,使用完成后要手动调用close来关闭。
                SessionFactory.getCurrentSession(); 获取一个与线程绑定的Session,当我们提交或事务回滚后会自动关闭。
                Session常用的方法:
                    save  保存对象
                    update 修改操作
                    delete删除
                    get/load 根据id进行查询
                    savenOrUpdate 执行save或update操作
                    createQuery()获取一个Query对象
                    CreateSQLQUery()获取一个可以操作sql的SQLQuery对象
                    createCriteria() 获取一个Criteria它可以完成条件查询

                        
        Transaction    
            Transaction接口主要用于管理事务,它是hibernate的事务接口,对底层的事务进行了封装。使用它可以进行事务操作
            commit 事务提交
            rollback 事务回滚

            问题:如何获取一个Transaction对象
                    Session.beginTransaction();

            问题:如果在程序中没有开启事务,是否存在事务?
                有事务,session的每一个操作就会开启一个事务。

            默认情况下事务是不会自动提交的。
                    <!-- 事务自动提交的设置 -->
                        <property name="hibernate.connection.autocommit">false</property>
                        不自动提交
                    <!-- 事务自动提交的设置 -->
                        <property name="hibernate.connection.autocommit">true</property>
                        自动提交
            
        Query
            Query接口让你方便地对数据库及持久对象进行查询,它可以有两种表达方式:
                HQL语言或本地数据库的SQL语句。Query经常被用来绑定查询参数,限制查询记录数量,并最终执行查询操作。
            通过Query主要完成查询操作.
            我们通过Query可以执行hql语句.
                Query query=Session.createQuery(hql);
                下面这个可以执行sql语句
                SQLQUery sqlQuery=Session.createSQLQuery(sql);

                SQLQuery是Query的子.

            查询所有操作---使用HQL:
                @Test
                public void Test1() {

                    Session session = HibernateUtils.openSession();

                    session.beginTransaction();// 开启事务
                    Query query = session.createQuery("from Customer");
                    List<Customer> list = query.list();
                    System.out.println(list);
                    session.getTransaction().commit();// 提交事务

                    session.close();

                }
            
            分页查询
                @Test
                public void Test3() {

                    Session session = HibernateUtils.openSession();

                    session.beginTransaction();// 开启事务
                    Query query = session.createQuery("from Customer");
                    query.setFirstResult(10);
                    query.setMaxResults(10);
                    List<Customer> list = query.list();
                    System.out.println(list);
                    session.getTransaction().commit();// 提交事务

                    session.close();

                }
            
            查询指定列信息
                @Test
                public void Test4() {

                    Session session = HibernateUtils.openSession();

                    session.beginTransaction();// 开启事务
                    Query query = session.createQuery("select new Customer(name , address) from Customer where id<5");
                    List<Customer> list = query.list();
                    System.out.println(list);
                    session.getTransaction().commit();// 提交事务

                    session.close();

                }
            
                Select name ,address from Customer; 得到的是List<Object[]>结果
                要想得到List<Customer>结果
                1.    在Customer类中生成以name,address为参数的构造,注意,无参数构造也要有。
                2.    Select new Customer(name,address) from Customer;

            条件查询
                可以使用where关键字
                @Test
                public void Test5() {
                    Session session = HibernateUtils.openSession();

                    session.beginTransaction();// 开启事务
                    // 无名称参数
                    // Query query = session.createQuery("from Customer where name=?");
                    // query.setParameter(0, "梅剑华2");
                    // 有名称参数
                    Query query = session.createQuery("from Customer where name=:myname");
                    query.setParameter("myname", "梅剑华2");

                    // List<Customer> list = query.list();
                    // System.out.println(list);

                    // 如果保证查询结果唯一 也可以使用
                    Customer c = (Customer) query.uniqueResult();
                    System.out.println(c);

                    session.getTransaction().commit();// 提交事务

                    session.close();

                }

                无名称参数  from Customer where name=?
                对其进行赋值   query.setParameter(0,”张三”)

                有名称参数  from Customer where name=:myname;
                对其进行赋值  query.setParameter(“myname”,”李四”);

                如果查询结果可以保证就是唯一 的,我们可以使用
                query. uniqueResult()来得到一个单独对象.

            执行本地SQL
                要想执行本地sql

                SQLQuery sqlQuery=session.createSqlQuery(String sql);

                使用addEntity方法来将结果封装到指定的对象中,如果不封装,得到的是List<Object>
                如果sql中有参数,我们使用setParameter方法完成参数传递。
                如果结果就是一个可以使用uniqueResult()来得到一个单独对象。

                /**
                 * 使用sql语句查询所有
                 *
                 * @zhoujiaxuan
                 */
                @Test
                public void Test6() {
                    Session session = HibernateUtils.openSession();

                    session.beginTransaction();// 开启事务
                    SQLQuery sqlQuery = session.createSQLQuery("select * from t_customer");
                    // 指定封装的对象
                    sqlQuery.addEntity(Customer.class);
                    List<Customer> list = sqlQuery.list();
                    System.out.println(list);
                    // 提交事务
                    session.getTransaction().commit();

                    session.close();

                }

                    /**
                     * 条件查询用sql
                     *
                     * @zhoujiaxuan
                     */
                    @Test
                    public void Test7() {
                        Session session = HibernateUtils.openSession();

                        session.beginTransaction();// 开启事务
                        SQLQuery sqlQuery = session.createSQLQuery("select * from t_customer where name=?");
                        sqlQuery.addEntity(Customer.class);
                        sqlQuery.setParameter(0, "梅剑华2");
                        // List<Customer> list = sqlQuery.list();
                        // System.out.println(list);
                        // 如果保证查询结果唯一 也可以使用
                        Customer c = (Customer) sqlQuery.uniqueResult();
                        System.out.println(c);
                        // 提交事务
                        session.getTransaction().commit();

                        session.close();

                    }
            
            
        Criteria
            Criteria接口与Query接口非常类似,允许创建并执行面向对象的标准化查询。
            值得注意的是Criteria接口也是轻量级的,它不能在Session之外使用。

            首先我想使用Criteria,必须得到Criteria
            Criteria criteria=Session.createCriteria()
            @Test
            public void Test8() {

                Session session = HibernateUtils.openSession();
                session.beginTransaction();// 开启事务

                Criteria criteria = session.createCriteria(Customer.class);

                // 查询所有
                // List<Customer> list = criteria.list();
                // System.out.println(list);

                // 分页查询
                // criteria.setFirstResult(10);
                // criteria.setMaxResults(10);

                // 条件查询
                // 查询姓名是梅剑华2的信息
                // criteria.add(Restrictions.eq("name", "梅剑华2"));
                // 查询地址时非洲的信息
                // criteria.add(Restrictions.eq("address", "非洲"));
                // Customer c = (Customer) criteria.uniqueResult();
                // System.out.println(c);

                // 查询姓名是梅剑华2或者地址时非洲的信息
                 criteria.add(Restrictions.or(Restrictions.eq("address", "非洲"),
                 Restrictions.eq("name", "梅剑华2")));
                 List<Customer> list = criteria.list();
                 System.out.println(list);

                // 提交事务
                session.getTransaction().commit();
                session.close();

            }

            查询所有操作
                Session.createCriteria(实体类.class)得到一个Criteria对象,调用list查询所有
                分页操作与query的方法一样
                setFirstResult()    setMaxResults()
                条件查询

                criteria.add(Restrictions.eq(“name”,”xxxx”));
                criteria.add(Restrictions.or(Restricitons.eq(),Restrictions.list()…..))

                我们使用Criteria可以更加面向对象去操作,它非常适合进行多条件组合查询。

            
Hibernate持久化类与主键生成策略:
    什么是持久化类?
        Persistent Object  (PO)
        PO=POJO+hbm映射配置

        对于hibernate中的PO编写规则:
        1.    必须提供一个无参数的public构造方法
        2.    所有属性要private ,对外提供public 的get/set方法
        3.    在PO类必须提供一个标识属性,让它与数据库中的主键对应,我们管这个属性叫OID
        4.    PO类中的属性尽量使用基本数据类型的包装类.
        Int-Integer  double--Double  float-Float
        5.    PO类它不能使用final修饰符

    OID作用:
    OID指的是与数据库中表的主键对应的属性。
    Hibernate框架它是通过OID来区分不同的PO对象,如果在内存中有两个相同的OID对象,那么hibernate认为它们是同一个对象。
    
    为什么PO类属性它要使用包装类型?
        使用基本数据类型是没有办法去描述不存在概念,如果使用包装类型,它就是一个对象,对于对象它的默认值是null.
    
        PO类不可以使用final修饰?(hibernate中的get/load方法的区别)
            Get/load方法它们都是根据id去查询对象。
                get直接得到了一个持久化类型对象,它就是立即查询操作
                load它得到的是持久化类开的代理类型对象(子类对象)。它采用了一种延迟策略来查询数据。
                
                get方法在查询时,如果不存在返回null
                load方法在查询时,如果不存在,会产生异常 ObjectNotFoundException.
                    
                    //持久化类
                    @Test
                    public void test1() {
                        Session session = HibernateUtils.openSession();

                        // Customer customer = session.get(Customer.class, 2);
                        // System.out.println(customer.getClass());//com.itheima.domain.Customer

                        // Customer customer = session.load(Customer.class, 2);
                        // System.out.println(customer.getClass());//com.itheima.domain.Customer_$$_jvstbeb_0

                        // Customer customer = session.get(Customer.class, 200);
                        // System.out.println(customer);//null

                        Customer customer = session.load(Customer.class, 200);
                        System.out.println(customer);// 异常

                        session.close();
                    }

    Hibernate主键生成策略
        Hibernate中定义的主键类型包括:自然主键和代理主键:

        自然主键:具有业务含义 字段 作为主键,比如:学号,身份证号

    代理主键:不具有业务含义 字段作为主键(例如 自增id),比如:
        mysql自增主键,oracle序列生成的主键,uuid()方法生成的唯一序列串    
        建议:企业开发中使用代理主键!

        主键生成器     描述
        increment    代理主键。由hibernate维护一个变量,每次生成主键时自动以递增。
        问题:如果有多个应用访问一个数据库,由于每个应用维护自己的主键,所以此时主键可能冲突。建议不采用。
        优点:可以方便跨平台
        缺点:不适合高并发访问
        
        identity    代理主键。由底层数据库生成表识符。条件是数据库支持自动增长数据类型。
                    比如:mysql的自增主键,oracle不支持主键自动生成。
        如果数据库支持自增建议采用。
        优点:由底层数据库维护,和hibernate无关
        缺点:只能对支持自动增长的数据库有效,例如mysql
        
        sequence    代理主键。Hibernate根据底层数据库序列生成标识符。条件是数据库支持序列。比如oracle的序列。
        如果数据库支持序列建议采用。
        优点:由底层数据库维护,和hibernate无关
        缺点:数据库必须支持sequence方案例如oracle
        
        native    代理主键。根据底层数据库对自动来选择identity,sequence,hilo
        由于生成主键策略的控制权由hibernate控制,所以不建议采用。
        优点:在项目中如果存在多个数据库时使用
        缺点:效率比较低
        
        uuid    代理主键。Hibernate采用128bit位的UUID算法来生成标识符。该算法
        能够在网络环境中生成唯一的字符串标识符。
        此策略可以保证生成主键的唯一性,并且提供了最好的数据库插入性能和数据库平台的无关性。建议采用。
        优点:与数据库无关,方便数据库移植,效率高,不访问数据库就可以直接生成主键值,并且它能保证唯一性。
        缺点:uuid长度大(32位),占用空间比较大,对应数据库中类型 char  varchar
        
        assigned    自然主键。由java程序负责生成标识符。
        不建议采用。
        尽量在操作中避免手动对主键操作
    
    Hibernate持久化对象状态
        持久化对象三种状态
            有三种:
            1.    瞬时态:也叫做临时态或自由态,它一般指我们new出来的对象,它不存在OID,与hibernate session无关联,在数据库中也无记录。它使用完成后,会被jvm直接回收掉,它只是用于信息携带。
            简单说:无OID 与数据库中的信息无关联,不在session管理范围内。

            2.    持久态:在hibernate session管理范围内,它具有持久化标识OID它的特点,在事务未提交前一直是持久态,当它发生改变时,hibernate是可以检测到的。
            简单说:有OID 由session管理,在数据库中有可能有,也有可有没有。

            3.    托管态:也叫做游离态或离线态,它是指持久态对象失去了与session的关联,托管态对象它存在OID,在数据库中有可能存在,也有可能不存在。
            对于托管态对象,它发生改变时hibernet不能检测到。
            //持久化对象的三种状态
            @Test
            public void test2() {
                Session session = HibernateUtils.openSession();

                session.beginTransaction();

                Customer customer = new Customer();// 瞬时态
                customer.setName("大司马");
                customer.setAddress("wuhu");
                customer.setSex("nan");

                session.save(customer);// 持久态

                session.getTransaction().commit();

                session.close();
                System.out.println(customer);// 托管态
            }
        
        持久化类三种状态切换
            判断持久化类对象三种状态:
            1.    是否有OID
            2.    判断是否与session关联
    
            1.    瞬时态(new 出来的)
                瞬时------持久  save   saveOrUpdate
                瞬时-----脱管(游离)  手动设置oid

            2.    .持久态   它是由session管理
                持久-------瞬时   delete() 被删除后持久化对象不在建议使用
                持久-----脱管  注意:session它的缓存就是所说的一级缓存
                                evict(清除一级缓存 中指定的一个对象)
                                clear(清空一级缓存)
                                close(关闭,清空一级缓存)

            3.    .脱管态   (它是无法直接获取)
                脱管-----瞬时    直接将oid删除
                脱管----持久  update  saveOrUpdate lock(过时)

            
    Hibernate一级缓存
        Hibernate的一级缓存就是指session缓存。
        private transient ActionQueue;
        private transient StatefulPersistenceContext persistentContext;
        actionQueue它是一个行列队列,它主要记录crud操作的相关信息
        persistenceContext它是持久化上下文,它其实是真正缓存。
        在session中定义了一系列的集合来存储数据,它们构成session缓存。
        只要session没有关闭,它就会一直存在。
        当我们通过hibernate中的session提供的一些API例如 save  get  update等进行操作时,就会将持久化对象保存到session中,当下一次在去查询缓存中具有的对象(OID值来判断),
        就不会去从数据库查询,而是直接从缓存中获取。
        Hibernate的一级缓存存在的目的就是为了减少对数据库访问。

        在hibernate中还有一个二级缓存,它是SessionFactory级别缓存。

            //一级缓存的存在
            @Test
            public void test3() {
                Session session = HibernateUtils.openSession();

                session.beginTransaction();

                Customer c = session.get(Customer.class, 1);
                Customer cc = session.get(Customer.class, 1);

                session.getTransaction().commit();

                session.close();
            }
    
    持久化对象具有自动更新数据库能力
        //持久化具有自动更新数据库的能力
        @Test
        public void test4() {
            Session session = HibernateUtils.openSession();

            session.beginTransaction();

            Customer c = session.get(Customer.class, 1);
            c.setName("TOM");

            session.getTransaction().commit();

            session.close();
        }

        为什么持久化对象具有自动更新数据库能力?    
            当事务提交,session关闭,向数据库发送请求是,会判断一级缓存中的数据是否与快照区一直,如果不一样,就会发送update语句
            
        一级缓存常用API
            一级缓存特点:
            1.    当我们通过session的save,update saveOrupdate进行操作时,如果一级缓存中没有对象,会将这些对象从数据库中查询到,存储到一级缓存。
            2.    当我们通过session的load,get,Query的list等方法进行操作时,会先判断一级缓存中是否存在,如果没有才会从数据库获取,并且将查询的数据存储到一级缓存中。
            3.    当调用session的close方法时,session缓存清空。

            clear 清空一级缓存.
            evict 清空一级缓存中指定的一个对象。
                refresh重新查询数据库,用数据库中信息来更新一级缓存与快照

            public void  test5(){
                Session session = HibernateUtils.openSession();

                session.beginTransaction();
                
                List<Customer> list = session.createQuery("from Customer").list();//存储数据到一级缓存
                
                session.clear();//清空一级缓存
                
                Customer c = session.get(Customer.class, 1);//先从一级缓存中获取,如果获取不到 则从数据库中查询
                
                session.evict(c);//从一级缓存中删除某个对象
                
                Customer cc = session.get(Customer.class, 1);
                
                cc.setName("kkkkkk");
                
                session.refresh(cc);//重新查询数据库 用数据库中的信息来更新我们的一级缓存 和快照
                
                session.getTransaction().commit();

                session.close();
            }
            
    Hibernate常用API-Session补充
        update
            udpate操作它主要是针对于脱管对象,持久对象具有自动更新能力。
            问题1:如果我们直接操作的对象是一个脱管对象,执行update会出现什么情况?
                Update操作时,如果对象是一个脱管对象,可以操作,它会将脱管对象转换成持久对象在操作
                        如果在session中出现相同的oid两个对象,会产生异常
                
            问题2脱管对象的oid如果在数据表中不存在,会报异常?
                所以:在操作中,建议我们通过持久化对象来直接修改其操作。
        
        saveOrUpdate
             如果对象是一个瞬时对象 --------执行save操作
            如果对象是一个脱管对象---------执行update
            如果是一个持久对象-------直接返回
        delete
            删除一个脱管对象,与session关联,在删除
            注意:如果执行delete操作,先删除一级缓存,在删除数据库中的数据。
            
    Hibernate关联映射--数据对象三种关系
        Hibernate框架基于ORM设计思想,它将关系型数据库中的表与我们java中的类进行映射,一个对象就对应着表中的一条记录,而表中的字段对应着类中的属性。
        数据库中表与表之间存在着三种关系,也就是系统设计中的三种实体关系

        一对一
            原则有两种:
            1.    唯一外键对应:在任意一方添加外键来描述对应关系
            2.    主键对应:一方的主键作为另一方的主键
            
        比如说员工跟档案,一个员工对应一份档案
        此时可以分别在两个表上加上外键指向对方的主键,也可以直接使用主键对应
        Class Employee{
            Private Archives archives;
        }
        Class Archives{
            Private Employee employee;
        }

        一对多(多对一)
            客户与订单之间一对多关系(多对一)
            建表原则:在多的一方添加外键来描述关联关系
                
            在客户表加上一个外键指向订单的主键来描述关系
            Class Customer{
                Private Set<Order> orders;
            }

            Class Order{
                Private Customer c;
            }

        多对多
            例如学生与老师
            建表原则:通过一张中间表来描述其对应关系
            中间表至少需要两个字段作为外键来分别指向多对多双方的主键
            Class Student{
                Set<Teacher> ts;
            }
            Class Teacher{
                Set<Student> ss;
            }
    Hibernate关联映射--一对多(多对一);
        我们以客户(Customer)与订单(Order)为例
            实体类创建
                订单
                    public class Order {
                        private Integer id;
                        private Double money;
                        private String receiverInfo;
                        
                        private Customer c;

                客户
                    public class Customer {
                        private Integer id;
                        private String name;
                        private Set<Order> order = new HashSet<Order>();
            
            Hbm映射文件编写
                Order.hbm.xml
                    
                    <hibernate-mapping>
                        <class name="com.itheima.one2Many.Order" table="t_order" catalog="hibernateTest">
                            <id name="id" column="c_id">
                                <generator class="identity"></generator>
                            </id>
                            <property name="money" column="c_money" />
                            <property name="receiverInfo" column="c_receiverInfo" length="50" />
                            <many-to-one name="c" class="com.itheima.one2Many.Customer"
                                column="c_customer_id" cascade="save-update"></many-to-one>
                        </class>
                    </hibernate-mapping>
            
                Customer.hbm.xml
                    <hibernate-mapping>
                        <class name="com.itheima.one2Many.Customer" table="t_customer"
                            catalog="hibernateTest">
                            <id name="id" column="c_id">
                                <generator class="identity"></generator>
                            </id>
                            <property name="name" column="c_name" length="20"  />  
                            <set name="order" cascade="save-update" inverse="true">
                                <key column="c_customer_id"></key>
                                <one-to-many class="com.itheima.one2Many.Order" />
                            </set>
                        </class>
                    </hibernate-mapping>
                                
            
                测试保存
                    /**
                     * 双向关联
                     *
                     * @zhoujiaxuan
                     */
                    @Test
                    public void test() {

                        Session session = HibernateUtils.openSession();

                        session.beginTransaction();

                        Customer c = new Customer();
                        c.setName("周嘉煊");

                        Order o1 = new Order();
                        o1.setMoney(1000d);
                        o1.setReceiverInfo("南京");

                        Order o2 = new Order();
                        o2.setMoney(2000d);
                        o2.setReceiverInfo("北京");

                        o1.setC(c);
                        o2.setC(c);

                        c.getOrder().add(o1);
                        c.getOrder().add(o2);

                        session.save(c);
                        session.save(o1);
                        session.save(o2);

                        session.getTransaction().commit();
                        session.close();

                    }
                            
                测试单向关联保存
                    ***org.hibernate.TransientObjectException:
                    object references an unsaved transient instance - save the transient instance before flushing:  
                    cn.itheima.oneToMany.Customer …………..
                    这个异常代表提一个持久化对象关联了一个瞬时对象。

                    我们可以使用级联操作来解决上述的问题.
                    我们现在要做的是保存订单时保存客户,需要在订单的hbm配置文件中修改
                    设置cascade=save-update 那么在保存订单时就可以自动将客户保存。
                    <many-to-one name="c" class="com.itheima.one2Many.Customer"
                    column="c_customer_id" cascade="save-update"></many-to-one>
                    如果我们要完成保存客户时,保存订单
                    <set name="order" cascade="save-update" inverse="true">
                        <key column="c_customer_id"></key>
                        <one-to-many class="com.itheima.one2Many.Order" />
                    </set>
                
                    /**
                     * 级联单项保存(保存订单自动保存客户)
                     *
                     * @zhoujiaxuan
                     */
                    @Test
                    public void test2() {

                        Session session = HibernateUtils.openSession();

                        session.beginTransaction();

                        Customer c = new Customer();
                        c.setName("周嘉煊");

                        Order o1 = new Order();
                        o1.setMoney(1000d);
                        o1.setReceiverInfo("南京");

                        Order o2 = new Order();
                        o2.setMoney(2000d);
                        o2.setReceiverInfo("北京");

                        o1.setC(c);
                        o2.setC(c);

                        session.save(o1);
                        session.save(o2);

                        session.getTransaction().commit();
                        session.close();

                    }

                    /**
                     * 级联保存(保存客户自动保存订单)
                     *
                     * @zhoujiaxuan
                     */
                    @Test
                    public void test3() {

                        Session session = HibernateUtils.openSession();

                        session.beginTransaction();

                        Customer c = new Customer();
                        c.setName("周嘉煊");

                        Order o1 = new Order();
                        o1.setMoney(1000d);
                        o1.setReceiverInfo("南京");
                        

                        Order o2 = new Order();
                        o2.setMoney(2000d);
                        o2.setReceiverInfo("北京");

                        c.getOrder().add(o1);
                        c.getOrder().add(o2);

                        session.save(c);

                        session.getTransaction().commit();
                        session.close();

                    }
                            
                
                双向关联维护
                    我们在开发中要配置双向关联配置。---------可以通过任意一方来操作对方
                    在操作代码,尽量来要进行单向关联。------可以尽量资源浪费。
                    在双向关联中,会存在多余的update语句。
                    我们可以使用inverse属性来设置,双向关联时由哪一方来维护表与表之间的关系。
                    <set name="order" cascade="save-update" inverse="true">
                        <key column="c_customer_id"></key>
                        <one-to-many class="com.itheima.one2Many.Order" />
                    </set>
                    Inverse它的值如果为true代表,由对方来维护外键。
                    Inverse它的值如果为false代表,由本方来维护外键。
                    关于inverse的取值:
                        外键在哪一个表中,我们就让哪一方来维护外键。

                对象导航
                    当一个订单关联一个客户,而这个客户关联了另外两个订单,
                    这时当我们保存其中的某一个客户或者订单时,数据库会以谁为导航来插入数据
                    @Test
                    public void test4() {

                        Session session = HibernateUtils.openSession();

                        session.beginTransaction();

                        Customer c = new Customer();
                        c.setName("周嘉煊");

                        Order o1 = new Order();
                        o1.setMoney(1000d);
                        o1.setReceiverInfo("南京");

                        Order o2 = new Order();
                        o2.setMoney(2000d);
                        o2.setReceiverInfo("北京");

                        Order o3 = new Order();
                        o3.setMoney(3000d);
                        o3.setReceiverInfo("上海");

                        // o1.setC(c);

                        c.getOrder().add(o2);
                        c.getOrder().add(o3);

                        // session.save(o1);//插入4条记录
                        // session.save(c);//插入3条记录
                        // session.save(o2);//插入1条记录

                        session.getTransaction().commit();
                        session.close();

                    }
                
                级联删除
                    我们在删除客户时,也要删除订单,如果没有做级联,那么这个操作是不允许。为了维护数据完整性
                    想要完成操作:我们可以在客户中添加cascade=”delete”;
                    <set name="order" cascade="delete" inverse="true">
                        <key column="c_customer_id"></key>
                        <one-to-many class="com.itheima.one2Many.Order" />
                    </set>
                
                    delete-orphan用法:
                        得到客户的订单集合,然后利用集合去删除客户的某一个订单.
            
                cascade总结
                    使用cascade可以完成级联操作
                    它可常用取值:
                        none这是一个默认值
                    save-update,当我们配置它时,底层使用save update或save-update完成操作,级联保存临时对象,如果是游离对象,会执行update.
                    delete 级联删除
                    delete-ophan 删除与当前对象解除关系的对象。
                    all 它包含了save-update  delete操作
                    all-delete-orphan 它包信了delete-orphan与all操作

                    笔试题:cascade与inverse有什么区别?
                        cascade它是完成级联操作
                        Inverse它只有在双向关联情况下有作用,它来指定由哪一方维护外键。

Hibernate注解开发
    在hibernate中使用注解开发,可以帮助我们简化hbm文件配置
        PO类注解配置:
            @Entity 声明一个实体
            @Table来描述类与表对应
                @Entity
                @Table(name="t_customer",catalog="hibernateTest")
                public class Customer {
            
            @Id来声明一个主键
                @GenerateValue 用它来声明一个主键生成策略
                    @Id
                    @GeneratedValue(strategy=GenerationType.IDENTITY)
                    private Integer id;
                            
            默认情况下相当于native
            可以选择的主键生成策略 AUTO IDENTITY SEQUENCE
                @Column来定义列
                    @Column(name="c_name",length=30)
                    private String name;
                
                
            注意:对于PO类中所有属性,如果你不写注解,默认情况下也会在表中生成对应的列。
            列的名称就是属性的名称
                @Temporal来声明日期类型
                    @Temporal(TemporalType.TIMESTAMP)
                    private Date publicationDate;
                
                    可以选择
                    TemporalType.DATA   只有年月日  
                    TemporalType.TIME   只有小时分钟秒
                    TemporalType.TIMESTAMP 有年月日小时分钟秒

                我们最终需要在hibernate.cfg.xml文件中将我们类中的注解配置引用生效
                    <mapping class="com.itheima.domain.Book"/>
                
            问题:1.如果我们主键生成策略想使用UUID类型?
                    @Id
                    @GenericGenerator(name="myuuid",strategy="uuid")
                    @GeneratedValue(generator="myuuid")
                    private String id;
                                
            问题2:如果设定类的属性不在表中映射?
                    @Transient
                    private String msString;
            对于我们以上讲解的关于属性配置的注解,我们也可以在其对应的getXxx方法去使用    
                
    一对多(多对一):
        @OneToMany
        @ManyToOne
        以Customer与Order为例
            Customer:    
                @Entity
                @Table(name="t_customer",catalog="hibernateTest")
                @NamedQuery(name="myHql",query="from Customer")
                @Proxy(lazy=true)
                public class Customer {
                    @Id
                    @GeneratedValue(strategy=GenerationType.IDENTITY)
                    private Integer id;
                    private String name;
                    @OneToMany(targetEntity=Order.class,mappedBy="c")
                    @Cascade(CascadeType.SAVE_UPDATE)
                    private Set<Order> order = new HashSet<Order>();
                    生成对应的get/set方法
                
            Order:    
                @Entity
                @Table(name = "t_order", catalog = "hibernateTest")
                @NamedQuery(name="findOrderByC",query="from Order where c=:c")
                public class Order {
                    @Id
                    @GeneratedValue(strategy = GenerationType.IDENTITY)
                    private Integer id;
                    private Double money;
                    private String receiverInfo;
                    @ManyToOne(targetEntity = Customer.class)
                    @Cascade(CascadeType.SAVE_UPDATE)
                    @JoinColumn(name = "c_customer_id")
                    private Customer c;
                    生成对应的get/set方法
                
                示例:保存客户时,保存订单
                    对于这个示例我们需要在Customer中配置cascade操作,save-update
                    第一种方式,可以使用JPA提供的注解
                    <many-to-one name="c" class="com.itheima.one2Many.Customer"
                        column="c_customer_id" cascade="save-update"></many-to-one>
                    第二种方式:可以使用hibernate提供的注解
                    @OneToMany(targetEntity = Customer.class)
                    @Cascade(CascadeType.SAVE_UPDATE)
                
                测试代码:
                    @Test
                    public void test3() {
                        Session session = HibernateUtils.openSession();
                        session.beginTransaction();
                        Customer c = new Customer();
                        c.setName("梅剑华");

                        Order o1 = new Order();
                        o1.setMoney(1000d);
                        o1.setReceiverInfo("北京");
                        o1.setC(c);
                        Order o2 = new Order();
                        o2.setMoney(2000d);
                        o2.setReceiverInfo("上海");
                        o2.setC(c);

                        c.getOrder().add(o1);
                        c.getOrder().add(o2);

                        session.save(c);
                        session.getTransaction().commit();
                        session.close();
                    }

                订单中没有关联客户的id,为什么?
                原因:我们在Customer中配置了mappedBy=”c”它代表的是外键的维护由Order方来维护,而Customer不维护,这时你在保存客户时,级联保存订单,是可以的,但是不能维护外键,所以,我们必须在代码中添加订单与客户关系。
                    //为了维护外键
                    o1.setC(c);
                    o2.setC(c);
                    //为了进行级联操作
                    c.getOrder().add(o1);
                    c.getOrder().add(o2);
                    //进行级联保存
                    session.save(c);
                    session.getTransaction().commit();
                    session.close();
                
                扩展:关于hibernate注解@Cascade中的DELETE_ORPHAN过时
                    @Cascade(CascadeType.DELETE_ORPHAN)
                    使用下面方案来替换过时方案
                    @OneToMany(targetEntity=Order.class,mappedBy="c",orphanRemoval=true)
                
    Hibernate关联映射-多对多:
        我们使用注解完成多对多配置.
        描述学生与老师.
        使用@ManyToMany来配置多对多,只需要在一端配置中间表,另一端使用mappedBy表示放置外键维护权。
        Techer:
            @Entity
            @Table(name="c_teacher",catalog="hibernateTest")
            public class Teacher {
                @Id
                @GeneratedValue(strategy=GenerationType.IDENTITY)
                private Integer id;
                private String name ;
                @ManyToMany(targetEntity=Student.class,mappedBy="teacher")
                private Set<Student> student = new HashSet<Student>();
                        生成对应的get/set方法
        Student:
            @Entity
            @Table(name = "t_student", catalog = "hibernateTest")
            public class Student {
                @Id
                @GeneratedValue(strategy = GenerationType.IDENTITY)
                private Integer id;
                private String name;
                @ManyToMany(targetEntity = Teacher.class)
                @JoinTable(name = "s_t", joinColumns = { @JoinColumn(name = "c_student_id") }, inverseJoinColumns = {
                        @JoinColumn(name = "c_teacher_id") })
                @Cascade(CascadeType.SAVE_UPDATE)
                private Set<Teacher> teacher = new HashSet<Teacher>();
                    生成对应的get/set方法
                
        因为我们将外键的维护权利由Student来维护,我们演示保存学生时,将都也级联保存。

            测试代码:
                @Test
                public void test4() {
                    Session session = HibernateUtils.openSession();
                    session.beginTransaction();
                    Student s1 = new Student();
                    s1.setName("梅剑华");
                    Student s2 = new Student();
                    s2.setName("马远");
                    Teacher t1 = new Teacher();
                    t1.setName("张玮");
                    Teacher t2 = new Teacher();
                    t2.setName("马坤坤");
                    //学生关联老师
                    s1.getTeacher().add(t1);
                    s1.getTeacher().add(t2);
                    s2.getTeacher().add(t1);
                    s2.getTeacher().add(t2);
                    
                    session.save(s1);
                    session.save(s2);
                    session.getTransaction().commit();
                    session.close();
                }
                
            我们在Student类中配置了级联    
                @Cascade(CascadeType.SAVE_UPDATE)
                
            级联删除操作测试,删除一个全部都会删掉    
                
    Hibernate关联映射-一对一:
        以人与身份证号为例
        一对一操作有两种映射方式:
        1.    在任意一方添加外键
        2.    主键映射

        外键映射:
            User:
                @Entity
                @Table(name="c_user",catalog="hibernateTest")
                public class User {
                    @Id
                    @GenericGenerator(name="myuuid",strategy="uuid")
                    @GeneratedValue(generator="myuuid")
                    private String id;
                    private String name;
                    @OneToOne(targetEntity=IDcard.class,mappedBy="user")//t_user表放弃对外键的维护权利
                    private IDcard iDcard;
                    生成对应的get/set方法
                                
            IDcard:
                @Entity
                @Table(name="c_idcard",catalog="hibernateTest")
                public class IDcard {
                    @Id
                    @GenericGenerator(name="myuuid",strategy="uuid")
                    @GeneratedValue(generator="myuuid")
                    private String id;
                    private String cardNum;
                    @OneToOne(targetEntity=User.class)
                    @JoinColumn(name="c_user_id")
                    @Cascade(CascadeType.SAVE_UPDATE)
                    private User user;
                    生成对应的get/set方法
                joinColumn指定外键列名称,当前配置外键是在t_idcard表中
                
            测试代码:
                @Test
                public void test5() {
                    Session session = HibernateUtils.openSession();
                    session.beginTransaction();
                    User user = new User();
                    user.setName("梅剑华");
                    IDcard iDcard = new IDcard();
                    iDcard.setCardNum("2141414325235");
                    iDcard.setUser(user);

                    session.save(iDcard);

                    session.getTransaction().commit();
                    session.close();
                }
                
        主键映射(了解):
            以Husband与Wife为例
                Husband:
                    @Entity
                    @Table(name = "t_husband", catalog = "hibernateTest")
                    public class Husband {
                        @Id
                        @GenericGenerator(name = "myForeignKey", strategy = "foreign", parameters = {
                                @Parameter(name = "property", value = "wife") })
                        @GeneratedValue(generator="myForeignKey")
                        private String id;
                        private String name;
                        @OneToOne(mappedBy="husband")
                        @PrimaryKeyJoinColumn
                        private Wife wife;
                        生成对应的get/set方法
                Husband的主键我们设置成参考wife的主键方式
                Wife:
                    @Entity
                    @Table(name="t_wife",catalog="hibernateTest")
                    public class Wife {
                        @Id
                        @GenericGenerator(name="myuuid",strategy="uuid")
                        @GeneratedValue(generator="myuuid")
                        private String id;
                        private String name ;
                        @OneToOne
                        @PrimaryKeyJoinColumn
                        @Cascade(CascadeType.SAVE_UPDATE)
                        private Husband husband;
                        生成对应的get/set方法
                
                测试代码:
                    @Test
                    public void test6() {
                        Session session = HibernateUtils.openSession();
                        session.beginTransaction();

                        Husband husband = new Husband();
                        husband.setName("王撕葱");

                        Wife wife = new Wife();
                        wife.setName("凤姐");

                        husband.setWife(wife);

                        wife.setHusband(husband);

                        session.save(wife);

                        session.getTransaction().commit();
                        session.close();
                    }
                
Hibernate检索方式概述:
    对数据库操作中,最常用的是select.使用hibernate如何select操作。

        分为五种:
        1导航对象图检索方式,根据已加载的对象导航到其它对象
        2.OID检索方式,按照对象的OID来检索对象
        3.HQL检索方式,使用面向对象的HQL查询语言
        4.QBC检索方式,使用QBC(Query by Criteria)API来检索对象,这种API封装了基于字符串形式的查询语句,提供了更加面向对象的查询接口
        5.本地SQL检索方式,使用本地数据库的SQL查询语句

        导航对象图检索方式
            Customer c=session.get(Customer.class,2);
            c.getOrders().size()
            通过在hibernate中进行映射关系,在hibernate操作时,可以通过导航方式得到
            其关联的持久化对象信息。
        OID检索方式
            Session.get(Customer.class,3);
            Session.load(Order.class,1);
            Hibernate中通过get/load方法查询指定的对象,要通过OID来查询。
        
        HQL
            HQL是我们在hibernate中是常用的一种检索方式。
            HQL(Hibernate Query Language)提供更加丰富灵活、更为强大的查询能力
            因此Hibernate将HQL查询方式立为官方推荐的标准查询方式,HQL查询在涵盖Criteria查询的所有功能的前提下,提供了类似标准SQL语 句的查询方式,同时也提供了更加面向对象的封装。完整的HQL语句形式如下: Select/update/delete…… from …… where …… group by …… having …… order by …… asc/desc 其中的update/delete为Hibernate3中所新添加的功能,可见HQL查询非常类似于标准SQL查询。
            基本步骤:
            1.    得到Session
            2.    编写HQL语句
            3.    通过session.createQuery(hql)创建一个Query对象
            4.    为Query对象设置条件参数
            5.    执行list查询所有,它反胃的是List集合  uniqueResut()返回一个查询结果。

            @SuppressWarnings("all")
            public class HQLTest {
                /**
                 * 命名检索
                 * 我们可以将hql语句先定义出来,在使用时通过session.getNamedQuery(hqlName);得到一个Query,在执行.
                    问题:hql定义在什么位置?
                        1.如果你有hbm配置文件,那么当前的hql操作是对哪一个实体进行操作,就在哪一个    实体的配置文件中声明。
                        <query name="myHql">
                        from Customer
                        </query>
                        2.如果是使用注解来描述PO的配置
                        我们直接在PO类中使用
                        @NamedQuery(name="myHql",query="from Customer")
                 * @zhoujiaxuan
                 */
                
                @Test
                public void test8() {
                    Session session = HibernateUtils.openSession();
                    session.beginTransaction();
                    Customer c = session.get(Customer.class, 1);
            //        List<Customer> list = session.getNamedQuery("myHql").list();
            //        System.out.println(list);
                    List<Order> list = session.getNamedQuery("findOrderByC").setEntity("c", c).list();
                    System.out.println(list);
                    session.getTransaction().commit();
                    session.close();

                }
                /**
                 * 投影检索
                 * 我们主要讲解是关于部分属性查询,可以使用投影将部分属性封装到对象中。
                 * 注意:我们必须在PO类中提供对应有参的构造方法,也要有无参数构造。
                  * @zhoujiaxuan
                 */
                
                @Test
                public void test7() {
                    Session session = HibernateUtils.openSession();
                    session.beginTransaction();
                    List<Customer> list = session.createQuery("select new Customer(id,name) from Customer").list();
                    System.out.println(list);
                    session.getTransaction().commit();
                    session.close();

                }
                /**
                 * 分组统计检索
                 *
                 * @zhoujiaxuan
                 */
                
                @Test
                public void test6() {
                    Session session = HibernateUtils.openSession();
                    session.beginTransaction();
                    // Query query = session.createQuery("select count(*) from Order");
                    // Object result = query.uniqueResult();
                    // System.out.println(result);
                    List<Order> list = session.createQuery("select sum(money) from Order group by c").list();
                    System.out.println(list);
                    session.getTransaction().commit();
                    session.close();

                }

                /**
                 * 分页检索
                 *
                 * @zhoujiaxuan
                 */
                @Test
                public void test5() {
                    Session session = HibernateUtils.openSession();
                    session.beginTransaction();
                    Query query = session.createQuery("from Order");
                    query.setFirstResult(6);
                    query.setMaxResults(6);
                    List<Order> list = query.list();
                    System.out.println(list);
                    session.getTransaction().commit();
                    session.close();

                }

                /**
                 * 条件检索
                 *
                 * @zhoujiaxuan
                 */
                @Test
                public void test4() {
                    Session session = HibernateUtils.openSession();
                    session.beginTransaction();
                    List<Order> list = session.createQuery("from Order where money>?").setParameter(0, 2000d).list();
                    // List<Order> list = session.createQuery("from Order where
                    // money>:mymoner").setParameter("mymoney", 2000d).list();
                    System.out.println(list);
                    session.getTransaction().commit();
                    session.close();

                }

                /**
                 * 排序检索
                 *
                 * @zhoujiaxuan
                 */
                @Test
                public void test3() {
                    Session session = HibernateUtils.openSession();
                    session.beginTransaction();
                    List<Order> list = session.createQuery("from Order order by money desc").list();
                    System.out.println(list);
                    session.getTransaction().commit();
                    session.close();

                }

                /**
                 * 基本检索
                 *
                 * @zhoujiaxuan
                 */
                @Test
                public void test2() {
                    Session session = HibernateUtils.openSession();
                    session.beginTransaction();
                    List<Customer> list = session.createQuery("from Customer").list();
                    System.out.println(list);
                    session.getTransaction().commit();
                    session.close();

                }

                /**
                 * 准备数据
                 *
                 * @zhoujiaxuan
                 */
                @Test
                public void test1() {
                    Session session = HibernateUtils.openSession();
                    session.beginTransaction();
                    Customer customer = new Customer();
                    customer.setName("梅剑华");

                    for (int i = 0; i < 10; i++) {
                        Order order = new Order();
                        order.setMoney(1000d + i * 100);
                        order.setReceiverInfo("南京");
                        order.setC(customer);
                        session.save(order);

                    }
                    session.getTransaction().commit();
                    session.close();

                }

            }

                
        QBC
            QBC(query by criteria),它是一种更加面向对象的检索方式。
            QBC步骤:
                1.通过Session得到一个Criteria对象   session.createCriteria()
                2.设定条件  Criterion实例 它的获取可以通过Restrictions类提供静态。
                    Criteria的add方法用于添加查询条件
            3.    调用list进行查询  criterfia.list.
            
                @SuppressWarnings("all")
                public class QBCTest {
                    /**
                     * 离线检索
                     *
                     * @zhoujiaxuan
                     */
                    @Test
                    public void test6() {
                        Session session = HibernateUtils.openSession();
                        session.beginTransaction();
                        List<Customer> list = DetachedCriteria.forClass(Customer.class).add(Restrictions.like("name", "梅%"))
                                .getExecutableCriteria(session).list();
                        System.out.println(list);
                        session.getTransaction().commit();
                        session.close();

                    }

                    /**
                     * 统计检索
                     *
                     * @zhoujiaxuan
                     */
                    @Test
                    public void test5() {
                        Session session = HibernateUtils.openSession();
                        session.beginTransaction();
                        Criteria criteria = session.createCriteria(Order.class);
                        // Object result =
                        // criteria.setProjection(Projections.rowCount()).uniqueResult();
                        // System.out.println(result);
                        List<Object[]> list = criteria
                                .setProjection(
                                        Projections.projectionList().add(Projections.sum("money")).add(Projections.groupProperty("c")))
                                .list();
                        for (Object[] objs : list) {
                            for (Object object : objs) {
                                System.out.println(object);
                            }
                        }
                        session.getTransaction().commit();
                        session.close();

                    }

                    /**
                     * 分页检索
                     *
                     * @zhoujiaxuan
                     */
                    @Test
                    public void test4() {
                        Session session = HibernateUtils.openSession();
                        session.beginTransaction();
                        Criteria criteria = session.createCriteria(Order.class);
                        criteria.setFirstResult(6);
                        criteria.setMaxResults(6);
                        List<Order> list = criteria.list();
                        System.out.println(list);
                        session.getTransaction().commit();
                        session.close();

                    }

                    /**
                     * 条件检索
                     *
                     * @zhoujiaxuan
                     */
                    @Test
                    public void test3() {
                        Session session = HibernateUtils.openSession();
                        session.beginTransaction();
                        Customer c = (Customer) session.createCriteria(Customer.class).add(Restrictions.like("name", "梅%"))
                                .uniqueResult();
                        // System.out.println(c);
                        List<Order> list = session.createCriteria(Order.class)
                                .add(Restrictions.and(Restrictions.gt("money", 1500d), Restrictions.eq("c", c))).list();
                        System.out.println(list);
                        session.getTransaction().commit();
                        session.close();

                    }

                    /**
                     * 排序检索
                     * 注意在criteri.addOrder()方法的参数使用的Order是hibernate中的对象
                     * @zhoujiaxuan
                     */
                    @Test
                    public void test2() {
                        Session session = HibernateUtils.openSession();
                        session.beginTransaction();

                        // List<Customer> list =
                        // session.createCriteria(Order.class).addOrder(org.hibernate.criterion.Order.desc("money")).list();
                        List<Customer> list = session.createCriteria(Order.class).addOrder(org.hibernate.criterion.Order.asc("money"))
                                .list();
                        System.out.println(list);
                        session.getTransaction().commit();
                        session.close();
                    }

                    /**
                     * 基本检索
                     *
                     * @zhoujiaxuan
                     */
                    @Test
                    public void test1() {
                        Session session = HibernateUtils.openSession();
                        session.beginTransaction();

                        List<Customer> list = session.createCriteria(Customer.class).list();
                        System.out.println(list);
                        session.getTransaction().commit();
                        session.close();
                    }

                }

            
    本地SQL:
            @SuppressWarnings("all")
            public class SQLTest {
                
                /**
                 * 本地检索
                 *
                 * @zhoujiaxuan
                 */
                @Test
                public void test6() {
                    Session session = HibernateUtils.openSession();
                    session.beginTransaction();
                    SQLQuery sqlQuery = session.createSQLQuery("select * from t_customer");
                    sqlQuery.addEntity(Customer.class);
                    List<Customer> list = sqlQuery.list();
                    System.out.println(list);
                    session.getTransaction().commit();
                    session.close();

                }


            }

            
            本地sql也支持命名查询。
            可以将sql语句定义在hbm文件中,也可以使用注解。
                <sql-query name="sql">
                select * from t_ustomer
                        </sql-query>
            
            本地命名sql注解定义
                @NamedNativeQuery(name="sql",query="select * from t_ustomer")
            如果执行这个命名的sql会产生异常
            出现问题的原因:是hibernate不知道执行select * from t_customer后如果将结果封装。
            
            
            
            
            
SQL多表操作:
    1.交叉连接  CROSS JOIN  会产生迪卡尔积
        SELECT * FROM t_customer CROSS JOIN t_order;

    2.内连接  INNER JOIN  ON
        SELECT * FROM t_customer AS c INNER JOIN t_order AS o ON c.id=o.c_customer_id;

        使用内连接它只能将有关联的数据得到。

        隐式内连接  使用 "逗号"将表分开,使用WHERE来消除迪卡尔积
        SELECT * FROM t_customer AS c ,t_order o WHERE c.id=o.c_customer_id;


    3.外连接  左外LEFT OUTER JOIN    右外RIGHT OUTER JOIN
        OUTER可以省略
        SELECT * FROM t_customer c LEFT OUTER JOIN t_order o ON c.id=o.c_customer_id;
        HQL多表操作
            Hql多表操作分类:
        1.    交叉连接
        2.    内连接
        a)    显示内连接
        b)    隐式内连接
        c)    迫切内连接
        3.    外连接
        左外连接
        迫切左外连接
        右外连接
        注意:在hibernate中有迫切连接的概念,而sql中没有。

                
        内连接
        显示内连接
        显示内连接使用的是inner join with
            
            /**
             * 显示内连接
             * @zhoujiaxuan
             */
            @Test
            public void test1() {
                Session session = HibernateUtils.openSession();
                session.beginTransaction();
                //String hql = "from Order o inner join o.c";
                String hql = "from Customer c inner join c.order with c.id = 1";
                List<Object[]> list = session.createQuery(hql).list();
                for (Object[] objects : list) {
                    for (Object object : objects) {
                        System.out.print(object + "\t");
                    }
                    System.out.println();
                }
                session.getTransaction().commit();
                session.close();
            }
                    
            
        隐式内连接
        隐式内连接也我们在sql中操作不一样,它是通过”.”运算符来关联
            /**
             * 隐式内连接
             * @zhoujiaxuan
             */
            @Test
            public void test2() {
                Session session = HibernateUtils.openSession();
                session.beginTransaction();
                //String hql = "from Order o where o.c.id=1";
                String hql = "from Customer c where c.order.id < 10";
                List list = session.createQuery(hql).list();
                System.out.println(list);
                session.getTransaction().commit();
                session.close();
            }
        
                
        迫切内连接
        迫切内连接得到的结果是直接封装到PO类中,而内连接得到的是Object[]数组,数组中封装的是PO类对象。
            /**
             * 迫切内连接
             * @zhoujiaxuan
             */
            @Test
            public void test3() {
                Session session = HibernateUtils.openSession();
                session.beginTransaction();
                String hql = "from Order o inner join fetch o.c";
                //因为迫切内连接可能造成重复 所以需要进行去重
                //String hql = "select distinct c from  Customer c inner join fetch c.order";
                List<Order> list = session.createQuery(hql).list();
                for (Order order : list) {
                    System.out.println(order);
                }
                session.getTransaction().commit();
                session.close();
            }
        外链接            
            /**
             * 外连接
             * @zhoujiaxuan
             */
            @Test
            public void test4() {
                Session session = HibernateUtils.openSession();
                session.beginTransaction();
                //左外链接
                String hql = "from  Customer c left join c.order";
                //右外连接
                //String hql = "from  Customer c right join c.order";
                List<Object[]> list = session.createQuery(hql).list();
                for (Object[] objects : list) {
                    for (Object object : objects) {
                        System.out.print(object + "\t");
                    }
                    System.out.println();
                }
                session.getTransaction().commit();
                session.close();
            }        
        迫切外链接
            /**
             * 迫切外连接
             * @zhoujiaxuan
             */
            @Test
            public void test5() {
                Session session = HibernateUtils.openSession();
                session.beginTransaction();
                //String hql = "select distinct c from  Customer c left join fetch c.order";
                String hql = "select distinct c from  Customer c left join fetch c.order where c.id=1";
                List<Customer> list = session.createQuery(hql).list();
                for (Customer customer : list) {
                    System.out.println(customer);
                }
                session.getTransaction().commit();
                session.close();
            }    

    Hibernate事务管理
        事务介绍
            问题:什么是事务?
                事务就是逻辑上的一组操作,组成这组操作的各个单元要么全部成功,要么全都失败。
            问题:事务四个特性?
                原子性:不可分割
                一致性:事务在执行前后,要保证数据的一致。
                隔离性:一个事务在执行的过程中,不应该受到其它事务的干扰。
                持久性:事务一旦结束,数据持久化到数据库。
            问题:不考虑事务的隔离性,会产生什么问题?
                脏读:一个事务读取到另一个事务的未提交数据
                不可重复读:一个事务读取到另一个事务提交的数据(主要是指update),会导致两次读取的结果不一致。
                虚读(幻读): 一个事务读取到另一个事务提交的数据(主要是指insert),会导致两次读取结果不一致.
            问题:对于上述问题如何解决?
                我们可以通过设置隔离级别来解决.
                READ_UNCOMMITED 读取未提交,它引发所有的隔离问题
                READ_COMMITTED  读已提交,阻止脏读,可能发生不可重复读与虚读.
                REPEATABLE_READ 重复读  阻止脏读,不可重复读 可能发生虚读
            SERIALIZABLE 串行化 解决所有问题 不允许两个事务,同时操作一个目标数据。(效率低下)

            ORACLE  默认的是事务隔离级别  READ_COMMITTED
            MYSQL 默认的事务隔离级别  REPEATABLE_READ

    Hibernate中设置事务隔离级别
        hibernate.connection.isolation
        它可取的值有 1 2 4 8
        1代表的事务隔离级别为READ UNCOMMITTED
        2代表的事务隔离级别为READ COMMITTED
        4代表的事务隔离级别为 REPEATABLE READ
        8代表的事务隔离级别为 SERIALIZABLE

        在hibernate.cfg.xml文件中配置
        <property name="hibernate.connection.isolation">4</property>
            <!-- 设置事务的管理级别 -->

    Hibernate中session管理
         Hibernate提供了三种管理session的方式:
        1.    Session对象的生命周期与本地线程绑定(ThreadLocal)
        2.    Session对象的生命周期与JTA事务绑定(分布式事务管理)
        3.    Hibernate委托程序来管理Session的生命周期
        我们之前所使用的是第三种 ,通过程序获取一个Session对象,使用它,最后session.close();
        
            在实际开发中我们一般使用的是前两种:
        主要介绍关于本地线程绑定Session。
        步骤:
            1.    需要在hibernate.cfg.xml文件配置
                    <property name="hibernate.current_session_context_class">thread</property>
            2.    在获取session时不要在使用openSession而是使用getCurrentSession()方法。
                    public static Session getCurrentSession(){
                            return sessionFactory.getCurrentSession();
                        }


            测试:
                @Test
                public void test1 (){
                    Session s1 = HibernateUtils.openSession();
                    Session s2 = HibernateUtils.openSession();
                    System.out.println(s1==s2);//false
                    
                    Session s3 = HibernateUtils.getCurrentSession();
                    Session s4 = HibernateUtils.getCurrentSession();
                    System.out.println(s3==s4);//true
                }

            关于getCurrentSession使用时的注意事项:
                
                @Test
                public void test2(){
                    Session session = HibernateUtils.getCurrentSession();
                    session.beginTransaction();
                    Customer c = session.get(Customer.class, 3);
                    System.out.println(c);
                    session.getTransaction().commit();
                    //session.close();
                }
            
            使用getCurrentSession获取的与线程绑定的session对象,
            在事务关闭时,session对象也会close,简单说,就不需要我们在手动close;


    Hibernate优化方案
        HQL优化
            1.使用参数绑定
                1.使用绑定参数的原因是让数据库一次解析SQL,对后续的重复请求可以使用用生成好的执行计划,这样做节省CPU时间和内存。
                2.避免SQL注入
            2.尽量少使用NOT
                如果where子句中包含not关键字,那么执行时该字段的索引失效。
            3.尽量使用where来替换having
            Having在检索出所有记录后才对结果集进行过滤,这个处理需要一定的开销,而where子句限制记录的数目,能减少这方面的开销
            4.减少对表的查询
                在含有子查询的HQL中,尽量减少对表的查询,降低开销
            5.使用表的别名
            当在HQL语句中连接多个表时,使用别名,提高程序阅读性,并把别名前缀与每个列上,这样一来,可以减少解析时间并减少列歧义引起的语法错误。
            6.实体的更新与删除
                在hibernate3以后支持hql的update与delete操作

    一级缓存优化
        一级缓存也叫做session缓存,
            在一个hibernate session有效,这级缓存的可干预性不强,大多于hibernate自动管理,但它提供清除缓存的方法,这在大批量增加(更新)操作是有效果的,例如,同时增加十万条记录,按常规进行,很可能会出现异常,这时可能需要手动清除一级缓存,session.evict以及session.clear.
        检索策略(抓取策略)
            延迟加载
                延迟加载 是hibernate为提高程序执行的效率而提供的一种机制,即只有真正使用该对象的数据时才会创建。
                    load方法采用的策略延迟加载.
                    get方法采用的策略立即加载。

            检索策略分为两种:
                1.    类级别检索
                2.    关联级别检索
                类级别检索
                    类级别检索是通过session直接检索某一类对应的数据,例如
                    Customer c=session.load(Customer.class,1)
                    Session.createQuery(“from Order”)
                类级别检索策略分为立即检索与延迟检索,默认是延迟检索,类级别的检索策略可以通过<class>元素的lazy属性来设置 ,默认值是true

                在hbm配置文件中设置
                        <class name="com.itheima.one2Many.Customer" table="t_customer"
                        catalog="hibernateTest" lazy="true">

                在类中使用注解
                    @Proxy(lazy=true)
                
                    如果将lazy设置为false,代表类级别检索也使用立即检索。这时load与get就一样,都是立即检索。

                    如果对一个延迟代理对象进行初始化?
                    hibernate.initialize()
                    
                    
                    
                关联级别检索
                    查询到某个对象,获得其关联的对象或属性,这种称为关联级别检索,例如
                    c.getOrders().size()
                    c.getName()
                    对于关联级别检索我们就要研究其检索策略(抓取策略)
    
        检索策略(抓取策略)
            抓取策略介绍
                 指的是查找到某个对象后,通过这个对象去查询关联对象的信息时的一种策略。
                一对一 <one-to-one>
                一对多(多对一) <set>下有<one-to-many>  <many-to-one>
                多对多 <set>下有<many-to- many>
                我们主要是在<set>与<many-to-one>或<one-to-one>上设置fetch  lazy

                例如:查询一个客户,要关联查询它的订单
                客户是一的一方,在客户中有set集合来描述其订单,在配置中我们是使用
                <set>
                    <one-to-many>
                </set>..
                可以在set标签上设置两个属性  fetch   lazy
                Fetch主要描述的是SQL语句的格式(例如是多条,子查询,多表联查
                Lazy 控制SQL语句何时发送


                例如:在查询一个订单时,要查询客户信息
                <many-to-one> 或<one-to-one>
                也可以设置fetch  lazy
                Fetch主要描述的是SQL语句的格式(例如是多条,子查询,多表联查
                Lazy 控制SQL语句何时发送

                总结:
                讲解抓取策略
                在两方面设置
                <set fetch=”” lazy=””>

                <many-to-one fetch=”” lazy=””>
                <one-to-one>

            注解配置抓取策略
                问题:如何使用注解来设置
                    在<setse>设置的fetch与lazy可以使用下面注解来描述
                    @Fetch(FetchMode.SELECT)
                    @LazyCollection(LazyCollectionOption.TRUE)
                    在<many-to-one>或<one-to-one>上如何设置 fetch与lazy
                    @Fetch(FetchMode.SELECT)
                    @LazyToOne(LazyToOneOption.PROXY)
            set上的fetch与lazy
                set上的fetch与lazy它主要是用于设置关联的集合信息的抓取策略。
                Fetch可取值有:
                1.    SELECT 多条简单的sql   (默认值)
                2.    JOIN 采用迫切左外连接
                3.    SUBSELECT 将生成子查询的SQL
                lazy可取值有:
                1.    TURE 延迟检索   (默认值)
                2.    FALSE 立即检索
                3.    EXTRA 加强延迟检索(及其懒惰)

                    第一种组合
                        @Fetch(FetchMode.SELECT)
                        @LazyCollection(LazyCollectionOption.TRUE)
                        会首先查询客户信息,当需要订单信息时,才会关联查询订单信息。
            
                    第二种组合
                        @Fetch(FetchMode.SELECT)
                        @LazyCollection(LazyCollectionOption.FALSE)
                        当查询客户信息时,就会将订单信息也查询,也就是说订单信息没有进行延迟。
                    
                    第三种组合
                        @Fetch(FetchMode.SELECT)
                        @LazyCollection(LazyCollectionOption.EXTRA)
                        当查询客户信息时,不会查询订单信息,当需要订单的个数时,也不会查询订单信息,
                        只会通过count来统计订单个数。
                        当我们使用size(),contains()或isEmpty()方法时不会查询订单信息。

                    第四种组合
                        如果fetch选择的是join方案,那么lazy它会失效。
                        生成SQl将采用的是迫切左外连接(left outer join fetch)
                        会立即查询。

                        @Fetch(FetchMode.JOIN)
                        @LazyCollection(LazyCollectionOption.EXTRA)
            
                    第五种组合
                        @Fetch(FetchMode.SUBSELECT)
                        @LazyCollection(LazyCollectionOption.TRUE)
                        会生成子查询,但是我们在查询订单时采用的是延迟加载。
            
                    第六种组合
                        @Fetch(FetchMode.SUBSELECT)
                        @LazyCollection(LazyCollectionOption.FALSE)
                        会生成子查询,在查询客户信息时,就会将订单信息也查询出来
            
                    第七种组合
                        @Fetch(FetchMode.SUBSELECT)
                        @LazyCollection(LazyCollectionOption.EXTRA)
                        在查询订单时,只会根据情况来确定是否要订单信息,如果不需要,例如我们
                        程序中size操作,那么就会发出select count(*) from Order where c_customer_id=?

            
            One的一言fetch与lazy
                <set fetch lazy>它主要是设置在获取到一的一方时,如果去查询多的一方。
                在<many-to-one>或<one-to-one>如果去查询对方。
                对于程序 就是在多的一方如何查询一的主方信息
                例如:获取到一个订单对象,要查询客户信息。

                Fetch可取值:
                    select 默认值,代表发送一条或多条简单的select语句
                    join  发送一条迫切左外连接
                lazy可取值
                    false 不采用延迟加载
                    proxy 默认值 是否采用延迟,需要另一方的类级别延迟策略来决定
                    no-proxy 不用研究

                    第一种组合
                        @Fetch(FetchMode.SELECT)
                        @LazyToOne(LazyToOneOption.PROXY)
                        private Customer c;
                        注意:Customer的类级别延迟策略
                        @Proxy(lazy=true)
                        public class Customer {
                        当我们执行时,会首先发送一条sql只查询订单信息,客户信息会延迟,只有真正需要客户信息时,才会发送sql来查询客户信息.
            
                    第二种组合
                        @Fetch(FetchMode.SELECT)
                        @LazyToOne(LazyToOneOption.PROXY)
                        private Customer c;
                        注意:Customer的类级别延迟策略
                        @Proxy(lazy=false)
                        public class Customer {
                        当查询订单时,就会将客户信息也查询到,原因是Customer它的类级别延迟为false,也就是立即查询。
                    
                    第三种组合
                        @Fetch(FetchMode.SELECT)
                        @LazyToOne(LazyToOneOption.PROXY)
                        private Customer c;
                        当查询订单时,不会对客户信息进行延迟,立即查询客户信息
                        
                    第四种组合
                        @Fetch(FetchMode.JOIN)
                        @LazyToOne(LazyToOneOption.PROXY)
                        private Customer c;
                        如果fetch值为join,那么lazy失效。
                        会发送一条迫切左外连接来查询,也就立即查询。


        批量抓取
            我们在查询多个对象的关联对象时,可以采用批量抓取方式来对程序进行优化.
            要想实现批量抓取:
            可以在配置文件中 batch-size属性来设置
            可以使用注解 @BatchSize(size=4)
            可以采用批量抓取来解决N+1问题.
                查询客户,查询订单    
                    @Test
                    public void test1(){
                        Session session = HibernateUtils.openSession();
                        session.beginTransaction();
                        List<Customer> list = session.createQuery("from Customer").list();
                        for (Customer customer : list) {
                            System.out.println(customer.getOrder().size());
                        }
                        session.getTransaction().commit();
                        session.close();
                        
                    }    
                        
                可以在客户配置文件中配置batch-size,是在<set>标签上        
                        @BatchSize(size=3)
                        private Set<Order> order = new HashSet<Order>();    
                        
                查询订单,查询客户        
                    @Test
                    public void test2(){
                        Session session = HibernateUtils.openSession();
                        session.beginTransaction();
                        List<Order> list = session.createQuery("from Order").list();
                        for (Order order : list) {
                            System.out.println(order.getC().getName());
                        }
                        session.getTransaction().commit();
                        session.close();
                        
                    }    
                        
                    订单与客户,客户它是一个主表,订单是一个从表。
                    在设置批量抓取时都是在主表中设置
                    在配置文件中在主表的<calss>标签上设置batch-size
                    在注解使用中
                    @BatchSize(size=3)
                    public class Customer {
                                            
                        
                    注意:无论是根据哪一方来查询别一方,在进行批量抓取时,都是在父方来设置 ,
                    如果是要查询子信息,那么我们是在<set>上来设置batch-size,如果是从子方来查询父方,
                    也是在父方设置在<class>设置batch-size.
                    
                父与子区分:
                    有外键的表是子(从关联方就是父(主)表