Hibernate总结(基于注解)
开始先对总结分个类。一共包括这几个方面。
1.获得工厂方法
2.操作数据库
3.主键生成策略
4.继承
5.关系映射
6.缓存
1.获得工厂的方法
//1加载配置
Configuration conf =new Configuration().configure();
//2 根据Configuration 配置信息创建 SessionFactory
SessionFactory sf = conf.buildSessionFactory();
还可以用5的新特性
SessionFactory sessionFactory=null;
final StandardServiceRegistry registry = new StandardServiceRegistryBuilder()
.configure().build();
sessionFactory = new MetadataSources(registry).buildMetadata()
.buildSessionFactory();
2.操作数据库
有多种方法可以操作,我介绍其中两种
第一:session操作数据库
基于Session的操作,session拥有一个链接,并对数据库经行操作。这个操作能力很有限,如果要定制可以用下面第二种方法完成
查询:
get和load方式是根据id取得一个记录
如果你使用load方法,hibernate认为该id对应的对象(数据库记录)在数据库中是一定存在的,所以它可以放心的使用,它可以放心的使用代理来延迟加载该对象。在用到对象中的其他属性数据时才查询数据库,但是万一数据库中不存在该记录,那没办法,只能抛异常。所说的load方法抛异常是指在使用该对象的数据时,数据库中不存在该数据时抛异常,而不是在创建这个对象时(注意:这就是由于“延迟加载”在作怪)。所以如果你知道该id在数据库中一定有对应记录存在就可以使用load方法来实现延迟加载。
session缓存中找到了该id对应的对象,如果刚好该对象前面是被代理过的,如被load方法使用过,或者被其他关联对象延迟加载过,那么返回的还是原先的代理对象,而不是实体类对象,如果该代理对象还没有加载实体数据(就是id以外的其他属性数据),那么它会查询二级缓存或者数据库来加载数据,但是返回的还是代理对象,只不过已经加载了实体数据。get方法首先查询session缓存,没有的话查询二级缓存,最后查询数据库;反而load方法创建时首先查询session缓存,没有就创建代理,实际使用数据时才查询二级缓存和数据库。
更新:
update()
1、用来更新detached对象,更新完成后转为persistent状态。
2、更新transient对象会报错
3、更新自己设定的唯一标识符(例如:Id)的transient对象且数据库有对应记录的可以update。
4、处于persistent状态的对象,对这个对象的属性内容进行更改后当commit时会自动触发Session
update方法。有一点需要注意的是如果更改前后的内容完成一样,则不会触发update语句。只有对象在缓存中的内容和数据库的记录不一样时,才会触发update语句进行更新。
删除:
delete()
delete()顾名思义删除,用于从数据库中删除java对象对应的记录。
delete()如果传入持久化对象,组装delete语句,执行删除;如果传入游离态对象,hibernate先把游离态关联到session,变成持久态,再生成delete语句,
执行删除。
都是只有当session缓存清空时,才执行。
以上执行都是一个对象,对应一条记录。
可以用session.delete("from Customer where ....");后面加上条件删除多条数据。
状态介绍:
说到session方法了不妨介绍一下状态
瞬时状态就是刚new出来一个对象,还没有被保存到数据库中,持久化状态就是已经被保存到数据库中,离线状态就是数据库中有
如果一个对象以及是持久化状态了,那么此时对该对象进行各种修改,或者调用多次update、save方法时,hibernate都不会发送sql语句,只有当事物提交的时候,此时hibernate才会拿当前这个对象与之前保存在session中的持久化对象进行比较,如果不相同就发送一条update的sql语句,否则就不会发送update语句
当session调用load、get方法时,此时如果数据库中有该对象,则该对象也变成了一个持久化对象,被session所托管。因此,这个时候如果对对象进行操作,在提交事务时同样会去与session中的持久化对象进行比较,因此这里会发送两条sql语句
然后调用session.clear()方法,这个时候就会将session的缓存对象清空,那么session中就没有了这个对象,这个时候在提交事务的时候,发现已经session中已经没有该对象了,所以就不会进行任何操作
对于离线对象,如果要使其变成持久化对象的话,我们不能使用save方法,而应该使用update方法
saveOrUpdate这个方法,这个方法其实是一个"偷懒"的方法,如果对象是一个离线对象,那么在执行这个方法后,其实是调用了update方法,如果对象是一个瞬时对象,则会调用save方法,记住:如果对象设置了ID值,例如u.setId(4),那么该对象会被假设当作一个离线对象,此时就会执行update操作。
总结:持久化状态对象update()或者commit()保存时,会和先前Session中保存的持久化对象比较,不同就会更新。删除了Session后再对对象经行操作都是无效的不会改变数据库数据。seesion中不能同时拥有两个持续化对象个,不能在已有id=x的情况下,再save()一个id=x的持续化对象。
第二,HQL语句操作数据库
执行HQL语句的步骤:
(1)、获取Session对象
(2)、编写HQL语句
(3)、创建Query
session.createQuery(hql);
(4)、执行查询,得到查询结果。
1.使用预处理Sql
好处防注入,增加效率
SQLQuery query = session.createSQLQuery("select * from note where id = ?");//设置第一个参数的值为12,即查询ID=12的notequery.setParameter(0,12);
需要注意的是就是,下标是从0开始,和原生是从1开始
2.转化器
Transformers,它提供了一些常用的转换器,能够帮助我们快速转换结果集,如Transformers.aliasToBean(Note.class)能够将查询结果依别名注入到Note实体中。
3.实体查询
把查询的数据直接映射要一个Bean上
session.createSQLQuery("selectid,note,createtime,authorfromnotewhereid = ?").addEntity(Note.class);
author字段即为Note实体和Author实体的关联字段,只需在查询时得到该字段的值,Hibernate即可使用该值找到对应的关联实体。如上例中,note.getAuthor()即可返回当前Note所属的Author对象。
4.query对象的一些实用接口
list();返回查询结果,并把查询结果转换成list对象;
executeUpdate();执行更新和删除语句
3.主键生成策略
1.jpa通用策略生成器
Jpa提供了4种标准法
TABLE,SEQUENCE,IDENTITY,AUTO
介绍常用的。SEQUENCE:根据数据库的序列来生成主键,条件是数据库支持序列
IDENTITY:主键由数据库自动生成(自动递增)
用法:直接写出GeneratedValue 和其属性strategy就可以了
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
2.GenericGenerator hibernate主键策略生成器
简单介绍几个:
native: 对于 oracle 采用 Sequence 方式,对于MySQL 和 SQL Server 采用identity(自增主键生成机制),native就是将主键的生成工作交由数据库完成,hibernate不管(很常用)。
uuid: 采用128位的uuid算法生成主键,uuid被编码为一个32位16进制数字的字符串。占用空间大(字符串类型)。
identity: 使用SQL Server 和 MySQL 的自增字段,这个方法不能放到 Oracle 中,Oracle 不支持自增字段,要设定sequence(MySQL 和 SQL Server 中很常用); 等同于JPA中的INDENTITY。
ncrement: 插入数据的时候hibernate会给主键添加一个自增的主键,但是一个hibernate实例就维护一个计数器,所以在多个实例运行的时候不能使用这个方法。
具体了解:Hibernate各种主键生成策略与配置详解 - starskyhu - 博客园
用法:@GenericGenerator注解配合@GeneratedValue一起使用,@GeneratedValue注解中的"generator"属性要与@GenericGenerator注解中name属性一致,strategy属性表示hibernate的主键生成策略
@Id
@GeneratedValue(generator="increment")
@GenericGenerator(name="increment", strategy = "increment")
4.继承
hibernate应用中,继承的用途或目的主要有两点:
组件化:故明思义,把重复性的代码抽取成组件,以便重用和维护。hibernate应用中,一些重复的字段,重复的映射配置,就需要抽取成组件。
多态性:类的多态性是指下层业务所需一个父类对象,而上层业务根据所需的父类对象,传递一个子类对象。hibernate应用中,下层业务操作父类对象进行持久操作,如增删改查,上层业务则传递一个子类对象。
所以,在应用hibernate的继承时,需要明确设计所需,即究竟是组件化需求,还是多态性需求。
@MappedSuperclass:组件化需求的继承注解。虽然它可以应用于类的多态性业务中,但它不能应用于hibernate持久操作的多态性业务中。
@Inheritance:多态性需求的继承注解。虽然它可以达到组件化的目的,但它要比@MappedSuperclass多负出一些代价。
@Inheritance的默认继承策略为SINGLE_TABLE,三种继承策略的区别在于:
SINGLE_TABLE:公共属性公共表,独立属性公共表。
需要使用监别器区分具体的子类,注解@DiscriminatorColumn设置监别器列,注解@DiscriminatorValue设置监别器值。
子类的属性映射配置时,需要设置为允许为空或默认值。
JOINED:公共属性公共表,独立属性独立表。
子类的独立表生成后,其主键是一个共享主键,意味着这是一对一的关联,默认名称与父类的主键一致,使用注解@PrimaryKeyJoinColumn可改变名称。
TABLE_PER_CLASS:公共属性独立表,独立属性独立表。
主键生成策略不能使用GenerationType.IDENTITY。
1.SINGLE_TABLE
是将父类和其所有的子类集合在一块,存在一张表中,并创建一个新的字段来判断对象的类型。
有两个注释要写
@DiscriminatorColumn在父类书写,它有两个属性,第一个是name,即区分子类的列名,discriminatorType 是指用什么方式区别(其实就是区分的数据类型),它可填DiscriminatorType.CHAR,DiscriminatorType.INTEGER,DiscriminatorType.STRING
@DiscriminatorValue (*)来修饰每个类 ,*号内就可以填一些可以区分该类的数据(该数据的类型由DiscriminatorType决定)
例:@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name="person_type", discriminatorType=DiscriminatorType.STRING)
@DiscriminatorValue("普通人")
@DiscriminatorValue("顾客")
2.JOINED
是将父类、子类分别存放在不同的表中,并且建立相应的外键,以确定相互之间的关系。
3.TABLE_PER_CLASS
是为每一个类创建一个表,这些表是相互独立的。
后两种用法就是@Inheritance(strategy = InheritanceType.***)
5.关系映射
一对一时:如果两张表是以主键关联的,比如Person表主键是id,Address表主键是address_id,则运用如下注释:
@OneToOne(cascade={CascadeType.ALL})
外键链接@JoinColumn(name="setid")
@PrimaryKeyJoinColumn(name = "id", referencedColumnName="address_id")//referencedColumnName是引用
主键链接@PrimaryKeyJoinColumn(name = "id")
在一对多单向关系中,多的一方(Person)没有注解,一的一方(Country)有注解,如果一的一方不加@JoinColumn指定外键字段的话,Hibernate会自动生成一张中间表Country_PERSON来对Person和Country进行绑定。放一个set集合用来装Many对象(注解也是在get set上书写)。@OneToMany描述一个一对多的关联,该属性应该为集体类型,在数据库中并没有实际字段.
@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name="setid")
多对一单向,相反。多的一方放单个对象即可(注解也是写在get 对象上)。
ManyToOne(cascade= {CascadeType.REFRESH,CascadeType.PERSIST})
@JoinColumn(name="jobid")
一对多双向,两边都放外键,都拥有彼此信息。其实就是一的一方加onetomany,多的一方manytoone
@OneToMany(cascade=CascadeType.ALL)
@JoinColumn(name="setid")
ManyToOne(cascade= {CascadeType.REFRESH,CascadeType.PERSIST})
@JoinColumn(name="jobid")
一般多对多的关系都是用中间表来维护的,中间创一个表,放入两方的关系,外键指向关系表
@ManyToMany//多对多外键关联的配置
@JoinTable(name="teachers_students",//中间表的表名
joinColumns={@JoinColumn(name="sid")},//本表的主键
inverseJoinColumns={@JoinColumn(name="tid")})//所映射表的主键
属性介绍
https://www.cnblogs.com/whgk/p/6135591.html 属性inverse和cascade及其关系
1.cascade
该属性定义类和类之间的级联关系。定义的级联关系将被容器视为对当前类对象及其关联类对象采取相同的操作,而且这种关系是递归调用的。
cascade的值只能从CascadeType.PERSIST(级联新建)、CascadeType.REMOVE(级联删除)、CascadeType.REFRESH(级联刷新)、CascadeType.MERGE(级联更新)中选择一个或多个。还有一个选择是使用CascadeType.ALL,表示选择全部四项。
2.fatch
可选择项包括:FetchType.EAGER和FetchType.LAZY。前者表示关系类在主类加载的时候同时加载,后者表示关系类在被访问时才加载。默认值是FetchType.LAZY。
3.targetEntity
放入目标的对象即可
4.@joincolumn(外键)
外键永远指向多的一方,在ManyToMany时指向中间表
6.缓存
Hibernate中提供了两级缓存,一级缓存是Session级别的缓存,它属于事务范围的缓存,该级缓存由hibernate管理,应用程序无需干预;二级缓存是SessionFactory级别的缓存,该级缓存可以进行配置和更改,并且可以动态加载和卸载,hibernate还为查询结果提供了一个查询缓存,它依赖于二级缓存;
缓存的概念
缓存是位于应用程序和永久性数据存储源之间用于临时存放复制数据的内存区域,缓存可以降低应用程序之间读写永久性数据存储源的次数,从而提高应用程序的运行性能;
hibernate在查询数据时,首先会到缓存中查找,如果找到就直接使用,找不到时才从永久性数据存储源中检索,因此,把频繁使用的数据加载到缓存中,可以减少应用程序对永久性数据存储源的访问,使应用程序的运行性能得以提升;
缓存的范围
缓存范围决定了缓存的生命周期,缓存范围分为3类:
1>事务范围
缓存只能被当前事务访问,缓存的生命周期依赖于事务的生命周期,事务结束时,缓存的生命周期也结束了;
2>进程范围
缓存被进程内的所有事务共享,这些事务会并发访问缓存,需要对缓存采用必要的事务隔离机制,缓存的生命周期取决与进程的生命周期,进程结束,缓存的生命周期也结束了;
3>集群范围
缓存被一个或多个计算机的进程共享,缓存中的数据被复制到集群中的每个进行节点,进程间通过远程通信来保证缓存中数据的一致性;
在查询时,如果在事务范围内的缓存中没有找到,可以到进程范围或集群范围的缓存中查找,如果还没找到,则到数据库中查询;
Hibernate中的第一级缓存
Hibernate的一级缓存由Session提供,只存在于Session的生命周期中,当应用程序调用Session接口的save(),update(),saveOrupDate(),get(),load()或者Query和Criteria实例的list(),iterate()等方法时,如果Session缓存中没有相应的对象,hibernate就会把对象加入到一级缓存中,当session关闭时,该Session所管理的一级缓存也会立即被清除;
Hibernate中的第二级缓存
二级缓存是一个可插拔的缓存插件,它是由SessionFactory负责管理的;
由于SessionFactory对象的生命周期与应用程序的整个过程对应,通常一个应用程序对应一个SessionFactory,因此,二级缓存是进程范围或者集群范围的缓存;
与一级缓存一样,二级缓存也是根据对象的id来加载与缓存,当执行某个查询获得结果集为实体对象集时,hibernate就会把它们按照对象id加载到二级缓存中,在访问指定的id的对象时,首先从一级缓存中查找,找到就直接使用,找不到则转到二级缓存中查找(必须配置且启用二级缓存),如果二级缓存中找到,则直接使用,否则会查询数据库,并将查询结果根据对象的id放到缓存中;
总结
1.hibernate 总共有三种形式的缓存,分为二级:一级session级别的缓存,二级sessionfactory级别的缓存。二级缓存有两种形式,一是外部插件(EHCache),二是查询缓存,查询缓存基于外部插件存在,要使用查询缓存,必须先配置好二级缓存。
2.一级缓存不会缓存普通属性,只会缓存整个对象,所以查询缓存可以进行补救,专门用于缓存查询普通属性的结果集,而用查询缓存来保存整个对象时,就只会保存Id
3.一级缓存和二级缓存都是根据Id来加载于缓存的
4.查询缓存大多数是用来缓存实体属性的结果集的,如果查询结果为实体对象的集合时,查询缓存里就只有实体对象的Id
5.当访问指定id的对象时,查找顺序是:一级缓存 ---> 查询缓存(实体Id) ---> 外部缓存(实体数据)--->数据库