hibernate入门(十二)一些细节问题分析

1. 实体配置文件中的类型问题

在前面的多个实例中配置<property> 映射类型时,我们都采取的是hibernate 默认的配置,即是说没有在<property> 中配置type 属性:其实基本类型一般不需要映射文件中配置,只有在一个java 类型与多个数据库类型相对应时,交且我们不希望使用默认的配置才会用type 来指明类型。 举例:java.util.Date 与数据库中的DATETIMEDATETIME,TIMESTAMP 相对应,如果我们不希望映射成默认的DATATIME, 而想映成DATE ,便配置成type=DATE”, 另需要说明的是我们也可配置成我们自定义的类型,但前提是我们自定义的类型必须实现 org.hibernate.UserTypeorg.hibernate.CompositeUserType 中的任何一个接口。且在配置时指定类的完整名

2.Session SessionFactory

Session 是非线程安全的,生命周期较短,代表一个数据库的连接,在B/S 系统中一般不会超过一个请求,内部维护一级缓存和数据库连接。通常一个Session 的生成也就意味着即将结束。

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

3.Session 中的flush 方法

此方法的作用是将一级缓存与数据库同步,通常在查询数据或者是提交前( 可能会采取批量更新的方式到数据库) 会实现同步,这样可有效保证一级缓存中的数据是有效的数据。这个方法是由hibernate 自身来维护调用的,因为它的调用意味着与数据库交互,所以不建议我们手工调用,而事实上hibernate 对它的调用主要是为了提升性能和保证数据的有效性且hibernate 自身的对它的维护调用已近完美。

4. 巧用flush 解决内存溢出问题

当我们进行类似如下的操作时: for ( int i = 0; i < 10000000; i++) s.save(obj);i 足大到我们的内存不足以承受时,便会出现内存溢出问题,对于此可以这样解决:

for ( int i = 0; i < 10000000; i++) {

    s.save(obj );

    if (i % 30 == 0) {

    s.flush();

    s.clear();

    }

} 分析 :当每保存30 个数据时,我们便清掉一级缓存中的内容,但是在清掉内容前我们必须让一级缓存中的数据与数据库进行一次交互,这样可以保证数据能被保存到数据库中。 试想下,如果不flush, 那么在一级缓存中的数据是不能同步到数据库中的,所以这里flush 是非常重要得。 其实只要再来回忆前面所说的一级缓存中的一些知识,便可以发现如果保存的对象的主键是以 increme 的方式生成的,可以不用flush 也能实现数据及时同步到数据库中

5.StatelessSession 接口解决批量更新问题:

在进行如上的批量操作时,我们通过flush 来解决和数据库的同步更新,但是这样会造成缓存的不断更新,因此在进行类似的批量操作时,我们通常会选择使用StatelessSession 接口来代替Session ,由于这种无状态的Session 具有以下特点:它不和一级缓存、二级缓存交互,也不触发任何事件,监听器,拦截器,通过该接口能把这些批量更新直接发送到数据库。说明:StatelessSession 的方法也Session 的方法相似。 除了此方法外,我们使用Query.executeUpdate (), 也可以执行批量更新,但是此方法会清除相关联的类的二级缓存,也可能造成级联,甚至是和乐观锁不相容。

6. 离线查询DetachedCriteria:

package com.asm.hibernate.test;

public class DetachedCriteriaTest {

    public static void main(String[] args) {

       add ();

        DetachedCriteria dec = DetachedCriteria.forClass (User. class );

       String name = "jack" ;

       dec.add(Restrictions.eq ( "name" , name));

       List<User> list = detaCri (dec);

       for (User u : list)

           System. out .println(u);

    }

    static List detaCri(DetachedCriteria dec) {

       Session s = HibernateUtil.getSession ();

       Criteria cr = dec.getExecutableCriteria(s);

       List list = cr.list();

       s.close();

       return list;

    }

 

    static void add() {

       ... 省略内容:主要作用是在数据库中插入两条名为“ jack ”的记录,以使查询操作可以进行。

    }

}

分析 : 为会么要使用这种离线查询,比如我们可以在业务逻辑层构造出一个DetachedCriteria 对象,且它不会依赖于Session ,这样当们把这个对象作为参数来传递给M 的方法时便可以实现连库查询。这样可以有效的使用Session ,避免与数据库的频繁交互。

7. 监听器的使用:

下面首通过一个简单的例子来说明此问题:

>> 步骤一、编写监听类:

package com.asm.hibernate.event;

