48 Hibernate

原文链接:https://blog.csdn.net/qq_38977097/article/details/81326503

什么是ORM?
对象关系映射(英语:Object Relation Mapping,简称ORM,或O/RM,或O/R mapping),是一种程序技术,用于实现面向对象编程语言里不同类型系统的数据之间的转换。
使用ORM可以将我们的对象(或类)去进行映射,使得我们可以去操作对象就能完成对表的操作。

对于Hiberate框架的学习重点,可以总结为:
掌握Hiberate的基本配置——即搭建Hiberate开发环境
掌握Hiberate常用API——即如何使用Hiberate框架进行开发
掌握Hiberate的关联映射——解决表与表之间存在的关系问题,有1:n(一对多)、 1:1(一对一)、m:n(多对多)关系
掌握Hiberate的检索方式——即掌握Hiberate的查询
掌握Hiberate的优化方式——即提高Hiberate的效率

Hibernate的体系结构与开发步骤在这里插入图片描述
Hibernate开发步骤:

创建持久化类
创建对象-关系映射文件
创建Hibernate配置文件
通过Hibernate API编写访问数据库的代码

Hibernate表关联配置

链接:https://www.jianshu.com/p/91ed14f53ca9

一对一
一对一主键关联

主控方使用@PrimaryKeyJoinColumn
被控方在@OneToOne中要加上(mappedBy = “card”)
//基于注解的配置

@Entity
@Table(name = "person")
public class Person {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @Column(name = "person_name")
    private String personName;

    //共享主键关联使用PrimaryKeyJoinColumn
    @OneToOne(cascade = CascadeType.ALL)
    @PrimaryKeyJoinColumn
    private Card card;
  }

@Entity
@Table(name = "card")
public class Card {
    @Id
    @GenericGenerator(name = "generator",strategy = "foreign",
            parameters = @org.hibernate.annotations.Parameter(name = "property",value = "person"))
    @GeneratedValue(generator = "generator")
    private Integer id;

    @Column(name = "card_num")
    private String cardNum;


    //必须设置OneToOne,mappedBy为自己在对方中的属性名
    @OneToOne(mappedBy = "card")
    private Person person;
  }

一对一外键关联
主控方使用@JoinColumn(name = “content_id”)
被控方使用@OneToOne(mappedBy = “passageContent”),mappedBy相当于XML中的property-ref,是一个反向声明

@Entity
@Table(name = "passage")
public class Passage {

    @Id
    @GeneratedValue
    private Integer id;
    private String title;

    @OneToOne
    @JoinColumn(name = "content_id")
    private PassageContent passageContent;
  }

@Entity
@Table(name = "passage_content")
public class PassageContent {

    @Id
    @GeneratedValue
    private Integer id;
    private String content;


    @OneToOne(mappedBy = "passageContent")
    private Passage passage;
  }

一对多关联
ManyToOne端要加上JoinColumn,OneToMany要加上mappedBy
如需级联操作,一端还需加上Cascade

@Entity
@Table
public class Address {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @Column(name = "city_name")
    private String cityName;
    private String address;

    @ManyToOne
    @JoinColumn(name = "user_id")
    private User user;
  }

@Entity
@Table
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;
    private String username;

    @OneToMany(mappedBy = "user")
    @Cascade(CascadeType.DELETE)
    private Set<Address> addressSet;
  }

多对多关联

@Entity
@Table
public class Student {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;



    @Column(name = "student_name")
    private String studentName;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    @ManyToMany
    @JoinTable(name = "student_teacher_real",joinColumns = {@JoinColumn(name = "student_id")},inverseJoinColumns = {@JoinColumn(name = "teacher_id")})
    private Set<Teacher> teacherSet;
  }

@Entity
@Table
public class Teacher {

    @Id
    @GeneratedValue(strategy = GenerationType.AUTO)
    private Integer id;

    @Column(name = "teacher_name")
    private String teacherName;

    @ManyToMany(mappedBy = "teacherSet")
    private Set<Student> studentSet;
  }

