Feng.Li's Java See

          抓緊時間,大步向前。
          隨筆 - 95, 文章 - 4, 評論 - 58, 引用 - 0
          數(shù)據(jù)加載中……

          DAO模式

            一 .有關(guān)DAO模式的介紹

                    業(yè)務(wù)對象只應(yīng)該關(guān)注業(yè)務(wù)邏輯,不應(yīng)該關(guān)心數(shù)據(jù)存取的細節(jié)。數(shù)據(jù)訪問對象必須實現(xiàn)特定的持久化策略(如,基于JDBC或Hibernate的持久化邏輯), 這樣就抽出來了DAO層,作為數(shù)據(jù)源層,而之上的Domain Model層與之通訊而已,如果將那些實現(xiàn)了數(shù)據(jù)訪問操作的所有細節(jié)都放入高層Domain model(領(lǐng)域模型)的話,系統(tǒng)的結(jié)構(gòu)一定層次上來說就變得有些混亂。低級別的數(shù)據(jù)訪問邏輯與高級別的業(yè)務(wù)邏輯分離,用一個DAO接口隱藏持久化操作的 細節(jié),這樣使用的最終目的就是讓業(yè)務(wù)對象無需知道底層的持久化技術(shù)知識,這是標(biāo)準(zhǔn) j2ee 設(shè)計模式之一。一個典型的的DAO組成:DAO工廠類,DAO接口,實現(xiàn)DAO接口的具體類(每個 DAO 實例負責(zé)一個主要域?qū)ο蠡驅(qū)嶓w),VO(Value Object)。如果一個DAO 工廠只為一個數(shù)據(jù)庫的實現(xiàn)(現(xiàn)在只考慮這種情況)而創(chuàng)建很多的DAO的時候,實現(xiàn)該策略時,我們考慮采用工廠方法設(shè)計模 式.

                   二.設(shè)計DAO要注意的問題
                   在采用這種工廠方法設(shè)計模式來實現(xiàn)時我們其實要注意很多問題,哪個對象負責(zé)開始事務(wù),哪個負責(zé)事務(wù)結(jié)束?DAO 是否要負責(zé)事務(wù)的開始和結(jié)束? 應(yīng)用程序是否需要通過多少個DAO訪問數(shù)據(jù)?事務(wù)涉及一個DAO還是多個DAO?一個DAO是否調(diào)用另一個DAO的方法?了解上述問題的答案將有助于我們 選擇最適合的 DAO 的事務(wù)界定策略。在 DAO 中有兩種主要的界定事務(wù)的策略。一種方式是讓 DAO 負責(zé)界定事務(wù),另一種將事務(wù)界定交給調(diào)用這個 DAO 方法的對象處理。如果選擇了前一種方式,那么就將事務(wù)代碼嵌入到 DAO 中。如果選擇后一種方式,那么事務(wù)界定代碼就是在 DAO 類外面,在這里我將用<<Hibernate項目開發(fā)寶典>>中留言版的小例子來理解后一種工作方式是如何工作的,以及如何自己 實現(xiàn)一個類似Spring的IOC輕量級容器中Bean工廠的功能(當(dāng)然是沒有使用Spring應(yīng)用程序框架的情況下,對于這個簡單的例子來說更有助于我 們理解Spring的DI模式)。這個小實例所要實現(xiàn)的業(yè)務(wù)功能包括創(chuàng)建用戶,用戶登錄,發(fā)表文章,瀏覽文章,修改文章和刪除文章,所以有兩個對應(yīng)的實體 對象User,Message。本文不涉及到業(yè)務(wù)邏輯,以及顯示層部分。

            三.DAO的實現(xiàn)

            DAO 模式對開發(fā)J2EE應(yīng)用的人員來說都應(yīng)該很熟悉的,但是模式的實現(xiàn)各不相同,在這里我將按下面的思路來實現(xiàn):

            1.系統(tǒng)中的所有數(shù)據(jù)庫訪問都通過 DAO 進行以實現(xiàn)封裝。

                   2. 每個 DAO 實例負責(zé)一個主要域?qū)ο蠡驅(qū)嶓w。

                   3.DAO 負責(zé)域?qū)ο蟮膭?chuàng)建、讀取(按主鍵)、更新和刪除(CRUD)。

            4. DAO 可允許基于除主鍵之外的標(biāo)準(zhǔn)進行查詢,返回值通常是DAO 負責(zé)的域?qū)ο蠹稀?/p>

            5.像上面說的,DAO 不負責(zé)處理事務(wù)、會話或連接,而把這交給一個工具類,這樣做是為了實現(xiàn)靈活性。

               (一)泛型 DAO 接口

            泛型 DAO 的基礎(chǔ)是其 CRUD 操作。下面的接口定義泛型 DAO 的方法:

                

            提供數(shù)據(jù)庫操作接口給業(yè)務(wù)層使用

                 清單1

            public interface IMessageDAO
          { //對應(yīng)留言信息Message這個實體對象的操作
          public void saveMessage( Message message );
          public void updateMessage( Message message );
          public List getMessages( );
          public void deleteMessage( String id, String userId );
          public Message getMessage( String id );
          }

                   清單2

            public interface IUserDAO
                 {
                 public void saveUser( User user );
                 public User getUser( String username );
                public User getUserById( String id );
                }

              (二)泛型DAO的實現(xiàn)

           第一個泛型 DAO 的實現(xiàn)
          DAO 的實現(xiàn)類,封裝數(shù)據(jù)庫邏輯,按<<Hibernate項目開發(fā)寶典>>書上所說的將那些持久化操作封裝到一個DAO基礎(chǔ)類,也相 當(dāng)于是一個工具類,通過繼承這個基礎(chǔ)類,DAO的實現(xiàn)類可以在很大程度上簡化持久化操作的步驟,減少代碼的重復(fù)量。這個基礎(chǔ)類命名為 HibernateDAO,具體的方法實現(xiàn)如清單2

          清單 3.

          /**
          * 使用Hibernate實現(xiàn)DAO的基礎(chǔ)類
          * 包括了持久化操作的一些基礎(chǔ)方法
          */
          public class HibernateDAO
          {
          /**
          * 保存對象信息到數(shù)據(jù)庫
          * @param obj 需要進行持久化操作的對象
          */
          public void saveObject(Object obj)
          {
             HibernateUtil.getCurrentSession().save(obj);
          }

          /**
          * 更新持久化對象
          * @param obj 需要更新的對象
          */
          public void updateObject(Object obj)
          {
             HibernateUtil.getCurrentSession().update(obj);
          }

          /**
          * 使用HQL語句進行查詢
          * @param hsql 查詢語句
          * @return 符合條件的對象集合
          */
          public List getObjects(String hsql)
          {
             List result = HibernateUtil.getCurrentSession().createQuery(hsql).list();
             return result;
          }

          /**
          * 使用HQL語句進行對象的查詢
          * @param hsql 查詢語句
          * @return 符合條件的對象
          */
          public Object getObject(String hsql)
          {
             Object result = HibernateUtil.getCurrentSession().createQuery(hsql).uniqueResult();
             return result;
          }

          /**
          * 根據(jù)ID值得到持久化的對象
          * @param cls 對象的類型
          * @param id ID值
          * @return 指定ID的對象
          */
          public Object getObject(Class cls, String id)
          {
             Object result = HibernateUtil.getCurrentSession().get(cls, id);
             return result;
          }

          /**
          * 刪除對象信息
          * @param obj 被刪除的對象
          */
          public void deleteObject(Object obj)
          {
             HibernateUtil.getCurrentSession().delete(obj);
          }
          }

          清單 4. IMessageDAO接口的實現(xiàn)類

          /**
          * IMessageDAO接口的Hibernate實現(xiàn)
          */
          public class MessageDAO extends HibernateDAO implements IMessageDAO
          {

          /**
          * 保存留言信息
          *
          * @param message
          *            被保存的留言對象
          */
          public void saveMessage(Message message)
          {
             super.saveObject(message);
          }

          /**
          * 得到所有的留言信息
          *
          * @return 返回所有的留言信息
          */
          public List getMessages()
          {
             String hsql = "from Message";
             return super.getObjects(hsql);
          }

          /**
          * 刪除留言信息
          *
          * @param id
          *            要刪除的留言信息的ID值
          * @param userId
          *            執(zhí)行刪除操作的用戶ID
          */
          public void deleteMessage(String id, String userId)
          {
             Message msg = getMessage(id);
             if (msg == null)
             {
              throw new MessageDAOException("找不到你所要刪除的留言!");
             }

             if (!msg.getUser().getId().equals(userId))
             {
              throw new MessageDAOException("你不能刪除別人的留言!");
             }

              deleteObject(msg);
          }

          /**
          * 得到留言信息
          *
          * @param id
          *            留言的ID值
          * @return 指定ID值得留言對象
          */
          public Message getMessage(String id)
          {
             return (Message) getObject(Message.class, id);
          }

          /**
          * 更新留言信息
          *
          * @param message
          *            欲更新的留言對象
          */
          public void updateMessage(Message message)
          {
             updateObject(message);
          }

          } 
          清單 5. IUserDAO接口的實現(xiàn)類

          /**
          * IUserDAO接口的Hibernate實現(xiàn)
          */
          public class UserDAO extends HibernateDAO implements IUserDAO
          {

          /**
          * 保存用戶信息到數(shù)據(jù)庫
          * @param user 被保存的用戶對象
          */
          public void saveUser(User user)
          {
             if (user == null)
              return;

             User u = getUser(user.getName());
             if (u != null)
              throw new MessageDAOException("用戶名已經(jīng)存在,請使用其它用戶名!");

           saveObject(user);
          }

          /**
          * 得到用戶對象
          * @param username 用戶的登錄名
          * @return 指定登錄名的用戶對象
          */
          public User getUser(String username)
          {
             User u = (User) getObject("from User u where u.name = '" + username
               + "'");
             return u;
          }

          /**
          * 得到用戶對象的信息
          * @param id 用戶的ID值
          * @return 指定的用戶信息
          */
          public User getUserById(String id)
          {
             return (User) getObject(User.class, id);
          }

          }

          四.事務(wù)界定

               前面說過, DAO 不負責(zé)處理事務(wù)、會話或連接,而把這交給一個工具類,封裝所有關(guān)于數(shù)據(jù)庫的操作。把Session的獲取,語句的關(guān)閉等放在這個類更好。通常的設(shè)計把數(shù)據(jù) 庫的代碼放到DAO的實現(xiàn)類中,這樣如果某個DAO實現(xiàn)類設(shè)計不良,要改動就必須牽涉到很多地方,不利于維護。在這里的工具類代碼如清單6。

           清單 6.

           public class HibernateUtil
          {

              private static Log log = LogFactory.getLog(HibernateUtil.class);

              private static final String INTERCEPTOR_CLASS = "hibernate.util.interceptor_class";

              private static Configuration configuration;
              private static SessionFactory sessionFactory;
              private static ThreadLocal threadSession = new ThreadLocal();
              private static ThreadLocal threadTransaction = new ThreadLocal();

              private static boolean useThreadLocal = true;

              static {
                  // Create the initial SessionFactory from the default configuration files
                  try {

                      // Replace with Configuration() if you don't use annotations or JDK 5.0
                      //configuration = new AnnotationConfiguration();
                      configuration = new Configuration();

                      // Read not only hibernate.properties, but also hibernate.cfg.xml
                      configuration.configure();

                      // Assign a global, user-defined interceptor with no-arg constructor
                      String interceptorName = configuration.getProperty(INTERCEPTOR_CLASS);
                      if (interceptorName != null) {
                          Class interceptorClass =
                                  HibernateUtil.class.getClassLoader().loadClass(interceptorName);
                          Interceptor interceptor = (Interceptor)interceptorClass.newInstance();
                          configuration.setInterceptor(interceptor);
                      }

                      // Disable ThreadLocal Session/Transaction handling if CMT is used
                      if (org.hibernate.transaction.CMTTransactionFactory.class.getName()
                           .equals( configuration.getProperty(Environment.TRANSACTION_STRATEGY) ) )
                          useThreadLocal = false;

                      if (configuration.getProperty(Environment.SESSION_FACTORY_NAME) != null) {
                          // Let Hibernate bind it to JNDI
                          configuration.buildSessionFactory();
                      } else {
                          // or use static variable handling
                          sessionFactory = configuration.buildSessionFactory();
                      }

                  } catch (Throwable ex) {
                      // We have to catch Throwable, otherwise we will miss
                      // NoClassDefFoundError and other subclasses of Error
                      log.error("Building SessionFactory failed.", ex);
                      throw new ExceptionInInitializerError(ex);
                  }
              }

              /**
               * Returns the original Hibernate configuration.
               *
               * @return Configuration
               */
              public static Configuration getConfiguration() {
                  return configuration;
              }

              /**
               * Returns the global SessionFactory.
               *
               * @return SessionFactory
               */
              public static SessionFactory getSessionFactory() {
                  SessionFactory sf = null;
                  String sfName = configuration.getProperty(Environment.SESSION_FACTORY_NAME);
                  if ( sfName != null) {
                      log.debug("Looking up SessionFactory in JNDI.");
                      try {
                          sf = (SessionFactory) new InitialContext().lookup(sfName);
                      } catch (NamingException ex) {
                          throw new RuntimeException(ex);
                      }
                  } else {
                      sf = sessionFactory;
                  }
                  if (sf == null)
                      throw new IllegalStateException("SessionFactory not available.");
                  return sf;
              }

              /**
               * Closes the current SessionFactory and releases all resources.
               * <p>
               * The only other method that can be called on HibernateUtil
               * after this one is rebuildSessionFactory(Configuration).
               */
              public static void shutdown() {
                  log.debug("Shutting down Hibernate.");
                  // Close caches and connection pools
                  getSessionFactory().close();

                  // Clear static variables
                  configuration = null;
                  sessionFactory = null;

                  // Clear ThreadLocal variables
                  threadSession.set(null);
                  threadTransaction.set(null);
              }


              /**
               * Rebuild the SessionFactory with the static Configuration.
               * <p>
               * This method also closes the old SessionFactory before, if still open.
               * Note that this method should only be used with static SessionFactory
               * management, not with JNDI or any other external registry.
               */
               public static void rebuildSessionFactory() {
                  log.debug("Using current Configuration for rebuild.");
                  rebuildSessionFactory(configuration);
               }

              /**
               * Rebuild the SessionFactory with the given Hibernate Configuration.
               * <p>
               * HibernateUtil does not configure() the given Configuration object,
               * it directly calls buildSessionFactory(). This method also closes
               * the old SessionFactory before, if still open.
               *
               * @param cfg
               */
               public static void rebuildSessionFactory(Configuration cfg) {
                  log.debug("Rebuilding the SessionFactory from given Configuration.");
                  synchronized(sessionFactory) {
                      if (sessionFactory != null && !sessionFactory.isClosed())
                          sessionFactory.close();
                      if (cfg.getProperty(Environment.SESSION_FACTORY_NAME) != null)
                          cfg.buildSessionFactory();
                      else
                          sessionFactory = cfg.buildSessionFactory();
                      configuration = cfg;
                  }
               }

              /**
               * Retrieves the current Session local to the thread.
               * <p/>
               * If no Session is open, opens a new Session for the running thread.
               * If CMT is used, returns the Session bound to the current JTA
               * container transaction. Most other operations on this class will
               * then be no-ops or not supported, the container handles Session
               * and Transaction boundaries, ThreadLocals are not used.
               *
               * @return Session
               */
              public static Session getCurrentSession() {
                  if (useThreadLocal) {
                      Session s = (Session) threadSession.get();
                      if (s == null) {
                          log.debug("Opening new Session for this thread.");
                          s = getSessionFactory().openSession();
                          threadSession.set(s);
                      }
                      return s;
                  } else {
                      return getSessionFactory().getCurrentSession();
                  }
              }

              /**
               * Closes the Session local to the thread.
               * <p>
               * Is a no-op (with warning) if called in a CMT environment. Should be
               * used in non-managed environments with resource local transactions, or
               * with EJBs and bean-managed transactions.
               */
              public static void closeSession() {
                  if (useThreadLocal) {
                      Session s = (Session) threadSession.get();
                      threadSession.set(null);
                      Transaction tx = (Transaction) threadTransaction.get();
                      if (tx != null && (!tx.wasCommitted() || !tx.wasRolledBack()) )
                          throw new IllegalStateException("Closing Session but Transaction still open!");
                      if (s != null && s.isOpen()) {
                          log.debug("Closing Session of this thread.");
                          s.close();
                      }
                  } else {
                      log.warn("Using CMT/JTA, intercepted superfluous close call.");
                  }
              }

              /**
               * Start a new database transaction.
               * <p>
               * Is a no-op (with warning) if called in a CMT environment. Should be
               * used in non-managed environments with resource local transactions, or
               * with EJBs and bean-managed transactions. In both cases, it will either
               * start a new transaction or join the existing ThreadLocal or JTA
               * transaction.
               */
              public static void beginTransaction() {
                  if (useThreadLocal) {
                      Transaction tx = (Transaction) threadTransaction.get();
                      if (tx == null) {
                          log.debug("Starting new database transaction in this thread.");
                          tx = getCurrentSession().beginTransaction();
                          threadTransaction.set(tx);
                      }
                  } else {
                      log.warn("Using CMT/JTA, intercepted superfluous tx begin call.");
                  }
              }

              /**
               * Commit the database transaction.
               * <p>
               * Is a no-op (with warning) if called in a CMT environment. Should be
               * used in non-managed environments with resource local transactions, or
               * with EJBs and bean-managed transactions. It will commit the
               * ThreadLocal or BMT/JTA transaction.
               */
              public static void commitTransaction() {
                  if (useThreadLocal) {
                      Transaction tx = (Transaction) threadTransaction.get();
                      try {
                          if ( tx != null && !tx.wasCommitted()
                                          && !tx.wasRolledBack() ) {
                              log.debug("Committing database transaction of this thread.");
                              tx.commit();
                          }
                          threadTransaction.set(null);
                      } catch (RuntimeException ex) {
                          log.error(ex);
                          rollbackTransaction();
                          throw ex;
                      }
                  } else {
                      log.warn("Using CMT/JTA, intercepted superfluous tx commit call.");
                  }
              }

              /**
               * Rollback the database transaction.
               * <p>
               * Is a no-op (with warning) if called in a CMT environment. Should be
               * used in non-managed environments with resource local transactions, or
               * with EJBs and bean-managed transactions. It will rollback the
               * resource local or BMT/JTA transaction.
               */
              public static void rollbackTransaction() {
                  if (useThreadLocal) {
                      Transaction tx = (Transaction) threadTransaction.get();
                      try {
                          threadTransaction.set(null);
                          if ( tx != null && !tx.wasCommitted() && !tx.wasRolledBack() ) {
                              log.debug("Tyring to rollback database transaction of this thread.");
                              tx.rollback();
                              log.debug("Database transaction rolled back.");
                          }
                      } catch (RuntimeException ex) {
                          throw new RuntimeException("Might swallow original cause, check ERROR log!", ex);
                      } finally {
                          closeSession();
                      }
                  } else {
                      log.warn("Using CMT/JTA, intercepted superfluous tx rollback call.");
                  }
              }

              /**
               * Reconnects a Hibernate Session to the current Thread.
               * <p>
               * Unsupported in a CMT environment.
               *
               * @param session The Hibernate Session to be reconnected.
               */
              public static void reconnect(Session session) {
                  if (useThreadLocal) {
                      log.debug("Reconnecting Session to this thread.");
                      session.reconnect();
                      threadSession.set(session);
                  } else {
                      log.error("Using CMT/JTA, intercepted not supported reconnect call.");
                  }
              }

              /**
               * Disconnect and return Session from current Thread.
               *
               * @return Session the disconnected Session
               */
              public static Session disconnectSession() {
                  if (useThreadLocal) {
                      Transaction tx = (Transaction) threadTransaction.get();
                      if (tx != null && (!tx.wasCommitted() || !tx.wasRolledBack()) )
                          throw new IllegalStateException("Disconnecting Session but Transaction still open!");
                      Session session = getCurrentSession();
                      threadSession.set(null);
                      if (session.isConnected() && session.isOpen()) {
                          log.debug("Disconnecting Session from this thread.");
                          session.disconnect();
                      }
                      return session;
                  } else {
                      log.error("Using CMT/JTA, intercepted not supported disconnect call.");
                      return null;
                  }
              }

              /**
               * Register a Hibernate interceptor with the current SessionFactory.
               * <p>
               * Every Session opened is opened with this interceptor after
               * registration. Has no effect if the current Session of the
               * thread is already open, effective on next close()/getCurrentSession().
               * <p>
               * Attention: This method effectively restarts Hibernate. If you
               * need an interceptor active on static startup of HibernateUtil, set
               * the <tt>hibernateutil.interceptor</tt> system property to its
               * fully qualified class name.
               */
              public static void registerInterceptorAndRebuild(Interceptor interceptor) {
                  log.debug("Setting new global Hibernate interceptor and restarting.");
                  configuration.setInterceptor(interceptor);
                  rebuildSessionFactory();
              }

              public static Interceptor getInterceptor() {
                  return configuration.getInterceptor();
              }
          }
          上 面的代碼中,如果是使用Hibernate3.1以上版本對Session的管理進行了優(yōu)化,提供了內(nèi)建的Session管理方式,所以上面也可以不用 ThreadLocal類型的實例對象來保存。書中提到一點,現(xiàn)在絕大多數(shù)的應(yīng)用都是基于Web來實現(xiàn)的,這里通過Web所提供的Filter機制實現(xiàn)持 久化操作的進一步的封裝,將一個用戶請求中所做的所有持久化操作看成一個事務(wù),當(dāng)然,如果某個業(yè)務(wù)確實需要將這個請求分解成多個事務(wù),那么也可以在業(yè)務(wù)實 現(xiàn)的方法中自行地進行事務(wù)的提交或者回流操作,完成的Hibernate如清單7

          posted on 2008-03-10 14:54 小鋒 閱讀(1729) 評論(0)  編輯  收藏


          只有注冊用戶登錄后才能發(fā)表評論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 三台县| 璧山县| 图木舒克市| 天台县| 元谋县| 军事| 都兰县| 尼勒克县| 桑日县| 花垣县| 古田县| 微博| 若羌县| 大埔县| 岳阳市| 教育| 阳东县| 莎车县| 济阳县| 清原| 甘南县| 镇平县| 蒙自县| 通州区| 临泉县| 绵竹市| 高雄市| 乌拉特前旗| 田东县| 深圳市| 呈贡县| 铜川市| 延川县| 朔州市| 太谷县| 哈密市| 桂东县| 清新县| 翁牛特旗| 青浦区| 邮箱|