现在的位置: 首页 > 综合 > 正文

hibernate学习总结

2012年01月30日 ⁄ 综合 ⁄ 共 12002字 ⁄ 字号 评论关闭

hibernate自动创建表:
配置文件中:<property name="hbm2ddl.auto">create</property>
create:启动时候创建,程序结束时候不删除表,下次启动时候删掉表然后在创建
create-drop:初始化创建,程序运行结束删掉表
update:映射文件和表结构不一样的话,就会更新表
validate:校验表和映射文件是否对应,如果不对应的话就会报错
JDBC默认的是自动提交
Hibernian默认的是手动提交,必须开启事务,手动提交,否则数据库中没有保存的记录:
eg:
            Transaction tx = getSession().beginTransaction();
            getSession().save(transientInstance);
            tx.commit();
这个是对于InnoDB引擎来说的。InnoDB支持事务和外键(插入后如果没有提交可以回滚)
如果我没有提交事物,但是mysql数据库中有数据,那么因为这个引擎是MyISAM
默认的MyISAM不支持事务和外键(开启事务与否没有关系,插入后不能回滚)
查询的时候不用打开事务
配置文件
<property name="show_sql">true</property>显示执行时候的sql语句
get():使用频繁
load():使用少
   load()不会立刻访问数据库,只是在调用的时候采取访问数据库,而且返回的值不会为null,
persist():保存,没有开启事务的时候不插入
save():保存,没有开启事务的时候会插入,然后回滚
Hibernate对象的状态:
瞬时:数据库中没有记录与之对应,超过作用域会被JVM垃圾回收器回收,一般是new出来且与session
没有关联的对象
持久:数据库中有数据与之对应,当前与session有关联,并且相关联的session没有关闭,事务没有提
交;持久对象状态发生改变,在事务提交时会影响到数据库
脱管:数据库中有数据与之对应,但是当前没有session与之关联;托管对象状态发生改变,hibernate
不能检测到
例子:user user=new User();//user处于瞬时
      session.save(user);//把user和session关联,user是持久
      commit();//user所有的更新被保存到数据库里面
      session.close();//user会与session断开联系,从session里面出来了,是脱管
update():处理脱管对象
saveOrUpdate():不知对象处于什么状态(根据id来获得是什么状态),调用他后进行保存或者更新,对象
成为持久的.而调用merge()后对象还是脱管的!
如果id是int型的,返回0表示数据库中没有相应的记录;如果id是string型的,返回null表示数据库中没
有相应的记录;那么我如果想让他返回别的值,例如-1,那么需要在映射文件中指定:<id name=""
unsaved-value="-1">
user处于持久化的时候,如果对象的属性发生了变化,那么默认是在提交的时候,对象的状态跟着数据库
同步,这样的话可以减少与数据库的交互,提高效率
查询操作:HQL和Criteria
HQL:
   面向对象的查询语言,与sql不同,HQL中的对象是区分大小写的(除了java类和属性其他部分不区分大
小写);HQL中查的是对象而不是表,并且支持多态;HQL主要通过Query来操作,Query的创建方式:
Query q=session.createQuery(hql);
.from Persion
.from user user where user.name=:name
.from User user where user.name=:name and user.birthday<:birthday
Criteria
Criteria是一种比HQL更面向对象的查询方式;Criteria的创建方式:
Criteria crit=session.createCriteria(DomainClass.class);
简单属性条件如下:criteria.add(Restriction.eq(propertyName.value)),
criteria.add(Restrictions.eqProperty(propertyName.otherPropertyName))
例子:
HQL:
根据用户的名字查找用户:
String hql="from User(类名) as user where user.name=?";
Query q=session.createQuery(hql);
q.setString(0,name);
List<User> list=q.list();//executeQuery()类似
//User user=(User)q.uniqueResult();//如果确定结果只有一个就用这个函数
for(User user:list){
system.out.println(user.getName());
}
上例中?如果太多,对应的话就不好对应了可以这样:
String hql="from User as user where user.name=:name";
q.setString("name",name);
//可以实现分页
q.setFirstResult(0);
q.setMaxResult(10);//取10条记录
Criteria:
Criteria c=s.createCriteria(User.class);
//约束
c.add(Restrictions.eq(等于)("name",name));//属性name的值是name
c.add(Restrictions.qt(大于)("birthday",new Date()))
                    or(或)
                    lt(小于)