Java注解总结
如果是主键关联,那么就使用@PrimaryKeyJoinColumn,只要声明@JoinColumn,@PrimaryKeyJoinColumn,@JoinTable的都属于主控方,外键通常也就存储在主控方,对应的含有mappedBy的都为被控方。
一对一关联不管是主键关联还是外键关联,基于注解都是OneToOne

@JoinColumn 详解

https://www.cnblogs.com/jpfss/p/11059629.html
1. 一对一

现假设有Person表和Address表,是一对一的关系,在Person中有一个指向Address表主键的字段addressID,所以主控方一定是Person,所谓主控方就是能改变关联关系的一方,Person只要改变addressID就改变了关联关系,所以Person是主控方,所以@JoinColumn写在Person类中

@OneToOne(cascade=CascadeType.ALL,optional=true)
@JoinColumn(name="addressID")//注释本表中指向另一个表的外键。
public Address getAddress() { 
    return address;
}

我们也可以不写@JoinColumn,Hibernate会自动在Person表生成关联字段,名称是被控方类名_被控方主键,如address_id
如果两张表是以主键关联的,比如Person表主键是id,Address表主键是address_id,则运用如下注释:

@OneToOne(cascade={CascadeType.ALL})
@PrimaryKeyJoinColumn(name = "id", referencedColumnName="address_id") 
public Address getAddress( ) {
      return homeAddress;
}

2.一对多单向
假如现在有Person表和Country表,那么一定是Person表中有country_id字段

由于我们是一对多的关系,所以站在一的角度,也就是Country的角度,在Country类中加入@JoinColumn:

@OneToMany(cascade=CascadeType.ALL) 
@JoinColumn(name="country_id")//注释的是另一个表指向本表的外键。 
public List<Person> getPersons() {
    return persons; 
}

在一对多单向关系中,多的一方(Person)没有注解,一的一方(Country)有注解,如果一的一方不加@JoinColumn指定外键字段的话,Hibernate会自动生成一张中间表Country_PERSON来对Person和Country进行绑定。

3.多对一单向

还是沿用一对多单向的例子,只是反过来,将Country中的注解去掉,挪到Person类中:

@ManyToOne 
@JoinColumn(name="country_id") 
public Country getCountry() { 
    return country; 
}

与一对多单向不同的是,此时若不用@JoinColumn指定外键字段的话,不会生成中间表,而是在Person表中生成一列指向Country的外键。
如果记不清这类关系的话,最好自己把表都设计好,通过注解将关系全部指定,不要让Hibernate代劳。使用中间表关联的注解:

@JoinTable(name = "Person_Country", 
joinColumns = {@JoinColumn(name = "person_id")},
inverseJoinColumns = {@JoinColumn(name = "country_id")})

joinColumns指定中间表中关联自己ID的字段,inverseJoinColumns表示中间表中关联对方ID的字段,在Country类中使用这个注解把它们对调一下即可。

4.一对多双向

单向关系的缺点还是比较明显的,比如一对多单向,多的那一方没有任何关于另一方的信息,如果我们通过person_id取得一个单独的Person对象,我们没办法知道它属于哪个Country,因为Person类中没有Country对象,多对一单向则是反之。所以正常使用时,一对多双向是最常用的,双方都有彼此的信息。单向变双向,只需在单向的基础上,在Person类中加入多对一的注解:

@ManyToOne(cascade = CascadeType.ALL, optional = false)
@JoinColumn(name="country_id")
private Country country;

5.多对多单向
一般多对多的关系都是用中间表来维护的,假设现在有Person表,Project表,Person_Project表,Person类中的注解:

@ManyToMany(cascade = CascadeType.ALL)
@JoinTable(name = "Person_Project",
joinColumns = {@JoinColumn(name = "person_id")},
inverseJoinColumns = {@JoinColumn(name = "project_id")}) 
public List<Project> getProjects() { 
    return project; 
}

Hibernate学完后需掌握的

