分享web开发知识

注册/登录|最近发布|今日推荐

主页 IT知识网页技术软件开发前端开发代码编程运营维护技术分享教程案例
当前位置:首页 > 代码编程

Hibernate学习小结

发布时间:2023-09-06 02:32责任编辑:傅花花关键词:Hibernate

1 Hibernate4.x 下获取SessionFactory

Configuration config = new Configuration().configure();// 创建服务注册对象ServiceRegistry serviceRegistry = new ServiceRegistryBuilder().applySettings(config.getProperties()).buildServiceRegistry();// 创建sessionFactory对象SessionFactory sessionFactory = config.buildSessionFactory(serviceRegistry);

2 配置hibernate.cfg.xml文件

<hibernate-configuration> ???<session-factory> ???????<!-- 连接数据库的基本信息 --> ???????<property name="connection.driver_class">com.mysql.jdbc.Driver</property> ???????<property name="connection.url">jdbc:mysql://localhost/hibernate?useUnicode=true&amp;characterEncoding=utf-8</property> ???????<property name="connection.username">root</property> ???????<property name="connection.password">root</property> ???????<!-- 指定数据库方言,hibernate对一些标准型数据库进行了默认的处理 --> ???????<property name="dialect">org.hibernate.dialect.MySQLDialect</property> ???????<property name="show_sql">true</property> ???????<property name="format_sql">true</property> ???????<!-- 是否自动建表create,update --> ???????<property name="hbm2ddl.auto">update</property> ???????<!-- 映射文件的路径或注解类,不支持包扫描 --> ???????<mapping resource="com/ma/hibernate/Student.hbm.xml"/> ???????<mapping class="com.ma.hibernate.Teacher"/> ???</session-factory></hibernate-configuration>

3 配置hibernate映射文件 .hbm.xml

<hibernate-mapping package="com.ma.entity"> ????<!-- 类名以及对应的数据库表名 --> ???<class name="User" table="USER"> ???????<!-- 属性对应的表的主键名 --> ???????<id name="id" column="id"> ?????????<!-- 主键生成策略 --> ?????????<generator class="native"/> ???????</id> ???????<!-- 属性对应的表的一般属性 --> ???????<property name="username"/> ???</class></hibernate-mapping>

4 openSession与getCurrentSession

  • open每次都获取一个新的session
  • 前者需手动关闭,后者自动关闭
  • current获取当前上下文中的session对象,将 session 与本地线程绑定,提交事务时自动刷新session缓存并关闭session,需要配置hibernate.cfg.xml文件
  • 根据Id查询并修改对象信息时需要使用getCurrentSession(),在同一事务下
<!-- 与Spring整合时,不能配置此项,否则Spring不会自动开启事务 --><property name="current_session_context_class">thread/jpa(全局的)</property>

5 get与load方法(装载对象)区别

  1. get立即发出select语句,load在使用返回的对象时才发出
  2. 对象不同:get得到的是实体类对象,load得到的是实体类的代理对象
  3. 查询结果不同:get未查到数据时返回空,load并使用该对象时会报ObjectNotFoundException,查到结果后在使用之前关闭了session会抛异常:LazyInitializationException

6 hibernate对象的几种状态

7 HQL

面向对象的sql语句,区分大小写

org.hibernate.query.Query query = session.createQuery("from Teacher t");//from语句默认返回结果为一个List集合List<Teacher> teachers = query.list();query.uniqueResult(), 确保 HQL 语句只返回一个结果// where:// ??is null// ??is [not] empty ?在MySQL中会自动转换为 exists()// 删除数据:最好是先通过id获取对象并删除而不是拼接sql语句通过id删除(能够使用级联删除)
  • 占位符?和 :param

    • ?:query.setParameter(int index, Object obj); //index从0开始
    • param:query.setParameter(Parameter param, Object obj)
  • 投影查询:select语句

    • 返回类型:默认为Object[], 只查询到一个数据时为Object
    • 返回相应的结果类型:new list(), new map(), new Constructor(..)
    • 去除重复值:使用distinct关键字,还可以通过将查到的List包装成一个HashSet(不允许重复)再包装成List
  • 迫切内连接:inner,不返回左侧不满足条件的结果

  • 多表连接查询:join

