A股上市公司传智教育(股票代码 003032)旗下技术交流社区北京昌平校区

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

本帖最后由 小石姐姐 于 2018-6-29 11:09 编辑

hibernate知识库
环境搭建
1.导入jar包
    1.导入相关jar包
        拷贝hibernate-release-5.0.7.Final目录下lib/required目录下所有jar包
    2.导入数据库驱动jar包
    3.导入日志相关jar包
        log4j-1.2.16.jar
        slf4j-api-1.6.1.jar
        slf4j-log4j12-1.7.2.jar
    4.拷贝hibernate-release-5.0.7.Final\lib\optional\c3p0下所有jar包  
        c3p0-0.9.2.1.jar
        hibernate-c3p0-5.0.7.Final.jar
        mchange-commons-java-0.2.3.4.jar   
    5.将hibernate-release-5.0.7.Final目录下project/etc/log4j.properties文件拷贝到工程src目录下
2.提供核心配置文件 hibernate.cfg.xml  (src目录下)
3.映射配置文件  类名.hbm.xml   (与实体类在同一包下)

核心配置文件
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
    "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
//上边的需要复制粘贴
<hibernate-configuration>
    <session-factory>
        <!-- 配置关于数据库连接的四个项 driverClass url username password -->
        <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">abc</property>
        <!-- 设置连接提供者 -->
        <property name="hibernate.connection.provider_class">org.hibernate.connection.C3P0ConnectionProvider</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> <!-- 空闲连接 -->
        <!-- 可以将向数据库发送的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>
        <!-- 自动创建表 -->
        <property name="hibernate.hbm2ddl.auto">update</property>
        <!-- 用于设置事务提交方式 -->
        <property name="hibernate.connection.autocommit">false</property>
        <!-- 配置hibernate的映射文件所在位置 -->
        <mapping resource="cn/itheima/domain/Customer.hbm.xml" />
    </session-factory>
</hibernate-configuration>

配置映射文件
<?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">
<!-- package属性代表javabean所在的包 -->
<hibernate-mapping>
            <!--
                name    实体类的全名
                table   表的名称
                catalog 数据库名称
            -->
        <class name="com.b3a4a.domain.Customer" table="t_customer" catalog="db_hibernate_study">                 
                <!--id它是用于描述主键
                    name      实体类中那个属性对应的是主键
                    column    数据库表中哪一列是主键-->               
                <id name="id" column="id">
                    <!-- 主键生成策略 -->
                    <generator class="native"></generator>
                </id>   
                <!--使用property来描述属性与字段的对应关系
                    name   实体类中的属性
                    column 实体类中属性对应数据库表中的列
                    length 该列的长度限制-->      
                <property name="name" column="name" length="20" type="string"></property>
                <property name="address" column="address" length="50"></property>
                <property name="sex" column="sex" length="20"></property>
                字符串一定给length    type可以不写
        </class>
</hibernate-mapping>
执行原理
1.通过Configuration().configure();读取并解析hibernate.cfg.xml配置文件
2.由bibernaate.cfg.xml中的
<mapping resource="cn/itheima/domain/Customer.hbm.xml"/>读取解析映射文件
3.通过config.buildSessionFactory();得到sessionFactory
4.sessionFactory.openSession();得到session
5.session.beginTransaction();开启事务
6.具体业务操作
7.session.getTransaction().commit;提交事务(update insert delete)
8.关闭session  session.close();
9.关闭session  sessionFactory.close();一般不关闭
hibrenate api
1.Configuration         加载hibernate.cfg.xml核心配置
2.SessionFactory        接口, 创建Session对象, 不是轻量级的不关闭
3.Session               接口, 非线程安全,与数据库CRUD,save/update/delete
4.Transaction           事务接口, 管理事务
5.Query                 接口, 查询操作 HQL/SQL
6.Criteria              接口, 面向对象标准化查询, 轻量级
详解
获得session:
    1.sessionFactory.openSession();
    这种方式获取的session不能被手动关闭, 不必再写session.close()
    2.sessionFactory.getCurrentSession();
    这种方式获得session每次都是新的,前提是本地线程绑定session
    即在cfg.xml配置了<property name="hibernate.current_session_context_class">thread</property>
   
Session常用方法:
    save/update/delete
    get/load  根据id进行查询
    saveOrUpdate   执行save或update操作
    createQuery()  获取Query对象
    createSQLQuery()  获取一个可以操作sql的SQLQuery对象
    createCriteria()  获取一个Criteria 查询操作
   