原文链接:https://blog.csdn.net/wu1317581750/article/details/81662929

简述hibernate运行原理或者工作原理
简述hibernate的get和load方法区别
简述hibernate数据三种状态
简述hibernate的缓存机制
简述hibernate中getCurrentSession和openSession区别
简述hibernate的乐观锁和悲观锁
简述hibernate的懒加载机制
简述hibernate的事务机制
1、hibernate运行原理:
hibernate里面提供了3个核心接口
Configuration、SessionFactory、Session
1、hibernate启动的时候利用Configuration读取xml配置文件
2、通过配置文件创建SessionFactory对象,初始化hibernate基本信息
3、获取session然后调用CRUD方法进行数据操作,hibernate会把我们的数据进行三种状态的划分,然后根据状态进行管理我们的数据,对应的发送SQL进行数据操作
4、关闭session,如果有事务的情况下,需要手动获取事务并开启,然后事务结束后提交事务。
5、在提交事务的时候,去验证我们的快照里面的数据和缓存数据是否一致,如果不一致,发送SQL进行修改,

2、hibernate的get方法和load方法的区别
1、get和load都是利用主键策略查询数据,
2、get默认不使用懒加载机制,load默认要使用懒加载机制,所谓的懒加载就是我们这个数据如果不使用,hibernate就不发送SQL到数据库查询数据。
3、当查询数据库不存在的数据的时候,get方法返回null,load方法抛出空指针异常,
原因是因为,load方法采用的动态代理的方式实现的,我们使用load方法的时候,hibernate会创建一个该实体的代理对象,该代理只保存了该对象的ID,当我们访问该实体对象其他属性,hibernate就发送SQL查询数据封装到代理对象,然后在利用代理对象返回给我们实际的数据,

3、hibernate的数据三种状态
hibernate把他所管理的数据划分为三种状态
瞬时的(刚new出来的数据–内存有,数据库没有)
持久的 (从数据查询的,或者刚保存到数据库,session没关闭的, 数据库有,内存也有)
游离的 、脱管的(数据库有,内存没有) 在这里插入图片描述
实际上hibernate对数据划分三种状态,主要是为了管理我们持久的数据,在事务提交的时候,hibernate去对比处于持久状态的数据是否发生改变,(快照区、一级缓存区),当我们会话结束前,对持久状态数据进行了修改的话,快照区的数据会跟着改变。当session提交事务的时候,如果发现快照区和一级缓存的数据不一致,就会发送SQL进行修改。

  1. 简述hibernate的缓存机制
    hibernate分为2级缓存
    一级缓存又叫session缓存,又叫事务级缓存,生命周期从事务开始到事务结束,一级缓存是hibernate自带的,暴力使用,当我们一创建session就已有这个缓存了。数据库就会自动往缓存存放,
    二级缓存是hibernate提供的一组开放的接口方式实现的,都是通过整合第三方的缓存框架来实现的,二级缓存又叫sessionFactory的缓存,可以跨session访问。常用的EHcache、OScache,这个需要一些配置。