String hql = "select p from Product p join p.categorysecond cs join cs.category c where c.cid = ?";
  • 左外连接:left join
from Teacher t LEFT JOIN t.students-- list()的 返回结果是个Object数组的集合,默认不会初始化右侧实体对象,使用distinct可以只查询到左侧对象
  • 迫切左外连接:left join fetch,左外连接且把集合属性初始化
// from Teacher t LEFT JOIN FETCH t.students// list()的返回结果是存放着左侧实体对象的引用的集合,均被初始化(左侧所有和右侧满足条件的对象)// 关闭延迟加载后直接查询Orders即可查询到Orderiten信息String hql = "from Orders o where o.user.uid = ?";

分页查询

//1. 使用query的两个方法query.setFirstResult((pageNo - 1) * pageSize).setMaxResults(pageSize)//2. 使用HibernateTemplate + Criteria实现DetachedCriteria criteria = DetachedCriteria.forClass(Product.class);criteria.add(Restrictions.eq("isHot", 1)).addOrder(Order.desc("pdate")); ?????List<Product> products = (List<Product>) this.getHibernateTemplate().findByCriteria(criteria, 0, 10);//3. 使用HibernateTemplate分页的另一种写法List<Product> list = this.getHibernateTemplate().execute(new PageHibernateCallback<>(hql, new Object[]{cid}, beginIndex, pageSize));//PageHibernateCallback类public class PageHibernateCallback<T> implements HibernateCallback<List<T>> { ????private String hql; ????private Object[] params; ????private int startIndex; ????private int pageSize; ?????????public PageHibernateCallback(String hql, Object[] params,int startIndex, int pageSize) { ??????????this.hql = hql; ??????????this.params = params; ??????????this.startIndex = startIndex; ??????????this.pageSize = pageSize; ????} ????public List<T> doInHibernate(Session session) throws HibernateException { ??????????//1 执行hql语句 ??????????Query query = session.createQuery(hql); ??????????//2 实际参数 ??????????if(params != null){ ???????????????for(int i = 0 ; i < params.length ; i ++){ ????????????????????query.setParameter(i, params[i]); ???????????????} ??????????} ??????????//3 分页 ??????????query.setFirstResult(startIndex); ??????????query.setMaxResults(pageSize); ??????????return query.list(); ????}}

自动生成字符串主键再添加对象到数据库

public String getNewSid() { ????Session session = sessionFactory.getCurrentSession(); ????String hql = "select max(sid) from Student"; ????String strid = (String) session.createQuery(hql).uniqueResult(); ????if(strid == null || strid.equals("")) { ??????strid = "S0001"; ????} else { ????????String temp = strid.substring(1); ????????int id = Integer.parseInt(temp); ????????temp = String.valueOf(++id); ??????????????StringBuilder s = new StringBuilder(); ????????s.append("S"); ??????????????int length = temp.length(); ????????for(int i=0; i<4-length; i++) { ????????????s.append("0"); ????????} ????????strid = s.append(temp).toString(); ????} ????return strid;}

8 QBC

