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

 找回密码
 加入黑马

QQ登录

只需一步,快速开始

【石家庄校区】Hibernate框架知识详解

Hibernate介绍
Hibernate:开源的对象关系映射(ORM)框架,对jdbc进行轻量级对象封装,将POJO与数据库表建立映射关系。在dao层使用
ORM(Object Relation Mapping,O/RM,O/R mapping)对象关系映射:使用orm可以将我们的对象与我们的类去进行映射,使的我们可以去操作对象就完成对表的操作
环境搭建
  下载:hibernate-5.0.7.Final.zip
  下载包目录结构
    documentation:存放hibernate相关文件与API
    lib:运行jar包,其中子目录required下jar包必须导入
    project:存放hibernate相关的源代码与资源
  导入hibernate必须jar包(子目录required下jar包)
  导入数据库驱动jar包
  导入日志相关jar包:log4j-xxx.jar,slf4j-api-xxx.jar,slf4j-log4j12-xxx.jar
  将hibernate/project/etc/log4j.properties文件导入到工程src下
基本开发
  //使用hibernate的api完成数据操作
  Configuration config=new Configuration().configure();
  SessionFactory sessionFactory=config.buildSessionFactory();
  Session session=sessionFactory.openSession(); //相当于得到一个Connection
  //开启事务
  session.beginTransaction();
  //操作...
  //事务提交
  session.getTransaction().commit();
  session.close();
  sessionFactory.close();
hibernate执行原理(重点)
  1.通过new Configuration().configure();读取解析hibernate.cfg.xml配置文件
  2.通过hibernate.cfg.xml中<mapping resource="">读取解析映射信息
  3.config.buildSessionFactory();得到sessionFactory
  4.sessionFactory.openSession();得到session
  5.session.beginTransaction();开启事务。
  6.persistent operate;增删改查操作
  7.session.getTransaction().commit();提交事务
  8.关闭session;
  9.关闭sessionFactory;
Hibernate配置文件
1.映射配置文件
建立表与类的映射关系
类名.hbm.xml
  位置:与实体类在同一个包下创建
  约束:在核心包hibernate-core-5.0.7.Fianl.jar/org.hibernate/hibernate-mapping-3.0.dtd
  <hibernate-mapping>
    package="cn.itheima.domain" //统一声明包名,在<class>中就不需写类全名,缺点:实体类都在统一包中
    <class name="实体全类名"> //如果统一声明包名,则可直接写类名
      table="表名" //不写表的名称与类名一致
      catalog="数据库名" //不写使用核心配置文件中url路径中的库名称
      <id name="类属性名"> //描述主键标签,与表中主键映射
        column="表主键名" //不写与类属性名一致
        length="字段长度"
        type="" //指定字段类型。对于SQL数据类型,需写子标签<column ... sql-type="varchar(50)" ></column>
        <generator class="native"></generator> //主键生成策略,native:自动
      <property name="类属性名"> //描述类属性与表非主键字段的映射关系,标签属性同id
    </class>
  </hibernate-mapping>
关于hibernate映射文件中类型问题
  对于type属性取值,有三种(参考图片)
  1.java数据类型
  2.hibernate数据类型(默认)
  3.SQL数据类型
2.框架核心配置文件
两种方式:hibernate.properties和hibernate.cfg.xml(常用)
hibernate.cfg.xml
  位置:src下创建
  约束:在核心包hibernate-core-5.0.7.Fianl.jar/org.hibernate/hibernate-configuration-3.0.dtd
  配置参考:hibernate\project\etc\hibernate.properties文件
  <hibernate-configuration>
    <session-factory>
      //属性配置
      <property name="key">value</property>
      //配置关于数据库连接的四个项,如:MySQL
      hibernate.connection.driver_class=com.mysql.jdbc.Driver
      hibernate.connection.url=jdbc:mysql:///test
      hibernate.connection.username=root
      hibernate.connection.password=123
      //配置此项,可以将数据库发送的SQL语句显示到控制台
      hibernate.show_sql=true
      //格式化sql
      hibernate.format_sql=true
      //hibernate方言,告诉hibernate使用的是什么数据库,如:使用MySQL数据库
      hibernate.dialect=org.hibernate.dialect.MySQLDialect
      //表的自动创建
      hibernate.hbm2ddl.auto=create-drop 每次都创建一个新表,执行完成后删除。一般测试中使用
          create 每次都会创建一个新表,一般在测试使用
          update 如果数据库中有表不创建,没有表则创建,如果映射不匹配,会自动更新表结构(只能添加)
          validate 只会使用存在的表,并且会对映射关系进行校验。表不存在则报错
      //配置hibernate映射文件所在位置,可多个,如:
      <mapping resource="cn/itheima/domain/xxx.hbm.xml" />
    </session-factory>
  </hibernate-configuration>
