Feng.Li's Java See

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

          DAO模式

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

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

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

            三.DAO的實(shí)現(xiàn)

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

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

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

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

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

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

               (一)泛型 DAO 接口

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

                

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

                 清單1

            public interface IMessageDAO
          { //對(duì)應(yīng)留言信息Message這個(gè)實(shí)體對(duì)象的操作
          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的實(shí)現(xiàn)

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

          清單 3.

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

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

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

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

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

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

          清單 4. IMessageDAO接口的實(shí)現(xiàn)類(lèi)

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

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

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

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

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

              deleteObject(msg);
          }

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

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

          } 
          清單 5. IUserDAO接口的實(shí)現(xiàn)類(lèi)

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

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

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

           saveObject(user);
          }

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

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

          }

          四.事務(wù)界定

               前面說(shuō)過(guò), DAO 不負(fù)責(zé)處理事務(wù)、會(huì)話(huà)或連接,而把這交給一個(gè)工具類(lèi),封裝所有關(guān)于數(shù)據(jù)庫(kù)的操作。把Session的獲取,語(yǔ)句的關(guān)閉等放在這個(gè)類(lèi)更好。通常的設(shè)計(jì)把數(shù)據(jù) 庫(kù)的代碼放到DAO的實(shí)現(xiàn)類(lèi)中,這樣如果某個(gè)DAO實(shí)現(xiàn)類(lèi)設(shè)計(jì)不良,要改動(dòng)就必須牽涉到很多地方,不利于維護(hù)。在這里的工具類(lèi)代碼如清單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以上版本對(duì)Session的管理進(jìn)行了優(yōu)化,提供了內(nèi)建的Session管理方式,所以上面也可以不用 ThreadLocal類(lèi)型的實(shí)例對(duì)象來(lái)保存。書(shū)中提到一點(diǎn),現(xiàn)在絕大多數(shù)的應(yīng)用都是基于Web來(lái)實(shí)現(xiàn)的,這里通過(guò)Web所提供的Filter機(jī)制實(shí)現(xiàn)持 久化操作的進(jìn)一步的封裝,將一個(gè)用戶(hù)請(qǐng)求中所做的所有持久化操作看成一個(gè)事務(wù),當(dāng)然,如果某個(gè)業(yè)務(wù)確實(shí)需要將這個(gè)請(qǐng)求分解成多個(gè)事務(wù),那么也可以在業(yè)務(wù)實(shí) 現(xiàn)的方法中自行地進(jìn)行事務(wù)的提交或者回流操作,完成的Hibernate如清單7

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


          只有注冊(cè)用戶(hù)登錄后才能發(fā)表評(píng)論。


          網(wǎng)站導(dǎo)航:
           
          主站蜘蛛池模板: 台安县| 保靖县| 盈江县| 古丈县| 绵竹市| 炎陵县| 南丰县| 邛崃市| 察隅县| 泰安市| 静宁县| 长宁县| 宝坻区| 西乌| 金坛市| 顺平县| 彩票| 蒲城县| 西昌市| 凤凰县| 陈巴尔虎旗| 济阳县| 乾安县| 儋州市| 诸暨市| 易门县| 彭泽县| 育儿| 梁山县| 女性| 文登市| 措勤县| 长泰县| 镶黄旗| 阿鲁科尔沁旗| 榕江县| 阿克苏市| 双辽市| 柳河县| 巴彦县| 乐清市|