当我们每次 查询数据的时候,首先是到一级缓存查看是否存在该对象,如果有直接返回,如果没有就去二级缓存进行查看,如果有直接返回,如果没有在发送SQL到数据库查询数据,
当SQL发送查询回该数据的时候,hibernate会把该对象以主键为标记的形式存储到二级缓存和一级缓存,如果返回的是集合,会把集合打散然后以主键的形式存储到缓存。一级缓存和二级缓存只针对以ID查询的方式生效,get、load方法。

  1. 简述hibernate中getCurrentSession和openSession区别
    getCurrentSession和openSession都是通过H的工厂去获取数据库的会话对象,
    1、getCurrentSession会绑定当前线程,而openSession不会,因为我们把hibernate交给我们的spring来管理之后,我们是有事务配置,这个有事务的线程就会绑定当前的工厂里面的每一个session,而openSession是创建一个新session。
    2、getCurrentSession事务是有spring来控制的,而openSession需要我们手动开启和手动提交事务,
    3、getCurrentSession是不需要我们手动关闭的,因为工厂会自己管理,而openSession需要我们手动关闭。
    4、而getCurrentSession需要我们手动设置 绑定事务的机制,有三种设置方式,jdbc本地的Thread、JTA、第三种是spring提供的事务管理机制org.springframework.orm.hibernate4.SpringSessionContext,而且srping默认使用该种事务管理机制,
    并发指的是多个线程同时访问同一个资源,而并发会存在很多问题,
    同步指的是多个线程访问同一资源的时候,当一个线程对该资源的操作完成了以后,才交给下一个线程进行操作,有点像排队
    异步指的是多个线程访问同一资源的时候,所有的线程一起访问这个资源,不需要等待其他线程访问完成,也可以进行访问和操作。有点像挤公交车
    而我们hiberante在并发的时候会存在如下问题:
    1、丢失数据更新
    在这里插入图片描述
    如上图我们可以看出,线程1和线程2都对同一条数据进行更新,
    在T8时间点线程2已经完成了数据的更新,在T9时间点如果线程1回滚age=20,那线程2的更新age=25就丢失,
    另外一种,如果T9时间点 我们线程1提交,那我们线程2的age=25也丢失了
    2、数据脏读
    在这里插入图片描述
    所谓的数据脏读,就是当并发的时候,一个线程进行修改,还未提交事务的时候另一个线程就读取了更新后的数据,但是后面第一个线程回滚,更新无效,那第二个线程读取到的数据就是脏数据
    3、数据虚读或者幻读
    在这里插入图片描述
    此处的线程1修改完age之后,线程2立马进来修改了name,并提交事务,线程1在读取我们的数据,发现被修改了2个字段,这就是虚读或者幻读

4、不可重复读

在这里插入图片描述
以上问题在并发中都可能存在,所以我们hiberante一定要处理线程并发的情况,hibernate就需要进行线程同步,提供的机制是利用锁来完成,

悲观锁
所谓的悲观锁,就是hibernate心态不好,认为每一次操作都会出现并发,所以当我们查询数据的时候就直接把这一条数据锁住,不让别人操作。底层是利用的数据库的for update来实现的,就是查询数据的时候利用数据库把当前查询的数据给锁住,不让其他线程操作

Hibernate是如何延迟加载(懒加载)?
Hibernate是如何延迟加载(懒加载)?

通过设置属性lazy进行设置是否需要懒加载

当Hibernate在查询数据的时候,数据并没有存在与内存中,当程序真正对数据的操作时,对象才存在与内存中,就实现了延迟加载,他节省了服务器的内存开销,从而提高了服务器的性能。

Hibernate中怎样实现类之间的关系?(如:一对多、多对多的关系)
Hibernate中怎样实现类之间的关系?(如:一对多、多对多的关系)

它们通过配置文件中的many-to-one、one-to-many、many-to-many来实现类之间的关联关系的。

hibernate的三种状态之间如何转换
hibernate的三种状态之间如何转换

Hibernate中对象的状态:

临时/瞬时状态
持久化状态
游离状态
临时/瞬时状态
当我们直接new出来的对象就是临时/瞬时状态的…

该对象还没有被持久化【没有保存在数据库中】
不受Session的管理
在这里插入图片描述
持久化状态
当保存在数据库中的对象就是持久化状态了

当调用session的save/saveOrUpdate/get/load/list等方法的时候,对象就是持久化状态
在数据库有对应的数据
受Session的管理
当对对象属性进行更改的时候,会反映到数据库中!
在这里插入图片描述
游离状态
当Session关闭了以后,持久化的对象就变成了游离状态了…

不处于session的管理
数据库中有对应的记录

在这里插入图片描述
new出来的对象是瞬时状态->保存到数据库中(受Session管理)就是持久化状态->将session close掉就是游离状态

比较hibernate的三种检索策略优缺点
比较hibernate的三种检索策略优缺点

立即检索:

