Hibernate知识梳理

来源:互联网 发布:ip camea网络摄像机 编辑:程序博客网 时间:2024/06/01 07:38
Hibernate:翻译成汉语意为冬眠.功能与mybatis一样都是操作数据库(增删改查)的框架.hibernate是跨数据库的框架,可以自动生成sql语句。
java中操作数据库唯一技术:JDBC技术。

Hibernate好处:
1.hibernate仅仅是对jdbc技术的封装。(原因是因为jdbc技术实现比较繁琐)
2.hibernate中对sql语句进行封装。(编写程序简单,使代码和数据库解耦合,使代码变为跨数据库)
3.效率
jdbc  >  mybatis > Hibernate
hibernate中为了解决效率的问题:新增缓存、懒加载等技术,基于这两点效率基本和mybatis差不多了。
Hibernate缺点:
1.效率还是会存在问题。(仍然没有mybatis效率高)
2.sql语句优化比较困难。

使用Hibernate:
1.导入jar包
hibernate-release-4.2.21.Final\lib\required\*.jar
2.创建表和类
类:
① 属性使用包装类型;
② 显示的写出默认的构造方法;
③ 实现一个可序列化接口。
3.创建hibernate的核心配置文件
默认名称: hibernate.cfg.xml
位置默认: classpath(src目录下)
1.配置JDBC链接属性        文档的3.3

<!--mysql JDBC链接配置 -->默认路径下省略hibernate.

    <property name="connection.driver_class">com.mysql.jdbc.Driver</property>

    <property name="connection.url">jdbc:mysql://127.0.0.1:3306/hibernate?useUnicode=true&amp;characterEncoding=UTF-8</property>

    <property name="connection.username">用户名</property>

    <property name="connection.password">密码</property>

    <!-- oracle JDBC链接信息 -->

    <property name="connection.driver_class">oracle.jdbc.OracleDriver</property>

    <property name="connection.url">jdbc:oracle:thin:@192.168.22.8:1521:ORC</property>

    <property name="connection.username">用户名</property>

    <property name="connection.password">密码</property>

2.配置可选配置            文档的3.4
方言   dialect      文档的3.4.1 

<!-- 方言(必须有) hibernate根据使用何种数据库方言生成对应的数据库sql语句 -->

    <property name="dialect">org.hibernate.dialect.MySQLDialect</property>

    <property name="dialect">org.hibernate.dialect.OracleDialect</property>

    <!--显示生成的sql语句到控制台 -->

    <property name="show_sql">true</property>

    <!--格式化sql语句没有该配置,sql语句会在一行显示-->

    <property name="format_sql">true</property>

    <!--映射文件的配置 -->

    <mapping resource="com/itany/hibernate/entity/User.hbm.xml"/>

4.创建映射文件
① 默认名称: XXX.hbm.xml
② 位置:和实体类同层
③ ORM:对象关系映射(对象和关系型数据库映射)
④ 类和表的映射
属性(getter/setter)和列的映射;
类和类的关系、表和表的关系。
主键:
type(java类型  也可以使用hibernate类型)  文档5.2
主健生成器:   文档5.1.4.1
//name类的全路径,table对应数据库中的表,dynamic-insert动态插入,默认为false,只为用户指定的列插入数据
//主键生成器generator,native表示自适应,如果是mysql数据库则自动变成identity,oracle数据库则自动变成sequence,并使用seq_user序列:
如果主键为string类型则需使用:uuid或guid(guid Oracle不支持)

<class name="com.itany.hibernate.entity.User" table="t_user" dynamic-insert="true">

    <id name="id" column="id" type="integer">

      <generator class="native">

         <param name="sequence">seq_user</param>

      </generator>

    </id>

    //类属性和表中列的映射

    <!-- 1. namecolumn相同column可以省略不写

         2. type类型无歧义的化也可省略不写,data类型有三种不可省略 -->

    <property name="username" column="username" type="string"></property>

    <property name="password" column="password" type="string"></property>

    <property name="sex" column="sex" type="character"></property>

    <property name="age" column="age" type="integer"></property>

    <property name="birthday" column="birthday" type="date"></property>

  </class>