Transaction:
    commit  事务提交
    rollback 回滚
    session.beginTransaction();  开启事务, 获得Transaction对象
    如果程序没有开启事务, 是否存在事务?
        有事务, session的每一个操作就会开启一个事务.
        
Query:
    session.createQuery(hql);
    session.createSQLQuery(sql);

Query查询操作Hibernate注解开发
在hibernate中我们使用注解,可以帮助我们简化伙伴们文件配置
PO类注解配置
@Entity 声明一个实体
@Table来米哦啊书类与表对应
@Table(name="t_book",catalog="hibernateTest")
public calss Book{
@Id 来声明一个主键
@GenerateValue 用它来声明一个主键生成策略

//@GeneratedValue //native
@GeneratedValue(strategy = GenerationType.IDENTITY) //identiy
默认情况下相当于native
可以选择的主键生成策略 AUTO IDENTITY SEQUENCE@Column 来定义列

@Column(name="c_name",length=30,nullable=true)
注意:对于PO类中所有属性,如果你不写注释,默认情况下也会在表中生成对应的列,列的名称就是属性的名称。
@Temporal来声明日期类型

@Etmporal(TemporalType.TIMESTAMP) //是用来定义日期类型
private Date publicationDate;  //出版日期
可以选择
TemporalType.DATAY 只有年月日
TemporalType.TIME 只有小时分钟秒
TemporalType.TIMESTAMP  有年月日小时分钟秒
@Type:可允许你去指定Hibernate里面的一些类型

@Type(type="double") //允许你去指定Hibernate里面的一些类型
private Double price;//价格,如果没有添加注解,也会自动的生成在表中
编写完上述Book实体类之后,千万不要忘记下面这一步,我们最终需要在hibernate.cfg.xml 文件中将我们类中的注解配置引用生效
<mapping class="cn.itheima.domain.Book"/>
问题:1.如果我们主键生成策略想使用UUID类型?
@Id

@GenericGenerator(name=“myuuid”,strategy="uuid")

@GeneratedVAlue(generator="myuuid")

private String id;
问题2:如果设定类的属性不在表中映射?

@Transient

private String msg; // 现在这个属性不想生成在表中
对于我们以上讲解的关于属性配置id注解,我们也可以在其对一个的getxxx方法去使用

一对多(多对一)
@OneToMany
​        一对多:
​        rargetEntity: 多的一方类型
​        mappedBy:放弃外键维护
​        orphanRemoval:删除孤儿
@ManyToOne
​                多对一:
​                rargetEntity:一的一方的类型
以Customer与Order为例
客户(Customer) 类

@OneToMany(targetEntity=Order.class,mappedBy="c")

private Set<Order> orders = new HashSet<Order>();
订单(Order) 类

@ManyToOne(targetEntity=Customer.class)

@JoinColumn(name="c_customer_id")//指定外键列

private Customer c; //描述订单属于一个客户
示例:保存客户时,保存订单
对于这个示例我们需要在Customer中配置cascade操作,save-update
第一种方式,可以使用 JPA 提供的注解

@OneToMany(targetEntity=Order.class,mappedBy="c",cascade=CascadeType.ALL)
private Set<Order> orders = new HashSet<Order>();
第二种方式:可以使用hibernate提供的注解
@OneMany(targetEntity=Order.class,mappedBy="c")
@Cascade(CascadeType.SAVE_UPDATE)
private Set<Order> orders=new HashSet<Order>();
下面是示例代码

//3.建立关系
c.getOrders().add(o1);
c.getOrders().add(02);
//4.保存客户,并级联保存订单
session.save(c);
订单中没有关联客户的id,为什么?
原因:我们在Customer中配置了mappedBy=“c”它代表的是外键的维护由Order方来维护,而Customer不维护,这时你在保存客户时,级联保存订单,是可以的,但是不能维护外键,所以,我们必须在代码中添加订单与客户关系。
多对多
@Cascade配置级联:
​                CascadeType.xxx

一对一
@OneToOne 声明一对一外键映射(主键映射了解)

检索方式
导航对象图检索方式,根据已加载的对象导航到其他对象
OID检索方式,按照对象的OID来检索对象(get/load)
HQL检索方式,使用面向对象的HQL插叙语句
QBC检索方式,使用QBC(Query  by Criteria)API来检索对象,这种API封装了基于字符串形式的查询语句,提供了更加米面向对象的查询接口:

基本检索:
Criteria criteria=session.createCriteria(Customer.class);
List<Customer>list=criteria.list();

排序检索:
cruteria.addOrder(org.hibernate.criteriion.Order.asc("money"));//升序

条件检索:
Restrictions.xxx()eq and or like get it....

分页检索:
criteria.setFirstResult(2-1)*6);
criteria.setMaxResults(6);

