Hibernate3 学习(三)

 
3. 基本 API
了解一下 Hibernate 常使用的几个类别之基本使用方式。
31 Session
Hibernate在对数据库进行操作之前,必须先取得Session实例,相当于JDBC在对数据库操作之前,必须先取得Connection实例, Session是Hibernate操作的基础,它不是设计为执行绪安全(Thread-safe),一个Session由一个执行绪来使用。
Session实例由SessionFactory开启获得,例如:
Configuration config = new Configuration().configure();
SessionFactory sessionFactory = config.buildSessionFactory();
Session session = sessionFactory.openSession();
....
session.close();
透过Session,可以对数据库进行新增、删除、更新,例如使用save()新增一笔数据:
User user = new User();
user.setName("momor");
user.setAge(new Integer(26));
session.save(user);
使用get()或load()方法取得id为1的数据:
User user = (User) session.get(User.class, new Integer(1));
如果未能发现相符合的数据,则get()方法会返回null,而load()方法会丢出ObjectNotFoundException,在进阶的应用中,load()方法可以返回代理对象,并可充分利用缓冲机制。
在Hibernate 3中,取消了find()方法,您必须透过Query或Criteria来进行数据查询。
接下来看看使用Session更新与删除数据,可使用delete()删除数据:
User user = (User) session.get(User.class, new Integer(1));
session.delete(user);
如果您开启了一个Session,从数据表中取出数据显示到使用者接口上,之后关闭Session,当使用者在接口上操作完毕并按下储存时,这时您要重新开启一个Session,使用update()方法将对象中的数据更新至对应的数据表中:
User user = (User) session.get(User.class, new Integer(2));
session.close();
....
user.setAge(new Integer(27));
session = sessionFactory.openSession();    
Transaction tx= session.beginTransaction();
session.update(user);
tx.commit();
session.close();
Session提供了一个saveOrUpdate()方法,为数据的储存或更新提供了一个统一的操作接口,藉由定义映像文件时,设定<id>标签的unsaved-value来决定什么是新的值必需,什么是已有的值必须更新:
<id name="id" column="id" type="java.lang.Integer" unsaved-value="null">
    <generator class="native"/>
</id>
unsaved-value可以设定的值包括:
  • any:总是储存
  • none:总是更新
  • null:id为null时储存(预设)
  • valid:id为null或是指定值时储存
这样设定之后,您可以使用Session的saveOrUpdate()方法来取代update()方法。
32 Session 管理
Session是Hibernate运作的中心,对象的生命周期、事务的管理、数据库的存取,都与Session息息相关,就如同在编写JDBC时需关心 Connection的管理,以有效的方法创建、利用与回收Connection,以减少资源的消耗,增加系统执行效能一样,有效的Session管理,也是Hibernate应用时需关注的焦点。
Session是由SessionFactory所创建,SessionFactory是执行绪安全的(Thread-safe),您可以让多个执行绪同时存取SessionFactory而不会有数据共享的问题,然而Session则不是设计为执行绪安全的,所以试图让多个执行绪共享一个 Session,将会发生数据共享而发生混乱的问题。
在Hibernate参考手册中的 Quickstart with Tomcat 中,示范了一个HibernateUtil,它使用了ThreadLocal类别来建立一个Session管理的辅助类,这是Hibernate的Session管理一个广为应用的解决方案,ThreadLocal是*Thread-Specific Storage 模式*的一个运作实例。
由于Thread-Specific Stroage模式可以有效隔离执行绪所使用的数据,所以避开Session的多执行绪之间的数据共享问题,以下列出Hibernate参考手册中的HibernateUtil类:
HibernateUtil.java
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.hibernate.*;
import org.hibernate.cfg.*;
 
public class HibernateUtil {
    private static Log log = LogFactory.getLog(HibernateUtil.class);
    private static final SessionFactory sessionFactory;
 
    static {
        try {
            // Create the SessionFactory
            sessionFactory = new Configuration().configure()
                                                .buildSessionFactory();
        } catch (Throwable ex) {
            // Make sure you log the exception, as it might be swallowed
            log.error("Initial SessionFactory creation failed.", ex);
            throw new ExceptionInInitializerError(ex);
        }
    }
 
    public static final ThreadLocal session = new ThreadLocal();
 
    public static Session currentSession() {
        Session s = (Session) session.get();
 
        // Open a new Session, if this Thread has none yet
        if (s == null) {
            s = sessionFactory.openSession();
            session.set(s);
        }
 
        return s;
    }
 