Hibernate  api:
Configuration       读取所有的配置文件  调用configure()方法即可读取
ServiceRegistry   将读取出来的信息注册给Hibernate
SessionFactory    创建Session的工厂,创建比较复杂,极其占用资源,sessionFactory线程安全(所有的线程可以公用一个)
Session                可以理解为Conncetion(其实是一个缓存) Session线程不安全
Transaction          事物
事物是由事物管理器维护的Transaction,如果session相同,只要事物提交过,那么就算使用同一个Session获取的事物管理器也不一样。
(开启事物:session.beginTransaction();
提交事物:session.getTransaction().commit();
回滚事物:session.getTransaction().rollback();)
mysql:
t_user
create table t_user(
id int primary key auto_increment,
username varchar(100),
password varchar(100),
sex varchar(1),
age int,
birthday date
)
oracle:
create table t_user(
id number(9) primary key,
username varchar2(100),
password varchar2(100),
sex varchar2(1),
age number(3),
birthday date
)
create sequence seq_user;
单表:
新增:
保存业务的方法有两个:
session.save();
session.persist();
session.savaOrUpdate();
//根据对象的状态确定是save还是update,对象是游离态/持久态则为update;对象是临时态则是sava
前二者唯一区别:
save方法含有返回值(返回主键id的值)
persist方法没有返回值
1.保存完对象就可以直接获取主键。
2.保存clob  blob
LobHelper lobHelper = session.getLobHelper();
 保存map
3.动态的DML

<classdynamic-insert=""dynamic-update="">

4.hibernate中表中的列对应的是类中getter和setter方法
删除:(根据主键删除)
session.delete(Object obj)
delete方法里面需要跟上游离态对象。
在Hibernate中对象状态:
临时态(瞬态):内存中存在   数据库中不存在   没有被session操作过(session缓存中没有)
游离态 (托管):内存中存在   数据库中存在一条与之对应的记录  没有被session操作过(有setId则为该态)
持久态:内存中存在   数据库中存在    session操作过(session缓存中存在)
--删除态:
修改:
1. update(Object obj)
2. saveOrUpdate(obj)
3. 使用hql语句调用query的excuteUpdate();
修改持久态对象:
直接调用update方法
ps:如果对象中含有clob或blob属性,需要在update之前进行刷新缓存
session.refresh(user, LockOptions.UPGRADE);
动态的DML  update:只对修改持久态对象有效果
修改游离态对象:
如果其他的属性没有赋值,update将null给更新到数据库
ps:所以更新一般采用先查询再更新
saveOrUpdate:根据对象的状态确定:
对象为游离态、持久态-->update
对象为临时态  --->save
查询:
根据主键查询:
1.查询所有的两种方法
session.get()
session.load()

二者区别: 

1.get方法是立即查询数据库,将数据封装到你的entity实例中.

 load方式懒加载模式(代理模式),返回只是一个代理的实例,只有第一次使用的时候才会进行查询(在缓存session中查询)。
2.如果查询不存在的记录:
get方法返回为null。
load方法返回还是一个代理的对象,但是第一次使用抛出org.hibernate.ObjectNotFoundException
2.使用hql语句查询
query.list(); //返回list集合
query.uniqueResult();//只能查一条记录
3.分页查询
query.setFirstResult()
query.setMaxResults()
查询所有记录:
使用HQL:
1.使用HQL语句更好的实现代码跨数据库功能.
2.更好显示出面向对象。
sql语句:select * from t_user u;
hql语句:from User u;(查所有select*要省略)
Query:
query.list(); //返回list集合
query.uniqueResult();
1.必须保证hql语句查询查来只有一条记录。
2.如果有多条记录:抛出org.hibernate.NonUniqueResultException
条件查询:
HQL
select * from t_user u where u.username='aaa' and u.hobbies like %唱歌%;
from User u where u.username='aaa' and u.hob like %唱歌%;
可以使用where子语句
分页查询:
query.setFirstResult()
query.setMaxResults()
   创建hql语句--->创建Query对象(session.createQuery(hql))--->调用list()或uniqueResulet()方法.