//分页
c.setFirstResult(0);
c.setMaxResult(10);
List<User> list=c.list();
//User u=(User)c.uniqueResult();一对一关系:
比如:Person和IdCard表
person表的主键id是自增长的;id_card表的主键id不用自增长的,和person中的id是一样的,即是主键
也是外键
可以这样设计表:
create table id_card(
     id int(11) not null,
     ...
     ...
     primary key(id)
     FOREIGN KEY (`id`) REFERENCES `person` (`id`) ON DELETE CASCADE ON UPDATE CASCADE,
)
Person类中有一个private IdCard idCard(主对象);
IdCard类中有一个private Person person(从对象);
映射文件:
    IdCard.hbm.xml:
       <id name="id">
           <generator class="foreign">
               <param name="property">person</param>
           </generator>
       </id>
       <one-to-one name="person" constrained="true"/>
    Person.hbm.xml
        <one-to-one name="idCard"/>
           
多对多:
create table teacher_student{
   teacher_id int not null,
   student_id int not null,
   ........
   ........
   primary key (student_id,teacher_id)
   FOREIGN KEY (`student_id`) REFERENCES `student` (`id`) ON DELETE CASCADE ON UPDATE
CASCADE,
   FOREIGN KEY (`teacher_id`) REFERENCES `teacher` (`id`) ON DELETE CASCADE ON UPDATE
CASCADE,
}
组件映射(User-Name)
关联的属性是个复杂类型的持久化类,但不是实体即:数据库中没有表与该属性对应,但是该类的属性要
持久保存的
<component name="name" class="com.wuxiaoxiao.Name">
       <property name="initial">
       <property name="first">
       <property name="last">
</component>
当组件的属性不能简单的和表中的字段对应起来的时候可以选择实现:
org.hibernate.usertype.UserType或者org.hibernate.usertype.CompositeUserType
集合映射:
默认的是set:没有顺序,也不允许重复
list:有顺序的
   List<Employee> emps=new ArrayList<>(Employee);
   配置:<list name="emps">
            <key column="depart_id"/>
            <list-index column="order_col"/>//保持顺序   
            <one-to-many class="Employee"/>
        </list>
//不保持顺序
   <bag name="emps">
       <key column="depart_id"/>
       <one-to-many class="Employee"/>
   </bag>
map;有个key
   private Map<String,Employee> emps;
   <map name="emps">
      <key column="depart_id"/>
      <map-key type="string" column="name"/>
      <one-to-many class="Employee"/>
   </map>
Employee emps1=new Employee();
emps1.set.....;
Employee emps2=new Employee();
emps2.set.....
Department depart=new Department();
depart.setEmployee(set);
s.save(emps1);
s.save(emps2);
s.save(depart);
那么emps是Department的一个集合属性,那么能不能只要执行s.save(depart),就可以保存emps1和emps2
???
是可以的:<set name="emps" cascade="save-update">
           ..........
          </set>
就是增加cascade属性
级联和关系维护:
.Cascade用来说明当对主对象进行某种操作时候是否对其挂链的从对象也做类似的操作,常用的cascade