Hibernate常用API
Configuration 用于加载hibernate配置文件
    () //加载的src下的hibernate.properties
    configure(); //加载src下的hibernate.cfg.xml
    configure("核心配置文件名称"); //加载指定的名称的配置文件
    //手动加载映射配置
    addResource("xxx/xxx.hbm.xml") //直接加载映射配置文件
    addClass(实体类名.class) //直接在实体类所在包下查找规范映射配置文件
SessionFactory 接口,负责初始化Hibernate,充当数据存储源的代理,并负责创建Session对象,这里用到工厂模式
    注意:不是轻量级,一个项目通常一个SessionFactory即可,多个数据库可对应多个SessionFactory
    获取通过Configuration对象.buildSessionFactory()
    //获取Session
    Session openSession() //从连接池中获取一个连接
    Session getCurrentSession() //获取一个与线程绑定的Session
内部维护了连接池,如果使用c3p0:
    1.导入 hibernate/lib/options下关于c3p0的jar包
    2.在hibernate.cfg.xml中<property>配置c3p0连接(参考etc/hibernate.properties)
    hibernate.c3p0.max_size=20 最大连接数
    hibernate.c3p0.min_size=5 最小连接数
    hibernate.c3p0.timeout=120 超时,单位s
    hibernate.c3p0.idle_test_period=3000 空闲连接,单位s
Session 接口,负责执行被持久化对象的CRUD,操作Session对象是非线程安全的
    Transaction beginTransaction() //获取Transaction对象
    save(实体类对象) //保存对象
    update(实体类对象) //针对于脱管对象,操作时会将脱管对象转换成持久对象再操作
    delete(实体类对象) //删除一个脱管对象,与session关联再删除
    saveOrUpdate(实体类对象)
    //查询操作,获取查询对象
    实体类对象 get/load(实体类.class,id) //根据id进行查询
    Query getNameQuery("命名检索名") //通过命名检索语句,获取查询对象
    Query createQuery("hql") //获取执行HQL的对象
    SQLQuery CreateSQLQUery("sql") //获取执行SQL的对象
    Criteria createCriteria(实体类.class) //获取一个Criteria它可以完成条件查询
解决session安全问题:只需在方法内部使用session即可
通过load形式加载的延迟加载的对象,如何进行初始化
    1.对象.方法() //使用以下该对象
    2.hibernate.initilize(对象)
    3.openSessionInView() //filter,Spring框架
Transaction 接口,事务管理封装
    commit()
    rollback()
设置自动提交
    问题:如果在程序中没有开启事务,session的每一个操作就会开启一个事务
    默认事务不自动提交,设置自动提交:hibernate.cfg.xml中设置属性:
    hibernate.connection.autocommit=true
查询两种表达方式:HQL语言(操作的全是类中属性)或SQL语句(SQLQuery是Query的子类)
Query 接口,执行HQL语言,方便地对数据库及持久对象进行查询
    //分页查询方法
    setFirstResult(10) //开始位置,公式:(当前页-1)*每页显示
    setMaxResult(10) //设置每页显示条数
    //查询指定列,投影查询
    查询一个字段 得到的结果是List<Object[]>
    查询多个字段 得到的结果是List<实体类> //实体类需再添加相对应查询列的构造方法
    //参数赋值方法
    setParameter(0,"value") //对参数赋值
    setParameter("myname","value") //对有名称参数赋值
    //执行查询,返回结果
    List<实体类> list() //返回多个数据时使用
    Object uniqueResult() //如果保证结果唯一,使用这个
SQLQuery //本地SQL检索
    //执行查询
    List<T> list()
    T uniqueResult()
    //将结果封装到指定对象,如果不封装,得到是List<Object>
    addEntity(实体类.class)
    //设置参数
    setParameter(0,"value")
    //对于命名检索,直接执行会抛异常,因为hibernate不知道执行的语句该如何封装数据
    需注解配置:PO类上
      @SqlResultSetMapping(name="setMappingName",
        entities={@EntityResult(entityClass=类.class,fields={
          @FieldResult(name="类属性",column="表字段"),@FieldResult(name="name",column="name")
        })}
      )
      @NameNativeQuery(name="myHql",query="hql语句",resultSetMapping="setMappingName")
