Hibernate复习(二)

一、多表设计
①一对多

public class Customer {

    private Integer cid;
    private String cname;

    //一对多:一个客户(当前客户) 拥有 【多个订单】
    // * 需要容器存放多个值,一般建议Set (不重复、无序)
    // * 参考集合:List、Map、Array等 
    // ** 建议实例化--使用方便
    private Set<Order> orderSet = new HashSet<Order>();

配置文件

<class name="com.itheima.b_onetomany.Customer" table="t_customer">
        <id name="cid">
            <generator class="native"></generator>
        </id>
        <property name="cname"></property>

        <!-- 一对多:一个客户(当前客户) 拥有 【多个订单】
            1 确定容器  set <set>
            2 name确定对象属性名
            3 确定从表外键的名称
            4 确定关系,及另一个对象的类型
            注意:
                在hibernate中可以只进行单向配置
                每一个配置项都可以完整的描述彼此关系。
                一般情况采用双向配置,双方都可以完成描述表与表之间关系。
         -->
        <!-- 一对多:一个客户(当前客户) 拥有 【多个订单】 -->
        <set name="orderSet" cascade="delete-orphan">
            <key column="customer_id"></key>
            <one-to-many class="com.itheima.b_onetomany.Order"/>
        </set>
    </class>

②多对一

public class Order {
    private Integer xid;
    private String price;

    //多对一:多个订单属于【一个客户】
    private Customer customer;

配置文件

<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>

③多对多

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>

二、级联操作
save-update:A保存,同时保存B
delete:删除A,同时删除B,AB都不存在
delete-orphan:孤儿删除,解除关系,同时将B删除,A存在的。
如果需要配置多项,使用逗号分隔。

all : save-update 和 delete 整合
all-delete-orphan : 三个整合

三、抓取策略(优化)

1.检索方式:
1.1 立即检索(get):立即查询,在执行查询语句时,立即查询所有的数据
1.2 延迟查询(load):延迟查询,在执行查询语句之后,在需要时在查询(懒加载)

 <class  lazy="true | false">

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

2.检索策略
2.1 类级别检索:当前的类的属性获取是否需要延迟。
2.2 关联级别的检索:当前类 关联 另一个类是否需要延迟。

3.关联级别检索
①一对多或多对多:

3.1 容器 提供两个属性:fetch、lazy
fetch:确定使用sql格式
lazy:关联对象是否延迟。

3.2 fetch:join、select、subselect
join:底层使用迫切左外连接

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

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

   当前对象 和 关联对象 使用多条select语句查询。
   lazy="false" , 立即,先查询客户select,立即查询订单select
   lazy="true",延迟,先查询客户select,需要订单时,再查询订单select
   lazy="extra",极其懒惰(延迟),先查询客户select, 如果只需要订单数,使用聚合函数(不查询详情)

subselect:使用子查询

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

3.3 lazy:false、true、extra
false:立即
true:延迟(默认值)
extra:极其懒惰

②多对一:
3.5介绍:
<many-to-one fetch="" lazy=""> (<one-to-one>)
 fetch取值:join、select
join:底层使用迫切左外连接
select:多条select语句
 lazy取值:false、proxy、no-proxy
false:立即
proxy:采用关联对象 类级别检索的策略。
订单 关联 客户 (多对一)
订单 立即获得 客户,需要在客户Customer.hbm.xml
订单 延迟获得 客户,需要在客户Customer.hbm.xml

四、HQL语句
HQL语句:描述对象操作的一种查询语句,Hibernate特有。(关键字不区分大小写,但类名和属性名区分大小写)

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");

/*选择查询*/
        //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");

/*投影查询*/
//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");

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

/*分页*/
Query query = session.createQuery("from Customer");
        // * 开始索引 , startIndex 算法: startIndex = (pageNum - 1) * pageSize;
        // *** pageNum 当前页(之前的 pageCode)
        query.setFirstResult(0);
        // * 每页显示个数 , pageSize
        query.setMaxResults(2);

/*绑定参数*/
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);

/*聚合函数和分组*/
//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();

/*连接查询*/
/*
1.交叉连接 ,等效 sql 笛卡尔积
2.隐式内连接,等效 sql 隐式内连接
3.内连接,等效sql内连接
4.迫切内连接,hibernate底层使用 内连接。
5.左外连接,等效sql左外连接
6.迫切左外连接,hibernate底层使用 左外连接
7.右外连接,等效sql右外连接
*/
//左外连接
//      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();

/*命名查询*/
//全局
        //List list = session.getNamedQuery("findAll").list();
        //局部
        List list = session.getNamedQuery("com.itheima.a_init.Customer.findAll").list();

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

五、常见配置
5.1事务

