<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
  <channel>
    <title>继续走，不要停也不回头</title>
    <description>擅长JAVA编程，熟练掌握STRUTS,HIERNATE,SPRING等框架技术，熟悉ORACLE，SQLSERVER数据库。</description>
    <link>http://coder1982.javaeye.com</link>
    <language>UTF-8</language>
    <copyright>Copyright 2003-2008, JavaEye.com</copyright>
    <docs>http://blogs.law.harvard.edu/tech/rss</docs>
    <generator>JavaEye - 做最棒的软件开发交流社区</generator>
      <item>
        <title>（转帖）java.util.concurrent   线程池 </title>
        <author>coder1982</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coder1982.javaeye.com">coder1982</a>&nbsp;
          链接：<a href="http://coder1982.javaeye.com/blog/175524" style="color:red;">http://coder1982.javaeye.com/blog/175524</a>&nbsp;
          发表时间: 2008年03月24日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          一般的服务器都需要线程池，比如Web、FTP等服务器，不过它们一般都自己实现了线程池，比如以前介绍过的Tomcat、Resin和Jetty等，现在有了JDK5，我们就没有必要重复造车轮了，直接使用就可以，何况使用也很方便，性能也非常高。 <br /><br />package concurrent;<br />import java.util.concurrent.ExecutorService;<br />import java.util.concurrent.Executors;<br />public class TestThreadPool {<br />  public static void main(String args[]) throws InterruptedException {<br />    // only two threads<br />    ExecutorService exec = Executors.newFixedThreadPool(2);<br />    for(int index = 0; index &lt; 100; index++) {<br />      Runnable run = new Runnable() {<br />        public void run() {<br />          long time = (long) (Math.random() * 1000);<br />          System.out.println("Sleeping " + time + "ms");<br />            try {<br />              Thread.sleep(time);<br />            } catch (InterruptedException e) {<br />            }<br />        }<br />      };<br />      exec.execute(run);<br />    }<br />    // must shutdown<br />    exec.shutdown();<br />  }<br />}  <br /><br />上面是一个简单的例子，使用了2个大小的线程池来处理100个线程。但有一个问题：在for循环的过程中，会等待线程池有空闲的线程，所以主线程会阻塞的。为了解决这个问题，一般启动一个线程来做for循环，就是为了避免由于线程池满了造成主线程阻塞。不过在这里我没有这样处理。[重要修正：经过测试，即使线程池大小小于实际线程数大小，线程池也不会阻塞的，这与Tomcat的线程池不同，它将Runnable实例放到一个“无限”的BlockingQueue中，所以就不用一个线程启动for循环，Doug Lea果然厉害] <br /><br />另外它使用了Executors的静态函数生成一个固定的线程池，顾名思义，线程池的线程是不会释放的，即使它是Idle。这就会产生性能问题，比如如果线程池的大小为200，当全部使用完毕后，所有的线程会继续留在池中，相应的内存和线程切换（while(true)+sleep循环）都会增加。如果要避免这个问题，就必须直接使用ThreadPoolExecutor()来构造。可以像Tomcat的线程池一样设置“最大线程数”、“最小线程数”和“空闲线程keepAlive的时间”。通过这些可以基本上替换Tomcat的线程池实现方案。 <br /><br />需要注意的是线程池必须使用shutdown来显式关闭，否则主线程就无法退出。shutdown也不会阻塞主线程。 <br /><br />  <br /><br />许多长时间运行的应用有时候需要定时运行任务完成一些诸如统计、优化等工作，比如在电信行业中处理用户话单时，需要每隔1分钟处理话单；网站每天凌晨统计用户访问量、用户数；大型超时凌晨3点统计当天销售额、以及最热卖的商品；每周日进行数据库备份；公司每个月的10号计算工资并进行转帐等，这些都是定时任务。通过 java的并发库concurrent可以轻松的完成这些任务，而且非常的简单。 <br /><br />package concurrent;<br />import static java.util.concurrent.TimeUnit.SECONDS;<br />import java.util.Date;<br />import java.util.concurrent.Executors;<br />import java.util.concurrent.ScheduledExecutorService;<br />import java.util.concurrent.ScheduledFuture;<br />public class TestScheduledThread {<br />  public static void main(String[] args) {<br />    final ScheduledExecutorService scheduler = Executors<br />        .newScheduledThreadPool(2);<br />    final Runnable beeper = new Runnable() {<br />      int count = 0;<br />      public void run() {<br />        System.out.println(new Date() + " beep " + (++count));<br />      }<br />    };<br />    // 1秒钟后运行，并每隔2秒运行一次<br />    final ScheduledFuture&lt;?> beeperHandle = scheduler.scheduleAtFixedRate(<br />        beeper, 1, 2, SECONDS);<br />    // 2秒钟后运行，并每次在上次任务运行完后等待5秒后重新运行<br />    final ScheduledFuture&lt;?> beeperHandle2 = scheduler<br />        .scheduleWithFixedDelay(beeper, 2, 5, SECONDS);<br />    // 30秒后结束关闭任务，并且关闭Scheduler<br />    scheduler.schedule(new Runnable() {<br />      public void run() {<br />        beeperHandle.cancel(true);<br />        beeperHandle2.cancel(true);<br />        scheduler.shutdown();<br />      }<br />    }, 30, SECONDS);<br />  }<br />}  <br /><br />为了退出进程，上面的代码中加入了关闭Scheduler的操作。而对于24小时运行的应用而言，是没有必要关闭Scheduler的。 <br /><br />  <br /><br />在实际应用中，有时候需要多个线程同时工作以完成同一件事情，而且在完成过程中，往往会等待其他线程都完成某一阶段后再执行，等所有线程都到达某一个阶段后再统一执行。 <br /><br />比如有几个旅行团需要途经深圳、广州、韶关、长沙最后到达武汉。旅行团中有自驾游的，有徒步的，有乘坐旅游大巴的；这些旅行团同时出发，并且每到一个目的地，都要等待其他旅行团到达此地后再同时出发，直到都到达终点站武汉。 <br /><br />这时候CyclicBarrier就可以派上用场。CyclicBarrier最重要的属性就是参与者个数，另外最要方法是await()。当所有线程都调用了await()后，就表示这些线程都可以继续执行，否则就会等待。
          <br/>
          <span style="color:red;">
            <a href="http://coder1982.javaeye.com/blog/175524#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Mon, 24 Mar 2008 18:04:17 +0800</pubDate>
        <link>http://coder1982.javaeye.com/blog/175524</link>
        <guid>http://coder1982.javaeye.com/blog/175524</guid>
      </item>
      <item>
        <title>(转贴)Hibernate数据加载——Session.get/load </title>
        <author>coder1982</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coder1982.javaeye.com">coder1982</a>&nbsp;
          链接：<a href="http://coder1982.javaeye.com/blog/169009" style="color:red;">http://coder1982.javaeye.com/blog/169009</a>&nbsp;
          发表时间: 2008年03月07日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Session.load/get方法均可以根据指定的实体类和id从数据库读取记录，并返回与之对应的实体对象。其区别在于：<br /><br />如果未能发现符合条件的记录，get方法返回null，而load方法会抛出一个ObjectNotFoundException。 <br />Load方法可返回实体的代理类实例，而get方法永远直接返回实体类。 <br />load方法可以充分利用内部缓存和二级缓存中的现有数据，而get方法则仅仅在内部缓存中进行数据查找，如没有发现对应数据，将越过二级缓存，直接调用SQL完成数据读取。 <br />Session在加载实体对象时，将经过的过程：<br /><br />首先，Hibernate中维持了两级缓存。第一级缓存由Session实例维护，其中保持了Session当前所有关联实体的数据，也称为内部缓存。而第二级缓存则存在于SessionFactory层次，由当前所有由本SessionFactory构造的Session实例共享。出于性能考虑，避免无谓的数据库访问，Session在调用数据库查询功能之前，会先在缓存中进行查询。首先在第一级缓存中，通过实体类型和id进行查找，如果第一级缓存查找命中，且数据状态合法，则直接返回。 <br />之后，Session会在当前“NonExists”记录中进行查找，如果“NonExists”记录中存在同样的查询条件，则返回null。“NonExists”记录了当前Session实例在之前所有查询操作中，未能查询到有效数据的查询条件（相当于一个查询黑名单列表）。如此一来，如果Session中一个无效的查询条件重复出现，即可迅速作出判断，从而获得最佳的性能表现。 <br />对于load方法而言，如果内部缓存中未发现有效数据，则查询第二级缓存，如果第二级缓存命中，则返回。 <br />如在缓存中未发现有效数据，则发起数据库查询操作（Select SQL），如经过查询未发现对应记录，则将此次查询的信息在“NonExists”中加以记录，并返回null。 <br />根据映射配置和Select SQL得到的ResultSet，创建对应的数据对象。 <br />将其数据对象纳入当前Session实体管理容器（一级缓存）。 <br />执行Interceptor.onLoad方法（如果有对应的Interceptor）。 <br />将数据对象纳入二级缓存。 <br />如果数据对象实现了LifeCycle接口，则调用数据对象的onLoad方法。 <br />返回数据对象。
          <br/>
          <span style="color:red;">
            <a href="http://coder1982.javaeye.com/blog/169009#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 07 Mar 2008 11:54:25 +0800</pubDate>
        <link>http://coder1982.javaeye.com/blog/169009</link>
        <guid>http://coder1982.javaeye.com/blog/169009</guid>
      </item>
      <item>
        <title>（转贴）hibernate延迟检索</title>
        <author>coder1982</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coder1982.javaeye.com">coder1982</a>&nbsp;
          链接：<a href="http://coder1982.javaeye.com/blog/169002" style="color:red;">http://coder1982.javaeye.com/blog/169002</a>&nbsp;
          发表时间: 2008年03月07日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          <p>简介：当执行Session的load()方法时，Hibernate不会立即执行查询CUSTOMERS表的select语句，仅仅返回Customer类的代理类的实例，这个代理类具由以下特征：（1）由Hibernate在运行时动态生成，它扩展了Customer类，因此它继承了Customer类的所有属性和方法，但它的实现对于应用程序是透明的。（2）当Hibernate创建Customer代理类实例时，仅仅初始化了它的OID属性，其他属性都为null，因此这个代理类实例占用的内存很少。（3）当应用</p><p>内容：</p><p><span style="font-family: Arial">其实这个异常写的非常之清楚，就是会话关闭，无法对Hibernate实体进行操作。造成这样的情况有很多，什么书写错误啊，逻辑错误啊。</span></p><p><span style="font-family: Arial">但就此说一下关于lazy机制：</span></p><p><span style="font-family: Arial">延迟初始化错误是运用Hibernate开发项目时最常见的错误。如果对一个类或者集合配置了延迟检索策略，那么必须当代理类实例或代理集合处于持久化状态（即处于Session范围内）时，才能初始化它。如果在游离状态时才初始化它，就会产生延迟初始化错误。</span></p><p><span style="font-family: Arial">下面把Customer.hbm.xml文件的&lt;class&gt;元素的lazy属性设为true，表示使用延迟检索策略：</span></p><p><span style="font-family: Arial">&lt;class name=&quot;mypack.Customer&quot; table=&quot;CUSTOMERS&quot; lazy=&quot;true&quot;&gt;</span></p><p><span style="font-family: Arial">当执行Session的load()方法时，Hibernate不会立即执行查询CUSTOMERS表的select语句，仅仅返回Customer类的代理类的实例，这个代理类具由以下特征：</span></p><p><span style="font-family: Arial">（1）由Hibernate在运行时动态生成，它扩展了Customer类，因此它继承了Customer类的所有属性和方法，但它的实现对于应用程序是透明的。<br />（2）当Hibernate创建Customer代理类实例时，仅仅初始化了它的OID属性，其他属性都为null，因此这个代理类实例占用的内存很少。<br />（3）当应用程序第一次访问Customer代理类实例时（例如调用customer.getXXX()或customer.setXXX()方法）， Hibernate会初始化代理类实例，在初始化过程中执行select语句，真正从数据库中加载Customer对象的所有数据。但有个例外，那就是当应用程序访问Customer代理类实例的getId()方法时，Hibernate不会初始化代理类实例，因为在创建代理类实例时OID就存在了，不必到数据库中去查询。</span></p><p><span style="font-family: Arial">提示：Hibernate采用CGLIB工具来生成持久化类的代理类。CGLIB是一个功能强大的Java 字节码生成工具，它能够在程序运行时动态生成扩展 Java类或者实现Java接口的代理类。关于CGLIB的更多知识，请参考：http://cglib.sourceforge.net/。</span></p><p><span style="font-family: Arial">以下代码先通过Session的load()方法加载Customer对象，然后访问它的name属性：</span></p><p><span style="font-family: Arial">tx = session.beginTransaction();<br />Customer customer=(Customer)session.load(Customer.class,new Long(1));<br />customer.getName();<br />tx.commit();</span></p><p><span style="font-family: Arial">在运行session.load()方法时Hibernate不执行任何select语句，仅仅返回 Customer类的代理类的实例，它的OID为1，这是由load()方法的第二个参数指定的。当应用程序调用customer.getName()方法时，Hibernate会初始化Customer代理类实例，从数据库中加载Customer对象的数据，执行以下select语句：</span></p><p><span style="font-family: Arial">select * from CUSTOMERS where ID=1;<br />select * from ORDERS where CUSTOMER_ID=1;</span></p><p><span style="font-family: Arial">当&lt;class&gt;元素的lazy属性为true，会影响Session的load()方法的各种运行时行为，下面举例说明。</span></p><p><span style="font-family: Arial">1．如果加载的Customer对象在数据库中不存在，Session的load()方法不会抛出异常，只有当运行customer.getName()方法时才会抛出以下异常：</span></p><p><span style="font-family: Arial">ERROR LazyInitializer:63 - Exception initializing proxy<br />net.sf.hibernate.ObjectNotFoundException: No row with the given identifier exists: 1, of class:<br />mypack.Customer</span></p><p><span style="font-family: Arial">2．如果在整个Session范围内，应用程序没有访问过Customer对象，那么Customer代理类的实例一直不会被初始化，Hibernate不会执行任何select语句。以下代码试图在关闭Session后访问Customer游离对象：</span></p><p><span style="font-family: Arial">tx = session.beginTransaction();<br />Customer customer=(Customer)session.load(Customer.class,new Long(1));<br />tx.commit();<br />session.close();<br />customer.getName();</span></p><p><span style="font-family: Arial">由于引用变量customer引用的Customer代理类的实例在Session范围内始终没有被初始化，因此在执行customer.getName()方法时，Hibernate会抛出以下异常：</span></p><p><span style="font-family: Arial">ERROR LazyInitializer:63 - Exception initializing proxy<br />net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed</span></p><p><span style="font-family: Arial">由此可见，Customer代理类的实例只有在当前Session范围内才能被初始化。</span></p><p><span style="font-family: Arial">3．net.sf.hibernate.Hibernate类的initialize()静态方法用于在Session范围内显式初始化代理类实例，isInitialized()方法用于判断代理类实例是否已经被初始化。例如：</span></p><p><span style="font-family: Arial">tx = session.beginTransaction();<br />Customer customer=(Customer)session.load(Customer.class,new Long(1));<br />if(!Hibernate.isInitialized(customer))<br />Hibernate.initialize(customer);<br />tx.commit();<br />session.close();<br />customer.getName();</span></p><p><span style="font-family: Arial">以上代码在Session范围内通过Hibernate类的initialize()方法显式初始化了Customer代理类实例，因此当Session关闭后，可以正常访问Customer游离对象。</span></p><p><span style="font-family: Arial">4．当应用程序访问代理类实例的getId()方法时，不会触发Hibernate初始化代理类实例的行为，例如：</span></p><p><span style="font-family: Arial">tx = session.beginTransaction();<br />Customer customer=(Customer)session.load(Customer.class,new Long(1));<br />customer.getId();<br />tx.commit();<br />session.close();<br />customer.getName();</span></p><p><span style="font-family: Arial">当应用程序访问customer.getId()方法时，该方法直接返回Customer代理类实例的 OID值，无需查询数据库。由于引用变量 customer始终引用的是没有被初始化的Customer代理类实例，因此当Session关闭后再执行customer.getName()方法， Hibernate会抛出以下异常：</span></p><p><span style="font-family: Arial">ERROR LazyInitializer:63 - Exception initializing proxy<br />net.sf.hibernate.HibernateException: Could not initialize proxy - the owning Session was closed</span></p><p><span style="font-family: Arial"><br />解决方法：</span></p><p>由于hibernate采用了lazy=true,这样当你用hibernate查询时,返回实际为利用cglib增强的代理类,但其并没有实际填充;当你在前端,利用它来取值(getXXX)时,这时Hibernate才会到数据库执行查询,并填充对象,但此时如果和这个代理类相关的 session已关闭掉,就会产生种错误.<br />在做一对多时，有时会出现&quot;could not initialize proxy - clothe owning Session was sed,这个好像是hibernate的缓存问题.问题解决:需要在&lt;many-to-one&gt;里设置lazy=&quot;false&quot;. 但有可能会引发另一个异常叫</p><p>failed to lazily initialize a collection of role: XXXXXXXX, no session or session was closed</p><p>此异常解决方案请察看本人博客（http://hi.baidu.com/kekemao1）的Hibernate异常中的《failed to lazily initialize a collection of role异常》</p><p>?<br />解决方法:在web.xml中加入<br />&lt;filter&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter-name&gt;hibernateFilter&lt;/filter-name&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter-class&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; org.springframework.orm.hibernate3.support.OpenSessionInViewFilter<br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;/filter-class&gt;<br />&lt;/filter<br />&lt;filter-mapping&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;filter-name&gt;hibernateFilter&lt;/filter-name&gt;<br />&nbsp;&nbsp;&nbsp;&nbsp; &lt;url-pattern&gt;*.do&lt;/url-pattern&gt;<br />&lt;/filter-mapping&gt;<br />就可以了;</p><p>参考了:<br />Hibernate与延迟加载：</p><p>Hibernate对象关系映射提供延迟的与非延迟的对象初始化。非延迟加载在读取一个对象的时候会将与这个对象所有相关的其他对象一起读取出来。这有时会导致成百的（如果不是成千的话）select语句在读取对象的时候执行。这个问题有时出现在使用双向关系的时候，经常会导致整个数据库都在初始化的阶段被读出来了。当然，你可以不厌其烦地检查每一个对象与其他对象的关系，并把那些最昂贵的删除，但是到最后，我们可能会因此失去了本想在ORM工具中获得的便利。</p><p><br />一个明显的解决方法是使用Hibernate提供的延迟加载机制。这种初始化策略只在一个对象调用它的一对多或多对多关系时才将关系对象读取出来。这个过程对开发者来说是透明的，而且只进行了很少的数据库操作请求，因此会得到比较明显的性能提升。这项技术的一个缺陷是延迟加载技术要求一个 Hibernate会话要在对象使用的时候一直开着。这会成为通过使用DAO模式将持久层抽象出来时的一个主要问题。为了将持久化机制完全地抽象出来，所有的数据库逻辑，包括打开或关闭会话，都不能在应用层出现。最常见的是，一些实现了简单接口的DAO实现类将数据库逻辑完全封装起来了。一种快速但是笨拙的解决方法是放弃DAO模式，将数据库连接逻辑加到应用层中来。这可能对一些小的应用程序有效，但是在大的系统中，这是一个严重的设计缺陷，妨碍了系统的可扩展性。</p><p>在Web层进行延迟加载</p><p>幸运的是，Spring框架为Hibernate延迟加载与DAO模式的整合提供了一种方便的解决方法。对那些不熟悉Spring与 Hibernate集成使用的人，我不会在这里讨论过多的细节，但是我建议你去了解Hibernate与Spring集成的数据访问。以一个Web应用为例，Spring提供了OpenSessionInViewFilter和OpenSessionInViewInterceptor。我们可以随意选择一个类来实现相同的功能。两种方法唯一的不同就在于interceptor在Spring容器中运行并被配置在web应用的上下文中，而Filter在 Spring之前运行并被配置在web.xml中。不管用哪个，他们都在请求将当前会话与当前（数据库）线程绑定时打开Hibernate会话。一旦已绑定到线程，这个打开了的Hibernate会话可以在DAO实现类中透明地使用。这个会话会为延迟加载数据库中值对象的视图保持打开状态。一旦这个逻辑视图完成了，Hibernate会话会在Filter的doFilter方法或者Interceptor的postHandle方法中被关闭。下面是每个组件的配置示例：</p><p>&nbsp;</p><p>Interceptor的配置:</p><p><br />&lt;beans&gt;<br />&lt;bean id=&quot;urlMapping&quot;<br />class=&quot;org.springframework.web.servlet.handler.SimpleUrlHandlerMapping&quot;&gt;<br />&lt;property name=&quot;interceptors&quot;&gt;<br />&lt;list&gt;<br />&lt;ref bean=&quot;openSessionInViewInterceptor&quot;/&gt;<br />&lt;/list&gt;<br />&lt;/property&gt;<br />&lt;property name=&quot;mappings&quot;&gt;</p><p>&lt;/bean&gt;</p><p>&lt;bean name=&quot;openSessionInViewInterceptor&quot;<br />class=&quot;org.springframework.orm.hibernate.support.OpenSessionInViewInterceptor&quot;&gt;<br />&lt;property name=&quot;sessionFactory&quot;&gt;&lt;ref bean=&quot;sessionFactory&quot;/&gt;&lt;/property&gt;<br />&lt;/bean&gt;<br />&lt;/beans&gt;</p><p>Filter的配置</p><p><br />&lt;web-app&gt;</p><p>&lt;filter&gt;<br />&lt;filter-name&gt;hibernateFilter&lt;/filter-name&gt;<br />&lt;filter-class&gt;<br />org.springframework.orm.hibernate.support.OpenSessionInViewFilter<br />&lt;/filter-class&gt;<br />&lt;/filter&gt;</p><p>&lt;filter-mapping&gt;<br />&lt;filter-name&gt;hibernateFilter&lt;/filter-name&gt;<br />&lt;url-pattern&gt;*. spring &lt;/url-pattern&gt;<br />&lt;/filter-mapping&gt;</p><p>&lt;/web-app&gt;</p><p><br />实现Hibernate的Dao接口来使用打开的会话是很容易的。事实上，如果你已经使用了Spring框架来实现你的Hibernate Dao,很可能你不需要改变任何东西。方便的HibernateTemplate公用组件使访问数据库变成小菜一碟，而DAO接口只有通过这个组件才可以访问到数据库。下面是一个示例的DAO：</p><p><br />public class HibernateProductDAO extends HibernateDaoSupport implements ProductDAO {</p><p>public Product getProduct(Integer productId) {<br />return (Product)getHibernateTemplate().load(Product.class, productId);<br />}</p><p>public Integer saveProduct(Product product) {<br />return (Integer) getHibernateTemplate().save(product);<br />}</p><p>public void updateProduct(Product product) {<br />getHibernateTemplate().update(product);<br />}<br />}</p><p><br />在业务逻辑层中使用延迟加载</p><p>即使在视图外面，Spring框架也通过使用AOP 拦截器 HibernateInterceptor来使得延迟加载变得很容易实现。这个Hibernate 拦截器透明地将调用配置在Spring应用程序上下文中的业务对象中方法的请求拦截下来，在调用方法之前打开一个Hibernate会话，然后在方法执行完之后将会话关闭。让我们来看一个简单的例子，假设我们有一个接口BussinessObject：</p><p><br />public&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; interface&nbsp;&nbsp;&nbsp;&nbsp; BusinessObject&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />public&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp;&nbsp;&nbsp; doSomethingThatInvolvesDaos();<br />}<br />类BusinessObjectImpl实现了BusinessObject接口:</p><p>public&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; class&nbsp;&nbsp;&nbsp;&nbsp; BusinessObjectImpl&nbsp;&nbsp;&nbsp;&nbsp; implements&nbsp;&nbsp;&nbsp;&nbsp; BusinessObject&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />public&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; void&nbsp;&nbsp;&nbsp;&nbsp; doSomethingThatInvolvesDaos()&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; {<br />//&nbsp;&nbsp;&nbsp;&nbsp; lots of logic that calls<br />//&nbsp;&nbsp;&nbsp;&nbsp; DAO classes Which access<br />//&nbsp;&nbsp;&nbsp;&nbsp; data objects lazily&nbsp;<br />}&nbsp;<br />}&nbsp;</p><p>&nbsp;</p><p>通过在Spring应用程序上下文中的一些配置，我们可以让将调用BusinessObject的方法拦截下来，再令它的方法支持延迟加载。看看下面的一个程序片段：</p><p>&nbsp;</p><p>&lt;beans&gt;<br />&lt;bean id=&quot;hibernateInterceptor&quot; class=&quot;org.springframework.orm.hibernate.HibernateInterceptor&quot;&gt;<br />&lt;property name=&quot;sessionFactory&quot;&gt;<br />&lt;ref bean=&quot;sessionFactory&quot;/&gt;<br />&lt;/property&gt;<br />&lt;/bean&gt;<br />&lt;bean id=&quot;businessObjectTarget&quot; class=&quot;com.acompany.BusinessObjectImpl&quot;&gt;<br />&lt;property name=&quot;someDAO&quot;&gt;&lt;ref bean=&quot;someDAO&quot;/&gt;&lt;/property&gt;<br />&lt;/bean&gt;<br />&lt;bean id=&quot;businessObject&quot; class=&quot;org.springframework.aop.framework.ProxyFactoryBean&quot;&gt;<br />&lt;property name=&quot;target&quot;&gt;&lt;ref bean=&quot;businessObjectTarget&quot;/&gt;&lt;/property&gt;<br />&lt;property name=&quot;proxyInterfaces&quot;&gt;<br />&lt;value&gt;com.acompany.BusinessObject&lt;/value&gt;<br />&lt;/property&gt;<br />&lt;property name=&quot;interceptorNames&quot;&gt;<br />&lt;list&gt;<br />&lt;value&gt;hibernateInterceptor&lt;/value&gt;<br />&lt;/list&gt;<br />&lt;/property&gt;<br />&lt;/bean&gt;<br />&lt;/beans&gt;</p><p>当businessObject被调用的时候，HibernateInterceptor打开一个Hibernate会话，并将调用请求传递给 BusinessObjectImpl对象。当BusinessObjectImpl执行完成后，HibernateInterceptor透明地关闭了会话。应用层的代码不用了解任何持久层逻辑，还是实现了延迟加载。</p><p><br />在单元测试中测试延迟加载</p><p>最后，我们需要用J-Unit来测试我们的延迟加载程序。我们可以轻易地通过重写TestCase类中的setUp和tearDown方法来实现这个要求。我比较喜欢用这个方便的抽象类作为我所有测试类的基类。</p><p><br />public abstract class MyLazyTestCase extends TestCase {</p><p>private SessionFactory sessionFactory;<br />private Session session;</p><p>public void setUp() throws Exception {<br />super.setUp();<br />SessionFactory sessionFactory = (SessionFactory) getBean(&quot;sessionFactory&quot;);<br />session = SessionFactoryUtils.getSession(sessionFactory, true);<br />Session s = sessionFactory.openSession();<br />TransactionSynchronizationManager.bindResource(sessionFactory, new SessionHolder(s));</p><p>}</p><p>protected Object getBean(String beanName) {<br />//Code to get objects from Spring application context<br />}</p><p>public void tearDown() throws Exception {<br />super.tearDown();<br />SessionHolder holder = (SessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);<br />Session s = holder.getSession();<br />s.flush();<br />TransactionSynchronizationManager.unbindResource(sessionFactory);<br />SessionFactoryUtils.closeSessionIfNecessary(s, sessionFactory);<br />}<br />}</p>
          <br/>
          <span style="color:red;">
            <a href="http://coder1982.javaeye.com/blog/169002#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 07 Mar 2008 11:48:32 +0800</pubDate>
        <link>http://coder1982.javaeye.com/blog/169002</link>
        <guid>http://coder1982.javaeye.com/blog/169002</guid>
      </item>
      <item>
        <title>转贴:JBOSS的hibernate-service配置</title>
        <author>coder1982</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coder1982.javaeye.com">coder1982</a>&nbsp;
          链接：<a href="http://coder1982.javaeye.com/blog/150771" style="color:red;">http://coder1982.javaeye.com/blog/150771</a>&nbsp;
          发表时间: 2007年12月25日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Hibernate Tip: 使用JBOSS MBEAN时，Session会在事务提交后自动关闭！   文章指数:0  CSDN Blog推出文章指数概念，文章指数是对Blog文章综合评分后推算出的，综合评分项分别是该文章的点击量，回复次数，被网摘收录数量，文章长度和文章类型；满分100，每月更新一次。<br /><br />做了一个hibernate app，相关代码如下：<br /><br />-------------------------------------<br />VoteServiceImpl.class<br />public class VoteServiceImpl{<br /> Session session;<br /> 。。。。<br /> public void closeSession(){ session.close();}<br /><br /> public void deleteVote(int voteId) {<br />      Transaction tx = session.beginTransaction();<br />      session.delete(getVote(voteId));<br />      tx.commit();<br /> }<br /><br />  public VoteQuestion getVote(int voteId) {<br />        return (VoteQuestion) session.load(VoteQuestion.class, new Long(voteId));<br />  }<br />}<br /> <br />调用代码为：<br />VoteServiceImpl vs=new VoteServiceImpl(session);<br />vs.deleteVote(1);<br />vs.getVote(2);<br />vs.closeSession();<br />-------------------------------------<br /><br />junit测试调用上面代码没问题，junit里创建sessionFactory的方式是：<br />     sessionFactory = new Configuration().configure().buildSessionFactory();<br /><br />而在JBOSS里，原来我使用的是把hibnerate pojo打包成har，并通过hibernate-service.xml来配置hibernate SessionFactory MBean。<br /><br />hibernate-service.xml content<br />-------------------------------------<br />&lt;server><br />    &lt;mbean code="org.jboss.hibernate.jmx.Hibernate"<br />           name="jboss.har:service=ETokenHibernate"><br />        &lt;attribute name="DatasourceName">java:/ETokenDB&lt;/attribute><br />        &lt;attribute name="Dialect">org.hibernate.dialect.MySQLDialect&lt;/attribute><br />        &lt;attribute name="SessionFactoryName">java:/hibernate/ETokenSessionFactory&lt;/attribute><br />        &lt;attribute name="CacheProviderClass">org.hibernate.cache.HashtableCacheProvider&lt;/attribute><br />       <br />        &lt;!-- &lt;attribute name="Hbm2ddlAuto">create-drop&lt;/attribute> --><br />    &lt;/mbean><br />&lt;/server><br />-------------------------------------<br /><br />这种情况下调用上面的代码，就会抛出exception。抛出exception的是在调用<br />             vs.getVote(2);<br />这一行时。exception大概的意思就是session已经关闭，无法操作。<br /><br />为什么会出现这种情况：在junit里可以，而在jboss里出错？？<br /><br />原因就在于通过上述配置的JBOSS hibernate SessionFactory MBean的缺省配置居然是设置Session会在事务提交后自动关闭（这和hibernate调用new Configuration().configure().buildSessionFactory()的缺省配置完全相反）。因此当调用 vs.deleteVote时，由于tx.commit提交了事务，所以session自动关闭，无法再执行vs.getVote。<br /><br /><br />怎么解决？？？<br /><br />本来想着在上面的hibernate-service.xml里设置session auto close属性为false，但<br />    &lt;mbean code="org.jboss.hibernate.jmx.Hibernate"<br />           name="jboss.har:service=ETokenHibernate"><br />并没有提供这个属性设置。<br /><br />上网查了很久，终于找到解决方案：用jboss-service.xml代替hibernate-service.xml，并在jboss-service.xml里配置下面的hibernate mbean class（可能在hibernate-service.xml里使用，但太懒，没试）即可！<br /><br />jboss-service.xml content<br />------------------------------------------------<br />&lt;server><br /><br />&lt;mbean code="org.hibernate.jmx.HibernateService"<br />    name="jboss.jca:service=HibernateFactory,name=HibernateFactory"><br /><br />    &lt;!-- 必须的服务 --><br />    &lt;depends>jboss.jca:service=RARDeployer&lt;/depends><br />    &lt;depends>jboss.jca:service=LocalTxCM,name=HsqlDS&lt;/depends><br /><br />    &lt;!-- 将Hibernate服务绑定到JNDI --><br />     &lt;attribute name="JndiName">java:/hibernate/ETokenSessionFactory&lt;/attribute> <br /><br /><br />    &lt;!-- 数据源设置 --><br />    &lt;attribute name="Datasource">java:HsqlDS&lt;/attribute><br />    &lt;attribute name="Dialect">org.hibernate.dialect.HSQLDialect&lt;/attribute><br /><br />    &lt;!-- 事务集成 --><br />    &lt;attribute name="TransactionStrategy"><br />        org.hibernate.transaction.JTATransactionFactory&lt;/attribute><br />    &lt;attribute name="TransactionManagerLookupStrategy"><br />        org.hibernate.transaction.JBossTransactionManagerLookup&lt;/attribute><br />    &lt;attribute name="FlushBeforeCompletionEnabled">true&lt;/attribute><br />    &lt;attribute name="AutoCloseSessionEnabled">false&lt;/attribute><br /><br />    &lt;!-- 抓取选项 --><br />    &lt;attribute name="MaximumFetchDepth">5&lt;/attribute><br /><br />    &lt;!-- 二级缓存 --><br />    &lt;attribute name="SecondLevelCacheEnabled">true&lt;/attribute><br />    &lt;attribute name="CacheProviderClass">org.hibernate.cache.EhCacheProvider&lt;/attribute><br />    &lt;attribute name="QueryCacheEnabled">true&lt;/attribute><br /><br />    &lt;!-- 日志 --><br />    &lt;attribute name="ShowSqlEnabled">true&lt;/attribute><br /><br />    &lt;!-- 映射定义文件 --><br />    &lt;attribute name="MapResources">auction/Item.hbm.xml,auction/Category.hbm.xml&lt;/attribute><br /><br />&lt;/mbean><br /><br />&lt;/server><br /><br />------------------------------------------------<br /><br />比较新旧两个mbean，使用的class是不同的：org.hibernate.jmx.HibernateService可以设置 AutoCloseSessionEnabled属性和其他更多的hibernate，而旧的 org.jboss.hibernate.jmx.Hibernate不行，这就是关键点。<br /><br />！！注意：使用org.hibernate.jmx.HibernateService mbean必须设置MapResources属性，该属性值是所有hibernate pojo hbm.xml的list，hbm xml之间用逗号隔开。<br /><br /><br />另外上面的jboss-service.xml代码在运行时出错：cache方面的错，应该是缺少某个jar，没空研究了，就使用了简化的配置，见下面：<br />jboss-service.xml content<br />------------------------------------------------<br />&lt;server><br /> &lt;mbean code="org.hibernate.jmx.HibernateService"<br />     name="jboss.jca:service=HibernateFactory,name=HibernateFactory"> <br /><br />     &lt;attribute name="JndiName">java:/hibernate/ETokenSessionFactory&lt;/attribute> <br />     &lt;attribute name="Datasource">java:/ETokenDB&lt;/attribute><br />     &lt;attribute name="Dialect">org.hibernate.dialect.MySQLDialect&lt;/attribute> <br />     &lt;attribute name="AutoCloseSessionEnabled">false&lt;/attribute><br /><br />     &lt;attribute name="MapResources">pojo/JQuizTemplate.hbm.xml,....,pojo/Vote.hbm.xml&lt;/attribute>    <br /> &lt;/mbean><br />&lt;/server><br />------------------------------------------------
          <br/>
          <span style="color:red;">
            <a href="http://coder1982.javaeye.com/blog/150771#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Tue, 25 Dec 2007 17:37:00 +0800</pubDate>
        <link>http://coder1982.javaeye.com/blog/150771</link>
        <guid>http://coder1982.javaeye.com/blog/150771</guid>
      </item>
      <item>
        <title>转贴 :hibernate 配置 dbcp</title>
        <author>coder1982</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coder1982.javaeye.com">coder1982</a>&nbsp;
          链接：<a href="http://coder1982.javaeye.com/blog/149907" style="color:red;">http://coder1982.javaeye.com/blog/149907</a>&nbsp;
          发表时间: 2007年12月21日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          Hibernate属性文件的配置参数 <br />#连接池的最大活动个数 <br />hibernate.dbcp.maxActive 100 <br />#当连接池中的连接已经被耗尽的时候，DBCP将怎样处理( 0 = 失败, 1 = 等待, 2= 增长) <br />hibernate.dbcp.whenExhaustedAction 1 <br />#最大等待时间 <br />hibernate.dbcp.maxWait 120000 <br />#没有人用连接的时候，最大闲置的连接个数。 <br />hibernate.dbcp.maxIdle 10 <br />##以下是对prepared statement的处理，同上。 <br />hibernate.dbcp.ps.maxActive 100 <br />hibernate.dbcp.ps.whenExhaustedAction 1 <br />hibernate.dbcp.ps.maxWait 120000 <br />hibernate.dbcp.ps.maxIdle 10<br /><br />## 可选，是否对池化的连接进行验证 <br />#给出一条简单的sql语句进行验证 <br />#hibernate.dbcp.validationQuery select 1 from dual <br />#在取出连接时进行有效验证 <br />#hibernate.dbcp.testOnBorrow true <br />#在放回连接时进行有效验证 <br />#hibernate.dbcp.testOnReturn false<br /><br />#Hibernate已经实现了DBCP Provider实现，别忘了在下面的键值去掉＃字符 <br />hibernate.connection.provider_class net.sf.hibernate.connection.DBCPConnectionProvider
          <br/>
          <span style="color:red;">
            <a href="http://coder1982.javaeye.com/blog/149907#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Fri, 21 Dec 2007 11:36:53 +0800</pubDate>
        <link>http://coder1982.javaeye.com/blog/149907</link>
        <guid>http://coder1982.javaeye.com/blog/149907</guid>
      </item>
      <item>
        <title>今天开始学习JAVA设计模式</title>
        <author>coder1982</author>
        <description>
          <![CDATA[
          <br/>
          作者: <a href="http://coder1982.javaeye.com">coder1982</a>&nbsp;
          链接：<a href="http://coder1982.javaeye.com/blog/147710" style="color:red;">http://coder1982.javaeye.com/blog/147710</a>&nbsp;
          发表时间: 2007年12月12日
          <br/><br/>
          声明：本文系JavaEye网站发布的原创博客文章，未经作者书面许可，严禁任何网站转载本文，否则必将追究法律责任！
          <br/><br/>
          将不定期做笔记<br />测试一下JAVAEYE的博客
          <br/>
          <span style="color:red;">
            <a href="http://coder1982.javaeye.com/blog/147710#comments" style="color:red;">本文的讨论也很精彩，浏览讨论>></a>
          </span>
          <br/><br/><br/>
          <span style="color:#E28822;">JavaEye推荐</span>
          <br/>
          <ul class='adverts'><li><a href='/adverts/41' target='_blank'><span style="color:red;font-weight:bold;">北京: 千橡集团暨校内网诚聘软件研发工程师</span></a></li><li><a href='/adverts/42' target='_blank'><span style="color:red;font-weight:bold;">搜狐网站诚聘Java、PHP和C++工程师</span></a></li></ul>
          <br/><br/><br/>
          ]]>
        </description>
        <pubDate>Wed, 12 Dec 2007 09:41:02 +0800</pubDate>
        <link>http://coder1982.javaeye.com/blog/147710</link>
        <guid>http://coder1982.javaeye.com/blog/147710</guid>
      </item>
  </channel>
</rss>