QBC检索方式(query by criteria):更加面向对象的检索方式,是轻量级,不能再session外使用
Criteria 类似查询
    List<T> list() //查询数据
    //排序
    addOrder(org.hibernate.criterion.Order.desc("属性名")) //降序,asc升序,参数是hibernate中的对象
    //添加条件
    add(Criterion)
    //分页,设置两个方法
    //分组 count sum avg max min
    setProjection(Projections.projectionList().add(Projections.sum("属性名")).add(Projections.groupProperty("属性名")))
离线条件查询
  //web端,封装条件
  DetachedCriteria dc=DetachedCriteria.forClass(类.class);
  dc.add(Restrictions.like("name","张_"))
  //传递dc对象到dao
  Criteria criteria=dc.getExecutableCriteria(session);
Hibernate持久化类PO
PO(Persistent Object)持久化类:PO=POJO+hbm映射配置
PO编写规则
  提供无参public构造
  所有属性private,提供public的get/set方法
  提供一个标识属性,与数据库中主键对应,这个属性叫OID
  属性尽量使用基本数据类型的包装类
  PO类不能使用final修饰符
OID作用:Hibernate框架通过OID区分不同的PO对象,如果内存中有两个相同OID对象,那么hibernate认为他们是同一对象
数据类型使用包装类原因:使用基本数据类型是没有办法去描述不存在概念,对于包装类对象默认值是null
不能使用final修饰符原因(hibernate中get/load区别)
  Get/load方法它们都是根据id去查询对象。
  1.get直接得到了一个持久化类型对象,它就是立即查询操作
  load它得到的是持久化类开的代理类型对象(子类对象)。它采用了一种延迟策略来查询数据。
  2.get方法在查询时,如果不存在返回null
  load方法在查询时,如果不存在抛ObjectNotFoundException.
持久化对象三种状态
  1.瞬时态:也叫临时态或自由态,没有OID和session,一般指new出来的对象,与数据库中信息无关联
  2.持久态:有OID,有session,在数据库中有可能有,也有可有没有。
    特点:在事务未提交前一直是持久态,当它发生改变时,hibernate是可以检测到的。
  3.脱管态:也叫游离态或离线态,有OID,没有session
    特点:对于托管态对象,它发生改变时hibernet不能检测到
判断持久化类对象三种状态:
  1.是否有OID
  2.是否与session关联
三种状态切换
  1.瞬时态(new 出来的)
    瞬时 -》 持久 save saveOrUpdate
    瞬时 -》 脱管 手动设置oid
  2.持久态  由session管理
    持久 -》 瞬时 delete() 被删除后持久化对象不在建议使用
    持久 -》 脱管 注意:session它的缓存就是所说的一级缓存
    evict(清除一级缓存中指定的一个对象)
    clear(清空一级缓存)
    close(关闭,清空一级缓存)
  3.脱管态 无法直接获取
     脱管 -》 瞬时 直接将oid删除
     脱管 -》 持久 update saveOrUpdate lock(过时)
Hibernate主键生成策略
Hibernate中定义主键类型
  自然主键:具有业务含义的字段作为主键,如:学号、身份证号
  代理主键:(推荐)不具有业务含义的字段作为主键,如:MySQL自增id,Oracle序列生成的主键,uuid方法生成的唯一序列串
主键生成器/描述
代理主键
  increment 由hibernate维护一个变量,每次生成主键时自动递增
    优点:方便跨平台
    缺点:不适合高并发访问,如果有多个应用访问一个数据库,由于每个应用维护自己的主键,所以此时主键可能冲突
  identity 由底层 支持自动增长数据类型的数据库生成表标识符,如MySQL自增主键
    优点:由底层数据库维护,和hibernate无关
  sequence 由底层 支持序列的数据库生成标识符,如Oracle
    优点:同上
  native (不建议采用)根据底层数据库地总选择 identity,sequence,hilo,
    优点:在项目中如果存在多个数据库时使用
    缺点:每次判断,效率低
  uuid (建议采用)采用128bit位的UUID算法来生成标识符
    优点:与数据库无关,方便数据库移植,效率高
    缺点:uuid长度大,占用空间大
自然主键
  assigned (不建议)由java程序负责生成标识符
  尽量在操作中避免手动对主键操作
Hibernate一级缓存
一级缓存:session级别缓存,一次会话
  作用:减少对数据库访问
  在session中定义了一系列的集合来存储数据,它们构成session缓存。如:
  actionQueue它是一个行列队列,主要记录crud操作的相关信息
  persistenceContext持久化上下文,真正缓存
二级缓存:SessionFactory级别缓存,整个系统
持久化对象具有自动更新数据库能力
  原因:当事务提交,session关闭,向数据库发送请求时,会判断一级缓存区中数据是否与快照区一致,如果不一样,就会发送update语句