优点: 对应用程序完全透明,不管对象处于持久化状态,还是游离状态,应用程序都可以方便的从一个对象导航到与它关联的对象;
缺点: 1.select语句太多;2.可能会加载应用程序不需要访问的对象白白浪费许多内存空间;
立即检索:lazy=false;
延迟检索:

优点: 由应用程序决定需要加载哪些对象,可以避免可执行多余的select语句,以及避免加载应用程序不需要访问的对象。因此能提高检索性能,并且能节省内存空间;
缺点: 应用程序如果希望访问游离状态代理类实例,必须保证他在持久化状态时已经被初始化;
延迟加载:lazy=true;
迫切左外连接检索:

优点: 1对应用程序完全透明,不管对象处于持久化状态,还是游离状态,应用程序都可以方便地冲一个对象导航到与它关联的对象。2使用了外连接,select语句数目少;
缺点: 1 可能会加载应用程序不需要访问的对象,白白浪费许多内存空间;2复杂的数据库表连接也会影响检索性能;
预先抓取: fetch=“join”;
hibernate都支持哪些缓存策略
hibernate都支持哪些缓存策略

usage的属性有4种:

放入二级缓存的对象,只读(Read-only);
非严格的读写(Nonstrict read/write)
读写; 放入二级缓存的对象可以读、写(Read/write);
基于事务的策略(Transactional)
hibernate里面的sorted collection 和ordered collection有什么区别
hibernate里面的sorted collection 和ordered collection有什么区别

sorted collection

是在内存中通过Java比较器进行排序的
ordered collection

是在数据库中通过order by进行排序的
对于比较大的数据集,为了避免在内存中对它们进行排序而出现 Java中的OutOfMemoryError,最好使用ordered collection。

说下Hibernate的缓存机制
说下Hibernate的缓存机制

一级缓存:

Hibenate中一级缓存,也叫做session的缓存,它可以在session范围内减少数据库的访问次数! 只在session范围有效! Session关闭,一级缓存失效!
只要是持久化对象状态的,都受Session管理,也就是说,都会在Session缓存中!
Session的缓存由hibernate维护,用户不能操作缓存内容; 如果想操作缓存内容,必须通过hibernate提供的evit/clear方法操作。
二级缓存:

二级缓存是基于应用程序的缓存,所有的Session都可以使用
Hibernate提供的二级缓存有默认的实现,且是一种可插配的缓存框架!如果用户想用二级缓存,只需要在hibernate.cfg.xml中配置即可; 不想用,直接移除,不影响代码。
如果用户觉得hibernate提供的框架框架不好用,自己可以换其他的缓存框架或自己实现缓存框架都可以。
Hibernate二级缓存:存储的是常用的类

Hibernate的查询方式有几种
Hibernate的查询方式有几种

对象导航查询(objectcomposition)
HQL查询
1、 属性查询
2、 参数查询、命名参数查询
3、 关联查询
4、 分页查询
5、 统计函数
Criteria 查询
SQLQuery本地SQL查询
如何优化Hibernate?
如何优化Hibernate?

Ø 数据库设计调整
Ø HQL优化
Ø API的正确使用(如根据不同的业务类型选用不同的集合及查询API)
Ø 主配置参数(日志,查询缓存,fetch_size, batch_size等)
Ø 映射文件优化(ID生成策略,二级缓存,延迟加载,关联优化)
Ø 一级缓存的管理
Ø 针对二级缓存,还有许多特有的策略

谈谈Hibernate中inverse的作用
谈谈Hibernate中inverse的作用

inverse属性默认是false,就是说关系的两端都来维护关系。

比如Student和Teacher是多对多关系,用一个中间表TeacherStudent维护。Gp)
如果Student这边inverse=”true”, 那么关系由另一端Teacher维护,就是说当插入Student时,不会操作TeacherStudent表(中间表)。只有Teacher插入或删除时才会触发对中间表的操作。所以两边都inverse=”true”是不对的,会导致任何操作都不触发对中间表的影响;当两边都inverse=”false”或默认时,会导致在中间表中插入两次关系。
如果表之间的关联关系是“一对多”的话,那么inverse只能在“一”的一方来配置!

  1. 说说Hibernate中的update()和saveOrUpdate()的区别,session的load()和get()的区别。