统计检索:
criteria.setProjection( projections.projectionList().add(Projections.sum("money")).add(Projections.groupProperty("c")));

离线条件检索:
​        DetachedCriteria dc=DetachedCriteria.forClass(Customer.class);不借助于session web
​        Criteria criteria=dc.getExecutableCriteria(session);dao
本地SQL检索方式,使用本地数据库的SQL查询语句

Hibernate-day04先来说明一件问题
  • 通过load形式加载的延迟加载的对象如何进行初始化


    • 对象.get方法()
    • hibernate.initilize(对象)
    • 这个很重要openSessionInView filter (Spring框架)



演示sql多表操作
  • 交叉连接  CROSS JOIN  会产生迪卡尔积,一般不用
    select * from t_customer cross join t_order;
  • 内链接 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;
  • 外连接,分为左外连接 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的迫切连接很重要,但是在sql中是没有的
  • 注意说明


    • fetch不可以与单独条件的with一起使用
    • fetch这个关键字,可以说是把结果封装到对象中去,底层也是执行inner join/left outer join
    • distinct这个关键字,只要是用来去重的,sql中也有,我们使用迫切连接结果可能会出现重复,所以要用他
    • 注意测试时候,要在类中重写toString方法哦

  • 也有交叉连接.这个就不多说了
  • 内连接

    • 显示内连接,使用的是inner join with
      from Customer c inner join c.orders with c.id=1
      返回结果是List<Object[]> 需要两层遍历
    • 隐式内连接,与sql中操作不一样,它是通过”.”运算符来关联
      from Order o where o.c.id=1
    • 迫切内连接,它得到的结果是直接封装到PO类中,而内连接得到的是Object[]数组,数组中封装的是PO类对象.
      select distinct c from Customer c inner join fetch c.orders
  • 外连接

    • 左外连接
      select * from 表1 a left join 表2 b on 表的关联条件 where 条件
    • 右外连接
      select * from 表1 a right join 表2 b on 表的关联条件 where 条件
    • 迫切左外连接
      select distinct c from Customer c left outer join fetch c.orders where c.id=1
Hibernate事务管理(非常重要,面试必问)事务介绍
  • 问题:什么是事务?

    • 事务就是逻辑上的一组操作,组成这组操作的各个单元要么全部成功,要么全都失败。

  • 问题:事务四个特性?

    • 原子性:不可分割
    • 一致性:事务在执行前后,要保证数据的一致。
    • 隔离性:一个事务在执行的过程中,不应该受到其它事务的干扰。
    • 持久性:事务一旦结束,数据持久化到数据库。


  • 问题:不考虑事务的隔离性,会产生什么问题?

    • 脏读:一个事务读取到另一个事务的未提交数据
    • 不可重复读:一个事务读取到另一个事务提交的数据(主要是指update),会导致两次读取的结果不一致。
    • 虚读(幻读): 一个事务读取到另一个事务提交的数据(主要是指insert),会导致两次读取结果不一致.

  • 问题:对于上述问题如何解决?

    • 我们可以通过设置隔离级别来解决.
    • READ_UNCOMMITED 读取未提交,它引发所有的隔离问题
    • READ_COMMITTED  读已提交,阻止脏读,可能发生不可重复读与虚读.
    • REPEATABLE_READ 重复读  阻止脏读,不可重复读 可能发生虚读
    • SERIALIZABLE 串行化 解决所有问题 不允许两个事务,同时操作一个目标数据。(效率低下)

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

事务提交方式