一级缓存常用API
  clear() 清空一级缓存.
  evict(obj) 清空一级缓存中指定的一个对象。
  refresh(obj) 重新查询数据库,用数据库中信息来更新一级缓存与快照
  一级缓存特点:
  1.当我们通过session的save,update saveOrupdate进行操作时,如果一级缓存中没有对象,会将这些对象从数据库中查询到,存储到一级缓存。
  2.当我们通过session的load,get,Query的list等方法进行操作时,会先判断一级缓存中是否存在,如果没有才会从数据库获取,并且将查询的数据存储到一级缓存中。
  3.当调用session的close方法时,session缓存清空。
Hibernate注解开发
PO类基本注解配置大部分注解都是javax.persistence包下,JPA规范)
实体类上添加
  @Entity //声明一个实体类
  @Table(name="表名",catalog="数据库名") //描述与表对应关系
主键属性上|get方法上
  @Id //声明主键
  @GeneratedValue(strategy=GenerationType.IDENTITY) //声明主键生成策略,不写默认相当于native
    策略 AUTO,IDENTITY,SEQUENCE
  //主键生成策略使用uuid类型
  @GenericGenerator(name="myuuid",stratrgy="uuid") //自定义生成策略,hibernate中的包
  @GeneratedValue(generator="myuuid") //引用自定义生成策略
其他属性上|get方法上
  @Column(name="表列名",length=20,nullable=true) //不写默认列的名称就是属性的名称,字符串类型才写length
  @Temporal(TemporalType.TIMESTAMP) //声明日期类型
    DATA年月日 TIME时分秒 TIMESTAMP年月日时分秒
设定类的属性不在表中映射
  属性上添加 @Transient
在hibernate.cfg.xml中将PO类中的注解引用生效
  <mapping class="实体类全类名">
一对多映射关系配置
  //1实体类
  在关联集合对象属性上
  @OneToMany(targetEntity=对方类.class,mappedBy="对方持有对象")
  //多实体类
  在关联对象属性上
  @ManyToOne(targetEntity=对方类.class)
  @JoinColumn(name="表外键列名") //指定外键列名
多对多映射关系配置
  只需在一端配置中间表,另一端使用mappedBy放弃外键维护权即可
  //一端配置中间表:在关联集合对象属性上
  @ManyToMany(targetEntity=对方类.class)
  @JoinTable(name="中间表名",
    joinColumns={@JoinColumn(name="列名")}, //本类与中间表的映射关系
    inverseJoinColumns={@JoinColumn(name="列名")} //对方类与中间表的映射关系
  )
  //一端放弃维护外键
  @ManyToMany(targetEntity=对方类.class,mappedBy="对方持有集合名")
一对一配置
  方式一:任意一方添加外键
  //一端维护外键
  @OneToOne(targetEntity=对方类.class)
  @JoinColumn(name="外键列名")
  //一端泛起维护外键
  @OneToOne(targetEntity=对方类.class,mappedBy="对方持有属性")
  方式二:主键映射方式
  //一端
  @OneToOne
  @PrimaryKeyJoinColumn //说明两个表是使用主键映射
  //一端
  @OneToOne
  @PrimaryKeyJoinColumn
  主键属性上
  @GenericGenerator(name="myForeignKey",strategy="foreign",
    parameters={@Parameter(name="property",value="本方持有属性名")}) //主键设置成指定参考类的主键方式
  @GenerateValue(generator="myForeignKey")
配置级联操作
  对于多方不配置级联删除操作
  方式一:使用JPA提供的注解
  在@OneToMany中添加 cascade=CascadeType.All
  方式二:使用hibernate提供的注解
  在关联集合属性对象上添加 @Cascade(CascadeType.SAVE_UPDATE)
  扩展:DELETE_ORPHAN过时问题,代替:在@OneToMany中添加 orphanRemoval=true
检索方式
五种检索方式
  1.导航对象图检索方式,根据已加载的对象导航到其关联对象
  2.OID检索方式,按照对象的OID检索对象,get/load方法
  3.HQL检索方式,使用面向对象的HQL查询语言
  4.QBC检索方式,使用QBC(Query by Criteria)API来检索对象,这种API封装了基于字符串形式的查询语句,提供了更加面向对象的查询接口
  5.本地SQL检索方式,使用本地数据库的SQL语言查询语句