HQL:
select * from t_user u;
select u from User u;
select username,password,hobbies from t_user;
1.select u.username,u.password,u.hob from User u; -->返回的为Object[]
2.select new User(u.username,u.password,u.hob) from User u; -->返回就是User对象   ps:User中必须含有一个与之对应的构造方法 
3.select new Map(u.username as username,u.password as password,u.hob as hob) from User u; -->返回的就是Map对象
ps:hql语句中没有insert操作.
只能执行
update/delete    直接调用query.executeUpdate();
select
模拟懒加载:

class com.itany.hibernate.entity.User_$$_jvste00_0extends User{

booleanisQueryDb= false;

public Integer getId(){

if(!isQueryDb){

//查询数据库

User user = session.get(User.class,Id);

this.id=user.getId();

this.username=user.getUsername();

...

this.isQueryDb=true;

}

returnthis.id;

}

public String getUsername(){

if(!isQueryDb){

//查询数据库

User user = session.get(User.class,Id);

this.id=user.getId();

this.username=user.getUsername();

...

this.isQueryDb=true;

}

returnthis.userName;

}

....

}

多对一关系      员工和部门    产品和公司...
一对多关系      部门和员工    产品类型产品....
多对多关系      权限系统       选课...
一对一关系      婚姻关系
继承关系        is a
组件关系        has a 
多对一:
t_product           t_company
id                       id
name                 name
company_id       FK
PS:外键在哪张表,哪边就是多的一方;维护关系默认给一的那方
Product                Company
id                     id
name               name
company
主配置文件可选配置:(主配置文件的信息,只要SessionFactory被创建便立即执行,底层调用SchemaExport类的create(true,true)方法)
hbm2ddl.auto  (由映射文件自动创建表)
none      默认,不会自动生成
create    每次运行都会新建 
update    如果表存在了就不会新建了,如果表不存在就会新建
建表的工具类:

publicclass CreateOrUpdateTable {

publicstaticvoid main(String[] args) {

//获取配置信息

Configuration cfg = new Configuration();

cfg.configure();

//相当在核心配置文件中添加hbm2ddl.auto=update

//SchemaUpdate schemaUpdate = newSchemaUpdate(cfg);

//schemaUpdate.execute(true,true);

//相当在核心配置文件中添加hbm2ddl.auto=create

SchemaExport export = new SchemaExport(cfg);

export.create(true,true);

}

}

配置:

<many-to-onename="类属性"column="表列,id"class="属性所在的类名"></many-to-one>

1.保存时候   按照逻辑保存(先保存一,再保存多)
2.级联操作   添加cascade属性

<many-to-onecascade="">

none                不级联
save-update    在保存或更新时候级联
delete              删除时候级联
all                    在保存或者更新或删除都级联
delete-orphan  删除时候级联删除孤儿  
问题:
查询出product后要得到company的name,此时Hibernate采用的是懒加载模式(不会立即查询companyName,使用到时再去查询,此时有可能no Session)

