标签:
Hibernate
1.orm和hibernate
orm (object relation mapping)对象关系数据库映射 ,是面向对象编程和数据库的桥梁.当我们采用ORM框架以后,应用程序不在直接访问底层数据库,而是以面向对象的方式操作持久化对象(PO),而orm将面向对象的操作转化成Sql操作
orm的映射关系:: 数据表映射类,即持久化类被映射到表中
数据表行映射对象(实例)即表的每一行是一个实例
数据表的列对应类的属性
目前流行的orm框架: JPA:官方标准
Hibernate :最流行的
iBatis 不是纯粹的面向对象,但更加灵活,允许使用sql语句
TopLink
2.
为了能够操作持久化对象,必须给持久化对象类添加映射文件xxx.hbm.xml
<hibernate-mapping package=” cn.my.domain”>
<class name=” 类名” table=” 对应的表名”>
<id name=”id”>
<generator class=”identity” /> //配置主键生成策略
</id>
//配置一般属性
<property name=”title” />
<property name= “telephone” />
</class>
</hibernate-mapping>
3,有了映射文件还不行,还要配置连接的数据库是哪一个,数据库的相关信息,需要Hibernate配置文件 默认名为 hibernate.cfg.xml
<hibernate-configuration>
<session-factory>
//数据库信息 ,数据库方言 ,自动创建表,数据库连接池
<property name=””></property>
//罗列映射文件
<mapping resource=”cn.my.domain.xxx.hbm.xml”>
</session-factory>
</hibernate-configuration>
4.持久化操作依赖的关键对象
configuration
sessionFactory
Session
Transaction
往数据库的插入操作
Configuration conf=new Configuration().configure();
SessionFactory sf=conf.buildSessionFactory();
Session se=sf.openSession();
Transaction tx==se.beginTransaction();
Person p=new Person();
p.setName(“tom”);
se.save(p);
tx.commit();
se.close();
sf.close();
对象的三种状态: 瞬时态:没有和session关联过的状态
持久态 和session关联起来并对应到数据库中的状态
脱管态 曾经和session关联过,但session关闭,脱离了session的管理
4.数据库方言
虽然所有关系型数据库都遵循标准SQL,但不同数据库有差异的,数据库方言就是告诉hibernate底层使用哪一种数据库
5.深入理解持久化对象
将瞬时态转化成持久态:
save() load() get()
Person p=session.load( Person.class,new Integer(pk));//通过标识属性加载持久化实例,延迟加载 如果使用了延迟加载,那么在调用load方法时返回的是一个代理对象,此时并不会访问数据库,当代理对象的方法被调用的时候,才去加载数据库.
如果希望在某个对象里面创建另外一个对象的关联,而从数据库中加载这个对象时,不想让其关联的对象也加载,就可以使用延迟加载
get()方法也是根据标识属性装载持久化对象,他是立即装载
对持久化对象做的修改会自动更新到数据库,可以不执行update()方法
因此修改对象的最简单方法是get()或者load()后直接修改即可
脱管状态的对象要修改 需要修改完成后执行 update() updateOrSave() 或者merge()方法来保存到数据库 ,这样脱管状态又回到了持久态
updateOrSave()方法会自动判断对象是脱管态还是瞬时态,如果是脱管态执行update,如果是瞬时态执行save.
merge()方法会将数据保存到数据库,但对象没有持久化
还可以通过delete()方法删除某个实例,数据库中的信息也被删除
主键生成策略: native 根据底层自动选择
identity mysql
sequence oracle
uuid
等
6.映射文件的集合属性
用集合接口声明集合属性
有list 用于映射list集合的属性
set
map
array 用于映射数组的属性
映射集合属性时通常指定name属性,用于表面该集合属性的名称,此为还有其他属性: table 指定保存集合属性的表名,如果不指定,默认和集合属性 的名称相同
lazy 懒加载 默认为true
cascade 是否级联
orderby 设置数据库对集合元素的排序 该属性的值是指定表的指定字段 加上asc 或者desc
这些集合属性都需要保存到另一张表中,所以需要一个外键,使用<key />标签表示 这个标签有下面属性:
column 指定外键字段的列名
unique 指定外键列是否有唯一约束
对应list和map还要指定索引列
<list-index />
<map-key />
用于集合映射的元素大概包含以下几种
<element /> 集合元素是基本类型以及他的包装类 ,字符串类型 ,日期类 型使用该元素
<composite-element />集合元素是复合类型时候,使用
<one- to- many> <many-to-many> 集合元素是其他持久化对象的引用,主 要用于进行关联关系映射
6.1 list集合的映射
list是有序的,因此持久化到数据库后必须增加一列索引列来表示集合的顺序
public class Person {
private List<String> schools=new ArrayList<String>();
}
Person.hbm.xml
<list name=”school” table=”school”>
<key column=”personId” />
<list-index column=”list_order”/>
<element type=”String” column=”school_name”/>
</list>
数组属性类似于list属性
6.2 set集合属性
pivate Set<String> schools= new HashSet<String>();
<set name=”school” table=”school”>
<key column=”personId” />
<element type=”String” column=”school_name”/>
</set>
6.3map集合属性映射
private Map<String,Float> scores= new HashMap<String,Float>();
<map name=”scores” table=”score”>
<key column=”personId”/>
<map-key column=”subject” type=”string” />
<element column=”grade” type=”float” />
</map>
有时候不需要加载集合属性,或者只加载一部分,因此为了在初始化时候不让它加载,采用延迟加载
数组不可以使用延迟加载 ,list map性能最高,set 其次
6.4 组件属性的映射
private Name name;
用component标签
这里不作详细研究
也可以采用JPA的annotation来代替xxx.hbm.xml文件来配置持久化对象,这里不做研究,请参考<<经典javaEE 企业应用实战>>
7.hibernate的关联映射
单向关联
双向关联
7.1单向N-1关联
通过N的一方找到1的一方使用< many-to-one>标签
public class Person{
private Address address;
}
public class Address{}
Address不是普通的组件,而是一个持久化对象
<class name=”Person ” table=” person”>
<many-to-one name=”address” class=”Address” cascade=”all” column=”address_id” />
</class>
column在这里指定的是外键
7.2单向1-1关联
只需在N-1关联的<many-to-one>增加一个属性 unique=true 即可
7.3单向1-N关联
public class Person{
private Set<Address> addresses=new HashSet<Address>();
}
<class name=”Person” table=”person”>
<set name=”addresses” >
<key column=”personId” />
<one-to-many class=”Address”>
</set>
</class>
7.4单向 N-N关联
单向N-N的持久化类和1-N的代码完全相同,N-N必须使用连接表
<class name=”Person” table=”person”>
<set name=”addresses” table=”person_address”>
<key column=”personId”/>
<many-to-many class=”Address” column=”addressId”/>
</set>
</class>
7.5双向1-N关联
public claas Person{
private Set<Address> addresses = new HashSet<Address>();
}
public class Address{
private Person person;
}
<class name=”Person” table=”person”>
<set name=”addresses” >
<key column=”personId” />
<one-to-many class=”Address”>
</set>
</class ”>
<class name=”Address” table=”address”>
<many-to-one name=”person” class=”Person” column=”personId”/>
</class>
7.5双向N-N关联
public claas Person{
private Set<Address> addresses = new HashSet<Address>();
}
public class Address{
private Set<Person> persons=new HashSet<Person>();;
}
Person.hbm.xml
<class name=”Person” table=”person”>
<set name=”addresses” table=”person_address”>
<key colunm=”personId”/>
<many-to-many class=”Address” column=”addressId” />
</set>
</class>
Address.hbm.xml
<class name=”Address” table=”addresss”>
<set name=”persons” table=”person_address”>
<key colunm=”addressId”/>
<many-to-many class=”Person” column=”personId” />
</set>
</class>
7.6双向1-1关联
需要 属性设置setter getter方法
Person.hbm.xml
<one-to-one name=”address” property-ref=”person”>
Address.hbm.xml
<many-to-one name=”person” column=”personId” unique=”true”>
7.7组件属性包含的关联
public class Person
{
private Address address;
}
public class Address
{
private String addressDetail;
private Set<String> schools=new HashSet<String> ();
}
这里address是一个组件,不是持久化类,因此无法进行和person的映射关联,而school是一个持久化类,person也是一个持久化类
<class name=”Person” table=”person”>
<component name=”address” class=”Address”>
<parent name=”person” />
<property name=”addressDetail”/>
<set name=”schools”>
<key column=”addressId”/>
<one-to-many calss =”School”/>
</set>
</component>
</class>
8.继承映射
两个持久化对象存在继承关系
8.1使用subclass元素进行映射
这种映射 父类和子类的持久类实例保存到一张表中,通过辨别者列(discriminator)区分属于哪个类
其实class属性用来映射普通持久化类,subclass用来映射子持久化类
使用subclass进行映射,不能指定子类持久化类的字段属性为非空约束,否则会造成数据完整性冲突,但这种策略,由于所有数据保存到一张表中,无需进行多表链接查询,也无需进行union查询,因此性能较高
public class Employee extends Person{}
<class name=”Person” table=”person” disciriminator-value=”普通人” >
<id name=”id” column=”personId”>
<generator class=”identity” />
</id>
<discriminator column=”wawa” type=”string” />
<property name=”name” length=”80” />
<subclass name=” Employee” disciriminator-value=”雇员”>//使用subclass映射 Employee类
...
</subclass>
...
</class>
8.2使用joined-subclass元素进行继承映射
该策略会将父类实例保存到父类表中,而子类实例的继承来的属性保存在父类表中,
子类特有的属性保存在子类表中
该策略无需使用辨别者列,但需要在子类表中有一个<key /> 指定共有主键
<class>
<joined-subclass name=”Employee”>
<key column=”employeeId” />
</joined-subclass>
</class>
子类增加的字段可以指定非空约束 ,由于要多表查询,性能有一定影响
8.3使用union-subclass 继承映射
这种策略将父类属性保存到父类表中,将子类属性保存到子类表中,不需要辨别者列名,也不需要在子类中使用<key />元素 ,这种映射比较简洁
<class name=”Person” table=”person”>
<union-subclass name=”Employee” table=”employee”>
</union-subclass>
</class>
使用该方法主键生成策略不能使用identity 和native
9.hibernate的批量处理
问题:如果同时处理一百万条数据,我们还用一条条更新么?答案是不用,我们采用批处理
共有三种 :批量插入
批量更新
批量删除
9.1批量插入
如果往数据库中插入100000条数据,那么有可能内存溢出异常,因此我们要设置一个累积器,定时将缓存中的数据刷入到数据库中
Sesseion session=HibernateUntil.currentSessioin();
Transaction tx=session.beginTransaction();
for(int i=0;i<1000000;i++)
{
User u=new User();
u.setName(“xxx” +i);
session.save(u);
if(i%20==0)
{
session.flush();
session.clear();
}
}
tx.commit();
HibernateUtil.closeSession();
此外,批量插入时最好关闭二级缓存
9.2DML风格的批量删除和批量更新
需要使用HQL语句
Sesseion session=HibernateUntil.currentSessioin();
Transaction tx=session.beginTransaction();
String hqlUpdate=”update User set name=:newName”;
int updateEntities=session.createQuery(hqlUpdate)
.serString(“newName”,”新名字”)
.excuteUpdate();//批量更新
tx.commit();
HibernateUtil.closeSession();
//批量删除
String hqlDelete=”delete User”
其他代码和批量更新相同
10 HQL查询
hibernate可以使用hql查询,也可以使用条件查询,还可以使用原生的sql查询,HQL查询是面向对象的查询
使用HQL查询的基本步骤:
1)获取 hibernate session对象
2)编写HQL语句
3)以hql语句为参数,调用session.createQuery()方法,创建queryd对象
4)如果hql语句包含参数,调用Query的setXXX方法为参数赋值
5)调用Query对象的list()等方法,返回查询结构列表
10.1 hql查询的几个例子
1) 第一个查询方法
Session sess=HibernateUtil.currentSession();
Transaction tx=sess.beginTransaction();
List p1=sess.createQuery(“select disticnt p from Person p join p.myEvents where title=:eventTitle”).setString(“eventTitle”,”很普通的事情”)//
.list();
for(Iterator pit=p1.iterator();pit.hasNext();)
{
Person p= (Person) pit.next();
System.out.println(p.getName());
}
tx.commit();
HibernateUtil.closeSessio();
2) 第二个查询方法
Session sess=HibernateUtil.currentSession();
Transaction tx=sess.beginTransaction();
SimpleDateFormat sdf=new SimpleDateFormat(“yyyy-MM-dd”);
Date start=sdf.parse(“2005-01-01”);
List p1=sess.createQuery(“select distinct p from Person p inner jion p.myEvents event where event.happenDate between :firstDate and :lastDate”)//
.setDate(“firstDate”,start)
.setDate(“lastDate”,new Date())//
.list();
for(Iterator pit=p1.iterator();pit.hasNext();)
{
Person p= (Person) pit.next();
System.out.println(p.getName());
}
tx.commit();
HibernateUtil.closeSessio();
3) 第三个查询 属性
List p1=session.createQuery(“select distinct p.id ,p.name.p.age from Person p join p.myEvents”).list();
for(pit=p1.iterator();pit.hasNext();)
{
Object [] objs= (Object [])pit.next();
System.out.println(Arrays.toString(objs));
}
此外Query还有两个方法
setFirstResult(int firstResult) //返回的结果从第几条记录开始
setMaxResults( int maxResults)//返回本次查询的条数
10.2 from 子句
from 后面紧跟的是持久化类名
例: from Person as p; //从person类中选出全部实例
from后面可以跟多个持久化类,表示跨表链接,但是一般不这么用,因为我们可以使用隐式连接或显式连接,不用这种from的方式
10.3关联和连接
当需要多表查询时,hibernate通过关联来连接,支持两种关联连接(join) :隐式关联和显式关联
隐式连接不使用join,而是使用英文点号
隐式: from Person p where p.myEvent.title < :title:
显示连接使用join : from Person p inner join p.myEvent event where event.happenDate <:endDate
使用join可以为相关联的实体指定别名
常用的join有 内连接 inner join 简写为join
左外连接 left outer join 简写为 left join
右外连接 right outer join 简写为 right join
可以使用with 关键字为显式连接指定额外的条件
with连接的是不等条件
例 from Person p join p.myEvent event with p.id> event.id;
fetch关键字常用在懒加载时候 ,关联持久化类中的集合
from Person p join fetch p.scores
fetch 不能和with setFirstResult setMaxResults 关键字一起使用
10.4HQL的select语句
select 后面如果只有一项属性或者实例 那么返回的结果就是这个属性或实例的集合
select后面如果有多项,那么返回的集合的每个元素是数组
select p.name p from Person p;
那么集合的每一项都是一个数组[string, Person]
默认是数组 ,也可以指定为list,则每一项都是list
select new list(p.name,p) from Person p;
10.5 HQL的聚集函数
avg:平均数
sum
min
max
count 统计个数
10.6 多态查询
如果Person 和Teacher 完成了继承映射
那么 from Person ;不仅会查询出Person实例,还会查询出Teacher实例
10.7 HQL查询 where 子句
where用于筛选结果,缩小范围
如果没有为持久化实例命别名,那么可以直接使用属性 ,如果有别名,要用别名引用属性
from Person where name like’tom%’;等价于
from Person p where p.name like “tom%”;
where 后面支持的表达式比较多:
数学运算符
逻辑运算符 and or not
in not in between ...and ... is null is not null is empty is not empty
case , case... when...then...else...end
时间操作函数current_timestamp() second()
...
10.8 order by子句
按属性排序 desc asc默认是asc(升序)
10.9 group by 子句
分组
类似SQL的规则,select后面的属性要么出现在聚集函数中,要么出现在group by子句中
select cat.color, count(cat), sum(cat.weight) from Cat cat group by cat.color having cat.color in( eg.Color.TABBY,eg.Color.BLACK);
having 是用来对分组条件进行过滤,因此,在有group by 时后,才有having
10.10子查询 要使用括号
from Cat fatcat where fatcat.weight> ( select avg(cat.weight) from DomesticCat cat );
10.11命名查询
将查询语句放在配置文件中,这样可以解耦和
在hibernate-mapping标签内的query标签可以定义HQL语句
<query name=”myNameQuery”>
form Person p where p.age>?
</query>
怎样使用这个配置的hql
Query query= session.getNamedQuery(“myNameQuery”);
11条件查询
是更具有面向对象特色的查询方式
条件查询步骤:
1)获得hibernate的session对象
2)以session对象创建Criteria对象
3)使用Restrictions的静态方法创建查询条件 Criterion查询条件
4)向Criteria里添加Criterion查询条件
5)执行Criteria的list()方法返回结果集
11.1例子
list l= session.createCriteria(“Student.class”)//
.add(Restrictions.gt(“studentNumber”,”20004444”))//
//如果要增加student关联类的属性限制,则需要重新createCriteria()
.createCriteria(“enrolments”)//
.add(Restrictions.gt(“semester”,2))
.list();
分页查询方法 setFirstResult(int firstResult)
setMaxResults(int maxResults)
add( Criteria criteria)增加条件
addOrder(Order order)增加排序规则
11.2投影 聚合 和分组 离线查询 子查询
此处暂不讨论
12 SQL查询
SQL查询是通过SQLQuery 接口来查询的,该接口继承自Query接口,因此具有Query接口的一下方法,同时自己特有的两个方法是:addEntity() 将查询到的记录和特定的实体关联
和addScalar() 将查询到的记录关联成标量值
SQL查询的步骤:
1)获取hibernate session对象
2)编写SQL语句
3)以sql语句为参数,调用session的createSQLQuery()方法创建查询对象
4)调用addEntity()和addScalar()方法将查询到的结果与标量值或者实体关联,进行标量查询或者实体查询
5)如果sql语句包含参数,还要调用setXXX()方法.设置参数
6)通过list方法返回结果集
12.1标量查询
最基本的SQL查询是返回一个标量(数值)列表
session.createSQLQuery(“select * from student”).list();
返回的list集合的每个元素是一个Object [] 的数组元素,系统会自己判断数组里面的类型,但是这样做会大大降低性能,因此我们用addScalar()方法指定类型
session.createSQLQuery(“select * from student”))//
.addScalar(“name”,”StandardBasticTypes.STRING”)//
.list();
该语句只是返回name的字段列表
12.2实体查询
实体查询将查询结果转换成对象
List l= session.createSQLQuery(select * form enrolment_inf where year=:year)//
.addEntity(Enrolment.class)//
.setInt(“year”,2015)//
.list();
12.3命名sql查询 调用存储过程 数据过滤
暂不讨论
13.事务控制
13.1事务的概念
事务是最小的逻辑执行单元,要么都一起执行,要么都不执行
比如转账操作要使用事务
事务的4个特性: ACID
原子性
一致性
隔离性 不同事务之间互不干扰
持续性 永久的保存到数据库中
13.2 session和事务
session.beginTransaction() 开启事务的
从底层看hibernate的事务石油TransactionFactory 实例产生的
由于SessionFactory 底层封装了TransactionFactory,因此无需手动操作事务工厂
SessionFactory在应用程序启动时候创建,被所有线程共享,一旦创建不会轻易关闭,知道应用程序关闭
session会定期被清理
如果想在视图显示层一直打开session, Spring框架提供了OpenSessionInViewFilter类
使用HibernateUtil类获得的session使线程不安全的session一直处于当前线程内
HibernateUtil.currentSession()
hibernate3.1提供的方法SessionFactory.getCurrentSession()可以直接获得上下文的session
13.3二级缓存
一级缓存是session级别的局部缓存,是一定开启的,当修改实体类时,修改后的数据不会立即flush到数据库,先放到缓存里面
二级缓存是SessionFactory 级别的全局缓存,默认是不开的,需要配置开启,二级缓存是对所有session起作用
当session需要抓取数据时优先从二级缓存抓取
开发中使用第三方提供的开源缓存,因此要设置二级缓存的实现类
在持久类的映射文件里指定开启策略
一级缓存和二级缓存都是对整个实体的缓存,如果想缓存普通属性可以使用查询缓存
14.hibernate的事件框架和拦截器
此处不作讨论
标签:
原文地址:http://www.cnblogs.com/chuanqimessi/p/4765154.html