saveOrUpdate()方法可以实现update()的功能,但会多些步骤,具体如下: 如果对象在该session中已经被持久化,不进行操作;对象的标识符属性(identifier property)在数据库中不存在或者是个暂时的值,调用save()方法保存它;如果session中的另一个对象有相同的标识符抛出一个异常;以上皆不符合则调用update()更新之。 Session.load/get方法均可以根据指定的实体类和id从数据库读取记录,并返回与之对应的实体对象。session的get()和load()其区别在于: 如果未能发现符合条件的记录,get方法返回null,而load方法会抛出一个ObjectNotFoundException;load方法可返回实体的代理类实例,而get方法永远直接返回实体类;load方法可以充分利用内部缓存和二级缓存中的现有数据,而get方法则仅仅在内部缓存中进行数据查找,如没有发现对应数据,将越过二级缓存,直接调用SQL完成数据读取。

hibernate中对象的三种状态

瞬时态(Transient)、 持久态(Persistent)、脱管态(Detached)。处于持久态的对象也称为PO(Persistence Object),瞬时对象和脱管对象也称为VO(Value Object)。

瞬时态

由new命令开辟内存空间的java对象,

eg. Person person = new Person(”amigo”, “女”);

如果没有变量对该对象进行引用,它将被java虚拟机回收。

瞬时对象在内存孤立存在,它是携带信息的载体,不和数据库的数据有任何关联关系,在Hibernate中,可通过session的save()或 saveOrUpdate()方法将瞬时对象与数据库相关联,并将数据对应的插入数据库中,此时该瞬时对象转变成持久化对象。

持久态

处于该状态的对象在数据库中具有对应的记录,并拥有一个持久化标识。如果是用hibernate的delete()方法,对应的持久对象就变成瞬时对象,因数据库中的对应数据已被删除,该对象不再与数据库的记录关联。

当一个session执行close()或clear()、evict()之后,持久对象变成脱管对象,此时持久对象会变成脱管对象,此时该对象虽然具有数据库识别值,但它已不在HIbernate持久层的管理之下。

持久对象具有如下特点:

  1. 和session实例关联;n

  2. 在数据库中有与之关联的记录。

脱管态

当与某持久对象关联的session被关闭后,该持久对象转变为脱管对象。当脱管对象被重新关联到session上时,并再次转变成持久对象。

脱管对象拥有数据库的识别值,可通过update()、saveOrUpdate()等方法,转变成持久对象。

脱管对象具有如下特点:

  1. 本质上与瞬时对象相同,在没有任何变量引用它时,JVM会在适当的时候将它回收;

比瞬时对象多了一个数据库记录标识值。4RESA3ES B
Detached Object(游离对象)有什么好处

Detached Object(游离对象)可以传递到任何层直到表现层而不是用任何DTO(Data Transfer Objects). 然后你还可以重新把游离对象赋给另外一个Session.

JDBC hibernate 和 ibatis 的区别

jdbc:手动 手动写sql
delete、insert、update要将对象的值一个一个取出传到sql中,不能直接传入一个对象。 select:返回的是一个resultset,要从ResultSet中一行一行、一个字段一个字段的取出,然后封装到一个对象中,不直接返回一个对象。 ibatis的特点:半自动化 sql要手动写 delete、insert、update:直接传入一个对象 select:直接返回一个对象
hibernate:全自动 不写sql,自动封装 delete、insert、update:直接传入一个对象 select:直接返回一个对象

1.在数据库中条件查询速度很慢的时候,如何优化?
1.建索引 2.减少表之间的关联 3.优化sql,尽量让sql很快定位数据,不要让sql做全表查询,应该走索引,把数据量大的表排在前面 4.简化查询字段,没用的字段不要,已经对返回结果的控制,尽量返回少量数据