解决方法(两种):
(1) 在<many-to-one lazy="false">中禁用懒加载模式即lazy="false",此时对象中的关系字段会被立即加载,查询出companyName(但此时会有两条sql语句,采用的是单表查询),另外,lazy="extra"增强,就是count技术。
(2) 要用一条sql语句解决,则采用联表查询,在<many-to-one fetch="join">(迫切左外连接),对象中的关键字段会立即查询(只对根据主键查询有效)。
抓取(迫切左外链接):
<many-to-one fetch="join"/>
对象中关系字段会立即加载(采用的是联表查询)
(3)如果使用hql语句查询:
配置lazy=false  采用即时加载
配置fetch只能对根据主键查询有效.如果想使用  那么就需要在HQL中添加join fetch
如:hql语句:from Product p join fetch p.company c join fetch p.apply a;(三表查询)
一对多:
t_apply_o2m              t_product_o2m
id                                 id
applyNm                     name
product_id 

Apply                        Product
id                              id
applyNm                   name 
product                     Set<Apply> applys(关系多的一方使用set集合(无序,唯一)较为简单)

配置:

<setname="applys">

<keycolumn="product_id"></key>

<one-to-manyclass="Apply"/>

</set>

在onetomany和manytoone的双向关系:
hibernate默认维护关系交给一的一方。
如果想使用多的一方维护关系  可以在<set inverse="true">将控制权反转交出(此时SQL语句没有重新update)
查询:
在onetomany和manytoone的双向关系:
查询所有会出现重复数据。
hibernate3.2之前   只能通过代码排重。
hibernate3.2版本之后  添加select  distinct

多对多的关系:(通过第三张表联系起来,该表采用联合主键,两个id不一致便可入库)
Person                    Permission
id                             id
name                       name
Set<Permission>     Set<Person>     (类设计)

t_person           t_permission           t_person_permission      (表设计)  
id                      id                          personId
name                name                    permissionId (联合主键)
****关系配置:(多对多关系中必须有一方要放弃维护权.invers="true")

<!-- 关系多对多 Permission.hbm.xml-->

<setname="persons"table="t_person_permission"lazy="false">

<keycolumn="permission_id"></key>

<many-to-manyclass="Person" column="person_id"></many-to-many>

</set>

<!-- Person.hbm.xml -->

<setname="permissions" table="t_person_permission"inverse="true">

<keycolumn="person_id"></key>

<many-to-manyclass="Permission" column="permission_id"></many-to-many>

</set>

set端    batch-size   批量查询(默认单表查询)

<propertyname="name">

<columnname="name"sql-type="varchar(55)"></column>   指定见表时该字段类型和长度 (length="55"直接指定长度)

</property>

一对一:(基于外键和基于主键两种表设计方案,基于外键便于扩展,较常用)
(1)基于外键:
t_computer_1            t_cpu_1
id                              id 
name                        name
computer_id             FK  UK

<!-- 一对一配置  Computer.hbm.xml-->

<one-to-onename="cpu" property-ref="computer"class="CPU"></one-to-one>

<!-- 一对一  CPU.hbm.xml-->

<many-to-onename="computer" column="computer_id"class="Computer" unique="true"></many-to-one>

(2)基于主键: 
t_computer_2            t_cpu_2
id                               id             FK

name                         name

Computer.hbm.xml中:<one-to-onename="cpu" class="CPU"></one-to-one>
CPU.hbm.xml中:
<one-to-onename="computer"class="Computer" constrained="true"></one-to-one>
一对一类设计:
Computer             CPU
id                          id
name                    name
cpu                       computer

组件:(人和订单中同时有地址属性使用组件方式)
t_person    
id          
name        
city
provice

<!--组件关系配置 -->

<componentname="address" class="Address">

<propertyname="city"></property>

<propertyname="province"></property>

</component>

继承关系:(三种设计方案,单表方式最常用)
User (父类)
  id
  username
  password
Manger (子类)        Member (子类)               
   phone                  email
****(1)单表: (采用单表查询效率高,但表中始终有一个字段为空,浪费内存空间.查询时会自动添加status条件)
extends_user_1
id
username
password
phone
email
status(类中不存在该属性,表中由hibernate自动加入,通常用不同数字代替member和manager)
该方式只需有一个映射文件(父类,子类配置在父类中,如下:)

