zoukankan      html  css  js  c++  java
  • Hibernate (ORM)

    框架体系结构

     

    hibernate入门

    2.1 ORM框架

     

    Hibernate是一个数据持久化层的ORM框架.

    Object:对象,java对象,此处特指JavaBean

    Relational:关系,二维表,数据库中的表。

    映射|映射元数据:对象中属性,与表的字段,存在对应关系。

     

    2.2 什么是hibernate

     

    l Hibernate是轻量级JavaEE应用的持久层解决方案,是一个关系数据库ORM框架

    • ORM 就是通过将Java对象映射到数据库表,通过操作Java对象,就可以完成对数据表的操作

    l Hibernate提供了对关系型数据库增删改成操作

    2.3 主流的ORM框架

    n JPA Java Persistence API.JPA通过JDK 5.0注解或XML描述对象-关系表的映射关系(只有接口规范)

    n Hibernate 最流行ORM框架,通过对象-关系映射配置,可以完全脱离底层SQL

    n MyBatis  本是apache的一个开源项目 iBatis,支持普通 SQL查询,存储过程和高级映射的优秀持久层框架

    n Apache DBUtils 、Spring JDBCTemplate

    2.4 优点

    l HibernateJDBC访问数据库的代码做了封装,大大简化了数据访问层繁琐的重复性代码

    l Hibernate是一个基于jdbc的主流持久化框架,是一个优秀的orm实现,它很大程度的简化了dao层编码工作  session.save(User);

    l Hibernate使用java的反射机制

    l Hibernate的性能非常好,因为它是一个轻量级框架。映射的灵活性很出色。它支持很多关系型数据库,从一对一到多对多的各种复杂关系

    入门案例【掌握】

    3.1 编写流程

    l 1. 导入jar

    l 2. 创建数据库和表

    l 3. 编写核心配置文件(hibernate.cfg.xml--> 配置获得链接等参数

    l 4. 编写映射文件 hibernate mapping(*.hbm.xml)

    l 5 使用api测试

    3.2 数据库和表

    create database h_day01_db;

    use h_day01_db;

    create table t_user(

      id int auto_increment primary key,

      username varchar(50),

      password varchar(30)

    );

    3.3 导入jar

    版本:3.6.10   --> hibernate 4 建议注解开发,hibernate 4 对 不兼容。

    l 目录结构

     

    l jar介绍

    核心:

    必须:lib equired

    jpa规范:libjpa

    mysql驱动:

    3.4 编写JavaBean + 映射文件

    文件位置:javabean同包

    文件名称:javabean同名

    扩展名:*.hbm.xml

    l 内容:

    添加约束

    public class User {

    /*

     * create table t_user(

      id int auto_increment primary key,

      username varchar(50),

      password varchar(30)

    );

     */

    private Integer uid;

    private String username;

    private String password;

    <!DOCTYPE hibernate-mapping PUBLIC 

        "-//Hibernate/Hibernate Mapping DTD 3.0//EN"

        "http://www.hibernate.org/dtd/hibernate-mapping-3.0.dtd">

    <hibernate-mapping>

    <class name="com.itheima.a_hello.User" table="t_user">

    <!-- 主键 -->

    <id name="uid">

    <!-- 固定值:主键生成策略 -->

    <generator class="native"></generator>

    </id>

    <!-- 普通属性 -->

    <property name="username"></property>

    <property name="password"></property>

    </class>

    </hibernate-mapping>

    3.5 编写核心配置文件

    位置:类路径(classpathsrc-->WEB-INF/classes

    名称:hibernate.cfg.xml

    l 内容:

    添加约束

    <!DOCTYPE hibernate-configuration PUBLIC

    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"

    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">

    <hibernate-configuration>

    <!-- SessionFactory,相当于之前学习连接池配置 -->

    <session-factory>

    <!-- 1 基本4项 -->

    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

    <property name="hibernate.connection.url">jdbc:mysql:///h_day01_db</property>

    <property name="hibernate.connection.username">root</property>

    <property name="hibernate.connection.password">1234</property>

    <!-- 添加映射文件 -->

    <mapping resource="com/itheima/a_hello/User.hbm.xml"/>

    </session-factory>

    </hibernate-configuration>

    3.6 测试

    @Test

    public void demo01(){

    User user = new User();

    user.setUsername("伟哥哥");

    user.setPassword("1234");

    //1 加载配置文件获得核心配置对象

    Configuration config = new Configuration().configure();

    //2 获得工厂 SessionFactory,相当于连接池

    SessionFactory factory = config.buildSessionFactory();

    //3获得会话session,相当于链接Connection

    Session session = factory.openSession();

    //4 开启事务

    Transaction transaction = session.beginTransaction();

    //操作

    session.save(user);

    //5 提交事务 回滚事务

    transaction.commit();

    //6 释放资源--关闭session

    session.close();

    //7 释放资源--关闭工厂factory

    factory.close();

    }

    3.7 常见异常

    解决方案:

    将映射文件添加到核心配置文件中  hbm.xml  --> hibernate.cfg.xml

     

    api详解【多练】

    4.1 体系结构

     

    POpersistent object ,用于与数据库交互数据。--dao层  (JavaBean + hbm 

    BOBusiness object 业务数据对象。--service

    VOValue Object 值对象。--web

    开发中:直接使用JavaBean 描述三个对象。

     

    4.2 Configuration 配置对象

    l hibernate 核心配置文件种类

    hibernate.cfg.xml 通常使用xml配置文件,可以配置内容更丰富。

    hibernate.properties 用于配置key/value 形式的内容,key不能重复的。配置有很多的局限性。一般不用。

    参考文件:hibernate-distribution-3.6.10.Finalprojectetc hibernate.properties

    提供了核心配置文件常用的配置项,及选择参数。

    1.提供构造  new Configuration() hibernate将自动加载 hibernate.properties文件

    hibernate.properties文件必须存放在类路径(src)下

    2.提供方法 configure() 将加载src下的hibernate.cfg.xml

     

    3.扩展api

    configure(String) 加载指定目录下的 xml文件

    4.手动加载配置文件

    // 手动加载指定的配置文件

    config.addResource("com/itheima/a_hello/User.hbm.xml");

    // 手动加载指定类,对应的映射文件 User--> User.hbm.xml

    config.addClass(User.class);

     

    l 常见异常:

     

    开发中:将hbm.xml映射 配置 hibernate.cfg.xml

    学习中:可以使用 addClass  或 addResource

    4.3 SessionFactory工厂

    l SessionFactory 相当于java web连接池,用于管理所有session

    获得方式:config.buildSessionFactory();

    l sessionFactory hibernate缓存配置信息 (数据库配置信息、映射文件,预定义HQL语句 等)

    l SessionFactory线程安全,可以是成员变量,多个线程同时访问时,不会出现线程并发访问问题。

    提供api

    //打开一个新的会话 session

    factory.openSession();

    //获得当前线程中绑定的会话session

    factory.getCurrentSession();

    hibernate支持,将创建的session绑定到本地线程中,底层使用ThreadLocal,在程序之间共享session

    1.必须在hibernate.cfg.xml 配置

    <!-- 2 与本地线程绑定 -->

    <property name="hibernate.current_session_context_class">thread</property>

    2.如果提交或回滚事务,底层将自动关闭session

    4.4 Session 会话

    l Session 相当于 JDBC的 Connection -- 会话

    通过session操作PO对象 --增删改查

    l session单线程,线程不安全,不能编写成成员变量。

    l session api

    save 保存

    update 更新

    delete 删除

    get 通过id查询,如果没有 null

    load 通过id查询,如果没有抛异常

    createQuery("hql")  获得Query对象

    createCriteria(Class) 获得Criteria对象

    4.5 Transaction 事务

    开启事务 beginTransaction()

    获得事务 getTransaction()

    提交事务:commit()

    回滚事务:rollback()

    try{

       //开启

       //session操作

       //提交

    } catch(e){

       //回滚

    }

    扩展:不需要手动的管理事务,之后所有的事务管理都交予spring

    4.6 Query对象

    l hibernate执行hql语句

    l hql语句:hibernate提供面向对象查询语句,使用对象(类)和属性进行查询。区分大小写。

    获得 session.createQuery("hql")

    l 方法:

    list()  查询所有

    uniqueResult() 获得一个结果。如果没有查询到返回null,如果查询多条抛异常。

    setFirstResult(int) 分页,开始索引数startIndex

    setMaxResults(int) 分页,每页显示个数 pageSize

    4.7 Criteria对象(了解)

    l QBCquery by criteria),hibernate提供纯面向对象查询语言,提供直接使用PO对象进行操作。

    l 获得方式:Criteria criteria = session.createCriteria(User.class);

    l 条件

    criteria.add(Restrictions.eq("username", "tom"));

    // Restrictions.gt(propertyName, value) 大于

    // Restrictions.ge(propertyName, value) 大于等于

    // Restrictions.lt(propertyName, value) 小于

    // Restrictions.le(propertyName, value) 小于等于

    // Restrictions.like(propertyName, value) 模糊查询,注意:模糊查询值需要使用 % _

    4.8 工具类

    public class H3Utils {

    // 会话工厂,整个程序只有一份。

    private static SessionFactory factory;

    static{

    //1 加载配置

    Configuration config = new Configuration().configure();

    //2 获得工厂

    factory = config.buildSessionFactory();

    //3 关闭虚拟机时,释放SessionFactory

    Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {

    @Override

    public void run() {

    System.out.println("虚拟机关闭!释放资源");

    sf.close();

    }

    }));

    }

    /**

     * 获得一个新的session

     * @return

     */

    public static Session openSession(){

    return factory.openSession();

    }

    /**

     * 获得当前线程中绑定session

     * * 注意:必须配置

     * @return

     */

    public static Session getCurrentSession(){

    return factory.getCurrentSession();

    }

    }

     

    核心配置文件详解

    5.1 详细配置【多读】

    <!-- SessionFactory,相当于之前学习连接池配置 -->

    <session-factory>

    <!-- 1 基本4项 -->

    <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>

    <property name="hibernate.connection.url">jdbc:mysql:///h_day01_db</property>

    <property name="hibernate.connection.username">root</property>

    <property name="hibernate.connection.password">1234</property>

    <!-- 2 与本地线程绑定 -->

    <property name="hibernate.current_session_context_class">thread</property>

      <!-- 3 方言:为不同的数据库,不同的版本,生成sql语句(DQL查询语句)提供依据

      * mysql 字符串 varchar

      * orcale 字符串 varchar2

      -->

      <property name="hibernate.dialect">org.hibernate.dialect.MySQL5Dialect</property>

    <!-- 4 sql语句 -->

    <!-- 显示sql语句 -->

    <property name="hibernate.show_sql">true</property>

    <property name="hibernate.format_sql">true</property>

    <!-- 5 自动创建表(了解) ,学习中使用,开发不使用的。

    开发中DBA 先创建表,之后根据表生产 PO

    取值:

    update:【】

    如果表不存在,将创建表。

    如果表已经存在,通过hbm映射文件更新表(添加)。(映射文件必须是数据库对应)

    表中的列可以多,不负责删除。

    create :如果表存在,先删除,再创建。程序结束时,之前创建的表不删除。【】

    create-drop:与create几乎一样。如果factory.close()执行,将在JVM关闭同时,将创建的表删除了。(测试)

    validate:校验 hbm映射文件 和 表的列是否对应,如果对应正常执行,如果不对应抛出异常。(测试)

    -->

    <property name="hibernate.hbm2ddl.auto">create</property>

    <!-- 6 java web 6.0 存放一个问题

    * BeanFactory 空指针异常

    异常提示:org.hibernate.HibernateException: Unable to get the default Bean Validation factory

    解决方案:取消bean校验

    -->

    <property name="javax.persistence.validation.mode">none</property>

    <!-- 添加映射文件

    <mapping >添加映射文件

    resource 设置 xml配置文件 (addResource(xml)

    class 配置类 (addClass(User.class)) 配置的是全限定类名

    -->

    <mapping  resource="com/itheima/a_hello/User.hbm.xml"/>

    </session-factory>

    Hibernate中持久化类

    6.1 编写规则

    • 提供一个无参数 public访问控制符的构造器
    • 提供一个标识属性,映射数据表主键字段
    • 所有属性提供public访问控制符的 set  get 方法(javaBean)
    • 标识属性应尽量使用基本数据类型的包装类型
    • 不要用final修饰实体 (将无法生成代理对象进行优化)

    6.2 持久化对象的唯一标识 OID

    l Java按地址区分同一个类的不同对象.

    l 关系数据库用主键区分同一条记录

    l Hibernate使用OID来建立内存中的对象和数据库中记录的对应关系

    结论: 对象的OID和数据库的表的主键对应。为保证OID的唯一性,应该让Hibernate来为OID付值

    6.3 区分自然主键和代理主键

    主键需要具备: 不为空/不能重复/不能改变

    自然主键: 在业务中,某个属性符合主键的三个要求.那么该属性可以作为主键列.

    代理主键: 在业务中,不存符合以上3个条件的属性,那么就增加一个没有意义的列.作为主键.

    6.4 基本数据与包装类型

    基本数据类型和包装类型对应hibernate的映射类型相同

    l 基本类型无法表达null、数字类型的默认值为0。

    l 包装类默认值是null。当对于默认值有业务意义的时候需要使用包装类。

    6.5 类型对应

    Java数据类型

    Hibernate数据类型

    标准SQL数据类型
    (PS:对于不同的DB可能有所差异)

    bytejava.lang.Byte

    byte

    TINYINT

    shortjava.lang.Short

    short

    SMALLINT

    intjava.lang.Integer

    integer

    INGEGER

    longjava.lang.Long

    long

    BIGINT

    floatjava.lang.Float

    float

    FLOAT

    doublejava.lang.Double

    double

    DOUBLE

    java.math.BigDecimal

    big_decimal

    NUMERIC

    charjava.lang.Character

    character

    CHAR(1)

    booleanjava.lang.Boolean

    boolean

    BIT

    java.lang.String

    string

    VARCHAR

    booleanjava.lang.Boolean

    yes_no

    CHAR(1)('Y''N')

    booleanjava.lang.Boolean

    true_false

    CHAR(1)('Y''N')

    java.util.Datejava.sql.Date

    date

    DATE

    java.util.Datejava.sql.Time

    time

    TIME

    java.util.Datejava.sql.Timestamp

    timestamp

    TIMESTAMP

    java.util.Calendar

    calendar

    TIMESTAMP

    java.util.Calendar

    calendar_date

    DATE

    byte[]

    binary

    VARBINARYBLOB

    java.lang.String

    text

    CLOB

    java.io.Serializable

    serializable

    VARBINARYBLOB

    java.sql.Clob

    clob

    CLOB

    java.sql.Blob

    blob

    BLOB

    java.lang.Class

    class

    VARCHAR

    java.util.Locale

    locale

    VARCHAR

    java.util.TimeZone

    timezone

    VARCHAR

    java.util.Currency

    currency

    VARCHAR

    6.6 普通属性

    <hibernate-mapping>

    package 用于配置PO类所在包

    例如: package="com.itheima.d_hbm"

    <class> 配置 PO类 和 表 之间对应关系

    namePO类全限定类名

    例如:name="com.itheima.d_hbm.Person"

    如果配置 packagename的取值可以是简单类名 name="Person"

    table : 数据库对应的表名

    dynamic-insert="false" 是否支持动态生成insert语句

    dynamic-update="false" 是否支持动态生成update语句

    如果设置truehibernate底层将判断提供数据是否为null,如果为nullinsertupdate语句将没有此项。

    普通字段

    <property>

    name : PO类的属性

    column : 表中的列名,默认name的值相同

    type:表中列的类型。默认hibernate自己通过getter获得类型,一般情况不用设置

    取值1: hibernate类型

    string 字符串

    integer 整形

    取值2: java类型 (全限定类名)

    java.lang.String 字符串

    取值3:数据库类型

    varchar(长度字符串

    int 整形

    <property name="birthday">

      <column name="birthday" sql-type="datetime"></column>

      </property>

      javabean 一般使用类型 java.util.Date

      jdbc规范提供3

      java类型 mysql类型

      java.sql.Date date

      java.sql.time time

      java.sql.timestamp timestamp

      null datetime

      以上三个类型都是java.util.Date子类

    length : 列的长度。默认值:255

    not-null : 是否为null

    unique : 是否唯一

    access:设置映射使用PO类属性或字段

    property : 使用PO类属性,必须提供settergetter方法

    field : 使用PO类字段,一般很少使用。

    insert 生成insert语句时,是否使用当前字段。

    update 生成update语句时,是否使用当前字段。

    默认情况:hibernate生成insertupdate语句,使用配置文件所有项

    注意:配置文件如果使用关键字,列名必须使用重音符

    6.7 主键

    主键

    <id>配置主键

    name:属性名称

    access="" 设置使用属性还是字段

    column=""  表的列名

    length=""  长度

    type="" 类型

    <generator> class属性用于设置主键生成策略

    1.increment hibernate自己维护自动增长

    底层通过先查询max值,再+1策略

    不建议使用,存在线程并发问题

    2.identity hibernate底层采用数据库本身自动增长列

    例如:mysql auto_increment

    3.sequence hibernate底层采用数据库序列

    例如:oracle 提供序列

    4.hilo 

             </generator>

    5.native 根据底层数据库的能力选择 identity、sequence 或者 hilo 中的一个。【】

    ##以上策略使用整形,long, short 或者 int 类型

    6.uuid 采用字符串唯一值【】

    ##以上策略 代理主键,有hibernate维护。

    7.assigned 自然主键,由程序自己维护。【】

    对象状态与一级缓存

    1.1 状态介绍

    l hibernate 规定三种状态:瞬时态、持久态、脱管态

    l 状态

    瞬时态:transientsession没有缓存对象,数据库也没有对应记录。

    OID特点:没有值

    持久态:persistentsession缓存对象,数据库最终会有记录。(事务没有提交)

    OID特点:有值

    脱管态:detachedsession没有缓存对象,数据库有记录。

    OID特点:有值

    1.2 转换

     

    1.2.1 瞬时态/临时态

    获得:一般都只直接创建(new

    瞬时态 转换 持久态

    一般操作:save方法、saveOrUpdate

    瞬时态 转换 脱管态

    一般操作:通过setId方法设置数据

    例如:

    User user = new User(); //瞬时态

    user.setUid(1); //脱管态

    1.2.2 持久态

    l 获得:

    查询操作:getloatcreateQuerycreateCriteria 等 获得都是持久态【】

    执行save之后持久态

    执行update之后持久态

    持久态 转换 瞬时态

    官方规定执行delete()  --民间:删除态

    持久态 转换 脱管态

    session没有记录

    session.close () 关闭

    session.clear() 清除所有

    session.evict(obj) 清除指定的PO对象

    1.2.3 脱管态/游离态

    l 获得:

    创建、并设置OID

    通过api获得

    脱管态 转换 瞬时态

    手动去除OID,设置成默认值

    脱管态 转换 持久态

    一般操作:update()、saveOrUpdate

    @Test

    public void demo01(){

    User user = new User(); //瞬时态

    user.setUsername("jack");

    user.setPassword("1234"); //瞬时态(与oid没有关系)

    Session session = factory.openSession();

    session.beginTransaction();

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

    //---- 持久态就应该有持久态的行为(特性)

    // user.setUsername("rose");  //持久态对象 被修改后,hibernate将自动生成update语句

    // session.flush();

    session.getTransaction().commit();

    session.close();

    System.out.println(user);  //脱管态

    }

    一级缓存

    2.1 介绍

    一级缓存:又称为session级别的缓存。当获得一次会话(session),hibernatesession中创建多个集合(map),用于存放操作数据(PO对象),为程序优化服务,如果之后需要相应的数据,hibernate优先从session缓存中获取,如果有就使用;如果没有再查询数据库。当session关闭时,一级缓存销毁。

    2.2 一级缓存操作

    2.2.1 证明一级缓存

    @Test

    public void demo02(){

    //证明一级缓存

    Session session = factory.openSession();

    session.beginTransaction();

    //1 查询 id = 1

    User user = (User) session.get(User.class, 1);

    System.out.println(user);

    //2 再查询 -- 不执行select语句,将从一级缓存获得

    User user2 = (User) session.get(User.class, 1);

    System.out.println(user2);

    session.getTransaction().commit();

    session.close();

    }

    2.2.2 移除

    @Test

    public void demo03(){

    //清除缓存

    Session session = factory.openSession();

    session.beginTransaction();

    User user = (User) session.get(User.class, 1);  //--select

    System.out.println(user);

    //清除

    //session.clear();

    session.evict(user);

    // 一级缓存没有缓存对象,从数据库直接查询

    User user2 = (User) session.get(User.class, 1);  //--select

    System.out.println(user2);

    session.getTransaction().commit();

    session.close();

    }

    2.2.3 一级缓存快照【掌握】

    快照:与一级缓存一样的存放位置,对一级缓存数据备份。保证数据库的数据与 一级缓存的数据必须一致。如果一级缓存修改了,在执行commit提交时,将自动刷新一级缓存,执行update语句,将一级缓存的数据更新到数据库。

     

    2.2.4 refresh 刷新

    l refresh 保证 一级缓存的数据 与 数据库的数据 保持一致。将执行select语句查询数据库,将一级缓存中的数据覆盖掉。只要执行refresh都将执行select语句。

    @Test

    public void demo04(){

    //刷新

    Session session = factory.openSession();

    session.beginTransaction();

    User user = (User) session.get(User.class, 1);  //--select

    System.out.println(user);

    session.refresh(user);

    session.getTransaction().commit();

    session.close();

    }

    2.2.5 快照演示(一级缓存刷新)

    @Test

    public void demo05(){

    //快照

    Session session = factory.openSession();

    session.beginTransaction();

    User user = (User) session.get(User.class, 1);  //--select

    System.out.println(user);

    //修改持久态对象内容(一级缓存内容)--默认在commit时,将触发update语句。

    user.setUsername("rose2");

    session.getTransaction().commit();

    session.close();

    }

    l 问题:一级缓存什么时候刷新?(了解)

    默认情况提交(commit())刷新。

    @Test

    public void demo06(){

    //设置刷新时机

    Session session = factory.openSession();

    session.beginTransaction();

    //1 设置

    session.setFlushMode(FlushMode.MANUAL);

    User user = (User) session.get(User.class, 1);

    user.setUsername("rose4");

    //1 查询所有 -- AUTO , 查询之前先更新,保存一级缓存和数据库一样的

    //List<User> allUser = session.createQuery("from User").list();

    //2手动刷新 --MANUAL 将执行update,注意:一级缓存必须修改后的

    session.flush();

    // 如果MANUAL 在执行commit 不进行update

    session.getTransaction().commit();

    session.close();

    }

    2.3 PO对象操作

    2.3.1 save & persist

    l save方法:瞬时态 转换 持久态 ,会初始化OID

    1.执行save方法,立即触发insert语句,从数据库获得主键的值(OID值)

    2.执行save方法前,设置OID将忽略。

    3.如果执行查询,session缓存移除了,在执行save方法,将执行insert

    @Test

    public void demo01(){

    User user = new User();

    user.setUid(100);

    user.setUsername("jack");

    user.setPassword("1234");

    Session session = factory.openSession();

    session.beginTransaction();

    session.save(user);

    session.getTransaction().commit();

    session.close();

    }

    @Test

    public void demo03(){

    //代理  assigned

    User user = new User();

    //user.setUid(100);

    user.setUsername("jack");

    user.setPassword("1234");

    Session session = factory.openSession();

    session.beginTransaction();

    session.save(user);

    session.getTransaction().commit();

    session.close();

    }

    注意:持久态对象不能修改OID的值

     

    @Test

    public void demo04(){

    Session session = factory.openSession();

    session.beginTransaction();

    User user = (User) session.get(User.class, 100);

    user.setUid(101);

    session.save(user);

    session.getTransaction().commit();

    session.close();

    }

    l persist方法:瞬时态 转换 持久态 ,不会立即初始化OID

    注意: persist方法不会立即得到ID,所以执行sql语句的时机要靠后.

    2.3.2 update

    l update:脱管态 转换 持久态

    如果OID在数据存放的,将执行update语句

    如果OID不存在将抛异常

    @Test

    public void demo01(){

    //自然 assigned

    User user = new User();

    user.setUid(101);

    user.setUsername("jack1");

    user.setPassword("12345");

    Session session = factory.openSession();

    session.beginTransaction();

    session.update(user);

    session.getTransaction().commit();

    session.close();

    }

    注意1:如果数据没有修改,执行save方法,将触发update语句。

    查询速度 比 更新速度快

    通过<class select-before-update> 来设置更新前先查询,如果没有改变就不更新。

     

    总结:

    update 之后对象 持久态

    @Test

    public void demo03(){

    // merge 合并

    User user = new User();

    user.setUid(1);

    user.setUsername("jack3");

    user.setPassword("12345");

    Session session = factory.openSession();

    session.beginTransaction();

    // 1 oid =1 持久态对象

    User user2 = (User) session.get(User.class, 1);

    // session.update(user);

    session.merge(user);

    session.getTransaction().commit();

    session.close();

    }

    2.3.3 saveOrUpdate

    l 代理主键:

    判断是否有OID

    如果没有OID,将执行insert语句

    如果有OID,将执行update语句。

    @Test

    public void demo02(){

    // 代理 native

    User user = new User();

    // user.setUid(2);

    user.setUsername("jack2");

    user.setPassword("12345");

    Session session = factory.openSession();

    session.beginTransaction();

    session.saveOrUpdate(user);

    session.getTransaction().commit();

    session.close();

    }

    l 自然主键:

    先执行select语句,查询是否存放

    如果不存在,将执行insert

    如果存在,将执行update

    @Test

    public void demo02(){

    // 自然 assigned

    User user = new User();

    user.setUid(2);

    user.setUsername("jack2333");

    user.setPassword("12345333");

    Session session = factory.openSession();

    session.beginTransaction();

    session.saveOrUpdate(user);

    session.getTransaction().commit();

    session.close();

    }

    注意1native下,默认OID是否存在,使用默认值。例如:Integer 默认null

    通过<id  unsaved-value="1"> 修改使用默认值,如果设置1进行insert语句。此内容提供hibernate使用的,录入到数据库后,采用自动增长。

     

    2.3.4 delete

    总结:

    PO对象状态:瞬时态、持久态、脱管态

     

    多表设计

    在开发中,前期进行需求分析,需求分析提供E--R图,根据ER图编写表结构。

    表之间关系存在3种:一对多、多对多、一对一。(回顾)

     

    一对多:1表(主表)必须主键 和 多表(从表)必须外键,主表的主键 与 从表外键 形成主外键关系

    多对多:提供中间表(从表),提供2个字段(外键)分别对应两个主表。

    一对一:???

    面单对象描述 对象与对象 之间关系?【掌握】

    一对多:客户和订单

    private class Customer{

    //一对多:一个客户 拥有 多个订单

    private Set<Order> orderSet;

    }

    private class Order{

    //多对一:多个订单 属于 一个客户

    private Customer customer;

    }

    多对多:Student学生 和 Course课程

    private class Student{

    //多对多:多个学生(当前)学习 【不同课程】

    private Set<Course> courseSet ...;

    }

    private class Course{

    //多对多:多个课程 可以被 【不同学生】学习

    private Set<Student> student = ...;

    }

    一对一:公司company 和 地址address

    private class Company{

    private Address address;

    }

    private class Address{

    private Company company;

    }

    关联关系映射

    4.1 一对多实现【掌握】

    4.1.1 实现类

    public class Customer {

    private Integer cid;

    private String cname;

    //一对多:一个客户(当前客户) 拥有 【多个订单】

    // * 需要容器存放多个值,一般建议Set (不重复、无序)

    // * 参考集合:ListMapArray

    // ** 建议实例化--使用方便

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

    public class Order {

    private Integer xid;

    private String price;

    //多对一:多个订单属于【一个客户】

    private Customer customer;

    4.1.2 配置文件

    l Customer.hbm.xml

    <class name="com.itheima.b_onetomany.Customer" table="t_customer">

    <id name="cid">

    <generator class="native"></generator>

    </id>

    <property name="cname"></property>

    <!-- 一对多:一个客户(当前客户) 拥有 【多个订单】

    确定容器  set <set>

    2 name确定对象属性名

    确定从表外键的名称

    确定关系,及另一个对象的类型

    注意:

    hibernate中可以只进行单向配置

    每一个配置项都可以完整的描述彼此关系。

    一般情况采用双向配置,双方都可以完成描述表与表之间关系。

     -->

    <!-- 一对多:一个客户(当前客户) 拥有 【多个订单】 -->

    <set name="orderSet" cascade="delete-orphan">

    <key column="customer_id"></key>

    <one-to-many class="com.itheima.b_onetomany.Order"/>

    </set>

    </class>

    l Order.hbm.xml

    <class name="com.itheima.b_onetomany.Order" table="t_order">

    <id name="xid">

    <generator class="native"></generator>

    </id>

    <property name="price"></property>

    <!-- 多对一:多个订单属于【一个客户】

    * name 确定属性名称

    * class 确定自定义类型

    * column 确定从表的外键名称

    -->

    <many-to-one name="customer" class="com.itheima.b_onetomany.Customer" column="customer_id"></many-to-one>

    </class>

     

    4.2 一对多操作

    4.2.1 保存客户

    @Test

    public void demo01(){

    // 1 创建客户,并保存客户--成功

    Session session = factory.openSession();

    session.beginTransaction();

    Customer customer = new Customer();

    customer.setCname("田志成");

    session.save(customer);

    session.getTransaction().commit();

    session.close();

    }

    4.2.2 保存订单

    @Test

    public void demo02(){

    // 2 创建订单,保存订单--成功,外键为null

    Session session = factory.openSession();

    session.beginTransaction();

    Order order = new Order();

    order.setPrice("998");

    session.save(order);

    session.getTransaction().commit();

    session.close();

    }

    4.2.3 客户关联订单,只保存客户

    @Test

    public void demo03(){

    // 3 创建客户和订单,客户关联订单,保存客户?

    Session session = factory.openSession();

    session.beginTransaction();

    //1 客户和订单

    Customer customer = new Customer();

    customer.setCname("成成");

    Order order = new Order();

    order.setPrice("998");

    //2 客户关联订单

    customer.getOrderSet().add(order);

    //3 保存客户

    session.save(customer);

    session.getTransaction().commit();

    session.close();

    }

     

    4.2.4 双向关联,使用inverse

     

    @Test

    public void demo04(){

    // 4  创建客户和订单,客户关联订单,订单也关联客户,保存客户和订单?

    // * 开发中优化程序 , n + 1 问题?

    // ** 解决方案1:客户不关联订单 ,不建议

    // ** 解决方案2:客户放弃对订单表外键值的维护。

    // **** Customer.hbm.xml <set name="orderSet" inverse="true">

    // ** inverse 将维护外键值的权利交予对象。相当于自己放弃。(反转)

    Session session = factory.openSession();

    session.beginTransaction();

    //1 客户和订单

    Customer customer = new Customer();

    customer.setCname("成成");

    Order order = new Order();

    order.setPrice("998");

    //2 客户关联订单

    customer.getOrderSet().add(order);

    //3 订单也关联客户

    order.setCustomer(customer);

    //4 保存客户

    // * 1 save(order) -- insert  --> 1,998 null

    // * 2 订单管理客户,此时null --预留update --> 更新所有(正常设置)

    // * 3 save(customer) -- insert --> 1,成成

    // * 4 客户关联订单  --> 预留update --> 更新订单外键 (维护外键)

    // * 5 提交commit --> 执行和 4

    session.save(order);

    session.save(customer);

    session.getTransaction().commit();

    session.close();

    }

    在一对多开发中,一方一般都放弃对外键值的维护。及<set inverse="true

    4.3 级联操作(读、理解)

    4.3.1 save-update  级联保存或更新

     

    @Test

    public void demo032(){

    // 32 创建客户和订单,客户关联订单,保存客户? --抛异常

    // ** 解决方案2:级联操作--级联保存或更新

    // ** Customer.hbm.xml <set cascade="save-update">

    // ** 在保存客户的同时,一并保存订单

    Session session = factory.openSession();

    session.beginTransaction();

    //1 客户和订单

    Customer customer = new Customer(); //瞬时态

    customer.setCname("成成");

    Order order = new Order(); //瞬时态

    order.setPrice("998");

    //2 客户关联订单

    customer.getOrderSet().add(order);

    //3 保存客户

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

    // 关联操作都是持久态的,此时 持久态Customer 引用 一个 瞬时态的Order 抛异常

    session.getTransaction().commit();

    session.close();

    }

    4.3.2 delete 级联删除

     

    @Test

    public void demo05(){

    // 5 查询客户,并删除(持久态)

    // 默认:当删除客户,默认将订单外键设置成null

    // 级联删除:删除客户时,并将客户的订单删除。

    // ** Customer.hbm.xml <set name="orderSet" cascade="delete">

    Session session = factory.openSession();

    session.beginTransaction();

    Customer customer = (Customer) session.get(Customer.class, 10);

    session.delete(customer);

    session.getTransaction().commit();

    session.close();

    }

    4.3.3 孤儿删除

    一对多关系,存在父子关系。1表(主表)可以成为父表,多表(从表)也可以子表。

     

    总结:

    主表不能删除,从表已经引用(关联)的数据

    从表不能添加,主表不存在的数据。

     

    @Test

    public void demo06(){

    // 6 查询客户,并查询订单,解除客户和订单订单的关系

    // * 默认:客户和订单解除关系后,外键被设置成null,此时订单就是孤儿。客户和订单都存在。

    // * 孤儿删除(孤子删除),当订单称为孤儿,一并删除。客户仍存在。

    Session session = factory.openSession();

    session.beginTransaction();

    //1 查询客户

    Customer customer = (Customer) session.get(Customer.class, 9);

    //2查询订单

    Order order = (Order) session.get(Order.class, 8);

    //3 解除关系

    customer.getOrderSet().remove(order);

    session.getTransaction().commit();

    session.close();

    }

    4.3.4 总结

    save-updateA保存,同时保存B

    delete:删除A,同时删除BAB都不存在

    delete-orphan:孤儿删除,解除关系,同时将B删除,A存在的。

    如果需要配置多项,使用逗号分隔。<set cascade="save-update,delete">

    all : save-update 和 delete 整合

    all-delete-orphan : 三个整合

    1.1.1 Hibernate的关联关系映射:(多对多)

    1.1.1.1 多对多的配置:

    步骤一创建实体和映射:

    Student:

    public class Student {

    private Integer sid;

    private String sname;

    // 学生选择多门课程.

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

    ...

    }

     

    Course:

    public class Course {

    private Integer cid;

    private String cname;

    // 课程可以被多个学生选择:

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

    ...

    }

     

    Student.hbm.xml

    <hibernate-mapping>

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

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

    <generator class="native"/>

    </id>

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

    <!-- 配置多对多关联关系 -->

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

    <key column="sno"/>

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

    </set>

    </class>

    </hibernate-mapping>

     

    Course.hbm.xml

    <hibernate-mapping>

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

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

    <generator class="native"/>

    </id>

     

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

     

    <!-- 配置多对多关联关系映射 -->

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

    <key column="cno"/>

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

    </set>

    </class>

    </hibernate-mapping>

    抓取策略(优化)

    2.1 检索方式

    l 立即检索:立即查询,在执行查询语句时,立即查询所有的数据。

    l 延迟检索:延迟查询,在执行查询语句之后,在需要时在查询。(懒加载)

    2.2 检查策略

    l 类级别检索:当前的类的属性获取是否需要延迟。

    关联级别的检索:当前类 关联 另一个类是否需要延迟。

    2.3 类级别检索

    l get:立即检索。get方法一执行,立即查询所有字段的数据。

    l load:延迟检索。默认情况,load方法执行后,如果只使用OID的值不进行查询,如果要使用其他属性值将查询 。 Customer.hbm.xml  <class  lazy="true | false">

    lazy 默认值true,表示延迟检索,如果设置false表示立即检索。

     

    @Test

    public void demo02() {

    //类级别

    Session session = factory.openSession();

    session.beginTransaction();

    //1立即

    // Customer customer = (Customer) session.get(Customer.class, 1);

    //2延迟

    Customer customer = (Customer) session.load(Customer.class, 1);

    //打印

    System.out.println(customer.getCid());

    System.out.println(customer.getCname());

    session.getTransaction().commit();

    session.close();

    }

    2.4 关联级别检索

    2.4.1 一对多或多对多

    2.4.1.1 介绍

    容器<set> 提供两个属性:fetchlazy

    fetch:确定使用sql格式

    lazy:关联对象是否延迟。

    l fetchjoinselectsubselect

    join:底层使用迫切左外连接

    select:使用多个select语句(默认值)

    subselect:使用子查询

    l lazyfalsetrueextra

    false:立即

    true:延迟(默认值)

    extra:极其懒惰

     

    2.4.1.2 fetch="join"

    l fetch="join" lazy无效。底层使用迫切左外连接,使用一条select将所有内容全部查询。

     

    @Test

    public void demo03() {

    //关联级别:一对多,

    // * Customer.hbm.xml  <set fetch="join">

    // *** select语句使用左外连接,一次性查询所有

    Session session = factory.openSession();

    session.beginTransaction();

    //1 查询客户

    Customer customer = (Customer) session.get(Customer.class, 1);

    System.out.println(customer.getCname());

    //2 查询客户订单数

    Set<Order> orderSet = customer.getOrderSet();

    System.out.println(orderSet.size());

    //3 查询客户订单详情

    for (Order order : orderSet) {

    System.out.println(order);

    }

    session.getTransaction().commit();

    session.close();

    }

    2.4.1.3 fetch="select"

    当前对象 和 关联对象 使用多条select语句查询。

    l lazy="false" , 立即,先查询客户select,立即查询订单select

    l lazy="true",延迟,先查询客户select,需要订单时,再查询订单select

    l lazy="extra",极其懒惰(延迟),先查询客户select, 如果只需要订单数,使用聚合函数(不查询详情)

     

     

    2.4.1.4 fetch="subselect"

    将使用子查询。注意:必须使用Query否则看不到效果。

    l lazy= 同上

    @Test

    public void demo04() {

    //关联级别:一对多,

    // 演示3* Customer.hbm.xml  <set fetch="subselect">

    Session session = factory.openSession();

    session.beginTransaction();

    //1 查询客户

    List<Customer> allCustomer = session.createQuery("from Customer").list();

    Customer customer = allCustomer.get(0);

    System.out.println(customer.getCname());

    //2 查询客户订单数

    Set<Order> orderSet = customer.getOrderSet();

    System.out.println(orderSet.size());

    //3 查询客户订单详情

    for (Order order : orderSet) {

    System.out.println(order);

    }

    session.getTransaction().commit();

    session.close();

    }

     

     

     

    2.4.2 多对一

    2.4.2.1 介绍

    l <many-to-one fetch="" lazy="">  (<one-to-one>)

    l fetch取值:joinselect

    join:底层使用迫切左外连接

    select:多条select语句

    l lazy取值:falseproxyno-proxy

    false:立即

    proxy:采用关联对象 类级别检索的策略。

    订单 关联 客户 (多对一)

    订单 立即获得 客户,需要在客户Customer.hbm.xml <class lazy="false">

    订单 延迟获得 客户,需要在客户Customer.hbm.xml <class lazy="true">

    no-proxy 不研究

     

    2.4.2.2 fetch="join"

    l fecth="join" select语句使用左外连接,此时lazy无效。

     

    @Test

    public void demo05() {

    //关联级别:多对一,

    // 演示1* Order.hbm.xml  <set fetch="join">  lazy无效

    // * 注意:检查Customer.hbm.xml 和 Order.hbm.xml 没有额外的配置

    Session session = factory.openSession();

    session.beginTransaction();

    //1 查询订单

    Order order = (Order) session.get(Order.class, 1);

    System.out.println(order.getPrice());

    //2 查询订单客户信息

    Customer customer = order.getCustomer();

    System.out.println(customer.getCname());

    session.getTransaction().commit();

    session.close();

    }

    2.4.2.3 fetch="select"

    将采用多条select语句,lazy="proxy"是否延迟,取决关联对象 类级别检索策略。

    l lazy="false"

     

    l lazy="proxy"

     

    2.5 批量查询

    当客户 关联查询 订单,给每一个客户生产一个select语句查询订单。批量查询使用in语句减少查询订单语句个数。

    默认:select * from t_order where customer_id = ?

    批量:select * from t_order where customer_id in (?,?,?,?)

    l <set batch-size="5"> 5表示括号中?个数。

     

    @Test

    public void demo06() {

    //批量查询

    Session session = factory.openSession();

    session.beginTransaction();

    //1 查询所有客户

    List<Customer> allCustomer = session.createQuery("from Customer").list();

    //2遍历

    for (Customer customer : allCustomer) {

    System.out.println(customer.getCname());

    System.out.println(customer.getOrderSet().size());

    }

    session.getTransaction().commit();

    session.close();

    }

    2.6 检索总结

    检索策略

    优点

    缺点

    优先考虑使用的场合

    立即检索

    对应用程序完全透明,不管对象处于持久化状态还是游离状态,应用程序都可以从一个对象导航到关联的对象

    (1)select语句多

    (2)可能会加载应用程序不需要访问的对象,浪费许多内存空间。

    (1)类级别

    (2)应用程序需要立即访问的对象

    (3)使用了二级缓存

    延迟检索

    由应用程序决定需要加载哪些对象,可以避免执行多余的select语句,以及避免加载应用程序不需要访问的对象。因此能提高检索性能,并节省内存空间。

    应用程序如果希望访问游离状态的代理类实例,必须保证她在持久化状态时已经被初始化。

    (1)一对多或者多对多关联

    (2)应用程序不需要立即访问或者根本不会访问的对象

    表连接检索

    (1)对应用程序完全透明,不管对象处于持久化状态还是游离状态,都可从一个对象导航到另一个对象。

    (2)使用了外连接,select语句少

    (1)可能会加载应用程序不需要访问的对象,浪费内存。

    (2)复杂的数据库表连接也会影响检索性能。

    (1)多对一或一对一关联

    (2)需要立即访问的对象

    (3)数据库有良好的表连接性能。

    Customer  Get(int id)

    Return Session.load(Customer.class,id);

    1. layz=false
    2. 在Service层获得在页面要上要用到的属性=> Service层中确保数据已经

    查询方式总结

    1.通过OID检索(查询)

    get()立即、如果没有数据返回null

    load()延迟,如果没有数据抛异常。

    2.导航对象图检索方式:关联查询

    customer.getOrderSet()

    user.getPost().getDepartment().getDepName();

    3.原始sql语句

    SQLQuery sqlQuery = session.createSQLQuery("sql 语句")   --->表,表字段(列)

    sqlQuery.list() 查询所有

    sqlQuery.uniqueResult() 查询一个

    4.HQLhibernate query language hibernate 查询语言【1

    Query query = session.createQuery("hql语句")  --> 对象,对象属性

    5.QBCquery by criteria 纯面对对象查询语言【2

    HQL【掌握】

    4.1 介绍

     

    4.2 查询所有客户

    @Test

    public void demo01(){

    //1 查询所有

    Session session = factory.openSession();

    session.beginTransaction();

    //1  使用简单类名 , 存在自动导包

    // * Customer.hbm.xml <hibernate-mapping auto-import="true">

    // Query query = session.createQuery("from Customer");

    //2 使用全限定类名

    Query query = session.createQuery("from com.itheima.a_init.Customer");

    List<Customer> allCustomer = query.list();

    for (Customer customer : allCustomer) {

    System.out.println(customer);

    }

    session.getTransaction().commit();

    session.close();

    }

    4.3 选择查询

    @Test

    public void demo02(){

    //2 简单条件查询

    Session session = factory.openSession();

    session.beginTransaction();

    //1 指定数据,cid OID名称

    // Query query = session.createQuery("from Customer where cid = 1");

    //2 如果使用id,也可以(了解)

    // Query query = session.createQuery("from Customer where id = 1");

    //3 对象别名 ,格式: 类 [as] 别名

    // Query query = session.createQuery("from Customer as c where c.cid = 1");

    //4 查询所有项,mysql--> select * from...

    Query query = session.createQuery("select c from Customer as c where c.cid = 1");

    Customer customer = (Customer) query.uniqueResult();

    System.out.println(customer);

    session.getTransaction().commit();

    session.close();

    }

    4.4 投影查询(部分)

    @Test

    public void demo04(){

    //4 投影

    Session session = factory.openSession();

    session.beginTransaction();

    //1 默认

    //如果单列 ,select c.cname from,需要List<Object>

    //如果多列,select c.cid,c.cname from ,需要List<Object[]>  ,list存放每行,Object[]多列

    // Query query = session.createQuery("select c.cid,c.cname from Customer c");

    //2 将查询部分数据,设置Customer对象中

    // * 格式:new Customer(c.cid,c.cname)

    // * 注意:Customer必须提供相应的构造方法。

    // * 如果投影使用oid,结果脱管态对象。

    Query query = session.createQuery("select new Customer(c.cid,c.cname) from Customer c");

    List<Customer> allCustomer = query.list();

    for (Customer customer : allCustomer) {

    System.out.println(customer.getCid() + " : " + customer.getOrderSet().size());

    }

    session.getTransaction().commit();

    session.close();

    }

    4.5 排序

    @Test

    public void demo03(){

    //3排序 ,mysql--> select... order by 字段  [asc]|desc ,....

    Session session = factory.openSession();

    session.beginTransaction();

    Query query = session.createQuery("from Customer order by cid desc");

    List<Customer> allCustomer = query.list();

    for (Customer customer : allCustomer) {

    System.out.println(customer.getCid());

    }

    session.getTransaction().commit();

    session.close();

    }

    4.6 分页

    @Test

    public void demo05(){

    //分页

    Session session = factory.openSession();

    session.beginTransaction();

    Query query = session.createQuery("from Customer");

    // * 开始索引 , startIndex 算法: startIndex = (pageNum - 1) * pageSize;

    // *** pageNum 当前页(之前的 pageCode

    query.setFirstResult(0);

    // * 每页显示个数 , pageSize

    query.setMaxResults(2);

    List<Customer> allCustomer = query.list();

    for (Customer customer : allCustomer) {

    System.out.println(customer.getCid());

    }

    session.getTransaction().commit();

    session.close();

    }

    4.7 绑定参数

    @Test

    public void demo06(){

    /* 6 绑定参数

     * 方式1:占位符,使用hql语句替换具体参数

     * 设置参数 query.setXxx(int , object)

     * 参数1?位置,从0开始。

     * 参数2:实际参数

     * 例如:String --> query.setString(int,String)

     * 方式2:别名 格式 属性= :别名 

     * 设置参数 query.setXxx(String,object)

     * 参数1:别名

     * 参数2:实际参数

     * 例如:Integer --> query.setInteger(String,Integer)

     * 提供公共设置方法

     * setParameter(int|string , Object)

     */

    Session session = factory.openSession();

    session.beginTransaction();

    Integer cid = 1;

    //方式1

    // Query query = session.createQuery("from Customer where cid = ?");

    // query.setInteger(0, cid);

    //方式2

    Query query = session.createQuery("from Customer where cid = :xxx");

    // query.setInteger("xxx", cid);

    query.setParameter("xxx", cid);

    Customer customer = (Customer) query.uniqueResult();

    System.out.println(customer);

    session.getTransaction().commit();

    session.close();

    }

    4.8 聚合函数和分组

    @Test

    public void demo07(){

    /* 7  聚合函数

     */

    Session session = factory.openSession();

    session.beginTransaction();

    //1

    // Query query = session.createQuery("select count(*) from Customer");

    //2 别名

    // Query query = session.createQuery("select count(c) from Customer c");

    //3 oid

    Query query = session.createQuery("select count(cid) from Customer");

    Long numLong = (Long) query.uniqueResult();

    int num = numLong.intValue();

    System.out.println(num);

    session.getTransaction().commit();

    session.close();

    }

    4.9 连接查询

     

    1.交叉连接 ,等效 sql 笛卡尔积

    2.隐式内连接,等效 sql 隐式内连接

    3.内连接,等效sql内连接

    4.迫切内连接,hibernate底层使用 内连接。

    5.左外连接,等效sql左外连接

    6.迫切左外连接,hibernate底层使用 左外连接

    7.右外连接,等效sql右外连接

    内连接和迫切内连接?

    左外连接和迫切左外连接?

    @Test

    public void demo08(){

    /* 8 链接查询 : 左外连接和迫切左外连接?

     * * 左外连接 , left outer join

     * 底层使用sql的左外连接,hibernate进行数据自动封装,将一条记录,封装给两个对象(CustomerOrder

     * 将两个对象添加到一个对象数组中Object[Customer,Order]

     * * 迫切左外链接 left outer join fetch

     * 底层使用sql的左外连接,hibernate将一条记录封装给Customer,讲order数据封装Order,并将order关联到Customer

     * customer.getOrderSet().add(order)

     * 默认查询的数据重复

     */

    Session session = factory.openSession();

    session.beginTransaction();

    //左外连接

    // List list = session.createQuery("from Customer c left outer join c.orderSet ").list();

    //迫切左外链接 (默认数据重复)

    // List list = session.createQuery("from Customer c left outer join fetch c.orderSet ").list();

    //迫切左外链接 (去重复)

    List list = session.createQuery("select distinct c from Customer c left outer join fetch c.orderSet ").list();

    session.getTransaction().commit();

    session.close();

    }

    4.10 命名查询

    思想:将HQLjava源码中,提取到配置文件中。

    l 分类:全局、布局

    l 配置

    全局:*.hbm.xml   <class></class><query name="名称">HQL语句

     

    局部: <class name="" table=""><id><property> <query name="">HQL</class>

     

    l 获得

    全局:

    session.getNamedQuery("queryName")

    局部:

    session.getNamedQuery("className.queryName")  需要使用类的全限定名称

     

    @Test

    public void demo09(){

    /* 9 命名查询

     */

    Session session = factory.openSession();

    session.beginTransaction();

    //全局

    //List list = session.getNamedQuery("findAll").list();

    //局部

    List list = session.getNamedQuery("com.itheima.a_init.Customer.findAll").list();

    System.out.println(list.size());

    session.getTransaction().commit();

    session.close();

    }

    QBC【了解】

    5.0.0.1 QBC查询:

    QBC:Query By Criteria条件查询.面向对象的查询的方式.

    5.0.0.2 QBC简单的查询:

    // 简单查询:

    List<Customer> list = session.createCriteria(Customer.class).list();

    for (Customer customer : list) {

    System.out.println(customer);

    }

    5.0.0.3 QBC分页的查询:

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

    criteria.setFirstResult(10);

    criteria.setMaxResults(10);

    List<Order> list = criteria.list();

    5.0.0.4 QBC排序查询:

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

    // criteria.addOrder(org.hibernate.criterion.Order.asc("age"));

    criteria.addOrder(org.hibernate.criterion.Order.desc("age"));

    List<Customer> list = criteria.list();

    5.0.0.5 QBC条件查询:

    // 按名称查询:

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

    criteria.add(Restrictions.eq("cname", "tom"));

    List<Customer> list = criteria.list();*/

     

    // 模糊查询;

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

    criteria.add(Restrictions.like("cname", "t%"));

    List<Customer> list = criteria.list();*/

     

    // 条件并列查询

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

    criteria.add(Restrictions.like("cname", "t%"));

    criteria.add(Restrictions.ge("age", 35));

    List<Customer> list = criteria.list();

     

     

    5.0.0.6 离线查询(了解)

    l DetachedCriteria 离线查询对象,不需要使用Session就可以拼凑查询条件。一般使用在web层或service层拼凑。将此对象传递给dao层,此时将与session进行绑定执行查询。

    离线查询条件与QBC一样的。

     

    @Test

    public void demo10(){

    /* 10 离线查询

     */

    //web & service

    DetachedCriteria detachedCriteria = DetachedCriteria.forClass(Customer.class);

    detachedCriteria.add(Restrictions.eq("cid", 1));

    //---------------dao

    Session session = factory.openSession();

    session.beginTransaction();

    // 离线Criteria session绑定

    Criteria criteria = detachedCriteria.getExecutableCriteria(session);

    List<Customer> allCustomer = criteria.list();

    System.out.println(allCustomer.size());

    session.getTransaction().commit();

    session.close();

    }

    常见配置

    6.1 整合c3p0(连接池(了解)

    整合c3p0

     

    步骤一:导入c3p0 jar

     

    步骤二:hibernate.cfg.xml 配置

    hibernate.connection.provider_class org.hibernate.connection.C3P0ConnectionProvider

     

    l c3p0具体配置参数

    ###########################

    ### C3P0 Connection Pool###

    ###########################

    #hibernate.c3p0.max_size 2

    #hibernate.c3p0.min_size 2

    #hibernate.c3p0.timeout 5000

    #hibernate.c3p0.max_statements 100

    #hibernate.c3p0.idle_test_period 3000

    #hibernate.c3p0.acquire_increment 2

    #hibernate.c3p0.validate false

    6.2 事务

    6.2.1 回顾

    l 事务:一组业务操作,要么全部成功,要么全部不成功。

    特性:ACID

    原子性:整体

    一致性:数据

    隔离性:并发

    持久性:结果

    l 隔离问题:

    脏读:一个事务读到另一个事务未提交的内容

    不可重复读:一个事务读到另一个事务已提交的内容(insert

    虚读(幻读):一个事务读到另一个事务已提交的内容(update

    隔离级别--解决问题

    read uncommittd,读未提交。存在3个问题。

    read committed,读已提交。解决:脏读。存在2个问题。

    repeatable read ,可重复读。解决:脏读、不可重复读。存在1个问题。

    serializable,串行化。单事务。没有问题。

     

    6.2.2 hibernate设置隔离级别

    在hibernate.cfg.xml 配置

    hibernate.connection.isolation 4

     

    6.2.3 lost update 丢失更新

     

    l 悲观锁:丢失更新肯定会发生。

    采用数据库锁机制。

    读锁:共享锁。

    select .... from  ... lock in share mode;

    写锁:排他锁。(独占)

    select ... from  ....  for update

     

    l 乐观锁:丢失更新肯定不会发生

    在表中提供一个字段(版本字段),用于标识记录。如果版本不一致,不允许操作。

     

    6.2.4 hibernate处理丢失更新

    l 悲观锁:写锁

     

    @Test

    public void demo01(){

    //1 查询所有

    Session session = factory.openSession();

    session.beginTransaction();

    Customer customer = (Customer) session.get(Customer.class, 1 ,LockMode.UPGRADE);

    System.out.println(customer);

    session.getTransaction().commit();

    session.close();

    }

     

    l 乐观锁:

    在PO对象(javabean)提供字段,表示版本字段。一般Integer

    在*.hbm.xml 文件配置 <version name="...">

    步骤一:

     

    步骤二:

     

    步骤三:测试

    @Test

    public void demo02(){

    //1 查询所有

    Session session = factory.openSession();

    session.beginTransaction();

    // Order order = new Order();

    // order.setPrice(998d);

    // session.save(order);

    Order order = (Order) session.get(Order.class, 32);

    order.setPrice(889d);

    session.getTransaction().commit();

    session.close();

    }

    整合log4j(了解)

    l slf4j 核心jar  : slf4j-api-1.6.1.jar 。slf4j是日志框架,将其他优秀的日志第三方进行整合。

     

    整合导入jar

    log4j 核心包:log4j-1.2.17.jar

    过渡jar(整合jar):slf4j-log4j12-1.7.5.jar

    l 导入配置文件

    log4j.properties  ,此配置文件通知log4j 如何输出日志

    l 配置文件内容:

    1.记录器

    2.输出源

    3.布局

    l 记录器

    例如: log4j.rootLogger=info, stdout,file

    格式:log4j.rootLogger=日志级别, 输出源1,输出源2,。。。。

    log4j 日志级别 : fatal 致命错误 error 错误 warn 警告 info 信息 debug 调试信息 trace 堆栈信息 (由高到底顺序)

    l 输出源:

    例如:log4j.appender.file=org.apache.log4j.FileAppender

    格式:log4j.appender.输出源的名称=输出源的实现类

    名称:自定义

    实现类:log4j提供

    输出源属性例如:log4j.appender.file.File=d:mylog.log

    输出源属性格式:log4j.appender.名称.属性=值

    每一个输出源对应一个实现类,实现类都属性(setter),底层执行setter方法进行赋值

    l 常见的输出源实现类

    org.apache.log4j.FileAppender  输出文件中

    file ,表示文件输出位置

    org.apache.log4j.ConsoleAppender 输出到控制台

    Target ,表示使用哪种输出方式,在控制台打印内容,取值:System.out / System.err

    布局  -- 确定输出格式

    例如:log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

    格式:log4j.appender.数据源.layout=org.apache.log4j.PatternLayout

    布局属性:log4j.appender. 数据源.layout.ConversionPattern=值

    12:56:30,123  info

    l 扩展:对指定的目录设置日志级别

    例如:log4j.logger.org.hibernate.transaction=debug

    格式:log4j.logger.包结构=级别

    一对一(了解)

    情况1:主表的主键,与从表的外键(唯一),形成主外键关系

    情况2:主表的主键,与从表的主键,形成主外键关系 (从表的主键又是外键)

    2.1.1 情况1

     

     

    2.1.2 情况2

     

     

    二级缓存【掌握】

    3.1 介绍

    3.1.1 缓存

    缓存(Cache): 计算机领域非常通用的概念。它介于应用程序永久性数据存储源(如硬盘上的文件或者数据库)之间,其作用是降低应用程序直接读写硬盘(永久性数据存储源)的频率,从而提高应用的运行性能。缓存中的数据是数据存储源中数据的拷贝。缓存的物理介质通常是内存

    缓存:程序<--(内存)-->硬盘

    3.1.2 什么是二级缓存

    l hibernate 提供缓存机制:一级缓存、二级缓存

    一级缓存:session级别缓存,在一次请求中共享数据。

    二级缓存:sessionFactory级别缓存,整个应用程序共享一个会话工厂,共享一个二级缓存。

    l SessionFactory的缓存两部分: 内置缓存:使用一个Map,用于存放配置信息,预定义HQL语句等,提供给Hibernate框架自己使用,对外只读的。不能操作。

    外置缓存:使用另一个Map,用于存放用户自定义数据。默认不开启。外置缓存hibernate只提供规范(接口),需要第三方实现类。外置缓存有成为二级缓存。

    3.1.3 二级缓存内部结构

     

    二级就是由4部分构成

    n 类级别缓存

    集合级别缓存

    n 时间戳缓存

    查询缓存(二级缓存的第2大部分,三级缓存)

    3.1.4 并发访问策略

     

    访问策略:读写型(read-write)、只读型(read-only

    3.1.5 应用场景

    适合放入二级缓存中的数据:

    很少被修改

    不是很重要的数据, 允许出现偶尔的并发问题

    不适合放入二级缓存中的数据:

    经常被修改

    财务数据, 绝对不允许出现并发问题

    与其他应用数据共享的数据

    3.1.6 二级缓存提供商

    EHCache可作为进程(单机)范围内的缓存存放数据的物理介质可以是内存或硬盘对 Hibernate 的查询缓存提供了支持。--支持集群。

    l OpenSymphony `:可作为进程范围内的缓存存放数据的物理介质可以是内存或硬盘提供了丰富的缓存数据过期策略对 Hibernate 的查询缓存提供了支持

    l SwarmCache: 可作为集群范围内的缓存但不支持 Hibernate 的查询缓存

    l JBossCache:可作为集群范围内的缓存支持 Hibernate 的查询缓存

     

    X表示支持

    3.2 配置(操作)

    1.导入jar包:ehcache-1.5.0.jar/ commons-logging.jar/ backport-util-concurrent.jar

    2.开启二级缓存(我要使用二级缓存)

    3.确定二级缓存提供商(我要使用哪个二级缓存)

    4.确定需要缓存内容

    1>配置需要缓存的类

    2>配置需要缓存的集合

    5.配置ehcache自定义配置文件

    3.2.1 导入jar

     

    3.2.2 开启二级缓存

     

    在hibernate.cfg.xml 配置二级缓存

    <!-- 9.1 开启二级缓存 -->

    <property name="hibernate.cache.use_second_level_cache">true</property>

    3.2.3 确定提供商

     

    l hibernate.cfg.xml 配置

    <!-- 9.2 提供商 -->

    <property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>

    3.2.4 确定缓存内容

    在hibernate.cfg.xml 确定 类缓存 和集合缓存配置项

     

    l 配置

    <!-- 9.3 确定缓存内容 -->

    <!-- 类缓存 -->

    <class-cache usage="read-write" class="com.itheima.a_init.Customer"/>

    <class-cache usage="read-write" class="com.itheima.a_init.Order"/>

    <!-- 集合缓存 -->

    <collection-cache usage="read-write" collection="com.itheima.a_init.Customer.orderSet"/>

    3.2.5 ehcache配置文件

    步骤1:从jar包复制xml文件

     

    步骤2:将xml重命名“ehcache.xml”

     

    步骤3:将修改后的xml,拷贝到src

     

    3.3 演示

    3.3.1 证明

    @Test

    public void demo01(){

    //1 证明二级缓存存在

    // * 修改toString()

    // * 如果二级缓存开启,查询没有select语句,表示从二级缓存获得的。

    // * 将二级缓存关闭,查询3将触发select语句。

    Session s1 = factory.openSession();

    s1.beginTransaction();

    //1 查询id=1 -- 执行select (查询后,将数据存放在一级缓存,之后由一级缓存同步到二级缓存)

    Customer c1 = (Customer) s1.get(Customer.class, 1);

    System.out.println(c1);

    //2 查询id=1 --从一级缓存获取

    Customer c2 = (Customer) s1.get(Customer.class, 1);

    System.out.println(c2);

    s1.getTransaction().commit();

    s1.close();

    System.out.println("----------");

    Session s2 = factory.openSession();

    s2.beginTransaction();

    //3 查询id=1 -- 从二级缓存获取

    Customer c3 = (Customer) s2.get(Customer.class, 1);

    System.out.println(c3);

    s2.getTransaction().commit();

    s2.close();

    }

    3.3.2 类缓存

    l 类缓存:只存放数据

    l 一级缓存:存放对象本身

     

    @Test

    public void demo02(){

    //2 类缓存:只存放数据,散装数据。

    // * 使用默认的toString();

    Session s1 = factory.openSession();

    s1.beginTransaction();

    //1 查询id=1 -- 执行select

    Customer c1 = (Customer) s1.get(Customer.class, 1);

    System.out.println(c1);

    //2 查询id=1 -- 从一级缓存获取,一级缓存存放对象本身

    Customer c2 = (Customer) s1.get(Customer.class, 1);

    System.out.println(c2);

    s1.getTransaction().commit();

    s1.close();

    System.out.println("----------");

    Session s2 = factory.openSession();

    s2.beginTransaction();

    //3 查询id=1 -- 对象不一样,数据一样

    Customer c3 = (Customer) s2.get(Customer.class, 1);

    System.out.println(c3);

    s2.getTransaction().commit();

    s2.close();

    }

    3.3.3 集合缓存

     

    @Test

    public void demo03(){

    //3 集合缓存:只存放关联对象OID的值,如果需要数据,从类缓存中获取。

    // * 3.1 默认:第一条select 查询客户,第二天 select 查询客户所有订单

    // * 3.2 操作:在hibernate.cfg.xml 将 Order 类缓存删除

    // *** <!--  <class-cache usage="read-write" class="com.itheima.a_init.Order"/>-->

    // *** 多了10select,通过订单的id查询订单

    Session s1 = factory.openSession();

    s1.beginTransaction();

    //1 查询id=1

    Customer c1 = (Customer) s1.get(Customer.class, 1);

    System.out.println(c1);

    //2 获得订单

    for (Order o1 : c1.getOrderSet()) {

    System.out.println(o1);

    }

    s1.getTransaction().commit();

    s1.close();

    System.out.println("----------");

    Session s2 = factory.openSession();

    s2.beginTransaction();

    //3 查询id=1

    Customer c3 = (Customer) s2.get(Customer.class, 1);

    System.out.println(c3);

    //4 获得订单

    for (Order o2 : c3.getOrderSet()) {

    System.out.println(o2);

    }

    s2.getTransaction().commit();

    s2.close();

    }

    3.3.4 时间戳

    l 时间戳:任何操作都在时间戳中记录操作时间。

     

    @Test

    public void demo04(){

    //4 时间戳所有的操作都会在时间戳中进行记录,如果数据不一致,将触发select语句进行查询

    // * 修改toString()

    Session s1 = factory.openSession();

    s1.beginTransaction();

    //1 查询id=1

    Integer cid = 1;

    Customer c1 = (Customer) s1.get(Customer.class, cid);

    System.out.println(c1);

    //2 绕过一级和二级缓存,修改数据库,修改客户cname=大东哥

    s1.createQuery("update Customer set cname = ? where cid = ?")

    .setString(0, "大东哥")

    .setInteger(1, cid)

    .executeUpdate();

    //3打印

    System.out.println(c1);

    s1.getTransaction().commit();

    s1.close();

    System.out.println("----------");

    Session s2 = factory.openSession();

    s2.beginTransaction();

    //4 查询id=1  -- ?

    Customer c3 = (Customer) s2.get(Customer.class, 1);

    System.out.println(c3);

    s2.getTransaction().commit();

    s2.close();

    }

    3.3.5 查询缓存

    l 查询缓存又称为三级缓存(民间)

    l 查询缓存默认不使用。需要手动开启

    查询缓存:将HQL语句与 查询结果进行绑定。通过HQL相同语句可以缓存内容。

    默认情况Query对象只将查询结果存放在一级和二级缓存,不从一级或二级缓存获取。

    查询缓存就是让Query可以从二级缓存获得内容。

    步骤一:开启查询缓存

     

    <!-- 9.4 开启查询缓存 -->

    <property name="hibernate.cache.use_query_cache">true</property>

    步骤二:在查询query对象,设置缓存内容(注意:存放和查询 都需要设置)

     

     

    @Test

    public void demo05(){

    //5 查询缓存

    Session s1 = factory.openSession();

    s1.beginTransaction();

    //1 query查询

    Query q1 = s1.createQuery("from Customer");

    q1.setCacheable(true);

    List<Customer> a1 = q1.list();

    for (Customer c1 : a1) {

    System.out.println(c1);

    }

    //2 cid =1  -- 一级缓存获得

    Customer customer = (Customer) s1.get(Customer.class, 1);

    System.out.println(customer);

    s1.getTransaction().commit();

    s1.close();

    System.out.println("----------");

    Session s2 = factory.openSession();

    s2.beginTransaction();

    //2 cid =1  -- 二级缓存获得

    Customer customer2 = (Customer) s2.get(Customer.class, 1);

    System.out.println(customer2);

    //3 query查询

    Query q2 = s2.createQuery("from Customer");

    q2.setCacheable(true);

    List<Customer> a2 = q2.list();

    for (Customer c2 : a2) {

    System.out.println(c2);

    }

    s2.getTransaction().commit();

    s2.close();

    }

    3.4 ehcache配置文件

    l <diskStore path="java.io.tmpdir"/>  设置临时文件存放位置。(缓存一般内存,一定程度时,写入硬盘。)

    l 缓存详细设置

    <defaultCache> 所有的缓存对象默认的配置

    <cache name=""> 指定对象单独配置

    l 参数设置

    maxElementsInMemory="10000"  内存最大数

    eternal="false"  是否永久(内存常驻留)

    timeToIdleSeconds="120"

    timeToLiveSeconds="120"

    overflowToDisk="true"  内存满了,是否写入到硬盘

    maxElementsOnDisk="10000000"  硬盘最大数

    diskPersistent="false"  关闭JVM,是否将内存保存硬盘中

    diskExpiryThreadIntervalSeconds="120"  轮询

    memoryStoreEvictionPolicy="LRU"

    Least Recently Used (specified as LRU).

    First In First Out (specified as FIFO)

    Less Frequently Used (specified as LFU)

    • maxElementsInMemory :设置基于内存的缓存中可存放的对象最大数目
    • eternal:设置对象是否为永久的,true表示永不过期,此时将忽略timeToIdleSeconds 和 timeToLiveSeconds属性默认值是false
    • timeToIdleSeconds:设置对象空闲最长时间,以秒为单位超过这个时间,对象过期。当对象过期时,EHCache会把它从缓存中清除。如果此值为0,表示对象可以无限期地处于空闲状态。
    • timeToLiveSeconds:设置对象生存最长时间,超过这个时间,对象过期。
      如果此值为0,表示对象可以无限期地存在于缓存中该属性值必须大于或等于 timeToIdleSeconds 属性值
    • overflowToDisk:设置基于内在的缓存中的对象数目达到上限后,是否把溢出的对象写到基于硬盘的缓存中
    • diskPersistent jvm结束时是否持久化对象 true false 默认是false
    • diskExpiryThreadIntervalSeconds 指定专门用于清除过期对象的监听线程的轮询时间
    •  memoryStoreEvictionPolicy - 当内存缓存达到最大,有新的element加入的时候, 移除缓存中element的策略。默认是LRU(最近最少使用),可选的有LFU(最不常使用)和FIFO(先进先出) 
  • 相关阅读:
    Shiro学习(19)动态URL权限限制
    Shiro学习(18)并发人数限制
    Shiro学习(17)OAuth2集成
    Shiro学习(16)综合实例
    Shiro学习(15)单点登录
    Shiro学习(14)SSL
    项目三:ssm仓库管理系统
    项目二:企业级java电商网站开发(服务端)
    项目一:ssm超市订单管理系统
    @ResponseBody注解
  • 原文地址:https://www.cnblogs.com/burningmyself/p/7451410.html
Copyright © 2011-2022 走看看