    public static void closeSession() {
        Session s = (Session) session.get();
 
        if (s != null) {
            s.close();
        }
 
        session.set(null);
    }
}
在同一个执行绪中,Session被暂存下来了,但无须担心数据库连结Connection持续占用问题,Hibernate会在真正需要数据库操作时才(从连接池中)取得Connection。
在程序中可以这么使用HibernateUtil:
Session session = HibernateUtil.currentSession();
User user = (User) session.load(User.class, new Integer(1));
System.out.println(user.getName());
HibernateUtil.closeSession();
在Web应用程序中,可以藉助Filter来进行Session管理,在需要的时候开启Session,并在Request结束之后关闭Session,这个部份,在 JavaWorld 技术论坛 的 Wiki 上有篇 在filter中关闭session可以参考。
33 Criteria 基本查询
Criteria对SQL进行封装,让开发人员可以用对象的方式来对数据库进行操作,例如下面的查询User表格中的所有数据:
Criteria criteria = session.createCriteria(User.class);
// 查询user所有字段
List users = criteria.list();
Iterator iterator = users.iterator();
System.out.println("id /t name/age");
while(iterator.hasNext()) {
    User user = (User) iterator.next();
    System.out.println(user.getId() +
                               " /t " + user.getName() +
                               "/" + user.getAge());           
}
Hibernate实际上使用以下的SQL来查询数据库:
select this_.id as id0_, this_.name as name0_0_, this_.age as age0_0_ from user this_
Criteria实际上只是个容器,如果想要设定查询条件,则要使用add()方法加入Restrictions的条件限制,例如查询age大于20且小于40的数据:
Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.gt("age", new Integer(20)));
criteria.add(Restrictions.lt("age", new Integer(40)));
List users = criteria.list();
您也可以使用逻辑组合来进行查询,例如结合age等于(eq)20或(or)age为空(isNull)的条件:
Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.or(
                   Restrictions.eq("age", new Integer(20)),
                   Restrictions.isNull("age")
               ));
List users = criteria.list();
也可以使用sqlRestriction()方法来提供SQL语法作限定查询,例如查询name以cater开头的数据:
Criteria criteria = session.createCriteria(User.class);
criteria.add(Restrictions.sqlRestriction("{alias}.name LIKE (?)", "cater%", Hibernate.STRING));
List users = criteria.list();
其中alias将被替换为与User类别相关的名称,而?将被替换为cater%,也就是第二个参数所提供的值,在SQL撰写时,不必再写WHERE,如果有多个查询条件,例如BETWEEN子句的查询,则可以如下:
Criteria criteria = session.createCriteria(User.class);
Integer[] ages = {new Integer(20), new Integer(40)};
Type[] types = {Hibernate.INTEGER, Hibernate.INTEGER};
criteria.add(Restrictions.sqlRestriction("{alias}.age BETWEEN (?) AND (?)", ages, types));
List users = criteria.list();
Restrictions的几个常用限定查询方法如下表所示:
方法
说明
Restrictions.eq
等于
Restrictions.allEq
使用 Map ,使用 key/value 进行多个等于的比对
Restrictions.gt
大于 >
Restrictions.ge
大于等于 >=
Restrictions.lt
小于 <
Restrictions.le
小于等于 <=
Restrictions.between
对应 SQL BETWEEN 子句
Restrictions.like
对应 SQL LIKE 子句
Restrictions.in
对应 SQL in 子句
Restrictions.and
and 关系
Restrictions.or
or 关系
Restrictions.sqlRestriction
SQL 限定查询
 
34 Criteria 进阶查询
您可以使用Criteria进行查询,并使用Order对结果进行排序,例如使用Oder.asc()由小到大排序(反之则使用desc()):
Criteria criteria = session.createCriteria(User.class);
criteria.addOrder(Order.asc("age"));
List users = criteria.list();
setMaxResults()方法可以限定查询回来的笔数,如果配合setFirstResult()设定传回查询结果第一笔数据的位置,就可以实现简单的分页,例如传回第51笔之后的50笔数据(如果有的话):
Criteria criteria = session.createCriteria(User.class);
criteria.setFirstResult(51);
criteria.setMaxResult(50);
List users = criteria.list();
您可以对查询结果进行统计动作,使用Projections的avg()、rowCount()、count()、max()、min()、countDistinct()等方法,例如对查询结果的"age"作平均:
Criteria criteria = session.createCriteria(User.class);
criteria.setProjection(Projections.avg("age"));
List users = criteria.list();
Iterator iterator = users.iterator();
while(iterator.hasNext()) {
    System.out.println(iterator.next());      
}
还可以配合Projections的groupProperty()来对结果进行分组,例如以"age"进行分组,也就是如果数据中"age"如果有20、20、25、30,则以下会显示20、25、30:
Criteria criteria = session.createCriteria(User.class);
criteria.setProjection(Projections.groupProperty("age"));
List users = criteria.list();
Iterator iterator = users.iterator();
while(iterator.hasNext()) {
    System.out.println(iterator.next());      
}
如果想结合统计与分组功能,则可以使用ProjectionList,例如下面的程序会计算每个年龄各有多少个人:
ProjectionList projectionList = Projections.projectionList();
projectionList.add(Projections.groupProperty("age"));
projectionList.add(Projections.rowCount());
 
Criteria criteria = session.createCriteria(User.class);
criteria.setProjection(projectionList);
List users = criteria.list();
Iterator iterator = users.iterator();
while(iterator.hasNext()) {
    Object[] o = (Object[]) iterator.next();
    System.out.println(o[0] + "/t" + o[1]);
}
如果有一个已知的对象,则可以根据这个对象作为查询的依据,看看是否有属性与之类似的对象,例如:
User user = new User();
user.setAge(new Integer(30));
 