2.在hibernate中进行多表查询,每个表中各取几个字段,也就是说查询出来的结果集并没有一个实体类与之对应,如何解决这个问题?
解决方案一,按照Object[]数据取出数据,然后自己组bean
解决方案二,对每个表的bean写构造函数,比如表一要查出field1,field2两个字段,那么有一个构造函数就是Bean(type1 filed1,type2 field2) ,然后在hql里面就可以直接生成这个bean了。具体怎么用请看相关文档,我说的不是很清楚。

session.load()和session.get()的区别
Session.load/get方法均可以根据指定的实体类和id从数据库读取记录,并返回与之对应的实体对象。其区别在于:

如果未能发现符合条件的记录,get方法返回null,而load方法会抛出一个ObjectNotFoundException。 Load方法可返回实体的代理类实例,而get方法永远直接返回实体类。 load方法可以充分利用内部缓存和二级缓存中的现有数据,而get方法则仅仅在内部缓存中进行数据查找,如没有发现对应数据,将越过二级缓存,直接调用SQL完成数据读取。 Session在加载实体对象时,将经过的过程:

首先,Hibernate中维持了两级缓存。第一级缓存由Session实例维护,其中保持了Session当前所有关联实体的数据,也称为内部缓存。而第二级缓存则存在于SessionFactory层次,由当前所有由本 SessionFactory构造的Session实例共享。出于性能考虑,避免无谓的数据库访问,Session在调用数据库查询功能之前,会先在缓存中进行查询。首先在第一级缓存中,通过实体类型和id进行查找,如果第一级缓存查找命中,且数据状态合法,则直接返回。 之后,Session会在当前“NonExists”记录中进行查找,如果“NonExists”记录中存在同样的查询条件,则返回null。 “NonExists”记录了当前Session实例在之前所有查询操作中,未能查询到有效数据的查询条件(相当于一个查询黑名单列表)。如此一来,如果 Session中一个无效的查询条件重复出现,即可迅速作出判断,从而获得最佳的性能表现。 对于load方法而言,如果内部缓存中未发现有效数据,则查询第二级缓存,如果第二级缓存命中,则返回。 如在缓存中未发现有效数据,则发起数据库查询操作(Select SQL),如经过查询未发现对应记录,则将此次查询的信息在“NonExists”中加以记录,并返回null。 根据映射配置和Select SQL得到的ResultSet,创建对应的数据对象。 将其数据对象纳入当前Session实体管理容器(一级缓存)。 执行Interceptor.onLoad方法(如果有对应的Interceptor)。 将数据对象纳入二级缓存。 如果数据对象实现了LifeCycle接口,则调用数据对象的onLoad方法。 返回数据对象。