public void testQBC() { ????Criteria c = session.createCriteria(User.class); ????//添加查询条件Criterion,使用Restrictions ????c.add(Restrictions.eq("id", 545)); ????// AND 使用Conjunction表示,实现了Criteria接口 ????Conjunction conjunction = Restrictions.conjunction(); ????conjunction.add(Restrictions.like("title", "tt", MatchMode.ANYWHERE)); ????// OR 使用Disjunction表示 ????Disjunction disjunction = Restrictions.disjunction(); ????c.add(conjunction).add(disjunction); ????????System.out.println(conjunction + "\n" + disjunction); ????// 统计查询,使用Projection ????c.setProjection(Projections.max("id")); ????// 排序 ????c.addOrder(Order.asc("id")); ????//返回条件查询的结果集合 ????List list = c.list();}

调用本地SQL查询:(复杂SQL语句)

String sql = "select * from user";SQLQuery sqlQuery = session.createSQLQuery(sql);// 单表查询,指定结果类型sqlQuery.addEntity(User.class);// 多表查询,将查询结果类型转换为UserVOsqlQuery.setResultTransformer(Transformers.aliasToBean(UserVO.class));// 设置查询结果列的类型addScalar("id",IntegerType.INSTANCE);List list = sqlQuery.list();

9 缓存策略:

  • 一级缓存:Session级别的缓存,默认使用,不能关闭

    • flush(),检查缓存中的对象状态是否和数据库表记录一致,可能会发送sql语句以保证数据一致性,

      • 提交事务时会自动调用,也可显式调用。
      • 执行HQL或QBC查询时会先调用 flush() 方法,以获取最新数据
      • 当底层数据库使用自增方式生成主键时,会在执行 save()方法时先生成sql语句,但不会执行插入操作,因为必须保证主键的值是存在的
    • refresh(),强制发送select语句,以保证缓存中的对象状态与数据库表中的记录一致,获取最新的对象信息。

      • 需要修改mysql的事务隔离级别,可在hibernate.cfg.xml中添加Connection.isolation值为2
    • evict(obj)和clear()方法可以清除当前缓存

  • 二级缓存(多个session可共同使用):

    1. 适用情形:不重要且很少被修改,允许出现偶尔的并发问题的数据
    2. query中的 list() 默认不使用缓存,iterate()使用二级缓存
    3. 导入 hibernate 文档 lib 目录下 optional 目录下的 ehcache 的所有 jar 包
    4. 导入 ehcache.xml 配置文件到当前WEB应用的src目录下
    5. 使用注解:在使用二级缓存的实体类上加注解:@Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
    6. 使用xml:配置 hibernate.cfg.xml
  • <!-- 启用二级缓存 --><property name="cache.use_second_level_cache">true</property><!-- 配置二级缓存的工厂类,即二级缓存的提供商 --><property name="hibernate.cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property><!-- 版本 4.x 后使用上述工厂类代替此配置 --><property name="cache.provider_class">net.sf.ehcache.hibernate.EhCacheProvider</property><!-- 此大顺序不能变 --><mapping resource="com/ma/entity/Teacher.hbm.xml"/><!-- 配置为哪个实体类使用二级缓存 --><class-cache usage="read-only" class="com.ma.entity.Teacher"/><!-- 也可在.hbm.xml 文件下的 class 标签下配置并可指定缓存策略--><cache usage="read-only"/><!-- 集合级别的二级缓存,也可在一方的 .hbm.xml 文件下的set标签下添加<cache usage="read-only"/> --><class-cache usage="read-only" class="com.ma.entity.Teacher"/><class-cache usage="read-only" class="com.ma.entity.Student"/><collection-cache usage="read-only" collection="com.ma.entity.Teacher.students"/><!-- 使用查询缓存,依赖于二级缓存--><property name="cache.use_query_cache">true</property><!-- session.createQuery("from User").setCacheable(true); -->

10 使用注解

  • 配置hibernate.cfg.xml:

  • 常用注解:

    • @Entity:映射实体类,对应数据库的表

    • @Table:配置对应表的信息

    • @Embeddable:可嵌入式的类

    • @Embedded:嵌入式类的对象(属性上的注解)

    • @Id: 主键生成策略:

      • @GeneratedValue(generator = "my"),使用hibernate的主键策略生成器
      • @GenericGenerator(name = "my", strategy = "native"),name值要和generator的值一样
    • @Column:数据库表字段注解

    • @Enumerated:枚举类型的注解

    • @Transient:映射时忽略此注解下的字段

    • @Temporal(TemporalType.TIMESTAMP):指定日期格式的类型

    • 联合主键(不常用):

      1. 需要有个主键类,并实现序列化接口,重写equals和hashcode方法
      2. @EmbeddedId,常用
      3. @Id,@Embeddable,@IdClass(value=主键类)

11 关联映射关系

双向原则:

  • 需指定控制方(通常为多方),inverse
  • 需在程序中设定双向关联(为引用赋值)

XML:

  • 一对多(多对一)双向映射:

    • 保存时先保存一方,再保存多方,也可指定cascade属性
    • 保存时生成的语句:单向多对一直接保存多方的外键;单向一对多先保存各自的信息,在更新多方的外键
    • 获取到的对象:一方的引用是代理对象;多方的集合是hibernate内置的集合类型:具有延迟加载并存放代理对象的功能
  • <!-- 单向一对多关联:在一方定义一个多方的Set集合(需要初始化),name为该集合的属性名,table为要关联的多方对应的表,order-by对无序的set集合按照指定的列进行排序 --><set name="students" table="student" inverse="true" order-by="gradeId" > ????<!-- 指定关联的多方对应数据库表中的外键列 ?--> ????<key column="gradeId"/> ????<!-- 指定关联的多方的类 ?--> ????<one-to-many class="Student"/></set><!-- 配置多对一关联关系,在多方定义一个一方的引用,name为当前持久化类的引用的属性名,class为一方的类名,column为多方对应表中的外键列名 --><many-to-one name="grade" class="Grade" column="gradeId" />
    • 一对一双向外键:双方均持有对方的引用
  • <!-- 一方使用many-to-one配置,指定unique为true即为一对一 --><many-to-one name="cardId" class="CardId" column="caedid" unique="true" /><!-- 由于已指定unique,故另一方(没有外键)只使用配置 one-to-one 即可,使用 property-ref 属性指定使用被关联实体主键以外的字段作为关联字段 --><one-to-one name="student" class="Student" property-ref="grade"/><!-- 获取无外键的实体对象时默认使用左外连接获取到关联的对象,否则无法利用引用属性获取 -->
    • 一对一双向主键:双方均持有对方的引用
  • <!-- 主外键相同的一方 --><id name="id" column="id"> ?<!-- 使用外键的方式来生成主键 --> ?<generator class="foreign"> ??????<!-- property属性用来指定使用当前持久化类的哪个属性的主键作为外键 --> ??????<param name="property">grade</param> ?</generator></id><!-- constrained 设为 true 表示为当前的主键添加外键约束 --><one-to-one name="grade" class="Grade" constrained="true"/><!-- 无外键的一方 --><one-to-one name="student" class="Student"/>
    • 多对多双向映射:需要中间表,存放两张表的主键id,每个实体类的映射文件都需要进行如下配置,需要指定由谁控制(inverse)
  • <set name="students" table="student_grade" inverse="true"> ?<key column="rgradeid" /> ?<many-to-many class="Student" column="rstuid" /></set>
    • 继承映射

      • subclass:只有一张表,父类配置如下:
      • 优点:只需要查询一张表
      • 缺点:1. 使用了辨别者列;2.子类特有字段不能添加非空约束;3.层次多时表字段数太多
    • <!-- 配置辨别者列 --><discriminator column="TYPE" type="string" /><!-- 指定辨别者列的值,即class及subclass标签中discriminator-value属性的值,辨别者列的值由hibernate自动插入 --><subclass name="Student" discriminator-value="STUDENT"> ????<property name="age"/></subclass>
      • joined-subclass:每个子类一张表
      • 插入子类对象需要插入到两张表中
      • 优:没有辨别者列,可以添加约束,没有冗余字段
      • 缺:需要添加外键约束,查询总是需要多表连接
    • <joined-subclass name="Student"table="STUDENT"> ???<key column="SID" /> ???<property name="age" /></joined-subclass>
      • union-subclass:
      • 父类需要使用子查询
      • 存在冗余字段
      • 更新父类信息麻烦
    • <union-subclass name="Student" table="STUDENT"> ?<property name="age"></property></union-subclass>
    • 联合主键(不常用)

<!-- 需要有个主键类,并实现序列化接口,重写equals和hashcode方法 --><composite-id class="Order_User_PK" name="pk"> ????<key-property name="oid" /> ????<key-property name="uid" /></composite-id>

Annotation:注解在属性或getter方法上

  1. 一对一单向外键:

    • 一方持有另一方的引用
    • @OneToOne(cascade=CascadeType.ALL)
    • @JoinColumn(name="card_id", unique=true)
  2. 一对一双向外键:

    • 两方都需要持有对方的引用
    • 一方参照一对一单向
    • 另一方:@OneToOne(mappedBy="card"),必须指定mappedBy属性(控制方)
  3. 多对一单向外键:

    • 多方持有一方的引用(外键)
    • @ManyToOne(cascade={CascadeType.ALL}, fetch=FetchType.EAGER)
    • @JoinColumn(name="sid") //name 为自定义为本表生成外键的列名称, referencedColumnName 为要关联表的列名称(可以不写,默认主键列)
  4. 一对多单向外键:

    • 一方持有多方的Set集合(需初始化)
    • @OneToMany(cascade={CascadeType.ALL}, fetch=FetchType.LAZY)
    • @JoinColumn(name="sid"),不写此项的话会生成中间表
  5. 一对多(多对一)双向外键:

    • 多方:持有一方的引用

      • @ManyToOne(cascade={CascadeType.ALL}, fetch=FetchType.EAGER)
      • @JoinColumn(name="user_id")
    • 一方:持有多方的集合

      • @OneToMany(mappedBy="user", cascade={CascadeType.ALL}, fetch=FetchType.LAZY),双向需要指定mappedBy属性,此时不能再使用@JoinColumn
  6. 多对多单向外键

    • 需要一个中间表
    • @ManyToMany
    • @JoinTable(name="tea_stu", joinColumns={@JoinColumn(name="stuid")}, inverseJoinColumns={@JoinColumn(name="teaid")})
  7. 多对多双向外键

    • @ManyToMany(mappedBy="teachers")
    • 另一方:参照多对多单向外键关联

12 检索策略

  • 类级别的检索策略

    • 立即检索:修改 hbm.xml 文件中的class节点,添加lazy属性为false,默认为true,仅对load方法有效
    • 延迟检索:load获取到的对象只保存OID属性,获取其他属性会发送SQL语句查询实例化实体对象
  • 一对多或多对多的检索策略:

    • 集合属性默认使用懒加载检索策略,修改set属性的lazy值(默认true/proxy),不建议改为false,可改为extra,增强的延迟检索(尽可能延迟集合初始化的时机)

    • set中的batch-size:设置一次性初始化set集合的数量

    • fetch:确定初始化集合的方式

      • select:决定初始化集合的 sql 语句形式,subselect使用子查询方式初始化所有 set 集合,将忽略batch-size值
      • join:决定初始化集合的时机,加载一方对象时使用迫切左外连接的方式检索n的一端的集合属性,将忽略lazy值
      • 使用Query.list()查询会忽略join,而使用默认的延迟检索

13 hibernate乐观锁(级联保存报错)

Hibernate学习小结

原文地址:https://www.cnblogs.com/mabaoqing/p/10346473.html

知识推荐

我的编程学习网——分享web前端后端开发技术知识。 垃圾信息处理邮箱 tousu563@163.com 网站地图
icp备案号 闽ICP备2023006418号-8 不良信息举报平台 互联网安全管理备案 Copyright 2023 www.wodecom.cn All Rights Reserved