none,all,save-update,delete,lock,refresh,evict,replicate,presist,merge,delete-orphan(oen-
to-many)
一般对many-to-one,many-to-many不设置级联,在<one-to-one>和<one-to-many>中设置级联
.inverse表"是否放弃维护关联关系"(在java里两个对象产生关联时,对数据库表的影响),在one-to-
many和many-to-many的集合定义中使用,inverse="true"表示该对象不维护关联关系;该属性的值一般在
使用有序集合时设置成false(注意hibernate的缺省值是false),one-to-many维护关联关系就是更新外键
。many-to-many维护关联关系就是在中间表增减记录
注意:配置成one-to-one的对象不维护关联关系
在list中不能出现inverse属性....
注意的内容:
继承映射:
   视频29
建议表的数目不要超过类的数目
Load的懒加载:
User user1=(User)session.load(userClass,id);
Hibernate.initialize(user1);
//user1不会为null
//懒加载是为了提高访问效率,当需要的时候才与数据库进行交互
one-to-one懒加载:
必须同时满足下面三个条件时才能实现懒加载(查询主对象时候不进行懒加载,查询从对象时候进行懒
加载)
(主表中不存在constrained="true",所以主表没有懒加载):
lazy!=false(lazy还有个值是proxy) constrained=true fetch=select
fecth属性是指通过什么方式抓取,lazy是指什么时候抓取
one-to-many懒加载:lazy!=false fetch=select
many-to-one:lazy!=flase fetch=select
many-tomany:lazy!=false fetch=select
能够懒加载的对象都是被改写过的代理对象,当相关联的session没有关闭时,访问这些懒加载对象(代理
对象)的属性(getId和getClass除外)hibernate会初始化这些代理,或用Hibernate.initialize(proxy)来
初始化代理对象;当相关联的session关闭后,在访问懒加载的对象将出现异常
适当的使用懒加载可以提高系统的访问速度!!!!
缓存:
例如:一个用户读取一个用户信息,又有一个用户来读取用户信息,那么前一个用户从数据库中读取,
后一个用户可以不从数据库中读取,这样就提高访问效率
例子:
static Map cache=new HashMap();
public static User getUser(int id){
String key=User.class.getName()+id;
User user=(User)cache.get(key);
if(user!=null)
   return user;
user=getFromDB();
cache.put(key,user);
return user;
}
private User getFromDB(){}
如果第一个用户查完之后,更新了该用户的信息,那么要跟新cache
public static void update(User user){
updateDB(User);
String key=user.class.getName()+user.getId();
cache.remove(key);
}
public static void updateDB(){}
Hibernate缓存
一级缓存:session级别的缓存
User user=(User)session.get(userClass,id)//从数据库里面查找
System.out.println(user.getClass());
user=(User)session.get(userClass,id);//从缓存里面查找
System.out.pritnln(user.getName());
session关闭,缓存会消失!!!所以session的缓存作用范围比较小
缓存的作用主要用来提高性能,可以简单的理解成一个Map;使用缓存涉及到三个操作:把数据放入缓存
,从缓存中获取数据,删除缓存中的无效数据
一级缓存,session级共享
save,update,saveOrUpdate,load,get,list.iterate,lock这些方法都会将对象放在一级缓存中,一级
韩村不能控制缓存的数量,所以要注意大批量操作数据时,可能造成内存溢出,可以用evict,clear方法
清除缓存中的内容!
Query和Creaita不能从缓存中读取数据,get和load可以从缓存中读取数据
二级缓存:
hibernate中使用的是第三方缓存
在配置文件中配置来告诉hibernate使用什么样的缓存
.<property name="cache.user_second_level_cache">true</property>//告诉hibernate二级缓存打开
.<property name="cache.provider_class">org.hibernate.cache.OSCacheProvider</property>//告诉
hibernate使用哪种二级缓存
.那么选择了OSCacheProvider,他也有配置文件oscache.properties应该放在classpath下面
.告诉hibernate对哪个对象进行缓存<class-cache class="....." usage="read-write"(策略,如果不会
修改对象就选择read-only)/>
还有一种方式指定要缓存的类:在映射文件 id前面有个缓存:<cache usage="read-write"/>
Hibernate先从一级缓存找,没有找到然后从二级缓存找,没有找到然后在从数据库中找
查看缓存的统计信息:<property name="generate_statistics">true</property>//测试时候使用,部署
时候关闭
在代码中使用Statistics st=HibetnateUtil.getSessionFactory.getStatistics();
            System.out.println(st.getSecondLevelCacheHitCount());