Hibernate的主键生成机制
assigned 主键由外部程序负责生成,无需Hibernate参与。
hilo 通过hi/lo 算法实现的主键生成机制,需要额外的数据库表保存主键生成历史状态。
seqhilo 与hilo 类似,通过hi/lo 算法实现的主键生成机制,只是主键历史状态保存在Sequence中,适用于支持Sequence的数据库,如Oracle。
increment 主键按数值顺序递增。此方式的实现机制为在当前应用实例中维持一个变量,以保存着当前的最大值,之后每次需要生成主键的时候将此值加1作为主键。这种方式可能产生的问题是:如果当前有多个实例访问同一个数据库,那么由于各个实例各自维护主键状态,不同实例可能生成同样的主键,从而造成主键重复异常。因此,如果同一数据库有多个实例访问,此方式必须避免使用。
identity 采用数据库提供的主键生成机制。如DB2、SQL Server、MySQL中的主键生成机制。
sequence 采用数据库提供的sequence 机制生成主键。如Oralce 中的Sequence。
native 由Hibernate根据底层数据库自行判断采用identity、hilo、sequence其中一种作为主键生成方式。
uuid.hex 由Hibernate基于128 位唯一值产生算法生成16 进制数值(编码后以长度32 的字符串表示)作为主键。
uuid.string 与uuid.hex 类似,只是生成的主键未进行编码(长度16)。在某些数据库中可能出现问题(如PostgreSQL)。
foreign 使用外部表的字段作为主键。
一般而言,利用uuid.hex方式生成主键将提供最好的性能和数据库平台适应性。 这10中生成OID标识符的方法,increment 比较常用,把标识符生成的权力交给Hibernate处理.但是当同时多个Hibernate应用操作同一个数据库,甚至同一张表的时候.就推荐使用identity 依赖底层数据库实现,但是数据库必须支持自动增长,当然针对不同的数据库选择不同的方法.如果你不能确定你使用的数据库具体支持什么的情况下.可以选择用native 让Hibernate来帮选择identity,sequence,或hilo. 另外由于常用的数据库,如Oracle、DB2、SQLServer、MySql 等,都提供了易用的主键生成机制(Auto-Increase 字段或者Sequence)。我们可以在数据库提供的主键生成机制上,采用generator-class=native的主键生成方式。 不过值得注意的是,一些数据库提供的主键生成机制在效率上未必最佳,大量并发insert数据时可能会引起表之间的互锁。数据库提供的主键生成机制,往往是通过在一个内部表中保存当前主键状态(如对于自增型主键而言,此内部表中就维护着当前的最大值和递增量),之后每次插入数据会读取这个最大值,然后加上递增量作为新记录的主键,之后再把这个新的最大值更新回内部表中,这样,一次Insert操作可能导致数据库内部多次表读写操作,同时伴随的还有数据的加锁解锁操作,这对性能产生了较大影响。因此,对于并发Insert要求较高的系统,推荐采用uuid.hex 作为主键生成机制
网友自己出的几道 spring 面试题

1、 简述你对IoC(Inversion of Control)的理解,描述一下Spring中实现DI(Dependency Injection)的几种方式。
spring的IOC有三种注入方式 第一是根据属性注入 也叫set方法注入;第二种是根据构造方法进行注入;第三种是根据注解进行注入,这种方式我认为比较好,方便,要是bean多的话,使用前两种方式会使得配置文件过于臃肿。

2、 Spring的Bean有多种作用域,包括: singleton、prototype、request、session、global session、application、自定义
3、 简单描述Spring Framework与Struts的不同之处,整合Spring与Struts有哪些方法,哪种最好,为什么?
答、 Spring是完整的一站式框架,而Struts仅是MVC框架,且着重于MVC中的C。Spring有三种方式整合Struts:使用 Spring 的 ActionSupport 类整合 Struts;使用 Spring 的 DelegatingRequestProcessor 覆盖 Struts 的 RequestProcessor;将 Struts Action 管理委托给 Spring 框架,动作委托最好。(详见使用Spring 更好地处理Struts 动作)

Spring 2.0新增一种方式:AutowiringRequestProcessor。

4、 Hibernate中的update()和saveOrUpdate()的区别
答、 saveOrUpdate()方法可以实现update()的功能,但会多些步骤,具体如下:

如果对象在该session中已经被持久化,不进行操作;

对象的标识符属性(identifier property)在数据库中不存在或者是个暂时的值,调用save()方法保存它;

如果session中的另一个对象有相同的标识符抛出一个异常;

以上皆不符合则调用update()更新之。

5、 Spring对多种ORM框架提供了很好的支持,简单描述在Spring中使用Hibernate的方法,并结合事务管理。
答、 在context中定义DataSource,创建SessionFactoy,设置参数;DAO类继承HibernateDaoSupport,实现具体接口,从中获得HibernateTemplate进行具体操作。

在使用中如果遇到OpenSessionInView的问题,可以添加OpenSessionInViewFilter或OpenSessionInViewInterceptor。(详见Spring framework 2.0 Reference的12.2节Hibernate)

声明式事务需声明事务管理器,在context中设置指定属性,用确定和。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值