HQL检索
  HQL(Hibernate Query Language)提供更加丰富灵活、更为强大的查询能力
  格式: Select/update/delete …… from …… where …… group by …… having …… order by …… asc/desc
  基本检索 from 类名
  排序检索 from 类名 order by 属性名 desc
  条件检索 from 类名 where 属性名>:name //有名称参数
    from 类名 where 属性名>?
  分页检索 设置两个方法
  分组统计检索 select sum(属性名) from 类名 group by 属性名
    count max min avg sum
    * 只能用来做数字统计
  投影检索 select new 类(属性名...) from 类 //将结果封装到类对象,需提供对应构造
  命名检索
    hbm配置:<hibernate-mapping><query name="myHql">hql语句<>
    注解配置:PO类上 @NameQuery(name="myHql",query="hql语句")
    使用:session.getNameQuery("myHql") 返回 Query对象
    如果有参数,可以通过命名参数赋值
HQL多表操作(自动消除笛卡尔积)
迫切:得到的结果是直接封装到PO类中的List<T>,而内连接/左外连接得到的是List<Object[]>
  交叉连接
  内连接
    显示内连接 from 类名 别名 inner join 别名.关联属性 with 条件
    隐式内连接 from 类名 别名 where 条件 //条件可以为 别名.关联属性.属性
    迫切内连接 join后加 fetch //如果类名是1的一方,则结果可能重复,from前加:select distinct 别名
  外连接
    左外连接 from 类名 别名 left outer join 别名.关联属性
    迫切左外连接 join后加 fetch
    右外连接
事务管理
设置事务隔离级别
  核心配置文件配置属性 hibernate.connection.isolation = 1|2|4|8
  1 隔离级别为 read uncommitted
  2 read committed
  4 repeatable read
  8 serializable
session管理
  三种管理session的方式(开发使用前2种):
  1.本地线程绑定session(ThreadLocal)
  2.JTA事务绑定session(分布式事务管理)
  3.程序管理Session
本地线程绑定使用
  需在hibernate.cfg.xml配置属性:hibernate.current_session_context_class=thread
  代码中获取session:sessionFactory.getCurrentSession();
  注意:获取的session在事务关闭时,session对象会close。所以不需要手动close
  应用:一个请求对应一个ThreadLocal,将session保存到ThreadLocal中,只要是同一个线程,就可以在任意层去创建session,并可以保证使用的是同一个session
优化方案
HQL优化
  1.使用占位符参数绑定方式,可以节省CPU时间和内存,避免SQL注入
  2.避免使用 not in 关键字
  3.尽量使用where替换having
  4.尽量减少对表的查询
  5.尽量使用表别名
  6.实体的更新与删除,hibernate3后可以使用 update ...
  7.一级缓存,在大批量的增加更新操作,需手动清除一级缓存,session.evict()和session.clear()
检索策略/抓取策略
  类级别检索:直接通过session检索某一类对应的数据
    分为:延迟检索(默认)、立即检索
    xml设置: hbm下<class>标签添加属性:lazy="true" //true默认,false延迟加载
    注解设置: 类上:@Proxy(lazy=true)
  关联级别检索:通过对象获得其关联的对象或属性,称为关联级别检索
    抓取策略:值得是找到某个对象后,通过这个对象去查询关联对象的信息时的一种策略
    fetch 描述SQL语句的格式(如:多条,子查询,多表联查)
    lazy 控制SQL语句何时发送
    注解配置:关联属性上
    //一方
    @Fetch(FetchMode.SELECT)
      SELECT 多条简单SQL(默认)
      JOIN 采用迫切左外连接,lazy失效,所以只能立即查询
      SUBBSELECT 生成子查询的SQL
    @LazyCollection(LazyCollectionOption.xxx)
      TRUE 延迟检索(默认),在真正使用该对象时才会发送SQL语句
      FALSE 立即检索
      EXTRA 加强延迟检索,只查询需要的数据,不全部查询信息
    7种结论:查询类信息使用 [fetch] 语句方式,关联信息加载时机为 [lazy]
    //多方
    @Fetch(FetchMode.SELECT)
      SELECT 多条简单SQL(默认)
      JOIN 采用迫切左外连接,lazy失效,所以只能立即查询
    @LazyToOne(LazyToOneOption.TRUE)
      false 立即检索
      proxy (默认) 是否采用延迟,根据1方类级别检索决定
批量抓取
  在查询对象的多个关联对象时,可以采取批量抓取方式对程序优化
  可以解决N+1问题
  //查询1方,查询多方
  1方(主表)上配置:
  xml配置:hbm.xml/<set> 设置属性 batch-size=4
  注解配置:关联属性上 @BatchSize(size=4)
  //查询多方,查询1方
  1方(主表)上配置:
  xml配置:hbm.xml/<class> 设置属性 batch-size=4
  注解配置:类上 @BatchSize(size=4)

0 个回复

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