<classname="User"table="extends_user">

<idname="id"><generatorclass="native"></generator></id>

<!-- 区分字段配置,类型默认为string,该配置必须在id配置后 -->

<discriminatorcolumn="status" type="string"></discriminator>

<propertyname="username"></property>

<propertyname="password"></property>

<!-- 区分字段值配置:当为Member是区分字段status值为1,属性为email -->

<subclassname="Member" discriminator-value="1">

<propertyname="email"></property>

</subclass>

<subclassname="Manager"discriminator-value="2">

<propertyname="phone"></property>

</subclass>

</class>

(2)每个子类一张表:(相当于两张单表)
extends_manager_2                extends_member_2
id                                              id
username                                 username
phone                                       password
password                                  email
(3)父类和子类每个类一张表:
extends_user_3
id
username
password
extends_manager_3          extends_member_3
phoneemail
user_id FK                          user_id  FK

<classname="User"table="extends_user_3">(user表)

<idname="id">

<generatorclass="native"></generator>

</id>

<propertyname="username"></property>

<propertyname="password"></property>

<joined-subclassname="Member" table="extends_member_3">(member表)

<keycolumn="user_id"></key>   (引用父类主键)

<propertyname="email"></property>

</joined-subclass>

<joined-subclassname="Manager" table="extends_manager_3">(manager表)

<keycolumn="user_id"></key>    (引用父类主键)

<propertyname="phone"></property>

</joined-subclass>

</class>

QBC:
Query  By  Criteria 条件查询。
创建:
Criteria criteria= session.createCriteria(User.class);
//单一条件添加
criteria.add(Restrictions.eq("username", user.getUsername()));
组合类型的条件:
criteria.add(Example.create(user));

criteria里面的方法可以模仿query对象使用.

使用sql语句:
SqlQuery sqlQuery = session.createSqlQuery(sql);
转换返回值为Hibernate管理的实体:
sqlQuery.addEntity(Entity.class);
转换返回值不为Hibernate管理的实体:
sqlQuery.setResultTransformer(Transformers.aliasToBean(UserVo.class));
注解:  将类与表,属性与列映射关系有.xml文件移到实体类中配置. Hibernate 3.3版本前不支持
类级别:
@Entity    表示该类为Hibernate管理的实体
@Table(name="表名")     表示类映射表
对于属性配置建议在getter方法上定义:
@Id
对于主键如果为oracle:

@Id

@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="aaa")

@SequenceGenerator(name="aaa", sequenceName="seq_user")

使用Hibernate自带的生成器:

@Id@GeneratedValue(generator="aaa")@GenericGenerator(name = "aaa", strategy = "native", parameters = { @Parameter(name = "sequence", value = "seq_user") })

如果是MySQL:@Id@GeneratedValue (默认主键生成器为native)

属性配置:属性与数据库列名称一致可省略不写,hibernate默认添加@Basic@Column(name="列名")  表示属性和列的对应关系
@Basic  默认给每个getter方法上添加如果想使@Basic注解无效可以添加@Transient(忽略该getter方法注解)
@Temporal(TemporalType.DATE)表示日期的类型

****多对一:注解方式只有多对一Hibernate是默认连表查询(fetch="join")
@ManyToOne      //在类中配置
@JoinColumn(name="一所在方的id")   //表与表的连接通过列实现
级联(@ManyToOne(cascade=CascadeType.ALL))
抓取 (@ManyToOne(fetch=FetchType.EAGER))     == fetch="join"
@ManyToOne(fetch=FetchType.LAZY)    == fetch="select"
一对多:
@OneToMany    //在类中配置,(mappedBy="放弃维护权的一方",fetch=fetchType.EAGER)
@JoinColumn(name = "一的一方id")
****   mappedBy 和@JoinColumn, @JoinTable互斥(不能同时存在)
多对多:(需要有一方放弃维护权)
@ManyToMany(fetch=FetchType.EAGER)
@JoinTable(name = "t_person_permission", 
  joinColumns = { @JoinColumn(name="permission_id") },
  inverseJoinColumns={@JoinColumn(name="person_id")})