<!-- 用于设置事务提交方式 -->
<property name="hibernate.connection.autocommit">false</property>在Hibernate中设置事务管理级别
  • 它可取的值有 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的方式:
  • Session对象的生命周期与本地线程绑定(ThreadLocal)

    • 他是自动关闭的,没有s.close()方法

    sessionFactory.getCurrentSession();
    • 需要在hibernate.cfg.xml文件配置

    <!-- 绑使用当前线程 -->
    <property name="current_session_context_class">thread</property>
  • Session对象的生命周期与JTA事务绑定(分布式事务管理)

    • 这里只稍微概述下,后期会学到哦,有个小概念
    • JTA提供了跨Session的事务管理能力,这是与JDBCTransaction最大的差异。
      JDBC 事务由Connection管理,也就是说,事务管理实际上是在JDBC Connection中实现,事务周期限于
      Connection的生命周期之内,对于基于JDBC Transaction的Hibernate事务管理机制,事务管理在
      Session所依托的JDBC Connection中实现,事务周期限于Session的生命周期。
    • JTA事务管理由JTA容器实现,JTA容器对当前加入事务的众多Connection进行调度,实现其事务性要求,
      JTA的事务周期可横跨多个JDBC Connection生命周期,同样,对基于JTA事务的Hibernate,JTA事务横跨
      多个Session。需要注意的是,参与JTA事务的 Connection需避免对事务管理进行干涉,如果采用JTA
      Transaction,就不应该再调用Hibernate的Transaction功能。

  • Hibernate委托程序来管理Session的生命周期
    sessionFactory.getOpenSession();
  • 我们之前测试所使用的是第三种 ,通过程序获取一个Session对象,使用它,最后session.close();
  • 在实际开发中我们一般使用的是前两种;


Hibernate优化方案HQL优化1.使用参数绑定
  • 使用绑定参数的原因是让数据库一次解析SQL,对后续的重复请求可以使用用生成好的执行计划,这样做节省CPU时间和内存。
  • 避免SQL注入

2.尽量少使用NOT
  • 如果where子句中包含not关键字,那么执行时该字段的索引失效。

3.尽量使用where来替换having
  • Having在检索出所有记录后才对结果集进行过滤,这个处理需要一定的开销,而where子句限制记录的数目,能减少这方面的开销


4.减少对表的查询
  • 在含有子查询的HQL中,尽量减少对表的查询,降低开销

5.使用表的别名
  • 当在HQL语句中连接多个表时,使用别名,提高程序阅读性,并把别名前缀与每个列上,这样一来,可以减少解析时间并减少列歧义引起的语法错误。

6.实体的更新与删除一级缓存优化
  • 一级缓存也叫做session缓存,在一个hibernate session有效,这级缓存的可干预性不强,大多于hibernate自动管理,但它提供清除缓存的方法,这在大批量增加(更新)操作是有效果的,例如,同时增加十万条记录,按常规进行,很可能会出现异常,这时可能需要手动清除一级缓存-

    • session.evict(),清除某个session
    • session.clear(),清清除session
    • session.refresh(),同步数据


注解配置抓取策略set上的fetch与lazy
  • set上的fetch与lazy它主要是用于设置关联的集合信息的抓取策略。
  • Fetch可取值有:

    • SELECT 多条简单的sql   (默认值)
    • JOIN 采用迫切左外连接
    • SUBSELECT 将生成子查询的SQL

  • lazy可取值有:

    • TURE 延迟检索   (默认值)
    • FALSE 立即检索
    • EXTRA 加强延迟检索(及其懒惰)                                                        

1.查询所有
    使用HQL

2.分页查询
    query.setFirstResult(10);
    query.setMaxResult(10);

3.查询指定列信息****(查询属性)

    和HQL投影检索一样
    先要在Customer实体类生成name,address的有参构造,并加无参构造.
    select new Customer(name,address) from Customer;
    将属性封装到对象中

4.条件查询
    from Customer where name=?
    query.setParameter(0,"张三")      对参数进行赋值, hql是从0开始的
    如果查询结果是唯一的, query.uniqueResult();

5.执行本地SQL
    sqlQuery=session.createSQLQuery("select*from t_customer");
    把查询到的sql语句结果封装到Customer对象中
    sqlQuery.addEntity(Customer.class);
    如果sql中有参数, 使用setParameter()方法完成参数传递

6.Criteria:
    适合多条件查询
    criteria.add(Restrictions.eq("name","xxx"));
    查询姓名为张三或者address为上海
    criteria.add(Restrictions.or(Restrictions.eq("name","张三"),Restrictions.eq("address","上海")));

0 个回复

您需要登录后才可以回帖 登录 | 加入黑马