零碎知识小结:

◆数据类型:

1,<property name="name" type="java.lang.String"/>

type可以是hibernate,java类型或者你自己的类型(需要实现hibernate的一个接口)

2,基本类型一般不需要在映射文件(hbm.xml)中说明,只有在一个java类型和多个数据库数据类型相对应时并且你想要的和hibernate缺省映射不一致时,需要在映射文件中指明类型(如java.util.Date ,数据库DATE,TIME,DATETIME,TIMESTAMP,hibernate缺省会把java.util.Date映射成DATETIME 型,而如果你想映射成TIME,则你必须在映射文件中指定类型)。

◆session是非线程安全的,生命周期较短,代表一个和数据库的连接,在B/S系统中一般不会超过一个请求;内部维护一级缓存和数据库连接,如果session长时间打开,会长时间占用内存和数据库连接。

◆sessionFactory是线程安全的,一个数据库对应一个SessionFactory,生命周期长,一般在整个系统生命周期内有效;SessionFactory保存着和数据库连接的相关信息(user,password,url)和映射信息,以及Hibernate运行时要用到的一些信息。


◆调用session的flush方法,使得一级缓存和数据库进行一次同步。我们在程序中一般不直接调用该方法,而是交由hibernate进行处理,hibernate会尽量把对数据库的操作延迟,在事务提交的时候,或不得不做的时候,一次性的处理,采用批处理能够减少与数据库的交互次数,提高性能。

◆大批量处理
  大量操作数据是可能造成内存溢出。
for(int i=0;i<100000;i++)
    session.save(obj);
解决方法如下:
1,清除session中的数据
for(int i=0;i<100000;i++){
    session.save(obj);
    if(i%20==0){
       session.flush();//调用flush是因为在清除一级缓存时,可能还有数据未真正的保存到数据库中,所以先做一次同步,再清除一级缓存
       session.clear();
    }
}

2,用StatelessSession接口:它不和一级缓存,二级缓存交互,也不触发任何事件,监听器,拦截器,通过该接口的操作会立刻发送给数据库,与jdbc的功能一样。

StatelessSession s = sessionFactory.openStatelessSession();该接口的方法与Session类似。

3,Query.executeUpdate()执行批量更新,会清除相关联的类二级缓存(sessionFactory.evict(class)),也可能会造成级联,和乐观锁定出现问题。


在hibernate 3.0之前,要大批量的更新数据,需执行如下操作:

Java代码
  1. session s = HibernateUtil.getSession();  
  2. Transaction tx = s.beginTransaction();  
  3. Query q = s.createQuery("from User");  
  4. List<User> users = q.list();  
  5. for(User u:users){  
  6.     u.setBirthday(new Date());  
  7. }  
  8.   
  9. tx.commit();  

 

hibernate 3.0后,可以这样做:

 

Java代码
  1. Query q = s.createQuery("update u set birthday=:bd from User as u");  
  2. q.executeUpdate();  

 

◆HQL

1,查询多个对象selecet art,user from Article art,User user where art.author.id=user.id and art.id=:id 这种方式返回的是Object[],Object[0]:article,Object[1]:user。

2,分页 query.setFirstResult, qurey.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(),groupPropery....

4,分页Projections.rowCount();criteria.setFirstResult();criteria.setMaxResults()

5,DetachedCriteria 可在session未创建时,根据用户的组合查询条件来构造查询。即它可在session外创建(在其他层创建比如在Service中创建)然后用 detachedCriteria.getExecutableCriteria(Session)方法创建Criteria对象来完成查询。

示例:
在外部构建好离线查询对象DetachedCriteria。一般的criteria对象的创建需要与session对象关联,而离线查询对象的创建则不需要与session相关。
DetachedCriteria dc = DetachedCriteria.forClass(User.class);
String name = request.getParameter("name");
int age = request.getParameter("age");
dc.add(Restrictions.eq("name",name));
dc.add(Restrictions.eq("age",age));

把构建好的离线查询对象传递给dc方法,进行查询。
static List dc(DetachedCriteria dc){

        Session s = HibernateUtil.getSession();
        Criteria c = dc.getExecutableCriteria(s);//这时才与session关联构造出criteria对象,实施查询。
        List rs = c.list();
        s.close();
        return rs;
}


6,Example查询,Example.create(obj);criteria.add(example);

N+1次查询和懒加载

1,用Query.iterator可能会有N+1次查询。
2,懒加载时获取关联对象
3,如果打开对查询的缓存即使用list也可能有N+1次查询。

示例代码:
public class Main {

    public static void main(String[] args) {
        User user = new User();
        user.setBirthday(new Date());
        Name name = new Name();
        name.setFirstName("firstName");
        name.setLastName("lastName");
        user.setUserName(name);
        for (int i = 0; i < 10; i++) {
            user.getUserName().setFirstName("name" + i);
            saveUser(user);
        }
        iterate();
    }

    static void iterate() {
        Session s = HibernateUtil.getSession();
        Query q = s.createQuery("from User");
        Iterator<User> users = q.iterate();
      while(users.hasNext())            {
          System.out.println(users.next().getUserName().getFirstName());
        }


    }
  