Criteria criteria = session.createCriteria(User.class);
criteria.add(Example.create(user));
 
List users = criteria.list();
Iterator iterator = users.iterator();
System.out.println("id /t name/age");
while(iterator.hasNext()) {
    User ur = (User) iterator.next();
    System.out.println(ur.getId() +
                                " /t " + ur.getName() +
                                "/" + ur.getAge());            
}
在这个例子中,user对象中有已知的属性"age"为30,使用Example会自动过滤掉user的空属性,并以之作为查询的依据,也就是找出"age"同为30的数据。
Criteria可以进行复合查询,即在原有的查询基础上再进行查询,例如在Room对User的一对多关联中,在查询出所有的Room数据之后,希望再查询users中"age"为30的user数据:
Criteria roomCriteria = session.createCriteria(Room.class);
Criteria userCriteria = roomCriteria.createCriteria("users");
userCriteria.add(Restrictions.eq("age", new Integer(30)));
List rooms = roomCriteria.list(); // 只列出users属性中有user之"age"为30的Room
Iterator iterator = rooms.iterator();
35 DetchedCriteria
Criteria与Session绑定,其生命周期跟随着Session结束而结束,使用Criteria时进行查询时,每次都要于执行时期动态建立对象,并加入各种查询条件,随着Session的回收,Criteria也跟着回收。
为了能够重复使用Criteria对象,在Hibernate 3中新增了DetchedCriteria,您可以先建立DetchedCriteria实例,并加入各种查询条件,并于需要查询时再与Session绑定,获得一个绑定Session的Criteria对象,例如:
// 先建立DetchedCriteria对象
DetachedCriteria detchedCriteria = DetachedCriteria.forClass(User.class);
// 加入查询条件
detchedCriteria.add(Restrictions.ge("age",new Integer(25)));
       
Session session = sessionFactory.openSession();
// 绑定Session并返回一个Criteria实例
Criteria criteria = detchedCriteria.getExecutableCriteria(session);
       
List users = criteria.list();
Iterator iterator = users.iterator();
System.out.println("id /t name/age");
while(iterator.hasNext()) {
    User ur = (User) iterator.next();
    System.out.println(ur.getId() +
                               " /t " + ur.getName() +
                               "/" + ur.getAge());           
}
 
36 Query
可以透过org.hibernate.Query接口的实例来进行查询,透过Query接口,您可以先设定查询参数,之后透过setXXX()等方法,将指定的参数值填入,而不用每次都撰写完整的HQL,直接来看个例子:
Session session = sessionFactory.openSession();
Query query = session.createQuery("select user.name from User as user where user.age > ?");
query.setInteger(0, 25);
 
List names = query.list();
Iterator iterator = names.iterator();
while(iterator.hasNext()) {
    System.out.println(iterator.next());
}
 
session.close();
在设定参数值时,必须依照 ? 所设定的顺序,并使用对应型态的setXXX()方法,一个执行的例子如下:
Hibernate: select user0_.name as col_0_0_ from user user0_ where user0_.age>?
momor
caterpillar
bush
您可以使用命名参数(Named Parameter)来取代这个方法,这可以不用依照特定的顺序来设定参数值,并拥有较好的可读性,直接来看个例子:
Session session = sessionFactory.openSession();
Query query = session.createQuery("select user.name from User as user where user.age > :minAge");
query.setInteger(":minAge", 25);
 
List names = query.list();
Iterator iterator = names.iterator();
while(iterator.hasNext()) {
    System.out.println(iterator.next());
}
 
session.close();
设定命名参数时,在建立Query时先使用:后跟着参数名,之后就可以在setXXX()方法中直接指定参数名来设定参数值,而不用依照特定的顺序。
也可以将HQL撰写在程序之外,以避免硬编码(Hard code)在程序之中,在需要修改HQL时就很方便,在*.hbm.xml中使用<query/>标签,并在<![CDATA[与]] >之间撰写HQL,撰写的位置是在</hibernate-mapping>之前,例如:
User.hbm.xml
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-mapping     
 PUBLIC "-//Hibernate/Hibernate Mapping DTD 3.0//EN"     
 "http://hibernate.sourceforge.net/hibernate-mapping-3.0.dtd">
 <hibernate-mapping>
    <class name="onlyfun.caterpillar.User" table="user">
      <id name="id" column="id" type="java.lang.Integer">
        <generator class="native"/>
      </id>
      <property name="name" column="name" type="java.lang.String"/>
      <property name="age" column="age" type="java.lang.Integer"/>
    </class>
    <query name="onlyfun.caterpillar.QueryUser">
      <![CDATA[            
        select user.name from User as user where user.age > :minAge       
      ]]>    
    </query>
 </hibernate-mapping>
<query>的name属性用来设定查询外部HQL时的名称依据,使用的例子如下:
Session session = sessionFactory.openSession();
Query query = session.getNamedQuery("onlyfun.caterpillar.QueryUser");
query.setInteger("minAge", 25);
 
List names = query.list();
Iterator iterator = names.iterator();
while(iterator.hasNext()) {
    System.out.println(iterator.next());
}
 
session.close();
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值