   事务:一组业务操作,要么全部成功,要么全部不成功。
   特性:ACID
原子性:整体
一致性:数据
隔离性:并发
持久性:结果
   隔离问题:
脏读:一个事务读到另一个事务未提交的内容
不可重复读:一个事务读到另一个事务已提交的内容(insert)
虚读(幻读):一个事务读到另一个事务已提交的内容(update)
   隔离级别--解决问题
read uncommittd,读未提交。存在3个问题。
read committed,读已提交。解决:脏读。存在2个问题。
repeatable read ,可重复读。解决:脏读、不可重复读。存在1个问题。
serializable,串行化。单事务。没有问题

5.2Hibernate设置隔离区级别

<property name="hibernate.connection.isolation">4</property>

5.3丢失更新

   悲观锁:丢失更新肯定会发生。
采用数据库锁机制。
读锁:共享锁。
    select .... from  ... lock in share mode;
写锁:排他锁。(独占)
    select ... from  ....  for update

   乐观锁:丢失更新肯定不会发生
在表中提供一个字段(版本字段),用于标识记录。如果版本不一致,不允许操作。

六、二级缓存
①定义:

   hibernate 提供缓存机制:一级缓存、二级缓存
一级缓存:session级别缓存,在一次请求中共享数据。
二级缓存:sessionFactory级别缓存,整个应用程序共享一个会话工厂,共享一个二级缓存。
   SessionFactory的缓存两部分:   
    1.内置缓存:使用一个Map,用于存放配置信息,预定义HQL语句等,提供给Hibernate框架自己使用,对外只读的。不能操作。
    2.外置缓存:使用另一个Map,用于存放用户自定义数据。默认不开启。外置缓存hibernate只提供规范(接口),需要第三方实现类。外置缓存有成为二级缓存。

②构成:

   类级别缓存
   集合级别缓存
   时间戳缓存
   查询缓存(二级缓存的第2大部分,三级缓存)

③应用场景:

   适合放入二级缓存中的数据:
很少被修改
不是很重要的数据, 允许出现偶尔的并发问题
   不适合放入二级缓存中的数据:
经常被修改
财务数据, 绝对不允许出现并发问题
与其他应用数据共享的数据

④配置操作

1.导入jar包:ehcache-1.5.0.jar/ commons-logging.jar/ backport-util-concurrent.jar
2.开启二级缓存(我要使用二级缓存)
<!-- 9.1 开启二级缓存 -->
<property name="hibernate.cache.use_second_level_cache">true</property>
3.确定二级缓存提供商(我要使用哪个二级缓存)
<!-- 9.2 提供商 -->
<property name="hibernate.cache.provider_class">org.hibernate.cache.EhCacheProvider</property>
4.确定需要缓存内容
1>配置需要缓存的类
2>配置需要缓存的集合
<!-- 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"/>
5.配置ehcache自定义配置文件

这里写图片描述

类缓存:
这里写图片描述

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();
    }

集合缓存:
这里写图片描述

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"/>-->
        // *** 多了10条select,通过订单的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();
    }

时间戳:任何操作都在时间戳中记录操作时间。
这里写图片描述

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();
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值