    public static void saveUser(User user) {
        Session session = HibernateUtil.getSession();
        Transaction tx = session.beginTransaction();
        session.save(user);
        tx.commit();
        session.close();
    }

先向数据库中插入10条数据。然后查询所有的数据,打印出firstname,输出的sql语句为:
....//前面为10条插入语句
....
Hibernate: select user0_.id as col_0_0_ from user user0_
name0
name1
name2
name3
name4
name5
name6
name7
name8
name9


没有产生10条具体查询的语句,是因为配置了二级缓存和User类的缓存。主键生成方式为hilo而不是native,在保存10个对象后,同时会在二级缓存中进行缓存。等到查询时,先获得满足条件的id ,这就是第一条查询语句的作用。到真正读取时,根据id先到一级缓存,再到二级,最后到数据库中去查。在这里都在二级缓存中命中了。所以没产生另外的10条select语句。

把主键的生成方式改为:native。这样在插入数据后就不会在二级缓存中保存数据了。输出的sql语句为:

Hibernate: select user0_.id as col_0_0_ from user user0_
Hibernate: select user0_.id as id0_0_, user0_.version as version0_0_, user0_.first_name as first3_0_0_, user0_.last_name as last4_0_0_, user0_.birthday as birthday0_0_ from user user0_ where user0_.id=?
name0
Hibernate: select user0_.id as id0_0_, user0_.version as version0_0_, user0_.first_name as first3_0_0_, user0_.last_name as last4_0_0_, user0_.birthday as birthday0_0_ from user user0_ where user0_.id=?
name1
Hibernate: select user0_.id as id0_0_, user0_.version as version0_0_, user0_.first_name as first3_0_0_, user0_.last_name as last4_0_0_, user0_.birthday as birthday0_0_ from user user0_ where user0_.id=?
name2
Hibernate: select user0_.id as id0_0_, user0_.version as version0_0_, user0_.first_name as first3_0_0_, user0_.last_name as last4_0_0_, user0_.birthday as birthday0_0_ from user user0_ where user0_.id=?
name3
Hibernate: select user0_.id as id0_0_, user0_.version as version0_0_, user0_.first_name as first3_0_0_, user0_.last_name as last4_0_0_, user0_.birthday as birthday0_0_ from user user0_ where user0_.id=?
name4
Hibernate: select user0_.id as id0_0_, user0_.version as version0_0_, user0_.first_name as first3_0_0_, user0_.last_name as last4_0_0_, user0_.birthday as birthday0_0_ from user user0_ where user0_.id=?
name5
Hibernate: select user0_.id as id0_0_, user0_.version as version0_0_, user0_.first_name as first3_0_0_, user0_.last_name as last4_0_0_, user0_.birthday as birthday0_0_ from user user0_ where user0_.id=?
name6
Hibernate: select user0_.id as id0_0_, user0_.version as version0_0_, user0_.first_name as first3_0_0_, user0_.last_name as last4_0_0_, user0_.birthday as birthday0_0_ from user user0_ where user0_.id=?
name7
Hibernate: select user0_.id as id0_0_, user0_.version as version0_0_, user0_.first_name as first3_0_0_, user0_.last_name as last4_0_0_, user0_.birthday as birthday0_0_ from user user0_ where user0_.id=?
name8
Hibernate: select user0_.id as id0_0_, user0_.version as version0_0_, user0_.first_name as first3_0_0_, user0_.last_name as last4_0_0_, user0_.birthday as birthday0_0_ from user user0_ where user0_.id=?
name9

一共产生了11条查询语句,这就是所谓的N+1次查询问题。

懒加载也会产生n+1次查询。

拦截器与事件

拦截器与事件都是hibernate的扩展机制,Interceptor接口是老的实现机制,现在改成事件监听机制;他们都是hibernate的回调接口,hibernate在save,delete,update...等会回调这些类。

通过实现监听器接口,编写自己的监听器实现,然后在hibernate配置文件中,注册监听器。
使用<event type="save"></event>标签。type表示监听器对哪种操作感兴趣。
示例:
<event type="save">
   <listener class="hibernatetest.SaveListener"/>
</event>

执行后,监听器的动作得到了执行,但是对象却未保存到数据库中去。
分析:
由于我们编写了自己的监听器,hibernate认为我们有了更好的实现,所以系统默认的监听器会被放弃使用。 defaultSaveOrUpdateEventListener该监听器是在发生save动作时,执行保存。我们一般在自己的监听器中做一些其他的事情,所以要保存对象,需要手动的添加监听器的默认实现,在监听器依次执行相关操作,默认的监听器就会保存对象了。
根据监听器配置的先后顺序,在事件发生时,依次调用。
正确做法:

Xml代码
  1. <event type="save">  
  2.    <listener class="hibernatetest.SaveListener"/>  
  3.    <listener class="org.hibernate.event.def.defaultSaveOrUpdateEventListener"/>  
  4. </event>  
 

SQL和命名查询。
session.createSQLQuery(arg0)
参数是原生的sql语句。不过不推荐使用,sql会依赖于底层的具体数据库,减弱了程序的可移植性。

session.getNamedQuery(arg0)

命名查询可以放在映射文件中。只不过是把HQL预先定义好,以后可以拿来就用而已。

用Map代替Domain对象;将对象转化为XML。

posted on 2009-03-17 09:56  javamen  阅读(353)  评论(0编辑  收藏  举报