可以查看二级缓存使用的信息
二级缓存,SessionFactory级共享
.session的save(这个方法不适合native生成方式的主键),update,
saveOrUpdate,list,iterator,get,load以及Query,Criteria都会填充二级缓存,但只有(查询缓存时)
Session的iterator,get,load会从二级缓存中取数据(iterator可能存在N+1次查询)
.Query,criteria(查询缓存)由于命中率低,所以Hibernate缺省是关闭的,修改cache.use_query_cache
为true打开对查询的缓存,并且调用query.setCacheable(true)或者criteria.setCacheable(true)
例如:Query q=s.createQuery(".....")
      q.setCacheable(true);
.sessionFactory中提供了evictXXX()方法来清除缓存中的内容
.统计信息打开generate_statistics,用sessionFactory.getSatistics()获取统计信息
分布式缓存和中央缓存:
使用缓存的条件:
   读取的次数要大于修改的次数
   数据量不能超过内存容量
   对数据要有独享的控制权
   可以容忍出现无效数据
事务:
JDBCTransaction:单个数据库(一个SessionFactory对应一个数据库),由JDBC实现
Session session=null;
Transaction tx=null;
try{
      session=sessionFactory.openSession();
      tx=session.beginTransaction();
      //process
      tx.commit();
}catch(HibernateException e){
      if(tx!=null)
              tx.rollback();
      throw e;
}finally{
    if(session!=null)session.close();
}
}
JTATransaction:可以简单的理解成跨数据库的事务,由应用JTA容器实现;使用JTATransaction需要配置
Hibernate.transaction.factory_class参数,该参数缺省值是
org.hibernate.trasaction.JDBCTransaction,当使用JTATransaction时需要将该参数改成
org.hibernate.trasaction.JTATransaction,并配置jta.UserTransaction参数JNDI名(Hibernate在启动
JTATransaction时要用该值到JNDI的上下文Context中去找javax.transaction.UserTransaction)
javax.transaction.UserTransaction tx=context.lookup("jndiname");
try{
tx.begin();
//多个数据库的session操作
//session1....
//session2....
tx.commit()
}catch(Exception e){
tx.rollback();
throw e;
}
事务边界:
什么时候打开 什么时候提交 什么时候回滚
事务边界控制在业务逻辑层
OpenSessionInView模式
解决:事务边界控制和懒加载问题
缺陷:事务存在周期放大了,session存在周期放大了,
mysql的并发在200左右,oracle的并发数在300左右
悲观锁和乐观所
例如两个管理员同时编辑同一个用户信息,如果没有锁的话 先提交的会被后提交的覆盖
悲观锁:读取用户信息的时候把用户锁定,其他用户只能等待直到释放锁,有数据库实现。
乐观锁:由Hibernate用version和timestamp来实现
version在User类中加上个字段private int version;
User.hbm.xml中在id下面加上<version name="version"/>
session是非线程安全的,生命周期较短,代表一个和数据库的连接,在b/s系统中一般不会超过一个请求
;内部维护一级缓存和数据库连接,如果session长时间打开,会长时间占用内存和数据库连接
sessionFactory是现场安全,一个数据库对应一个sessionFactory,生命周期长,一般在整个系统生米昂
周期内有效;sessionFactory保存着和数据库连接的相关信息(user,password,url)和映射信息,以及
Hibernate运行时要用到的一些信息
flush():让session中的一级缓存和数据库中同步(建议自己不要调用)
对于批量操作数据时候可能造成内存溢出,解决如下:
1.清除session中数据:
for(int i=0;i<100000;i++){
s.save(user);
if(i%20==0){
s.flush();s.clear();
}
}
2.用StatelessSession接口,它不和一级缓存,二级缓存交互,也不触发任何事件,拦截器,监听器,通过
该接口的操作会立刻发送给数据库,与JDBC的功能一样,StatelessSession
s=sessionFactory.openStatelessSession();该接口的方法与Session类似
3.Query.executeUpdate()执行批量更新,会清除相关的
Query q=session.createQuery("update u set birthday=:bd from User as u");
q.setString("bd",1);
q.executeUpdate();
HQL
1.查询多个对象select art,user from Article,User user where art.author.id=user.id and
art.id=:id这种方式返回的是Object[],Object[0]:article,Object[1]:user
2.分页query.setFirstResult,query.setMaxResults,查询记录总数query.iterate("select count(*)
from Person").next()
3.批量更新query.executeUpdate()可能造成二级缓存有失效数据
Criteria
1.排序Criteria.addOrder(Order.desc(propertyName))
2.关联查询criteria.setFetchMode("propertyName",FetchMode.SELECT)与映射文件中关联关系的
fetch作用一致
3.投影Projections.rowCount(),max(propertyName),avg,groupProperty....
4.分页Projections.rowCount(),criteria.setFirstResult(),criteria.setMaxResult()
5.DetachedCriteria(离线查询,动态查询)可在session外创建(在其它层创建比如在Service中创建)
然后用getExecutableCriteria(session)方法创建Criteria对象来完成查询
例子:查询条件不固定:
public List dc(DetachedCriteria dc){
    Session s=....
    Criteria c=dc.getExecutableCriteria();
    List rs=c.list();
    s.close();
    return rs;
}
DetachedCriteria dc=DetachedCriteria.forClass(User.class)
String name=request.getParameter("name");
if(name!=null)
    dc.add(Restrictions.eq("name",name))
List users=dc(dc);
6.Example查询,Example.create(obj);criteria.add(example)
N+1次查询和懒加载:
query.iterator():执行N+1次查询,先从数据库中查找所有的id,在根据每个id从数据库中查找记录
fetch抓取属性改为join可以避免N+1次缓存
拦截器和事件:
拦截器和事件都是hibernate的扩展机制,Interceptor接口是老的实现机制,现在改成事件监听器,他
们都是hibernate的回调接口,hibernate在save,delete,update.....等会调用这些类,比如我们拦截保
存的事件:在执行save方法之前执行事件监听器
public class SaveListerner implements or.hinernate.SaveOrUpdateEventLister{
public void onSaveOrUpdate(SaveOrUpdateEvent event)throws HibernateException{
if(event.getObject() instanceof cn.itcast.User){
User user=(User)event.getObject();
输出用户的名字.....
}
}
}
在配置文件中配置监听器:
<event type="save">         
         <listener class="org.hibernate.event.def.DefaultSaveOrUpdateEventListener"/>
         <listener class="com.wuxiaoxiao.SaveListerner"/>
</event>
SQL:
select * from user:全是sql语句
Query q=session.createSQLQuery("select * from user").addEntity(User.class);
List<User> rs=q.list();
for(User u:rs)
   System.out.println(u.getName());
命名查询:
将查询条件放在别的地方,统一管理:
例如:在映射文件中配置:放在class外面或者里面
<query name="getUserByBirthday">
<![CDATA[from User where birthday=:birthday]]>
</query>
在代码中使用通过命名查询:
Query q=session.getNameQuery("getUserByBirthday(外面)");//(里面)包名.getUserByBirthday
q.setDate("birthday",new Date())
return q.list();
Hibernate不适合的场景:
.不适合OLAP(联机分析处理),以查询分析数据为主的系统;适合OLTP(联机事务处理)
.对于一些关系模型设计不合理的老系统,也不能发挥Hibernate优势
.数据量大,性能要求苛刻的系统,hibernate也很难达到要求,批量操作数据的效率也不高

本文转自www.35java.com

抱歉!评论已关闭.