一对一:
      *****使用外键方式:

   一面注解如下:

@OneToOne

         @PrimaryKeyJoinColumn   (采用主键连接,不生成外键)
   另一面注解:
@ManyToOne
@JoinColumn(name="computer_id",unique=true)
使用主键方式,使用注解不可添加外键。
组件:
类组件类添加
@Embeddable(表示该类是一个可嵌入的类)
@Embedded 表示嵌入属性
属性覆盖(同时在两张表中引用属性,使列名称显示不一致需用属性覆盖)
@AttributeOverrides(value = { @AttributeOverride(name = "city", column = @Column(name="orderCity")) ,
@AttributeOverride(name = "province", column = @Column(name="orderProvince"))})
继承关系:(父类中需配置:类、表、继承策略、区分字段)
(1)单表方式:
@Inheritance(strategy=Inheritance.SINGLE_TABLE)  配置继承策略
@DiscriminatorColumn(name="区分字段名")  配置区分字段,在父类中配置
@DiscriminatorValue(Value="2")  区分字段的值,在子类中配置
(2)每个子类一张表方式:
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)  配置继承策略
@MappedSuperclass  映射父类 (此两项均在父类中配置)
(3)每个类一张表:
@Inheritance(strategy=InheritanceType.JOINED)  配置继承策略
该方式子类在父类中引用的id默认名为id若要修改则在子类中配置:@PrimaryKeyJoinColumn(name="user_id")
数据源:
1. 添加jar包
hibernate-release-4.2.21.Final\lib\optional\c3p0\*.jar
2. 在核心配置文件中添加

<!-- 数据源配置 -->

<propertyname="c3p0.max_size">5</property>

<propertyname="c3p0.min_size">2</property>

<propertyname="c3p0.timeout">5000</property>

二级缓存:
一级缓存:(不可以关闭,只要使用Hibernate就存在)
Session级别
session.clear(); 清除所有缓存数据
session.evict(user);  清除缓存中的user对象
二级缓存:(可以关闭,可以打开) 默认关闭
SessionFactory级别
可以放入缓存的对象:
1.不会经常发生变化的数据.
2.比较不重要的数据。
1.导入jar包
hibernate-release-4.2.21.Final\lib\optional\ehcache\*.jar
2.配置文件hibernate.cfg.xml

<!-- 二级缓存 -->

<propertyname="cache.use_second_level_cache">true</property>

<!-- 缓存提供者 -->

<propertyname="cache.region.factory_class">org.hibernate.cache.ehcache.EhCacheRegionFactory</property>

3.配置ehcache缓存配置文件.
maxElementsInMemory   缓存的最大记录数(0 没有限制)
eternal                              表示缓存的对象是否永远存活
timeToIdleSeconds           表示空闲清除时长(秒)
timeToLiveSeconds          表示最大存活时长(秒)
overflowToDisk                 缓存满了是否溢出到硬盘
maxElementsOnDisk        溢出到硬盘最大多少记录数
memoryStoreEvictionPolicy  缓存硬盘全满了对象时长还没有达到,配置移除策略
 FIFO   先进先出
 LRU    最远使用删除 (默认)
 LFU    最少使用频率删除
4.配置需要放入缓存的对象.
三种配置方式:
1.在核心配置文件hibernate.cfg.xml中配置:
<class-cache usage="read-write" class="com.itany.hibernate.entity.User"/>
2.可以在映射文件中配置:
在class标签中<cache usage="read-write"/>
3.还可以在注解中配置:
在要放入缓存的类上添加 @Cache(usage=CacheConcurrencyStrategy.READ_WRITE)
注意:clob、blob不可以使用二级缓存。
原创粉丝点击