public class SaveUserListener implements SaveOrUpdateEventListener {

    public void onSaveOrUpdate(SaveOrUpdateEvent event)

           throws HibernateException {

       if (event.getObject() instanceof com.asm.hibernate.domain.User) {

           User user = (User) event.getObject();

           System. out .println( "find save User:" + user.getName());

           user.setDate( new Date());

       }

    }

}

分析:当有保存/ 更新事件发生时,便被此监听类监听到,而我们处理的只是保存/ 更新User 对象,即是说当发生保存/ 更新一个User 对象时,才会被这个监听器监听操作;其它对象的保存/ 更新操作尽管也会被它监听,但不会有处理动作。

>> 步骤二、编写测试类:

package com.asm.hibernate.event;

public class TestListener {

    public static void main(String[] args) {

       addUser ();

    }

    static void addUser() {

       Session s = null ;

       Transaction tx = null ;

       try {

           User u = new User();

           u.setName( "jack" );

           s = HibernateUtil.getSession ();

           tx = s.beginTransaction();

           s.save(u);

           tx.commit();

       } finally {

           if (s != null )

              s.close();

       }

    }

}      分析 :执行后,发现没有任何作用。原因是未配置监听器,联想到gui 程序监听时,总会在程序中给出相应的代码,而这里我们只需要在主配置文件中配置这个监听器,在由hibernate 把这个监听器写成代码到程序中去。在主配置文件中配置的内容如下 :

< event type = "save" >

           < listener class = "com.asm.hibernate.event.SaveUserListener" />

</ event >

这样配置后,尽管监听器类中的方法能实现,但是发现这些数据没有保存到数据库中。因为当们配置一监听器时,相对应的默认监听器就失效了,即是说 org.hibernate.event.def.DefaultSaveOrUpdateEventListener 这个默认的监听器不起作用了,为了能继续使用默认的监听器,应再配置上这个监听器,即配置成这样:

< event type = "save" >

< listener class = "com.asm.hibernate.event.SaveUserListener" />

< listener class = "org.hibernate.event.def.DefaultSaveOrUpdateEventListener" />

</ event >

这样配置后,即可以让我们自己的监听器生效,又可以让默认监听器起作用。

8. 使用原始的sql 查询:

代码如下:

package com.asm.hibernate.test;

public class JdbcSqlSelectTest {

    public static void main(String[] args) {

       addUsers ();

       query ();

    }

    static void query() {

       Session s = HibernateUtil.getSession ();

       Query q = s.createSQLQuery( "select * from user" ).addEntity(User. class );

       List<User> rs = q.list();

       for (User u : rs) {

           System. out .println( "Result" +u.getName() + "---" + u.getDate());

       }

    }

    static void addUsers() {

       ... 增加一些记录,以检验查询

    }  

}    分析: 关键是看查询方法:中间用到的查询语句from user,user 是指表名,后面增加addEntity 方法的主要是了了保证返回的list 对象中保存的User, 这样方便foreach 遍历。 需要说明的是尽管hibernate 支持这种sql 查询,但是它不具有通用性,即是说我们在写这些查询语句时总会依赖于一个具体的数据库,一旦换了数据库就可能出现问题,因此最好不要使用这种JdbcSql 查询。

9. 命名查询:

>> 步骤一:在User 的实体配置文件 </ hibernate-mapping > 的元素下增加如下内容:

< query name = "selectUserbyId" >

       <![CDATA[ from User where id=:id ]]>

</ query >

>> 步骤二、编写测试:

static List namedQuery( int id) {

       Session s = HibernateUtil.getSession ();

       Query q = s.getNamedQuery( "selectUserbyId" );

       //Query q=s.getNamedQuery("com.asm.hibernate.domain.User.selectUserbyIdTheSecond");

       q.setInteger( "id" , id);

       return q.list();

    }   

说明 : 这种方式虽然简单,但是容易在整个系统中引起重名,所以最好的方式是在Use 的实体配置文件的<class> 元素下配置这个属性,比如我们在<class> 元素的最后增加如下内容:

< query name = "selectUserbyIdTheSecond" >

           <![CDATA[ from User where id=:id ]]>

</ query > 则在上面的代码中要想用到这个命名查询,则应写成注释掉的代码那样。如果项目中的命名查询不多,建议写在 </ hibernate-mapping > 元素下,这样引用较方便。

补充说明 :我们也可以配置SQl 的命名查询,步骤是先在 </ hibernate-mapping > 元素下配置如下内容:

< sql-query name = "selectUserSql" >

   

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值