對天乙社區bbscs8實現的詳細分析一

由於BBSCS8是由數據庫設計-bean/hbm.xml-DAO-Service-Web(作者laoer回答)這樣的創建過程,因此分析這個系統最好是先查看數據庫設計(見http://bbs.laoer.com/main-read-15-ff80808113baa8140113d201333e5274.html下載研究),而我的分析是由Service層開始引出討論的,所以你需對論壇的常用功能有所體會,知道什麼是投票貼,怎麼樣去用,還要有論壇後臺管理使用過等等.如果不知道的話,請先在www.laoer.com處或在自己電腦上本地測試以便先對其功能進行體會,請注意!!!
com.laoer.bbscs.service層下有衆多的接口:
AgreeAgainstService,它有三個方法:
AgreeAgainst saveAgreeAgainst(AgreeAgainst agreeAgainst) throws BbscsException;
AgreeAgainst findAgreeAgainstByUidPidBid(String userID,String postID,long bid);
void removeOutTime(long time)throws BbscsException;
AgreeAgainstImp爲其實現:
首先定義了一個logger,設置好agreeAgainstDAO;getAgreeAgainstDAO及setAgreeAgainstDAO;
public AgreeAgainst saveAgreeAgainst(AgreeAgainst agreeAgainst) throws BbscsException{
try{
return this.getAgreeAgainstDAO().saveAgreeAgainst(agreeAgainst);
(注:此處爲合寫,也可寫成:
agreeAgainst=this.getAgreeAgainstDAO().saveAgreeAgainst(agreeAgainst);
return agreeAgainst;我這裏囉嗦了一下,以後源代碼中會用到,別問我爲什麼~~~)
}catch(Exception ex) {
logger.error(ex);
throw new BbscsException(ex);
}
}
注意的是getAgreeAgainstDAO返回的是AgreeAgainstDAO來自於com.laoer.bbscs.dao包中的interface;
它有三個接口方法:
public AgreeAgainst saveAgreeAgainst(AgreeAgainst agreeAgainst);
public AgreeAgainst findAgreeAgainstByUidPidBid(String userID,String postID,long bid);
public void removeOutTime(long time);
而實現上注入到service方法中的是其實現類:
com.laoer.bbscs.dao.hibernate.AgreeAgainstHibernateDAO,其注入了:
<property name="sessionFactory">
   <ref local="sessionFactory" />
  </property>
一個sessinFacotry唯一,用於獲得可操作的session,讓我們看看其實現:
它將兩個HQL語句都重構成private static final String  了.一個是LOAD_BY_UID_PID_BID,一個是ROMOVE_OUTTIME,注意其extends HibernateDAOSupport抽象類(得到了org.springframework.orm.hibernate3.support.HibernateDAOSupport的支持哦~)這樣我們就可以不用get和set這個sessionFacotry了,另外可能使用this.getHibernateTemplate()來進行實際操作了.
saveorupdate(agreeAgainst);對於查找:
先構造一個Object[] o={postID,userID,new Long(bid)};
List list=this.getHibernateTemplate().find(LOAD_BY_UID_PID_BID,o);
if(l==null||l.isEmpty()){
 return null;
}
else{
return (AgreeAgaist) l.get(0);
 }
}
最後removeOutTime(final long time) {
 getHibernateTemplate().execute(new HibernateCallback(){
 public Object doInHibernate(Session session)   throws HibernateException, SQLException{
 Query query=s.createQuery(REMOVE_OUTTIME);
 query.setLong(0,time);
 query.executeUpdate();
 return null;
}
});
};
}
我們看看hbm.xml文件和bean文件,先是bean(AgreeAgainst.java)
它有如下屬性:
private String id;
private String userID;
private String postID;
private long boardID;
private int voteType;
private long createTime;
及其set/get方法的一個構造.
看下AgreeAgainst.hbm.xml文件:
<hibernate-mapping package="com.laoer.bbscs.bean">
  <class name="AgreeAgainst" table="bbscs_agreeagainst">
    <id name="id" column="ID" type="string" unsaved-value="null">
      <generator class="uuid"/>
    </id>
    <property column="UserID" length="40" name="userID" not-null="true" type="string"/>
    <property column="PostID" length="60" name="postID" not-null="true" type="string"/>
    <property column="BoardID" length="13" name="boardID" not-null="true" type="long"/>
    <property column="VoteType" length="1" name="voteType" type="int"/>
    <property column="CreateTime" name="createTime" not-null="true" type="long"/>
  </class>
</hibernate-mapping>
id爲主鍵uuid算法,還要其長度的定義等等!其它不是......
看下數據庫表!!! Null Default
ID varchar(40) NO 
UserID varchar(40) No
PostID varchar(40)No
BoardID bigint(20)No  0
VoteType tinyint(1)Yes  0
CreateTime bigint(20)No  0
我們來看下實現:
在帖子的支持和反對處選擇!
Hibernate: insert into bbscs_agreeagainst (UserID, PostID, BoardID, VoteType, CreateTime, ID) values (?, ?, ?, ?, ?, ?)
Hibernate: select agreeagain0_.ID as ID24_, agreeagain0_.UserID as UserID24_, agreeagain0_.PostID as PostID24_, agreeagain0_.BoardID as BoardID24_, agreeagain0_.VoteType as VoteType24_, agreeagain0_.CreateTime as CreateTime24_ from bbscs_agreeagainst agreeagain0_ where agreeagain0_.PostID=? and agreeagain0_.UserID=? and agreeagain0_.BoardID=?
數據庫中的數據:
ID:402881 e513bd c85501 13be01 e70d00 1f(32位)
UserID:4028818208ed006b0108ed020bd50001
PostID:402881e513bdc8550113bdefb43c0014
BoardID:2(第2個建的)
VoteType:1(反對)0(支持)
CreateTime:1184303802125
對比一下,就OK了

 


BoardAuthUserService(版塊授權的用戶)
有public BoardAuthUser saveBoardAuthUser(BoardAuthUser boardAuthUser) throws BbscsException;
punlic BoardAuthUser findBoardAuthUserById(String id);
public BoardAuthUser findBoardAuthUserByBidUid(long bid,String uid);
public BoardAuthUser findBoardAuthUserByBidUserName(long bid,String userName);
public List findBoardAuthUserByBid(long bid);
public void removeBoardAuthUser(BoardAuthUser boardAuthUser);
public void removeBoardAuthuserByBidUid(long bid,String uid) throws BbscsException;
public void removeBoardAuthUserByBidUserName(long bid,String userName) throws BbscsExeption;
同樣,imp裏BoardAuthUserServiceImp中,注入DAO
由DAO來完成對應方法的實際工作.注意:有Exception方法的寫法,例如:
public void removeBoardAuthUserByBidUserName(long bid, String userName) throws BbscsException {
    try {
      this.getBoardAuthUserDAO().removeBoardAuthUserByBidUserName(bid, userName);
    }
    catch (Exception ex) {
      logger.error(ex);
      throw new BbscsException(ex);
    }
  }
來到DAO接口層,沒有一個Exception方法哦!!!同樣實現它的HibernateDAO也沒有Ecxeption,可見,Serivce層有Exception.而DAO層沒有Exception(一般情況下),除了沒有Exception外,接口層與Serivce接口層好象.說說Exception:這裏用到的只不過是一個BbscsException而已,在專門的com.laoer.bbscs.exception包中有這個類:原來它也繼承了Exception而已,有三個重載的方法:
public BbscsException(String message) {
  super(message);
 }

 public BbscsException(String message, Throwable cause) {
  super(message, cause);
 }

 public BbscsException(Throwable cause) {
  super(cause);
 }
自定義異常類的主要作用是區分異常發生的位置,當用戶遇到異常時,根據異常名就可以知道哪裏有異常,根據異常提示信息進行修改。

看其hibernate實現:
整個源碼重構過似的,上面爲HQL語句字串常量定義:
private static final String LOAD_BY_BID_UID = "from BoardAuthUser where boardID = ? and userID = ?";
private static final String LOADS_BY_BID = "from BoardAuthUser where boardID = ? order by createTime asc";
看看方法吧:
 public BoardAuthUser findBoardAuthUserById(String id) {
    return (BoardAuthUser)this.getHibernateTemplate().get(BoardAuthUser.class, id);
  }
這個ID明顯是對象標識!
 public BoardAuthUser findBoardAuthUserByBidUserName(long bid, String userName) {
    Object[] o = {new Long(bid), userName};
    List l = this.getHibernateTemplate().find(LOAD_BY_BID_USERNAME, o);
    if (l == null || l.isEmpty()) {
      return null;
    }
    else {
      return (BoardAuthUser) l.get(0);
    }
  }
  public List findBoardAuthUsersByBid(long bid) {
    return this.getHibernateTemplate().find(LOADS_BY_BID, new Long(bid));
  }
需要注意的是Object數組中的元素必爲對象;我們來看看是怎麼刪除操作的:
  public void removeBoardAuthUser(BoardAuthUser boardAuthUser) {
    this.getHibernateTemplate().delete(boardAuthUser);
  }
而不是一個BoradAuthUser對象,是通過以下代碼實現:
 public void removeBoardAuthUserByBidUid(final long bid, final String uid) {
    getHibernateTemplate().execute(new HibernateCallback() {
      public Object doInHibernate(Session s) throws HibernateException, SQLException {
        Query query = s.createQuery(REMOVE_BY_BID_UID);
        query.setLong(1, bid);
        query.setString(0, uid);
        query.executeUpdate();
        return null;
      }
    });
  }

BoardPermissionService是版區(版塊)的權限對應服務;其BEAN BoardPermission如下:

private String id;
  private long boardID;
  private int groupID;
  private List permissions = new ArrayList(); //特殊的哦~~~
再看下其hbm.xml文件:
<hibernate-mapping package="com.laoer.bbscs.bean">
  <class name="BoardPermission" table="bbscs_boardpermission">
    <id name="id" column="ID" type="string" unsaved-value="null">
      <generator class="uuid"/>
    </id>
    <property column="BoardID" length="20" name="boardID" not-null="true" type="long"/>
    <property column="GroupID" length="11" name="groupID" not-null="true" type="int"/>
    <property column="Permissions" name="permissions" type="com.laoer.bbscs.ext.hibernate.SplitList"/>
  </class>
</hibernate-mapping>
注意到:permissions對應於表的Permssions,而其type爲com.laoer.bbscs.ext.hibernate.SplitList;
讓我們看看SplitList這個類:(它在ext.hibernate包中):

[轉自網絡:
使用Hibernate自定義UserType遇到的一個問題
    Hibernate自定義UserType可以使設計更爲優雅,邏輯更爲清晰。第一次使用遇到了問題。Rule表中有一個字段methods,類型爲VARCHAR(30),not null, 允許有多個method,中間用逗號分開,之所以這麼設計是不想爲此增加一個關聯表。爲methods實現了一個自定義UserType叫MethodsList,該類對用戶隱藏了實現細節,使用戶不用處理method的連接和拆分,而使用List來操作就行了,非常直觀。在保存Rule實體的時候,Hibernate報methods字段不允許爲空,說明methods在持久化的時候還是null,DEBUG發現在調用session.saveOrUpdate()方法的時候methods不爲空,但在調用MethodsList的nullSafeSet(PreparedStatement st, Object value, int index)時顯示value的值爲null,Google了很久仍然沒有找到原因 後來發現只要把methods字段的not null屬性設爲false(即允許爲空)問題就不復存在了,很是奇怪...
     據此猜測,Hibernate在應用自定義UserType(即MethodsList)之前進行字段是否爲空之類的檢查,而這時methods字段還是null,所以出現以上錯誤,把該字段改爲允許爲空之後自然就沒有問題了。個人愚見,僅供參考,有空研究一下Hibernate源碼一探究
robbin:
1、UserType不是用來做主鍵的(雖然也可以,但是那樣和複合主鍵沒有區別了,並且複合主鍵是非常不推薦的做法)
2、UserType比Component更加靈活,適用性更強,封裝的更透明。
]
其中有以下方法:assembledeepCopy(重要)disassmbleequals(重要)hashCodeisMutable ullSafeGet eplace eturnedClasssqlTypes等方法需要實現(不一定)!
rs--->Object
public Object nullSafeGet(ResultSet resultSet, String[] stringArray, Object object) throws HibernateException,
   SQLException {
  String value = (String) Hibernate.STRING.nullSafeGet(resultSet, stringArray[0]);
  if (value != null) {
   return parse(value);
  } else {
   return new ArrayList();
  }
 }
用了parse方法:
private List parse(String value) {
  String[] strs = StringUtils.split(value, SPLITTER);
  List set = new ArrayList();
  for (int i = 0; i < strs.length; i++) {
   if (!StringUtils.isBlank(strs[i])) {
    set.add(Long.valueOf(strs[i]));
    // System.out.println(strs[i]);
    // set.add(new Long(Long.parseLong(strs[i])));
   }
  }
  return set;
 }

object--->rs
public void nullSafeSet(PreparedStatement preparedStatement, Object object, int _int) throws HibernateException,
   SQLException {
  if (object != null) {
   String str = assemble((List) object);
   Hibernate.STRING.nullSafeSet(preparedStatement, str, _int);
  } else {
   Hibernate.STRING.nullSafeSet(preparedStatement, "", _int);
  }
 }
用了assemble方法:
private String assemble(List set) {
  StringBuffer sb = new StringBuffer();
  Iterator it = set.iterator();
  while (it.hasNext()) {
   sb.append(it.next());
   sb.append(SPLITTER);
  }
  String fs = sb.toString();
  if (fs != null && fs.length() > 0 && fs.endsWith(SPLITTER)) {
   fs = fs.substring(0, fs.length() - 1);
  }
  return fs;
 }
附參考資料:http://blog.csdn.net/ckangtai/archive/2007/05/23/1622396.aspx
好,看完這個後,我們進入到服務內容:
 public BoardPermission saveBoardPermission(BoardPermission bp) throws BbscsException;
 public BoardPermission updateBoardPermission(BoardPermission bp) throws BbscsException;
  public BoardPermission findBoardPermissionByID(String id);
  public BoardPermission findBoardPermissionByBidGid(long bid, int gid);
  public List findBoardPermissionsByBid(long bid);
public List findBoardPermissionsByGid(int gid);
 public void removeBoardPermissionsByBid(long bid) throws BbscsException;
  public void removeBoardPermissionsByGid(int gid) throws BbscsException;
實際是由注入的boardPermissionDAO其實現類完成的.
注意這裏引入了  private Cache userPermissionCache;這個緩存類!
我們從applicationContext.xml看看是什麼東東:
<bean id="userPermissionCache"
  class="com.laoer.bbscs.service.imp.OsCacheImp">
  <constructor-arg>
   <value>${cacheup.config}</value>
  </constructor-arg>
 </bean>
哦,原來是另外一個服務,${cacheup.config}指的是cacheup.config=oscache_up.properties;
在classes下有許多配置文件,是用不同的配置文件是爲了方便集羣,以區分是不同的緩存。 
我們從com.laoer.bbscs.serivce.Cache接口看起,它提供瞭如下方法:
public void add(Object key,Object value);
public Object get(Object key);
public void remove(Object key);
public void removeAll();
再看imp:
由於spring中的bean帶construtctor-arg:
將調用構造方法:
public OsCacheImp(String profile) {
  Properties properties = new Properties();
  ClassPathResource classPathResource = new ClassPathResource(profile);
//這個類標識從classpath獲得的資源
  try {
   logger.info("Init Cache...");
   properties.load(classPathResource.getInputStream());//使用Properties的load(InputStream   inStream)   來讀取配置文件的時候 
   admin = new GeneralCacheAdministrator(properties);
  } catch (Exception ex) {
   logger.error(ex);
   admin = new GeneralCacheAdministrator();
  }
 }
注意,這個admin對象來自OSCache包,用於管理Cache內容吧.另外,我們看看其對Cache的實現:
public void add(Object key, Object value) {
  logger.debug("Add into cache [Key:" + key + "]");
  this.admin.putInCache(String.valueOf(key), value);
 }
  public Object get(Object key) {
  try {
   logger.debug("Get from cache [Key:" + key + "]");
   return this.admin.getFromCache(String.valueOf(key));
  } catch (NeedsRefreshException ex) {
   logger.debug("Object not in cache, return null");
   this.admin.cancelUpdate(String.valueOf(key));
   return null;
  }
 }
 public void remove(Object key) {
  logger.debug("Remove from cache [Key:" + key + "]");
  this.admin.flushEntry(key.toString());
 }
 public void removeAll() {
  logger.debug("Remove all");
  this.admin.flushAll();
 }
這樣就可以對緩存內容進行控制操作了(就用這幾個方法)!讓我們回到BoardPermissionSerivceImp類中:
注入了DAO和Cache(userPermissionCache)後,進行方法實現時,主要還是DAO去做,不過在saveBoardPermission和updateBoardPermission,還有各種remove方法中還用了this.clearPermissionCache();(除了find),讓我們來看看它的代碼吧:
  private void clearPermissionCache() {
    if (Constant.USE_PERMISSION_CACHE) {
 //public static final boolean USE_PERMISSION_CACHE = true;
      this.getUserPermissionCache().removeAll();//調用Cache中的方法哦!removeALL看上面this.admin.flushAll();!
    }
  }
555555,這個服務類也了Cache服務(與別的服務結合了),下面是DAO實現:
private static final String LOAD_BY_BID_GID ="from BoardPermission where boardID = ? and groupID = ?";
  private static final String LOADS_BY_BID = "from BoardPermission where boardID = ?";
  private static final String LOADS_BY_GID = "from BoardPermission where groupID = ?";
  private static final String REMOVE_BY_BID = "delete from BoardPermission where boardID = ?";
  private static final String REMOVE_BY_GID = "delete from BoardPermission where groupID = ?";
我們看update:
  public BoardPermission updateBoardPermission(BoardPermission bp) {
    //System.out.println("update bp");
    this.getHibernateTemplate().update(bp);
    return bp;
  }
再看:
/**
   * 根據GroupID刪除BoardPermission對象
   *
   * @param gid int
   * @todo Implement this com.laoer.bbscs.dao.BoardPermissionDAO method
   */
  public void removeBoardPermissionsByGid(final int gid) {
    getHibernateTemplate().execute(new HibernateCallback() {
      public Object doInHibernate(Session s) throws HibernateException, SQLException {
        Query query = s.createQuery(REMOVE_BY_GID);
        query.setLong(0, gid);
        query.executeUpdate();
        return null;
      }
    });
  }

接下來,是BoardSaveService:
先看bean吧,它實現了implements Serializable...,加入private static final long serialVersionUID = 5390014211916049604L;它有三個屬性:
 private String id;
 private String userID;
 private long boardID;
[
Java的JavaBeans. Bean的狀態信息通常是在設計時配置的。Bean的狀態信息必須被存起來,以便當程序運行時能恢復這些狀態信息。這也需要serializaiton機制。
總之如果在網絡的環境下做類傳輸,應該還是implements Serializable。
]
再看其hbm.xml:
<hibernate-mapping package="com.laoer.bbscs.bean">
  <class name="BoardSave" table="bbscs_boardsave">
    <id name="id" column="ID" type="string" unsaved-value="null">
      <generator class="uuid"/>
    </id>
    <property column="UserID" length="40" name="userID" not-null="true" type="string"/>
    <property column="BoardID" length="13" name="boardID" not-null="true" type="long"/>
  </class>
</hibernate-mapping>
從這樣便知是用於收藏版區的對象模型啊!那接下來看其接口中的方法:
 public BoardSave saveBoardSave(BoardSave boardSave) throws BbscsException;
  public BoardSave findBoardSaveById(String id);
public BoardSave findBoardSaveByUidBid(String userId, long bid);
  public List findBoardSavesByUid(String userId);
  public List findBoardSaveBidsByUid(String userId);//找Bid的List
  public void removeBoardSave(BoardSave boardSave) throws BbscsException;
  public void removeBoardSaveByUidBid(String userId, long bid) throws BbscsException;
  public void removeBoardSaveByBid(long bid) throws BbscsException;
  public void removeBoardSaveByBidsUid(String userId, List ids) throws BbscsException;
對於imp調用dao-->daoimp我們直接進入dao的imp中:
其中的常量定義如下:
  private static final String LOAD_BY_UID_BID = "from BoardSave where userID = ? and boardID = ?";
  private static final String LOADS_BY_USERID = "from BoardSave where userID = ?";
  private static final String LOADS_BOARDID_BY_USERID = "select boardID from BoardSave where userID = ?";//很特別哦!(其實也沒什麼,相對而已)
  private static final String REMOVE_BY_UID_BID = "delete from BoardSave where userID = ? and boardID = ?";
  private static final String REMOVE_BY_BID = "delete from BoardSave where boardID = ?";
  private static final String REMOVE_IN_IDS_BY_UID =
      "delete from BoardSave where userID = :userID and boardID in (:ids)";
我們來看方法實現吧.
 public BoardSave saveBoardSave(BoardSave boardSave) {
    this.getHibernateTemplate().saveOrUpdate(boardSave);
    return boardSave;
  }
可用於保存,也可以用於更新!
 public List findBoardSaveBidsByUid(String userId) {
    return this.getHibernateTemplate().find(LOADS_BOARDID_BY_USERID, userId);
//一個參數
 public void removeBoardSaveByBidsUid(final String userId, final List ids) {
    getHibernateTemplate().execute(new HibernateCallback() {
      public Object doInHibernate(Session s) throws HibernateException, SQLException {
        Query query = s.createQuery(REMOVE_IN_IDS_BY_UID);
        query.setString("userID", userId);
        query.setParameterList("ids", ids);
        query.executeUpdate();
        return null;
      }
    });
  }
OK!

接下來,是BoardService,先看其相關的Bean!它也實現了可序列化操作...它的屬性特別多..
很關鍵哦!
private Long id;//主鍵
private long parentID;//父級版區ID
private List parentIDs;//所有父級版區ID列表
private List childIDs;
private String boardName;//版區名稱
private String explains;(說明)
private String bulletin;(公告)
private String boardPic;//圖片
private int useStat;(使用狀態)
private int orders;//序
private int needPasswd;//是否需要密碼訪問
private String passwd;//訪問密碼
private int level;---->數據庫中的BoardLevel//級別
private int boardType;(類型1,2,3)//版區類型
private int allowHTML;//HTML是否支持
private int allowUBB;//UBB...
private int auditPost; (帖子是否需要審覈)
private int auditAttach;//附件是否需要審覈
private int addUserPostNum;//是否增加用戶發貼數
private int isHidden;
private int isAuth;//是否需要論證用戶才能訪問
private long mainPostNum;//主貼數
private long postNum;//帖子數量
private Map boardMaster=new HashMap();
private Set boardTag=new HashSet();
我們看看Board.hbm.xml,錯了,我們應該看看applicationCotext.xml中的內容是不是這個,經查看:
<value>com/laoer/bbscs/bean/Board-${datasource.type}.hbm.xml</value>,我用mysql當然是Board-mysql.hbm.xml,其內容主要如下:
<hibernate-mapping package="com.laoer.bbscs.bean">
  <class name="Board" table="bbscs_board">
    <id name="id" column="ID" type="long" unsaved-value="null">
      <generator class="identity"/> //long類型的identity!
    </id>
    <property column="ParentID" length="20" name="parentID" not-null="true" type="long"/>
    <property column="ParentIDs" name="parentIDs" type="com.laoer.bbscs.ext.hibernate.SplitList"/>//parentIDs有用了userType
    <property column="ChildIDs" name="childIDs" type="com.laoer.bbscs.ext.hibernate.SplitList"/>
    <property column="BoardName" length="60" name="boardName" not-null="true" type="string"/>
    <property column="Explains" name="explains" type="text"/>//text類型
    <property column="Bulletin" name="bulletin" type="text"/>
    <property column="BoardPic" length="200" name="boardPic" type="string"/>
    <property column="UseStat" length="1" name="useStat" type="int"/>//一個開關的功能
    <property column="Orders" length="11" name="orders" type="int"/>//論壇排序
    <property column="NeedPasswd" length="1" name="needPasswd" type="int"/>//開關
    <property column="Passwd" length="100" name="passwd" type="string"/>
    <property column="BoardLevel" length="11" name="level" type="int"/>
    <property column="BoardType" length="2" name="boardType" type="int"/>
    <property column="AllowHTML" length="1" name="allowHTML" type="int"/>
    <property column="AllowUBB" length="1" name="allowUBB" type="int"/>
    <property column="AuditPost" length="1" name="auditPost" type="int"/>
    <property column="AuditAttach" length="1" name="auditAttach" type="int"/>//不懂???
    <property column="AddUserPostNum" length="1" name="addUserPostNum" type="int"/>//應該是一個開關吧
    <property column="IsHidden" length="1" name="isHidden" type="int"/>
    <property column="IsAuth" length="1" name="isAuth" type="int"/>
    <property column="MainPostNum" length="11" name="mainPostNum" type="long"/>
    <property column="PostNum" length="11" name="postNum" type="long"/>
  //關鍵點,boardMaster和boardTag均爲one-to-many類型!
    <map name="boardMaster" inverse="true" cascade="all-delete-orphan" lazy="false">
      <key column="boardID"/>
      <map-key column="UserName" type="string"/>
      <one-to-many class="BoardMaster"/>
    </map>
    <set name="boardTag" inverse="true" cascade="all-delete-orphan" lazy="false" order-by="orders asc">
      <key column="boardID"/>
      <one-to-many class="BoardTag"/>
    </set>
  </class>
</hibernate-mapping>
看完這些後,我們就知道這個bean是用於版區操作了,讓我們看看方法:(先看接口BoardService)
public Board saveBoard(Board board) throws BbscsException;//保存或更新Board對象
public Board createBoard(Board board) throws BbscsException;
public Board updateBoard(Board board, long oldParentID) throws BbscsException;
public Board getBoardByID(long id);
public List findBoardsByParentID(long pid, int useStat, int hidden, int orderType);
public List findBoardsAllTree(long pid, List topList, int useStat, int hidden, int orderType);
public int getNextOrder(long pid);
public int getPostSumNum(int mainorall, int useStat, int hidden);
public void removeBoard(Board board) throws BbscsException;
public Map[] getBoardPermission(long bid, int groupID);
public Map[] getBoardMasterPermission(int roleID);
public boolean isBoardMaster(Board board, String userName);
public List findBoardsInIDs(List ids);
public void removeBoardTag(Board board, String tagID) throws BbscsException;
public List getBoardIDs(List boards);
public void saveBoardsPostNumCount() throws BbscsException;
這裏方法有16個左右,注意我們進行除了查詢後的其它CURD操作都可能會產生異常.對於具體方法我們需要去了解其實現才能理清其作用.看下服務實現層:(注BoardServiceCacheImp爲其實現類,可能是加入了許多緩存的原因吧):
首先仍是logger(這裏發現了原來其實在service層和dao層只有這裏需要logger,其它地方都沒,對於異常也只有service接口和實現層有BbscsException這個異常處理,而對於DAO層的異常則已經由HibernateTemplate完全拋出了,如:  
 public void saveOrUpdate(final Object entity)
        throws DataAccessException
    {
        execute(new HibernateCallback() {

            public Object doInHibernate(Session session)
                throws HibernateException
            {
                checkWriteOperationAllowed(session);
                session.saveOrUpdate(entity);
                return null;
            }

        }
, true);
    }
)
我們回到正題上,除了logger,還有boardDAO(當然要了)
,userGroupDAO,boardPermissionDAO(講過的),permissionDAO,roleDAO,forumDAO,forumHistoryDAO,還有一些Cache服務:sysListObjCache,boardCache,userPermissionCache(以前用過),還有一個額外的服務類:sysStatService;對他們進行getter/setter方法一下...
我們分析下sysStatService:(applicationContext.xml) 系統統計服務
<bean id="sysStatService"
  class="com.laoer.bbscs.service.imp.SysStatServiceImp"
  scope="prototype"> //每次請求都產生一個對象
  <property name="userConfig">
   <ref bean="userConfig" />
  </property>
 </bean>
userConfig指:
<bean id="userConfig"
  class="com.laoer.bbscs.service.config.UserConfig">
  <property name="safePath">
   <value>${bbscs.safePath}</value> //安全目錄我的指的是D:/safe/
  </property>
 </bean>

UserConfig是一個config配置服務,在服務類中首先注入了safePath 這個String對象,它向外提供了String getUserFilePath(String userID)[簡單,最終得到一個用戶文件Path,見實際目錄就可知道其原理]和String getIndexPath()和File getIndexFilePath()以及boolean indexExist()四個對外公共方法.詳細看方法實現:
public String getIndexPath() {     //好象沒用到過
  StringBuffer sb = new StringBuffer();
  sb.append(this.getSafePath());
  sb.append("index/");
  return sb.toString();
 }
 public File getIndexFilePath() {
  File indexFilePath = new File(this.getIndexPath());//構造一個File對象
  if (!indexFilePath.exists()) {
   indexFilePath.mkdirs();
  }
  return indexFilePath;
 }
public boolean indexExist() {
  File file = new File(this.getIndexPath() + "segments");
  return file.exists();//使用文件是否存在的方法作判斷!
 }
可見config服務類較簡單(沒用到logger)!提供一個常用服務!而com.laoer.bbscs.service.imp.SysStatService是怎麼用到它的呢?它繼承了抽象類SysStatService,在這個抽象類中,有一個東西是私有的:
 private long onlineNum = 0;
  private long appearTime = 0; 發表時間long類型
  private String appearTimeStr = "";
  private long allUserNum = 0;
  private String lastRegUser = "";
  private long postMainNum = 0;
  private long postNum = 0;
(這個東西和safe文件夾下的sysstat.properties好像啊,看看先:

#sysstat.properties
#Mon Jul 16 11:35:16 CST 2007   //appearTimeStr ??
onlineNum=2
postMainNum=1
allUserNum=2
appearTime=963209825828
postNum=1
lastRegUser=sgwood

)下面有其get/set方法,子類可以繼承哦!當然,還有一個子類需實現的方法:
 public abstract void load();
  public abstract void saveOnline(long nowonlinenum);//在線數
  public abstract void saveAllUserNum(long allusernum, String lastreguser);//所有用戶數和最後註冊用戶名
  public abstract void savePostNum(long main, long all); //存入主題貼和總數
接着我們看下SysStatServiceImp.java文件好了:它加入了logger和UserConfig對象先,看下面的load方法:

 public void load() {
  Properties prop = new Properties();
  File f = new File(this.getUserConfig().getSafePath() + "sysstat.properties");  //getSafePath()當然會暴露出來!
  if (f.exists()) {
   try {
    FileInputStream fis = new FileInputStream(f);
    prop.load(fis);
/**只需傳遞這個文件的 InputStream 給 load() 方法,就會將每一個鍵-值對添加到 Properties 實例中。然後用 list() 列出所有屬性或者用 getProperty() 獲取單獨的屬性。
 list() 方法的輸出中鍵-值對的順序與它們在輸入文件中的順序不一樣。 Properties 類在一個散列表(hashtable,事實上是一個 Hashtable 子類)中儲存一組鍵-值對,所以不能保證順序。
*/
    this.setOnlineNum(Long.parseLong(prop.getProperty("onlineNum", "0").trim()));//繼承過來的!!!
    this.setAppearTime(Long.parseLong(prop.getProperty("appearTime", "0").trim()));
    this.setAllUserNum(Long.parseLong(prop.getProperty("allUserNum", "0").trim()));
    this.setLastRegUser(prop.getProperty("lastRegUser", ""));
    this.setPostMainNum(Long.parseLong(prop.getProperty("postMainNum", "0").trim()));
    this.setPostNum(Long.parseLong(prop.getProperty("postNum", "0").trim()));
    this.setAppearTimeStr(Util.formatDateTime(new Date(this.getAppearTime())));//寫時間用
    fis.close();
   } catch (NumberFormatException ex) {
    logger.error(ex);
   } catch (FileNotFoundException ex) {
    logger.error(ex);
   } catch (IOException ex) {
    logger.error(ex);
   }
  } else {
   save(); //文件不存在時!內部private方法哦
  }
 }

 private void save() {
  String path = this.getUserConfig().getSafePath() + "sysstat.properties";
  Properties prop = new Properties();
  prop.setProperty("onlineNum", String.valueOf(this.getOnlineNum()));
  prop.setProperty("appearTime", String.valueOf(this.getAppearTime()));
  prop.setProperty("allUserNum", String.valueOf(this.getAllUserNum()));
  prop.setProperty("lastRegUser", this.getLastRegUser());
  prop.setProperty("postNum", String.valueOf(this.getPostNum()));
  prop.setProperty("postMainNum", String.valueOf(this.getPostMainNum()));
  try {
   FileOutputStream fos = new FileOutputStream(path);//寫入新文件中
   prop.store(fos, "sysstat.properties");
   fos.close();
  } catch (FileNotFoundException ex) {
   logger.error(ex);
  } catch (IOException ex) {
   logger.error(ex);
  }
 }

我們看下對抽象類的實現吧:(先load一下資源文件,再set相關的key值,最後用私有的save一下)
 public void saveAllUserNum(long allusernum, String lastreguser) {
  this.load();
  this.setAllUserNum(allusernum);
  this.setLastRegUser(lastreguser);
  this.save();
 }
 public void saveOnline(long nowonlinenum) {
  this.load();
  if (nowonlinenum > this.getOnlineNum()) {  //好象不太對,不然只有多沒有少!
   long atime = System.currentTimeMillis();
   this.setOnlineNum(nowonlinenum);
   this.setAppearTime(atime);
   this.setAppearTimeStr(Util.formatDateTime(new Date(atime)));
   this.save();
  }
 }
 public void savePostNum(long main, long all) {
  this.load();
  this.setPostMainNum(main);
  this.setPostNum(all);
  this.save();
 }
OK!終於可以回到BoardServiceCachImp實現類中了,繞了好大一個彎!由於還有許多東西沒用過,只能推測一下其用法了哦~~~還是先看幾個,createBoard(Board board)先:
 @SuppressWarnings("unchecked")
 public Board createBoard(Board board) throws BbscsException {
  try {
   Board pboard = this.getBoardDAO().getBoardByID(board.getParentID()); // 取得父級版區
   if (pboard != null) { // 父級版區存在
    List pboards = new ArrayList();
    pboards.addAll(pboard.getParentIDs());//父的祖先
    pboards.add(pboard.getId());//父
/** addAll(Collection c)
add(int index,Elelemt e)
注意parentIDs和childIDs均爲List是由自定義userType的
public Class returnedClass() {
  return List.class;
 }
決定返回是List類型的!
*/
    board.setParentIDs(pboards); // 設置父級版區列表字段
    board.setLevel(pboard.getLevel() + 1); // 設置級別,在父級別上+1
   }
   board = this.getBoardDAO().saveBoard(board);//這裏的其它參數由添加時決定,不需要改變,或由其它因素相關!由DAO完成實質的工作

   if (pboard != null) {
    List pcboards = this.getBoardDAO().findBoardsByParentID(board.getParentID(), 1, -1,
      Constant.FIND_BOARDS_BY_ORDER);
/** 取得父級半區的所有子版區列表pcboard,1是useStat,-1是hidden,FIND_BOARD_BY_ORDER=0另外有,
 public static final int FIND_BOARDS_BY_MAINPOSTNUM = 1;
 public static final int FIND_BOARDS_BY_POSTNUM = 2;
/*

    List cids = this.getBoardIDs(pcboards);//得到子版ID的List
/**
 public List getBoardIDs(List boards) {
  List<Long> l = new ArrayList<Long>();
  for (int i = 0; i < boards.size(); i++) {
   Board b = (Board) boards.get(i);
   l.add(b.getId());
  }
  return l;
 }

*/
    pboard.setChildIDs(cids); // 設置父級版區的所有子版區列表字段
    this.getBoardDAO().saveBoard(pboard);//更新父信息
    this.getBoardCache().remove(pboard.getId());
/**從Board Cache中清除, 我們看下BoardCache的bean定義:
<bean id="boardCache"
  class="com.laoer.bbscs.service.imp.OsCacheImp"> 仍然是這個,以前講過!
  <constructor-arg>
   <value>${cache.config}</value> cache.config=oscache.properties
  </constructor-arg>
 </bean>
 public void remove(Object key) {
  logger.debug("Remove from cache [Key:" + key + "]");
  this.admin.flushEntry(key.toString());
 }
從oscache.properties配置處的Cache內容中去掉key=pboard.getID()的對象,讓OSCache自動緩存內容吧!
*/
   }

   this.clearBoradListSysListCache(board.getParentID());
/**它其實是一私有方法哦!需要注意的是實質用了SysListOjbCache,它其中的對象標識竟是[][][][][],5555,可能有些對象後三者可不用:
 private void clearBoradListSysListCache(long pid) {
  String[] useStats = { "-1", "0", "1" };
  String[] hiddens = { "-1", "0", "1" };
  String[] orderTypes = { "0", "1", "2" };
  for (int i = 0; i < useStats.length; i++) {
   for (int j = 0; j < hiddens.length; j++) {
    for (int x = 0; x < orderTypes.length; x++) {
     this.getSysListObjCache().remove(
       "[B][" + pid + "][" + useStats[i] + "][" + hiddens[j] + "][" + orderTypes[x] + "]");
    }
   }
  }
 }
*/

   // 爲版區增加用戶組版區權限
   List gl = this.getUserGroupDAO().findUserGroupsAll();
/**取得用戶組列表
public List findUserGroupsAll() {
  return this.getHibernateTemplate().find(LOADS_ALL);
 }
private static final String LOADS_ALL = "from UserGroup order by id";
原始數據爲6個記錄的表(見數據庫),id1---6,以GroupName區分之,TypeID表示代表類型,默認是系統類型的,不能刪除,用戶自己建的就是另外一個類型了。
*/
   BoardPermission bp;

   for (int i = 0; i < gl.size(); i++) {
    UserGroup ug = (UserGroup) gl.get(i);
    bp = new BoardPermission();
    bp.setBoardID(board.getId().longValue());//冗餘字段
    bp.setGroupID(ug.getId().intValue());//冗餘字段

    switch (ug.getId().intValue()) {
    case 1:
     bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_1);
/**<property column="Permissions" name="permissions" type="com.laoer.bbscs.ext.hibernate.SplitList"/>
     break;
    case 2:
     bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_2);
     break;
    case 3:
     bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_3);
     break;
    case 4:
     bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_4);
     break;
    case 5:
     bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_5);
     break;
    case 6:
     bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_6);
     break;
    default:
     bp.setPermissions(Constant.BOARD_PERMISSION_GROUP_LIST_1);
    }
    this.getBoardPermissionDAO().saveBoardPermission(bp);
/**而在Constant.java中有段static段:
 for (int i = 0; i < BOARD_PERMISSION_GROUP_1.length; i++) {
   BOARD_PERMISSION_GROUP_LIST_1.add(new Long(BOARD_PERMISSION_GROUP_1[i]));
  }

  for (int i = 0; i < BOARD_PERMISSION_GROUP_2.length; i++) {
   BOARD_PERMISSION_GROUP_LIST_2.add(new Long(BOARD_PERMISSION_GROUP_2[i]));
  }

  for (int i = 0; i < BOARD_PERMISSION_GROUP_3.length; i++) {
   BOARD_PERMISSION_GROUP_LIST_3.add(new Long(BOARD_PERMISSION_GROUP_3[i]));
  }

  for (int i = 0; i < BOARD_PERMISSION_GROUP_4.length; i++) {
   BOARD_PERMISSION_GROUP_LIST_4.add(new Long(BOARD_PERMISSION_GROUP_4[i]));
  }

  for (int i = 0; i < BOARD_PERMISSION_GROUP_5.length; i++) {
   BOARD_PERMISSION_GROUP_LIST_5.add(new Long(BOARD_PERMISSION_GROUP_5[i]));
  }
*/
   }

   return board;

  } catch (Exception e) {
   logger.error(e);
   throw new BbscsException(e);
  }

 }
從創建中可以知道,它調用了兩個cache(sysListObjCache和boardCache,對父級和本級的相關項進行更新(或產生),再保存.還有就是對此分版區(論壇)的用戶權限的寫入.總的來說,原則上,要數據服務(保存和查找)從DAO層來,要Cache從服務層來,要常用服務也從服務層來(因爲它不參與DAO工作)

接下來,我們繼續看findBoardAllTree:
 @SuppressWarnings("unchecked")//類型安全問題
 public List findBoardsAllTree(long pid, List topList, int useStat, int hidden, int orderType) {
  List l = this.getBoardDAO().findBoardsByParentID(pid, useStat, hidden, orderType);//這個pid指的是當前論壇,與前面的pid不一樣哦

  for (int i = 0; i < l.size(); i++) {
   Board b = (Board) l.get(i);
   topList.add(b);
   this.findBoardsAllTree(b.getId().longValue(), topList, useStat, hidden, orderType);//由於topList是一個引用類型,可遞歸得到All子論壇及子論壇的子論壇...
  }
  return topList;
 }
而這個服務類的findBoardByParentID(非DAO)它首先是查詢SysListObjCache裏面有沒有,若沒有的話再由DAO從數據庫中找,且放入到SysListObjCache中,用了add方法,需注意的是其命名有點怪哦!

 public List findBoardsByParentID(long pid, int useStat, int hidden, int orderType) {
  List l = (List) this.getSysListObjCache().get(
    "[B][" + pid + "][" + useStat + "][" + hidden + "][" + orderType + "]");
  if (l == null) {
   // l = this.getBoardDAO().findBoardsByParentID(pid, useStat, hidden,
   // orderType);
   l = this.getBoardDAO().findBoardIdsByParentID(pid, useStat, hidden, orderType);
   this.getSysListObjCache().add("[B][" + pid + "][" + useStat + "][" + hidden + "][" + orderType + "]", l);
  }
  List<Board> bl = new ArrayList<Board>();
  if (l != null && !l.isEmpty()) {
   for (int i = 0; i < l.size(); i++) {
    // Board b = this.getBoardByID(((Long)l.get(i)).longValue());
    Board b = this.getBoardByID((Long) l.get(i));
    if (b != null) {
     bl.add(b);
    }
   }
  }
由於Cache中不一定是Borad對象了,由於add進的是list,便由List<Board>類型的bl重新加載一次爲Board的List.findBoardInIDs根據IDs一個取一個,邊放入到l中!
 public List findBoardsInIDs(List ids) {
  List<Board> l = new ArrayList<Board>();
  if (ids != null && !ids.isEmpty()) {
   for (int i = 0; i < ids.size(); i++) {
    // Board b = this.getBoardByID(((Long) ids.get(i)).longValue());
    Board b = this.getBoardByID((Long) ids.get(i));
    if (b != null) {
     l.add(b);
    }
   }
  }
  return l;
 }
  return bl;
 }
與其相反的方法是:
public List getBoardIDs(List boards) {
  List<Long> l = new ArrayList<Long>();
  for (int i = 0; i < boards.size(); i++) {
   Board b = (Board) boards.get(i);
   l.add(b.getId());
  }
  return l;
 }
注意以上都有this.getBoardByID(),它是帶Cache的哦!從BoardCache中取!
 public Board getBoardByID(long id) {
  Board board = (Board) this.getBoardCache().get(new Long(id));
  if (board == null) {
   board = this.getBoardDAO().getBoardByID(id);
   if (board != null) {
    this.getBoardCache().add(board.getId(), board);
   }
  }
  return board;
 }
另外,對於Permission,有以下幾個方法:
public Map[] getBoardMasterPermission(int roleID)
public Map[] getBoardPermission(long bid, int groupID)
private Map[] getPermissionMaps(long bid, int groupID)
private Map[] getPermissionMaps(int roleID)
前面2個是公開的方法,後2個是被調用的..
 public Map[] getBoardMasterPermission(int roleID) {
  if (Constant.USE_PERMISSION_CACHE) {
   Map[] mapPermission = (Map[]) this.getUserPermissionCache().get("R_" + String.valueOf(roleID));
   if (mapPermission == null) {
    mapPermission = this.getPermissionMaps(roleID);
    this.getUserPermissionCache().add("R_" + String.valueOf(roleID), mapPermission);
   }
   return mapPermission;
  } else {
   return this.getPermissionMaps(roleID);
  }
 }
private Map[] getPermissionMaps(int roleID) {
  Map[] mapPermission = { new HashMap(), new HashMap() }; //MAP數組,555

  Role role = this.getRoleDAO().findRoleByID(roleID);//得到id爲roleID的角色對象
  List permissions = role.getPermissions(); // 取得角色的權限ID列表(ID的List)
  if (permissions != null && !permissions.isEmpty()) {
   List permissionList = this.getPermissionDAO().findPermissionnIDs(permissions); // 取得權限列表Permission對象的List
   for (int i = 0; i < permissionList.size(); i++) {
    Permission permission = (Permission) permissionList.get(i);
    if (permission.getTypeID() == 2) {
     mapPermission[0].put(permission.getResource() + "," + permission.getAction(), permission);
    }
    if (permission.getTypeID() == 3) {
     mapPermission[1].put(permission.getId(), permission);
    }  //這段需理解!!!
   }
  }
  return mapPermission;
 }
另外,除了版主外,還有版區權限:
public Map[] getBoardPermission(long bid, int groupID) {
  if (Constant.USE_PERMISSION_CACHE) {
   Map[] mapPermission = (Map[]) this.getUserPermissionCache().get(
     "BG_" + String.valueOf(bid) + "_" + String.valueOf(groupID));
   if (mapPermission == null) {
    mapPermission = this.getPermissionMaps(bid, groupID);
    this.getUserPermissionCache().add("BG_" + String.valueOf(bid) + "_" + String.valueOf(groupID),
      mapPermission);
   }
   return mapPermission;
  } else {
   return this.getPermissionMaps(bid, groupID);
  }
 }
同樣,它用了類似的重載方法:
 private Map[] getPermissionMaps(long bid, int groupID) {
  Map[] boardPermission = { new HashMap(), new HashMap() };
  BoardPermission bp = this.getBoardPermissionDAO().findBoardPermissionByBidGid(bid, groupID);//用的是BoardPermissionDAO
  List permissions = bp.getPermissions(); // 取得權限ID列表
  if (permissions != null && !permissions.isEmpty()) {
   List permissionList = this.getPermissionDAO().findPermissionnIDs(permissions); // 取得權限列表Permission對象的List
   for (int i = 0; i < permissionList.size(); i++) {
    Permission permission = (Permission) permissionList.get(i);
    if (permission.getTypeID() == 2) {
     boardPermission[0].put(permission.getResource() + "," + permission.getAction(), permission);
    }
    if (permission.getTypeID() == 3) {
     boardPermission[1].put(permission.getId(), permission);
    }
   }
  }
  return boardPermission;
 }
接下來,看看getNextOrder(long pid),getPostSumNum(int mainorall,int usStat,int hidden),它們完全由DAO去查詢數據庫!我們看下remove系列:
public void removeBoard(Board board) throws BbscsException {
  try {
   Long lbid = board.getId();
   long pbid = board.getParentID();
   Board pboard = this.getBoardDAO().getBoardByID(board.getParentID()); // 取得父版區

   this.getBoardDAO().removeBoard(board); // 刪除版區
   this.getBoardPermissionDAO().removeBoardPermissionsByBid(board.getId().longValue());//刪除對應版區的權限

   if (pboard != null) { // 父版區存在,對ChildIDs字段做矯正
    List pcboards = this.getBoardDAO().findBoardsByParentID(pboard.getId().longValue(), 1, 0,
      Constant.FIND_BOARDS_BY_ORDER);//pcboards是子對象
    List cids = this.getBoardIDs(pcboards);//cids是子IDs
    pboard.setChildIDs(cids);
    this.getBoardDAO().saveBoard(pboard);
   }

   this.getBoardCache().remove(lbid);
   this.clearBoradListSysListCache(pbid);
//清理本id的BoardCache,清理父id的SysListObjectCache,而userPermission中出現過去2類:R_ BG_的..都不好清理!
  } catch (Exception ex) {
   logger.error(ex);
   throw new BbscsException(ex);
  }
 }
public void removeBoardTag(Board board, String tagID) throws BbscsException {
  BoardTag bt = null;
  Iterator it = board.getBoardTag().iterator(); //由Iterator遍歷查找,找到後remove掉
  while (it.hasNext()) {
   bt = (BoardTag) it.next();
   if (bt.getId().equals(tagID)) {
    board.getBoardTag().remove(bt);
    break;
   }
  }
  try {
   board = this.getBoardDAO().saveBoard(board); //保存修改結果
   this.getForumDAO().updateForumsTag(tagID, "0", "");//帖子TAG
   this.getForumHistoryDAO().updateForumsTag(tagID, "0", "");//歷史貼TAG
   this.getBoardCache().remove(board.getId());//BoardCache清理一次
  } catch (Exception e) {
   logger.error(e);
   throw new BbscsException(e);
  }

 }
最後再看兩個方法:
public Board updateBoard(Board board, long oldParentID) throws BbscsException {
  try {
   Board pboard = this.getBoardDAO().getBoardByID(board.getParentID());

   if (pboard != null) {
    List pboards = new ArrayList();
    pboards.addAll(pboard.getParentIDs());
    pboards.add(pboard.getId());
    board.setParentIDs(pboards);
    board.setLevel(pboard.getLevel() + 1);
   } else {
    board.setParentIDs(new ArrayList());//hbm.xml決定需List對象
    board.setLevel(0);
   }
/**
 數據庫中的數據id /parentID/ParentIDs/ChildIDs
1/  0/     /2,3
2/  1/    1/
3/  1/    1/
*/
   board = this.getBoardDAO().saveBoard(board);

   if (pboard != null) {
    List pcboards = this.getBoardDAO().findBoardsByParentID(board.getParentID(), 1, -1,
      Constant.FIND_BOARDS_BY_ORDER);
    List cids = this.getBoardIDs(pcboards);
    pboard.setChildIDs(cids);
    this.getBoardDAO().saveBoard(pboard);
    this.getBoardCache().remove(pboard.getId());
   }
   this.clearBoradListSysListCache(board.getParentID());

   if (oldParentID != -1) { // 父級版區改變。修正父級版區數據 關鍵點,由傳入的oldParentID決定是否改變ParentID
    Board pboardOld = this.getBoardDAO().getBoardByID(oldParentID);
    if (pboardOld != null) {
     List pcboards = this.getBoardDAO().findBoardsByParentID(pboardOld.getId().longValue(), 1, -1,
       Constant.FIND_BOARDS_BY_ORDER);
     List cids = this.getBoardIDs(pcboards);
     pboardOld.setChildIDs(cids);
     this.getBoardDAO().saveBoard(pboardOld);
     this.getBoardCache().remove(pboardOld.getId());
     this.clearBoradListSysListCache(oldParentID);
    }
   }
   this.getBoardCache().remove(board.getId()); // 從Cache中清除

   return board;
  } catch (Exception ex) {
   logger.error(ex);
   throw new BbscsException(ex);
  }
 }
最後一個方法了:
 public void saveBoardsPostNumCount() throws BbscsException {
  long totalNum = 0;
  long totalMainNum = 0;
  List bl = findBoardsByParentID(0, 1, -1, Constant.FIND_BOARDS_BY_ORDER);//從0繼承的版區有顯示主題數和貼子數的需求
  for (int i = 0; i < bl.size(); i++) {
   Board b = (Board) bl.get(i);
   if (b.getBoardType() == 3) { //可發貼的版區吧!
    b.setMainPostNum(this.getForumDAO().getForumNum(b.getId(), 1, 0, 0, -1)
      + this.getForumHistoryDAO().getForumNum(b.getId(), 1, 0, 0, -1));
    b.setPostNum(this.getForumDAO().getForumNum(b.getId(), -1, 0, 0, -1)
      + this.getForumHistoryDAO().getForumNum(b.getId(), -1, 0, 0, -1));
//isNew=-1表示非主題貼,而1則相反!
    try {
     b = this.getBoardDAO().saveBoard(b);
     totalNum = totalNum + b.getPostNum();
     totalMainNum = totalMainNum + b.getMainPostNum();
     // if (Constant.USE_CLUSTER) {
     this.getBoardCache().remove(b.getId()); // 從Cache中清除
     // } else {
     // this.getBoardCache().add(b.getId(), b);
     // }
    } catch (Exception ex1) {
     logger.error(ex1);
     throw new BbscsException(ex1);
    }
   }
   List bl2 = findBoardsByParentID(b.getId(), 1, -1, Constant.FIND_BOARDS_BY_ORDER); //子論壇的更新
   if (!bl2.isEmpty()) {
    for (int j = 0; j < bl2.size(); j++) {
     Board b2 = (Board) bl2.get(j);
     if (b2.getBoardType() == 3) {
      b2.setMainPostNum(this.getForumDAO().getForumNum(b2.getId(), 1, 0, 0, -1)
        + this.getForumHistoryDAO().getForumNum(b2.getId(), 1, 0, 0, -1));
      b2.setPostNum(this.getForumDAO().getForumNum(b2.getId(), -1, 0, 0, -1)
        + this.getForumHistoryDAO().getForumNum(b2.getId(), -1, 0, 0, -1));
      try {
       b2 = this.getBoardDAO().saveBoard(b2);
       totalNum = totalNum + b2.getPostNum();
       totalMainNum = totalMainNum + b2.getMainPostNum();
       // if (Constant.USE_CLUSTER) {
       this.getBoardCache().remove(b2.getId()); // 從Cache中清除
       // } else {
       // this.getBoardCache().add(b2.getId(), b2);
       // }
      } catch (Exception ex1) {
       logger.error(ex1);
       throw new BbscsException(ex1);
      }
     }
    }
   }
  }
  logger.info("postMainNum:" + totalMainNum + " postNum:" + totalNum);//打印
  this.getSysStatService().savePostNum(totalMainNum, totalNum);//SysStatService服務終於用上了!
 }
OK!我們直接進入DAO層:先看接口,發現與service接口層的方法並不相同,主要是由於service層有些方法是業務處理用,而不是用於數據處理,如createBoard,updateBoard,findBoardsAllTree,getBoardPermission,getBoardMasterPermission,isBoardMaster,當然DAO接口層也有一些方法是沒有的:findBoardsNeedCount,findBoardsInIDsfindBoardsIdByParentIDInUse等等,我們看其實現吧:
進入com.laoer.bbscs.BoardHibernateDAO中首先是一些字符常量:
 private static final String LOADS_BY_PARENTID_BY_ORDER = "from Board where parentID = ? order by orders";
 private static final String LOADS_ALL = "from Board";
 public static final String[] FIND_BOARDS = new String[3];
 public static final String LOAD_NEXT_ORDER = "select max(orders) from Board where parentID = ?";
 private static final String LOAD_IDS_IN_USE = "select id from Board where parentID = ? and useStat = 1";
我們看幾個重點的實現方法:
 public List findBoardsByParentID(long pid) {
  return this.getHibernateTemplate().find(LOADS_BY_PARENTID_BY_ORDER, new Long(pid));
 }
其重載方法:
public List findBoardsByParentID(final long pid, final int useStat, final int hidden, final int orderType) {
  return getHibernateTemplate().executeFind(new HibernateCallback() {
   public Object doInHibernate(Session s) throws HibernateException {
    Criteria c = s.createCriteria(Board.class);
    c.add(Restrictions.eq("parentID", new Long(pid)));
    if (useStat != -1) {
     c.add(Restrictions.eq("useStat", new Integer(useStat)));
    }
    if (hidden != -1) {
     c.add(Restrictions.eq("isHidden", new Integer(hidden)));
    }
    if (orderType != -1) {
     if (orderType == Constant.FIND_BOARDS_BY_ORDER) {
      c.addOrder(Order.asc("orders"));
     }
     if (orderType == Constant.FIND_BOARDS_BY_MAINPOSTNUM) {
      c.addOrder(Order.desc("mainPostNum"));
     }
     if (orderType == Constant.FIND_BOARDS_BY_POSTNUM) {
      c.addOrder(Order.desc("postNum"));
     }
    }

    return c.list();
   }
  });
 }
其中用到了條件查詢(Criteria Query),參考http://blog.sina.com.cn/u/4a5e7dc401000878  和http://hi.baidu.com/yaolihui/blog/item/7c77b58286a56792f703a663.html,而public List findBoardIdsByParentID(final long pid, final int useStat, final int hidden, final int orderType)採用了構造HQL語句的方式來完成查詢!根據parentID預取得Board序列:
 public int getNextOrder(long pid) {
  List l = getHibernateTemplate().find(LOAD_NEXT_ORDER, new Long(pid));
  if (l != null && !l.isEmpty()) {
   if (l.get(0) == null) {
    return 5;
   } else {
    return ((Integer) l.get(0)).intValue() + 5;
   }
  } else {
   return 5;
  }
 }
public int getPostSumNum(final int mainorall, final int useStat, final int hidden) {
  int sum = 0;

  List list = getHibernateTemplate().executeFind(new HibernateCallback() {
   public Object doInHibernate(Session s) throws HibernateException {
    Criteria c = s.createCriteria(Board.class);
    if (mainorall == 0) {
     c.setProjection(Projections.projectionList().add(Projections.sum("mainPostNum")));  //求mainPostNum的和爲結果
    }
    if (mainorall == 1) {
     c.setProjection(Projections.projectionList().add(Projections.sum("postNum")));//求postNum的和爲結果
    }
    if (useStat != -1) {
     c.add(Restrictions.eq("useStat", new Integer(useStat)));
    }
    if (hidden != -1) {
     c.add(Restrictions.eq("isHidden", new Integer(hidden)));
    }
    return c.list();
   }
  });
  if (!list.isEmpty()) {
   Object obj = (Object) list.get(0);//注:結果集只有一個字段
   if (obj != null) {
    sum = ((Integer) obj).intValue();
   }
  }
  return sum;
 }

 public List findBoardsInIDs(final List ids, final int useStat, final int hidden) {
  return getHibernateTemplate().executeFind(new HibernateCallback() {
   @SuppressWarnings("unchecked")
   public Object doInHibernate(Session s) throws HibernateException {
    Criteria c = s.createCriteria(Board.class);
    if (ids == null) {  //ids爲空
     List idss = new ArrayList();
     idss.add(new Long(0));
     c.add(Restrictions.in("id", idss));
    } else if (ids.isEmpty()) {//ids爲empty
     ids.add(new Long(0));
     c.add(Restrictions.in("id", ids));
    } else {
     c.add(Restrictions.in("id", ids));//Restrictions.in
    }
    if (useStat != -1) {
     c.add(Restrictions.eq("useStat", new Integer(useStat)));
    }
    if (hidden != -1) {
     c.add(Restrictions.eq("isHidden", new Integer(hidden)));
    }
    c.addOrder(Order.asc("orders"));
    return c.list();
   }
  });
 }

 public List findBoardsNeedCount(final int useStat, final int hidden) {
  return getHibernateTemplate().executeFind(new HibernateCallback() {
   public Object doInHibernate(Session s) throws HibernateException {
    Criteria c = s.createCriteria(Board.class);

    c.add(Restrictions.or(Restrictions.eq("boardType", new Integer(3)), Restrictions.eq("boardType",
      new Integer(4))));//Restrictions.or 3,4的都需要count

    if (useStat != -1) {
     c.add(Restrictions.eq("useStat", new Integer(useStat)));
    }
    if (hidden != -1) {
     c.add(Restrictions.eq("isHidden", new Integer(hidden)));
    }
    c.addOrder(Order.desc("mainPostNum"));
    c.addOrder(Order.desc("postNum"));
    return c.list();
   }
  });
 }

public List findBoardsByParentID(final long pid, final int useStat, final int hidden) {
  return getHibernateTemplate().executeFind(new HibernateCallback() {
   public Object doInHibernate(Session s) throws HibernateException {
    Criteria c = s.createCriteria(Board.class);
    c.add(Restrictions.eq("parentID", new Long(pid)));//條件!
    if (useStat != -1) {
     c.add(Restrictions.eq("useStat", new Integer(useStat)));//附加條件!
    }
    if (hidden != -1) {
     c.add(Restrictions.eq("isHidden", new Integer(hidden)));
    }
    return c.list();
   }
  });
 }

 public List findBoardsIdByParentIDInUse(long pid) {
  return this.getHibernateTemplate().find(LOAD_IDS_IN_USE, new Long(pid));
 }
OK!分析完畢!當然,對於具體的方法是怎麼被用上的,要看web層了.
接下來,看下BookMarkFactory接口,是個工廠接口.它只有一個一個公有方法, public BookMark getInstance(String userId);其實現爲BookMarkFactoryImp,它產生一個BookMark bean,不過是同步的! public synchronized BookMark getInstance(String userId) {
    return new BookMark();//返回的是com.laoer.bbscs.bean.BookMark
  }
另有一個實現:BookMarksFactoryImp:(有個私有屬性:int modNum及其get/set方法)
public synchronized BookMark getInstance(String userId) {
    try {
      return (BookMark) Class.forName(BBSCSUtil.getClassName("BookMark", userId, this.getModNum())).
          newInstance();
    }
    catch (ClassNotFoundException ex) {
      logger.error(ex);
      return null;
    }
    catch (IllegalAccessException ex) {
      logger.error(ex);
      return null;
    }
    catch (InstantiationException ex) {
      logger.error(ex);
      return null;
    }

  }
這裏用到了com.laoer.bbscs.common包中的工具類:BBSCSUtil.java
 public static String getClassName(String className, String userID) {
  int num = Math.abs(userID.hashCode());
  className = Constant.BEANPERFIX + className + (num % 10);//public static String BEANPERFIX = "com.laoer.bbscs.bean.";
  return className;
 }

 public static String getClassName(String className, String userID, int modnum) {
  int num = Math.abs(userID.hashCode());
  className = Constant.BEANPERFIX + className + (num % modnum);
  return className; //應該返回是就是com.laoer.bbscs.bean.BookMark0~~~9之間的class了
 }

 public static String getClassName(String className, long bid, int modnum) {
  className = Constant.BEANPERFIX + className + (bid % modnum);
  return className;
 }

而BookMarkService則完全負責這個業務!先看BEAN:
private String id;
 private String userID;
 private String bookMarkName;
 private String url;
 private String alt;
 private int isShare;
 private Date createTime;
<hibernate-mapping package="com.laoer.bbscs.bean">
  <class name="BookMark" table="bbscs_bookmark">
    <id name="id" column="ID" type="string" unsaved-value="null">
      <generator class="uuid"/>
    </id>
    <property column="UserID" length="40" name="userID" not-null="true" type="string"/>
    <property column="BookMarkName" length="255" name="bookMarkName" not-null="true" type="string"/>
    <property column="Url" length="255" name="url" type="string"/>
    <property column="Alt" length="255" name="alt" type="string"/>
    <property column="IsShare" length="1" name="isShare" type="int"/>
    <property column="CreateTime" name="createTime" not-null="true" type="timestamp"/>//timestamp類型!
  </class>
</hibernate-mapping>
看service接口中的方法:
public BookMark findBookMarkByIDUserID(String id, String userID);
public BookMark saveBookMark(BookMark bm) throws BbscsException;
  public long getBookMarkNumByUserID(String userID);
 public PageList findBookMarks(String userID, Pages pages);
public PageList findBookMarksByUserIDShare(String userID, int isShare, Pages pages);
public void removeBookMark(BookMark bm) throws BbscsException;
 public void removeBookMarkByIDUserID(String id, String userID) throws BbscsException;
我們看實現層:先注入BookMarkDAO對象.特別的是:
public PageList findBookMarks(String userID, Pages pages) {
    PageList pl = new PageList();
    if (pages.getTotalNum() == -1) {
      pages.setTotalNum(this.getBookMarkDAO().getBookMarkNumByUserID(userID));
    }
    pages.executeCount();

    List l = this.getBookMarkDAO().findBookMarks(userID, pages.getSpage(), pages.getPerPageNum()); //DAO層方法,Service層沒有
    pl.setObjectList(l);
    pl.setPages(pages);
    return pl;
  }

  public PageList findBookMarksByUserIDShare(String userID, int isShare, Pages pages) {
    PageList pl = new PageList();
    if (pages.getTotalNum() == -1) {
      pages.setTotalNum(this.getBookMarkDAO().getBookMarkNumByUserIDShare(userID, isShare));
    }
    pages.executeCount();

    List l = this.getBookMarkDAO().findBookMarksByUserIDShare(userID, isShare, pages.getSpage(),
        pages.getPerPageNum());//DAO層方法,Service層沒有
    pl.setObjectList(l);
    pl.setPages(pages);
    return pl;
  }
這裏用到了2個分頁功能的類:它們都在com.laoer.bbscs.service.web包中,一個Page一個PageList;
兩個都是javaBEAN,不過可以帶少量的業務邏輯處理功能.先看Pages:
int page=1;//頁號
long totalNum=-1;//記錄總數
int perPageNum=1;//每頁顯示記錄數
int allPage=1;//總頁數
int cpage=1;//當前頁
int spage=1;//開始記錄數
String fileName="";
boolean useUrlRewirte=false;
public Pages(int page, long totalNum, int perPageNum) {
  this.page = page;
  this.totalNum = totalNum;
  this.perPageNum = perPageNum;
  this.executeCount();
 }

public void executeCount() {
  this.allPage = (int) Math.ceil((this.totalNum + this.perPageNum - 1) / this.perPageNum);
  int intPage = this.page;
  if (intPage > this.allPage) { // pages == 0
   this.cpage = 1;
  } else {
   this.cpage = intPage;
  }
  this.spage = (this.cpage - 1) * this.perPageNum;
 }
而PageList包括一個pages和List類型的objectList 及其set/get方法

  public PageList() {
  }
我們回到findBookMarks,先得到totalNum,再執行executeCount(),設置其值後,用this.getBookMarkDAO().findBookMark(userID,pages.getspage,pages.getPerPageNum())得到後賦給PageList的objectList和page對象.這樣,就可以給web層用List去遍歷了,當然也要配合page.
我們直接進入DAO接口層:(其實它完全爲service層服務,整體上差不多不過有些方法卻不一樣)
public BookMark saveBookMark(BookMark bm);
public BookMark findBookMarkByIDUserID(String id,String userID);
public long getBookMarkNumByUserID(String userID);
public List findBookMark(final String userID,final int firstResult,final int maxResults);
public long getBookMarkNumByUserIDShare(String userID, int isShare);
public List findBookMarksByUserIDShare(final String userID, final int isShare, final int firstResult,final int maxResults);//final int類型,方法體裏不可改變
public void removeBookMark(BookMark bm);
public void removeBookMarkByIDUserID(String id, String userID);
看它的實現BookMarkHibernateDAO.java:(private static final String類型)
private static final String LOAD_BY_ID_USERID = "from BookMark where id = ? and userID = ?";
  private static final String GET_NUM_BY_USERID = "select count(*) from BookMark where userID = ?";
  private static final String LOADS_BY_USERID =
      "from BookMark where userID = ? order by createTime desc";
  private static final String REMOVE_BY_ID_USERID =
      "delete from BookMark where id = ? and userID = ?";
  private static final String GET_NUM_BY_USERID_ISSHARE =
      "select count(*) from BookMark where userID = ? and isShare = ?";
  private static final String LOADS_BY_USERID_ISSHARE =
      "from BookMark where userID = ? and isShare = ? order by createTime desc";
我們看其中的一些方法(根據UserID取得BookMark數量)
public long getBookMarkNumByUserID(String userID) {
    List l = this.getHibernateTemplate().find(GET_NUM_BY_USERID, userID);
    if (l == null || l.isEmpty()) {
      return 0;
    }
    else {
      return ( (Long) l.get(0)).longValue();
    }
  }
另外一個方法:(根據UserID和isShare取得BookMark列表)
  public List findBookMarksByUserIDShare(final String userID, final int isShare,
                                         final int firstResult,
                                         final int maxResults) {
    return getHibernateTemplate().executeFind(new HibernateCallback() {
      public Object doInHibernate(Session s) throws HibernateException, SQLException {
        Query query = s.createQuery(LOADS_BY_USERID_ISSHARE);
        query.setString(0, userID);
        query.setInteger(1, isShare);
        query.setFirstResult(firstResult);
        query.setMaxResults(maxResults);

        List list = query.list();
        return list;
      }
    });
  }

我們看下CommendService:(推薦)
 <bean id="commendServiceTarget"
  class="com.laoer.bbscs.service.imp.CommendServiceImp">
  <property name="commendDAO">
   <ref local="commendDAO" />
  </property>
  <property name="forumDAO">
   <ref local="forumMainDAO" />
  </property>
  <property name="commendFileIO">
   <ref local="commendFileIO" />
  </property>
  <property name="sysListObjCache">
   <ref local="sysListObjCache" />
  </property>
 </bean>
先看bean:
private String id;
private long boardID;//版區ID
private String boardName;
private String postID;
private String postMainID;
private String userID;
private String userName;
private long commendBoardID;//推薦頂層版區ID
private int commendTop;//是否推薦到首頁
private String title;//帖子標題
private long createTime;//創建時間
<hibernate-mapping package="com.laoer.bbscs.bean">
  <class name="Commend" table="bbscs_commend">
    <id name="id" column="ID" type="string" unsaved-value="null">
      <generator class="uuid"/>
    </id>
    <property column="BoardID" length="13" name="boardID" not-null="true" type="long"/>
    <property column="BoardName" length="60" name="boardName" not-null="true" type="string"/>
    <property column="PostID" length="40" name="postID" not-null="true" type="string"/>
    <property column="PostMainID" length="40" name="postMainID" not-null="true" type="string"/>
    <property column="UserID" length="40" name="userID" not-null="true" type="string"/>
    <property column="UserName" length="20" name="userName" not-null="true" type="string"/>
    <property column="CommendBoardID" length="13" name="commendBoardID" not-null="true" type="long"/>
    <property column="CommendTop" length="1" name="commendTop" not-null="true" type="int"/>
    <property column="Title" length="150" name="title" type="string"/>
    <property column="BoardCategory" length="40" name="boardCategory" type="string"/>
    <property column="TopCategory" length="40" name="topCategory" type="string"/>
    <property column="CreateTime" name="createTime" not-null="true" type="long"/>
  </class>
</hibernate-mapping>
CommendServie接口中有以下主要方法:
public Commend saveCommend(Commend commend) throws BbscsException;
public int getCommendNumByCommendBoardID(long commendBoardID);
public PageList findCommendsByCommendBoardID(long commendBoardID, Pages pages);
public int getCommendNumByCommendTop(int commendTop);
public void removeCommend(long commendBoardID, List ids) throws BbscsException;
public void createCommendTopFile(int num) throws BbscsException;
public List findCommendsByCommendTopCache(int commendTop, int num);
而其實現層大多方法給DAO去實現之.下面爲其中的幾個方法:
 public PageList findCommendsByCommendBoardID(long commendBoardID, Pages pages) {
  PageList pl = new PageList();
  if (pages.getTotalNum() == -1) {
   pages.setTotalNum(this.getCommendDAO().getCommendNumByCommendBoardID(commendBoardID));//DAO實現
  }
  pages.executeCount();
  List l = this.getCommendDAO().findCommendsByCommendBoardID(commendBoardID, pages.getSpage(),
    pages.getPerPageNum());
  pl.setObjectList(l);
  pl.setPages(pages);
  return pl;
 }
下面是從頂層版區刪除推薦....
 public void removeCommend(long commendBoardID, List ids) throws BbscsException {
  List l = this.getCommendDAO().findCommendsInIds(ids);
  try {
   for (int i = 0; i < l.size(); i++) {
    Commend c = (Commend) l.get(i);//取出
    Forum f = this.getForumDAO().findForumByID(c.getPostID(), c.getBoardID());
    f.setCommend(0);//修改是否推薦標誌
    this.getForumDAO().saveOrUpdateForum(f);
    this.getCommendDAO().removeCommend(c);
   }
   List commendList = this.getCommendDAO().findCommendsByCommendBoardID(commendBoardID, 0, 10);
   this.getCommendFileIO().saveCommendInReadPageFile(commendBoardID, commendList);//寫入推薦文件中!!!!
  } catch (Exception ex) {
   logger.error(ex);
   throw new BbscsException(ex);
  }
 }
public void createCommendTopFile(int num) throws BbscsException {
  List l = this.getCommendDAO().findCommendsByCommendTop(0, 0, num);
  try {
   this.getCommendFileIO().saveCommendInReadPageFile(0, l);
  } catch (IOException ex) {
   logger.error(ex);
   throw new BbscsException(ex);
  }
 }
而findCommendByCommendTopCache爲從SysListObjCache中獲得10條推薦信息!
 public List findCommendsByCommendTopCache(int commendTop, int num) {
  List l = (List) this.getSysListObjCache().get(Constant.COMMEND_CACHE_NAME);//public static final String COMMEND_CACHE_NAME = "CommendSceipt";
  if (l == null) {
   l = this.getCommendDAO().findCommendsByCommendTop(commendTop, 0, num);
   this.getSysListObjCache().add(Constant.COMMEND_CACHE_NAME, l);
  }
  return l;
 }
我們來分析下commendFileIO:
<bean id="commendFileIO"
  class="com.laoer.bbscs.fio.imp.CommendFileIOImp" />
由於service實現層用的是fio接口層,實際注入的卻是fil.imp裏面的東西:
public interface CommendFileIO {
  public void saveCommendInReadPageFile(long commendid, List commendList) throws IOException;//只有一個方法
}
看其實現,裏面用了BBSCSUtilTextUtils工具類,它其實寫了2個文件!!!前一個在貼子顯示時用到,後一個可見www.laoer.com斑主推薦部分!
 public void saveCommendInReadPageFile(long commendid, List commendList) throws IOException {
    StringBuffer sb = new StringBuffer();
    for (int i = 0; i < commendList.size(); i++) {
      Commend commend = (Commend) commendList.get(i);
      sb.append("·");
      sb.append("<a href="");
      if (Constant.USE_URL_REWRITE) {
        sb.append("read-topic-" + commend.getBoardID() + "-" + commend.getPostMainID() + "-0-1-index-1.html");
      }
      else {
        sb.append(BBSCSUtil.getActionMappingURLWithoutPrefix("read?action=topic&id=" + commend.getPostMainID() +
            "&bid=" + commend.getBoardID()));
      }
      sb.append("">");
      sb.append(TextUtils.htmlEncode(commend.getTitle()));
      sb.append("</a><BR/>");
    }
    File commendFile = new File(BBSCSUtil.getIncludePath() + "Commend_" + commendid + ".html");
    FileUtils.writeStringToFile(commendFile, sb.toString(), Constant.CHARSET);


    commendFile = null;
    sb = null;
    sb = new StringBuffer();

    //int counter = 0;
    for (int i = 0; i < commendList.size(); i++) {
      Commend c = (Commend) commendList.get(i);
      sb.append("<tr>");
      sb.append("<td>");
      sb.append("<a href="");
      if (Constant.USE_URL_REWRITE) {
        sb.append("read-topic-" + c.getBoardID() + "-" + c.getPostMainID() + "-0-1-index-1.html");
      }
      else {
        sb.append(BBSCSUtil.getActionMappingURLWithoutPrefix("read?action=topic&id=" + c.getPostMainID() +
            "&bid=" + c.getBoardID()));
      }
      sb.append("" title="");
      sb.append(c.getTitle());
      sb.append("">");
      sb.append(c.getTitle());
      sb.append("</a>");
      sb.append("[<a href="");
      if (Constant.USE_URL_REWRITE) {
        sb.append("forum-index-" + c.getBoardID() + ".html");
      }
      else {
        sb.append(BBSCSUtil.getActionMappingURLWithoutPrefix("forum?action=index&bid=" + c.getBoardID()));
      }
      sb.append("">");
      sb.append(c.getBoardName());
      sb.append("</a>]");

      sb.append("</td>");
      sb.append("</tr>");

    }
    commendFile = new File(BBSCSUtil.getIncludePath() + "ForumCover_Commend_" + commendid + ".html");
    FileUtils.writeStringToFile(commendFile, sb.toString(), Constant.CHARSET);

  }
BBSCSUtil中的方法:(前面的一些的分析)
getUserWebFilePath()返回一個本地的用戶資料文件路徑,絕對的(加了ROOTPATH)
getUserWebPath()相對的...
getWebRealPath()返回URL(一級)
getUpFilePath和getUpFileWebPath類似與上getUserWebFilePath
getIncludePath()返回本地的路徑/include/
對於BBSCSUtil.getActionMappingURLWithoutPrefix("forum?action=index&bid=" + c.getBoardID()),用了工具類封裝了真實的網頁後綴!
public static String getActionMappingURLWithoutPrefix(String action) {
//action="forum?action=index&bid=" + c.getBoardID()
  StringBuffer value = new StringBuffer();
  // Use our servlet mapping, if one is specified
  String servletMapping = Constant.SERVLET_MAPPING;//*.bbscs
  if (servletMapping != null) {

   String queryString = null;
   int question = action.indexOf("?");//6
   if (question >= 0) {
    queryString = action.substring(question);//"action=index&bid="+c.getBoardID()
   }
   String actionMapping = getActionMappingNameWithoutPrefix(action);//forum
   if (servletMapping.startsWith("*.")) {
    value.append(actionMapping);//forum
    value.append(servletMapping.substring(1));//forum.bbscs
   } else if (servletMapping.endsWith("/*")) {
    value.append(servletMapping.substring(0, servletMapping.length() - 2));
    value.append(actionMapping);
   } else if (servletMapping.equals("/")) {
    value.append(actionMapping);
   }
   if (queryString != null) {
    value.append(queryString);//value="forum.bbscs?action=index&bid="+c.getBoardID()
   }
  }

  return (value.toString());
 }

 public static String getActionMappingNameWithoutPrefix(String action) {
  String value = action;////action="forum?action=index&bid=" + c.getBoardID()
  int question = action.indexOf("?");//6
  if (question >= 0) {
   value = value.substring(0, question);//value="forum"
  }
  int slash = value.lastIndexOf("/");//slash=0;如果是main/forum.bbscs的話slash=4
  int period = value.lastIndexOf(".");//period=0;.....................period=9
  if ((period >= 0) && (period > slash)) {
   value = value.substring(0, period);//value=0....perod main/forum
  }
  return (value); //forum
 }
接下來,我們看CommendDAO,這個接口中提供瞭如下方法:
public Commend saveCommend(Commend commend);
public Commend findCommendByID(String id);
public Commend findCommendByPostID(String postID);
public int getCommendNumByCommendBoardID(long commendBoardID);
public List findCommendsByCommendBoardID(long commendBoardID, final int firstResult, final int maxResults);
public int getCommendNumByCommendTop(int commendTop);
public List findCommendsByCommendTop(int commendTop, final int firstResult, final int maxResults);
 public List findCommendsInIds(List ids);
public void removeCommend(Commend commend);
 public void removeCommend(String postID);
看實現:
 private static final String LOADS_IN_IDS = "from Commend where id in (:ids)";
  public List findCommendsInIds(final List ids) {
    return getHibernateTemplate().executeFind(new HibernateCallback() {
      public Object doInHibernate(Session s) throws HibernateException, SQLException {
        Query query = s.createQuery(LOADS_IN_IDS);
        query.setParameterList("ids", ids);//List!!!final List ids
        List list = query.list();
        return list;
      }
    });
  }

ConfigService用於配置服務:(它有兩個屬性:id,confContext其hbm.xml
<hibernate-mapping package="com.laoer.bbscs.bean">
  <class name="Config" table="bbscs_config">
    <id name="id" column="ID" type="string" unsaved-value="undefined">
      <generator class="assigned"/>
    </id>
    <property column="ConfContext" name="confContext" type="text"/>
  </class>
</hibernate-mapping>
有public Config updateConfig(Config config) throws BbscsException;
 public Config findConfigByID(String id);
 public List findConfigs();
 public void updateAllConfigs(HashMap configs) throws BbscsException;
其實現:
  public void updateAllConfigs(HashMap configs) throws BbscsException {
    Iterator it = configs.values().iterator();
    try {
      while (it.hasNext()) {
        Config config = (Config) it.next();
        this.getConfigDAO().updateConfig(config);
      }
    }
    catch (Exception ex) {
      logger.error(ex);
      throw new BbscsException(ex);
    }
  }
由於比較簡單,直接PASS:
  public List findConfigs() {
    return this.getHibernateTemplate().find(LOAD_ALL);
  }

看EliteService,其對應的BEAN有以下屬性:
private Long id;
  private long boardID;//版區ID
  private long parentID; //父ID
  private List parentIDs;//你級ID列表,用","分開
  private String eliteName;//精華目錄名稱
  private String createUser;//創建者
  private long eliteTime;//創建時間
  private int orders;//序
<hibernate-mapping package="com.laoer.bbscs.bean">
  <class name="Elite" table="bbscs_elite">
    <id name="id" column="ID" type="long" unsaved-value="null">
      <generator class="identity"/>//自動增長
    </id>
    <property column="BoardID" length="20" name="boardID" not-null="true" type="long"/>
    <property column="ParentID" length="20" name="parentID" not-null="true" type="long"/>
    <property column="ParentIDs" name="parentIDs" type="com.laoer.bbscs.ext.hibernate.SplitList"/>//自定義類型
    <property column="EliteName" length="90" name="eliteName" type="string"/>
    <property column="CreateUser" length="60" name="createUser" type="string"/>
    <property column="EliteTime" length="20" name="eliteTime" type="long"/>
    <property column="Orders" length="11" name="orders" type="int"/>
  </class>
</hibernate-mapping>
進行方法中:
public Elite createElite(Elite elite) throws BbscsException;
public Elite saveElite(Elite elite) throws BbscsException;
public Elite findEliteByID(long id);
public List findElitesByPidBid(long pid, long bid);
public void removeElite(Elite elite) throws BbscsException;
public List findElitesInIds(List ids);
它首先注入了DAO:eliteDAO和forumDAO;需要注意的是:
public Elite createElite(Elite elite) throws BbscsException {
    Elite pElite = this.getEliteDAO().findEliteByID(elite.getParentID());
    if (pElite != null) {
      List pElites = new ArrayList();
      pElites.addAll(pElite.getParentIDs());
      pElites.add(pElite.getId());
      elite.setParentIDs(pElites);//構造一下其ParentIDs
    }
    try {
      return this.getEliteDAO().saveElite(elite);
    }
    catch (Exception ex) {
      logger.error(ex);
      throw new BbscsException(ex);
    }
  }
  public void removeElite(Elite elite) throws BbscsException {
    try {
      List l = this.getForumDAO().findForumsElite(elite.getBoardID(), elite.getBoardID(),
                                                  elite.getId().longValue());
      for (int i = 0; i < l.size(); i++) {
        Forum forum = (Forum) l.get(i);
        forum.setEliteID(elite.getParentID());//改變之
        this.getForumDAO().saveOrUpdateForum(forum);
      }
      this.getEliteDAO().removeElite(elite);
    }
    catch (Exception ex) {
      logger.error(ex);
      throw new BbscsException(ex);
    }
  }
我們直接看DAO:
  public Elite saveElite(Elite elite);
  public Elite findEliteByID(long id);
  public List findElitesByPidBid(long pid, long bid);
public void removeElite(Elite elite);
  public List findElitesInIds(List ids);
對於具體地實現我們PASS.

現在,讓我們看一下最複雜的一個業務對象,forum,它是一個論壇的核心部分,貼子(其實這個單詞是論壇的意思)當然其關係到的業務邏輯及其它東東也最多了!我們從BEAN開始研究:(右邊爲一條數據庫表的記錄,注意其中有許多Default值)
        private String id;       //主鍵   402881e513bdc8550113bdefb43c0014
 private String parentID; //父級ID  NULL值
 private String mainID;   //MainID  402881e513bdc8550113bdefb43c0014
 private long boardID;    //版區ID  2
 private String boardName;           java
 private int reNum;  //回覆數       0
 private int face;   //表情        0
 private String userID; //用戶ID    4028818208ed006b0108ed020bd50001
 private String userName;       webmaster
 private String nickName;       webmaster
 private String title; //標題   dffdsfds
 private String detail;//文件名稱  P_2_402881e513bdc8550113bdefb43c0014.txt
 private String sign;//簽名     NULL
 private int artSize;//字數     58
 private int click;//點擊數     5
 private long postTime;//發貼時間  1184302609453
 private long lastTime;//最後回覆時間  1184302609453
 private String ipAddress;//IP地址   127.0.0.1
 private int isNew;//是否是主貼(這個取名有點問題)  1
 private long elite;//是否精華   0
 private long eliteID;//精華目錄ID  0
 private int agree;//贊成    0
 private int beAgainst;//反對  1
 private int canNotDel;不是水貼標誌  0
 private int delSign;//刪除標誌   0
        private String delUserID;//刪除者   NULL
 private String delUserName;   NULL
 private long delTime;     0
 private String delIP;    NULL
 private String amend;//刪除附加信息   NULL
        private String doEliteName;//加入精華者用戶名   NULL
 private long doEliteTime;//加入精華時間    0
 private int haveAttachFile;//是否有附件   0
 private List attachFileName = new ArrayList();//附件文件名列表  NULL
 private String lastPostUserName;//最後回覆用戶名 ---
 private String lastPostTitle;//最後回覆帖子標題  NULL
 private String lastPostNickName;    ---
 private long isTop;//置頂標誌   0
 private int isLock;//鎖定標誌   0
 private int auditing;//審覈標誌   0
        private int auditingAttachFile;//附件審覈標誌  0
 private int isVote;//是否投票貼標誌  0
 private int isHidden;//是否隱藏貼標誌  0
 private int editType;//編輯器類型   0
 private String quoteText;//引用文字   NULL
 private int postType;//發貼類型    0
 private int titleColor;//標題color   5
 private int canNotRe;//已經是否回覆標誌         0
 private long commend;//推薦標誌            0
 private int isHiddenValue;//隱藏貼參數    0
 private int userBlog;//加入個人文集標誌   0
 private int indexStatus;//索引標誌       0
 private int quoteEditType;//引用文字編輯器類型  0
 private int emailInform;//通知發信到哪裏(不用)  0
 private int msgInform;//消息到誰(不用)    0
 private String voteID;//投票ID   NULL
 private String tagID;//版區Tag   0
 private String tagName;//版區Tag名稱   NULL
        private int isGuest;//是否遊客發貼     0
 private int previewAttach;//附件預覽標誌     1
對於ForumMain-mysql.hbm.xml,
    <property column="AttachFileName" name="attachFileName" type="com.laoer.bbscs.ext.hibernate.SplitStringList"/>
我們看下這個另外的hibernate自定義類型類,它與SplitList類似......
而在數據庫中:(請多看看數據庫中的數據)
PRIMARY KEY  (`ID`),
  UNIQUE KEY `ID` (`ID`),
  KEY `MainID` (`MainID`),
  KEY `BoardID` (`BoardID`), 
  KEY `LastTime` (`LastTime`),
  KEY `PostTime` (`PostTime`),
  KEY `UserID` (`UserID`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8;
進入到forumservice中,有一大堆方法:(不加javadoc的話應該不會有100個吧!嚇人!)
public Forum saveOrUpdateForum(Forum forum) throws BbscsException;
public Forum saveForum(Forum forum) throws BbscsException;
public Forum updateForum(Forum forum) throws BbscsException;
public Forum findForumByID(String id);
public Forum findForumByID(String id, long bid);//根據ID,Bid取得Forum對象
public long getForumNum(long bid, int isNew, int delSign, int auditing, int auditingAttachFile);//取得帖子數量
public long getForumNum(long bid);//取得正常帖子數量
public long getForumMainNum(long bid);//取得主帖數量
public long getForumDelNum(long bid);//取得已刪除帖子數量
public long getForumAuditingNum(long bid);//取得未審覈帖子數量
public long getForumTopicNum(long bid, String mainID, int delSign, int auditing);//取得同一主題帖子數量
public long getForumTopicNum(long bid, String mainID);//取得一個主題的帖子數量(正常的)
public long getForumTopicDelNum(long bid, String mainID);//取得一個主題已刪除帖子數量
public long getForumTopicAuditingNum(long bid, String mainID);//取得一個主題未審覈帖子數量
public List findForums(long bid, int isNew, int delSign, int auditing, OrderObj[] oo);//取得帖子列表
public PageList findForums(long bid, int isNew, int delSign, int auditing, int auditingAttachFile, OrderObj[] oo,
   Pages pages);
public PageList findForumsMainWWW(long bid, Pages pages);//取得主帖分頁列表(WWW方式,發帖時間排序)
public PageList findForumsMainLastRe(long bid, Pages pages);//取得主帖分頁列表(頂帖方式,回覆時間排序)
public PageList findForumsAll(long bid, Pages pages);//取得正常帖子分頁列表,不分主從
public PageList findForumsDel(long bid, Pages pages);// 取得已刪除帖子分頁列表
public PageList findForumsAuditing(long bid, Pages pages);// 取得未審覈帖子分頁列表
public PageList findForumsTopic(long bid, String mainID, int delSign, int auditing, OrderObj[] oo, Pages pages);//取得同一主題帖子分頁列表
public PageList findForumsTopic(long bid, String mainID, Pages pages);
public List findForumsTopicAll(long bid, String mainID, int delSign, int auditing, OrderObj[] oo);
public List findForumsTopicDel(long bid, String mainID);//取得已刪除同一主題帖子列表
public List findForumsTopicAuditing(long bid, String mainID);取得未審覈同一主題帖子列表
public List findForumsElite(long bid, long elite, long eliteId);取得精華帖列表
public long getSearchNum(long bid, String con, String text, int delSign, int auditing);取得搜索帖子數量
public PageList getSearchList(long bid, String con, String text, int delSign, int auditing, String orderby,
   int ascOrDesc, Pages pages);取得搜索結果分頁列表
public long getSearchNum(long bid, String con, String text);取得搜索帖子數量(不帶條件)
public PageList getSearchList(long bid, String con, String text, Pages pages);
public long getForumOwnerNum(String userID);取得自己的帖子數量
public PageList findForumsOwner(String userID, int isNew, Pages pages);取得自己的帖子分頁列表
public void removeForum(String id, long bid) throws BbscsException;刪除Forum對象
public void removeForum(String id) throws BbscsException;
public void removeForum(Forum forum) throws BbscsException;
public Forum createForum(Forum forum) throws BbscsException;發帖
public Forum createForum(Forum forum, Board board, UserInfo ui, UploadFile uploadFile) throws BbscsException;
public Forum createReForum(Forum forum, Forum mainForum, Board board, UserInfo ui, UploadFile uploadFile,
   boolean isQuote) throws BbscsException;回帖
public Forum createForumUpFile(Forum forum, UploadFile uploadFile) throws BbscsException;
public Forum editForum(Forum forum) throws BbscsException;帖子修改
public long getForumNumCommend(long bid, long commen);
public long getForumNumCommend(long commend);
public PageList findForumsCommend(long bid, long commen, Pages pages);
public PageList findForumsCommend(long commend, Pages pages);
public boolean isReedUser(long bid, String mainID, String userID);
public void delaPost(Forum forum, Board board, UserInfo ui) throws BbscsException;刪除一個帖子
public void savePostElite(Forum forum, UserInfo ui) throws BbscsException;
public void saveForums(List forums) throws BbscsException;
public void saveForumsEliteDel(List forums) throws BbscsException;
public Forum createVoteForum(Forum forum, Board board, Vote vote, UserInfo ui,String voteItem) throws BbscsException;
public long getForumOwnerNum(long bid, String userID, String mainID);摟主帖子數
public PageList findForumsOwner(long bid, String userID, String mainID, Pages pages);只看樓主
public void removeAttachFile(Forum forum, List fileNames) throws BbscsException;刪除附件
public void removeAllAttachFile(Forum forum) throws BbscsException;刪除某個帖子所有附件
public void delForumsNotAuditingAttachFile(List ids) throws BbscsException;刪除未通過審覈的帖子附件
public long getForumNumHotTopic(long bid, int reNum, int click);
public PageList findForumsHotTopic(long bid, int reNum, int click, Pages pages);
public void removeToHistory(long atime) throws BbscsException;
public void delPosts(List forums, Board board) throws BbscsException;
public List findForumsInIds(long bid, List ids);
public List findForumsInIds(List ids);
public void delPostReal(Forum forum) throws BbscsException;
public void delPostsReal(List ids) throws BbscsException;
public void delWastePost(long bid) throws BbscsException;
public PageList findForumsAllManage(long bid, Pages pages);
public void saveForumsResume(List ids, Board board) throws BbscsException;
public void saveForumsAuditingAttachFile(List ids) throws BbscsException;
public long getForumsAuditingAttachFileNum(long bid);
public PageList findForumsAuditingAttachFile(long bid, Pages pages);
public List findForumsTopicAuditingAttachFile(long bid, String mainID);
public void saveForumChangeUser(Forum forum, int[] titleType, int[] values) throws BbscsException;
public void saveForumChangeUser(Forum forum, int titleType, int values) throws BbscsException;
public void createCommendPage(long commend) throws BbscsException;
public void saveForumCommend(int createOrDel, Board board, Forum forum) throws BbscsException;
public List findForumsByIndexStatus(int indexStatus);
public List findForumsAllNew(int num);
public List findForumsAllNewCache(int num);
public PageList findForumsAll(long bid, String tagID, Pages pages);
public PageList findForumsMainWWW(long bid, String tagID, Pages pages);
public PageList findForumsMainLastRe(long bid, String tagID, Pages pages);
public void saveForumBuy(long bid, String postId, Forum f, UserInfo buyFromUi) throws BbscsException;
public long getForumBuyNumByPostId(String postId);
public ForumBuy findForumBuyByPostIdFromId(String postId, String fromId);
public Forum saveEditForum(Forum forum) throws BbscsException;
public void saveMoveForum(Forum forum, Board toboard) throws BbscsException;
public void saveMoveForum(long frombid, String mainid, Board toboard) throws BbscsException;
public String getForumDetail(Forum forum, boolean forcefromfile);
我們進入ForumServiceImp類:
首先它注入了許多DAO和其它服務,如:forumDAO,userInfoDAO,forumUploadFile,sysListObjCache,sysConfig,userInfoFileIO,subscibeFactory,subscibeDAO,boardDAO,subscibeQueue,forumBuyDAO,voteDAO,voteItemDAO,commendDAO,commendFileIO,forumHistoryDAO,forumConfig,postCache....當然,還有logger,真得太嚇人了,太多東東了!
public Forum createForum(Forum forum) throws BbscsException {
  Board board = this.getBoardDAO().getBoardByID(forum.getBoardID());//得到版區
  UserInfo ui = this.getUserInfoDAO().findUserInfoById(forum.getUserID());//得到用戶信息
  return createForum(forum, board, ui, null);//讓別人去完成!
 }
@SuppressWarnings("unchecked") //因爲有些非JDK5.0的開源庫如hibernate, 函數返回的一定是List,而不會是List<User>,這時候IDE就會爆出很多warning。用SuppressWarning("unchecked")可以讓IDE安靜一些。
 public Forum createForum(Forum forum, Board board, UserInfo ui, UploadFile uploadFile) throws BbscsException {
  try {
   if (Constant.POST_STORAGE_MODE == 0) {//貼子存儲方式,其實在web.xml中有設定
    forum = this.getForumDAO().saveOrUpdateForum(forum);
   } else {
    String detail = forum.getDetail();//現在用這種方式
    forum = this.getForumDAO().saveOrUpdateForum(forum);

    String postFileName = "P_" + forum.getBoardID() + "_" + forum.getId() + ".txt";//可見safe文件夾下的post下的....
    File postFile = new File(this.getForumConfig().getForumPath(forum.getBoardID(), forum.getPostTime())
      + postFileName);
    FileUtils.writeStringToFile(postFile, detail, Constant.CHARSET);//寫入文件
    forum.setDetail(postFileName);//而寫入數據庫的只是文件名
   }

   if (uploadFile != null) {
    String fileName = "File_" + forum.getId() + "_" + System.currentTimeMillis()//當前時間 + "."
      + FilenameUtils.getExtension(uploadFile.getFileName());//文件後綴
    String toFilePath = BBSCSUtil.getUpFilePath(forum.getBoardID(), forum.getPostTime());//以前講過的,應該生成safe/upload/...
/**
public static String getUpFilePath(long bid, long adate) {
  StringBuffer sb = new StringBuffer();
  sb.append(Constant.ROOTPATH);
  sb.append(getUpFileWebPath(bid, adate));
  File ft = new File(sb.toString());
  if (!ft.exists()) {
   ft.mkdirs();
  }
  return sb.toString();
 }

 public static String getUpFileWebPath(long bid, long adate) {
  StringBuffer sb = new StringBuffer();
  sb.append("upload/");
  sb.append((bid % 20));
  sb.append("/");
  sb.append(bid);
  sb.append("/");
  sb.append(Util.formatDate4(new Date(adate)));
  sb.append("/");
  return sb.toString();
 }
*/
    this.getForumUploadFile().saveUploadFile(toFilePath + fileName, uploadFile, this.getSysConfig());//帖子上傳文件
    forum.setHaveAttachFile(1);//有附件
    forum.getAttachFileName().add(fileName);//List里加入內容!
    if (board.getAuditAttach() == 1) {
     forum.setAuditingAttachFile(1);
    }
   }
   forum.setMainID(forum.getId());
   forum = this.getForumDAO().saveOrUpdateForum(forum);//刷新一下
   if (board.getAuditPost() == 0 && board.getAddUserPostNum() == 1) { // 不需要審覈,並且版區爲增加用戶發帖數量
    ui.setArticleNum(ui.getArticleNum() + 1);
    ui.setExperience(ui.getExperience() + 2); // 發帖增加經驗值2點。
    ui = this.getUserInfoDAO().saveUserInfo(ui);//用戶信息更新
    this.getUserInfoFileIO().writeUserFile(ui);//用戶信息寫入文件!見safe/user
   }
   if (forum.getEmailInform() != 0 || forum.getMsgInform() != 0) {
    if (this.getSubscibeDAO().findSubscibeByPostID(forum.getId(), ui.getId(), forum.getBoardID()) == null) {
     Subscibe subs = this.getSubscibeFactory().getInstance(forum.getBoardID());
     subs.setBoardID(forum.getBoardID());
     subs.setCreateTime(new Date());
     subs.setEmailinform(forum.getEmailInform());
     subs.setMsginform(forum.getMsgInform());
     subs.setNickName(ui.getNickName());
     subs.setPostID(forum.getId());
     subs.setPostTitle(forum.getTitle());
     subs.setUserEmail(ui.getEmail());
     subs.setUserID(ui.getId());
     subs.setUserName(ui.getUserName());
     subs.setUserLocale(ui.getUserLocale());
     this.getSubscibeDAO().saveSubscibe(subs);//訂閱服務!
    }
   }

   this.getSysListObjCache().remove(Constant.FORUM_NEW_CACHE_NAME);//文章Cache,remove方法來自Cache接口!
   return forum;
  } catch (Exception ex) {
   logger.error(ex);
   throw new BbscsException(ex);
  }
 }
我們看下ForumConfig的getForumPath:(注意:forum.getBoardID()--->bid,forum.getPostTime()-->adate)
這個服務類注入的是private String safePath;
 public String getSafePath() {
  return safePath;
 }
 public void setSafePath(String safePath) {
  this.safePath = safePath;
 }
它有兩個方法:getForumPathOld與getForumPath其實完全一樣!555
public String getForumPath(long bid, long adate) {
  StringBuffer sb = new StringBuffer();
  sb.append(this.getSafePath());
  if (!this.getSafePath().endsWith("/")) {
   sb.append("/");
  }
  sb.append("post/");
  sb.append(bid % 20); //bid%20的餘數 2
  sb.append("/");
  sb.append(bid);   //2
  sb.append("/");
  sb.append(Util.formatDate4(new Date(adate)));//20070713
  sb.append("/");
  sb.append(adate % 100);//餘數 53
  sb.append("/");
  File ft = new File(sb.toString());
  if (!ft.exists()) {
   ft.mkdirs();
  }
  return sb.toString();
 }
這裏有個Util:來自com.laoer.bbscs.comm包,裏面有許多格式時間的方法:
 public static String formatDateTime(Date date) {
  SimpleDateFormat outFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  return outFormat.format(date);
 }
 public static String formatDate2(Date myDate) {
  SimpleDateFormat formatter = new SimpleDateFormat("yyyy/MM/dd");
  String strDate = formatter.format(myDate);
  return strDate;
 }
        public static String formatDate3(Date myDate) {
  SimpleDateFormat formatter = new SimpleDateFormat("MM-dd HH:mm");
  String strDate = formatter.format(myDate);
  return strDate;
 }
 public static String formatDate4(Date myDate) {
  SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
  String strDate = formatter.format(myDate);
  return strDate;
 }//用的是這個囉!
 public static String formatDate5(Date myDate) {
  String strDate = getYear(myDate) + "-" + getMonth(myDate) + "-" + getDay(myDate);
  return strDate;
 }
 public static String formatDate6(Date myDate) {
  SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm");
  String strDate = formatter.format(myDate);
  return strDate;
 }
而另外一個是文件上傳操作!貼子附件上傳和用戶信息寫入文件兩種:它們都在com.laoer.bbscs.fio中,有三個接口:CommendFileIO,ForumUploadFile,UserInfoFileIO,另外,這個包裏也提供了一個javabean:UploadFile,有兩個上傳文件需要用到的屬性fileName和InputStream類型的inputStream,它們提供了對外實用方法..我們一個一個看它們的實現過程,CommendFileIO是推薦內容,我們曾講過,safe/include/...兩個文件,而本forum用到的是另外兩個:
ForumUploadFile中有
 public void saveUploadFile(String toFileName, UploadFile uploadFile, SysConfig sysConfig) throws IOException; //sysConfig用於獲得系統的配置參數!,這裏也用到了UploadFile這個javabean!
 public void delUploadFile(Forum f) throws IOException;
 public List delUploadFile(Forum f, List fileNames) throws IOException;
 public void moveUploadFile(Forum forum, long tobid) throws IOException;
 public void delDetailFile(Forum forum) throws IOException;
我們看它的實現,由於實現它是通過spring注入的!
<bean id="userInfoFileIO"
  class="com.laoer.bbscs.fio.imp.UserInfoFileIOImp">
  <property name="userConfig">
   <ref bean="userConfig" />
  </property>
  <property name="sysConfig">
   <ref bean="sysConfig" />
  </property>
 </bean>
 <bean id="forumUploadFile"
  class="com.laoer.bbscs.fio.imp.ForumUploadFileImp" />
 <bean id="commendFileIO"
  class="com.laoer.bbscs.fio.imp.CommendFileIOImp" />
這個類也先使用了forumConfig及其getter/setter!(不過怎麼沒注入呢)我們看其:
 public void saveUploadFile(String toFileName, UploadFile uploadFile, SysConfig sysConfig) throws IOException {
  OutputStream bos = new FileOutputStream(toFileName);
  IOUtils.copy(uploadFile.getInputStream(), bos);
  if (sysConfig.isAttachImg(uploadFile.getFileName()) && sysConfig.getReduceAttachImg() == 1) {
   ImgUtil.reduceImg(toFileName, toFileName + Constant.IMG_SMALL_FILEPREFIX, sysConfig
     .getReduceAttachImgSize(), sysConfig.getReduceAttachImgSize(),1);//縮微圖的生成,同目錄內!public static String IMG_SMALL_FILEPREFIX = "_Small";
  }
---------->
public boolean isAttachImg(String fileName) {
  return FilenameUtils.isExtension(fileName, getAttachImgTypes());
 }
public String[] getAttachImgTypes() {
  String[] types = getAttachImgType().split(","); 
  if (types == null || types.length == 0) { //提供默認值!
   types = new String[3];
   types[0] = "gif";
   types[1] = "jpg";
   types[2] = "jpeg";
  }
  return types;
 }
public String getAttachImgType() {
  return this.getStringValue("AttachImgType");
 }
public int getReduceAttachImg() {
  return this.getIntValue("ReduceAttachImg", 1);
 }
是圖片且系統允許生成!
 public int getReduceAttachImgSize() {
  return this.getIntValue("ReduceAttachImgSize", 200);
 }//長寬都用這個
注意我們將繼續深入下去:
FilenameUtils是org.apache.common.io下的!而ImgUtil.reduceImg則是com.laoer.bbscs.comm包裏的工具類:(這個類就只有一個方法),我們摘入主要的一段:
 public static void reduceImg(String imgsrc, String imgdist, int widthdist, int heightdist, int benchmark) {

  // int benchmark說明:0,長寬哪個長,以哪個爲標準;1,以寬爲基準;2,以高爲基準
  try {
   File srcfile = new File(imgsrc);
   if (!srcfile.exists()) {
    return;
   }
   Image src = javax.imageio.ImageIO.read(srcfile);
   int width = src.getWidth(null);
   int height = src.getHeight(null);

   if (width <= widthdist && height <= heightdist) {
    // SysUtil.cpoyFile(imgsrc, imgdist);
    FileUtils.copyFile(new File(imgsrc), new File(imgdist));
    return;
   }
// 寬度除以高度的比例
   float wh = (float) width / (float) height;
if(benchmark==0) {
...
}
if (benchmark == 1) {
    float tmp_heigth = (float) widthdist / wh;
    BufferedImage tag = new BufferedImage(widthdist, (int) tmp_heigth, BufferedImage.TYPE_INT_RGB);
    tag.getGraphics().drawImage(src, 0, 0, widthdist, (int) tmp_heigth, null);
    FileOutputStream out = new FileOutputStream(imgdist);
    JPEGImageEncoder encoder = JPEGCodec.createJPEGEncoder(out);
    encoder.encode(tag);
    out.close();
   }
...主要還是BufferedImage和JPEGCodec.createJPEGEncoder!
對於ForumUploadFileImp中的其它方法我們在用到的時候在來考慮之.
好,我們看看UserInfoFileIO的writeUserInfo(ui),對於這個文件的效果見:safe/user目錄下!我們首先注入的是userConfig和sysConfig兩個配置服務!下面是某用戶文件中的內容:
402881e513bdc8550113bdf70a1b0019|1184303089000|1184303117000|0|0|0|0|100|0|0|0|0|-|本機地址|
用戶ID|註冊時間|登錄時間|文章數|精華文章數|用戶頭銜|用戶生命值(不一定)|積分|......|社區幣|是否有相片|(有的話,圖片名,無 爲"-")|(有的話,用戶來自)|
我們看其中的一段代碼:
if (StringUtils.isBlank(userInfo.getUserFrom())) {
   sb.append("-");
  } else {
   sb.append(userInfo.getUserFrom());
  }
  sb.append("|");
  File usrfile = new File(this.getUserConfig().getUserFilePath(userInfo.getId()) + Constant.USER_PROFILE);//public static final String USER_PROFILE = "UserProFile.txt";
  FileUtils.writeStringToFile(usrfile, sb.toString(), Constant.CHARSET);
//關於userConfig其實與sysConfig和ForumConfig類似,提供的是簡單的配置方法:
注入了safePath:(以前講過,這裏重複一次)
 public String getUserFilePath(String userID) {
  StringBuffer sb = new StringBuffer();
  int num = Math.abs(userID.hashCode());
  sb.append(this.getSafePath());
  if (!this.getSafePath().endsWith("/")) {
   sb.append("/");
  }
  sb.append("user/");
  sb.append(num % 100);
  sb.append("/");
  sb.append(userID);
  sb.append("/");
  File ft = new File(sb.toString());
  if (!ft.exists()) {
   ft.mkdirs();
  }
  return sb.toString();
 }
好了,看下面的方法:(較長)
public Forum createReForum(Forum forum, Forum mainForum, Board board, UserInfo ui, UploadFile uploadFile,
   boolean isQuote) throws BbscsException {
  try {
   String detail = forum.getDetail();
   if (board.getAuditPost() == 0) { // 不需要審覈
    mainForum.setLastPostNickName(forum.getNickName());
    mainForum.setLastPostTitle(forum.getTitle());
    mainForum.setLastPostUserName(forum.getUserName());
    mainForum.setLastTime(forum.getPostTime());
    mainForum.setReNum(mainForum.getReNum() + 1);//加入回覆數
    if (forum.getParentID().equals(forum.getMainID())) { // 回覆的是主帖
     if (mainForum.getCanNotRe() == 0) {
      mainForum.setCanNotRe(1);//已經回覆
     }
     if (isQuote) {
      forum.setQuoteEditType(mainForum.getEditType());//引用編輯類型!
      if (this.getSysConfig().getQuoteMaxSize() > 0) {// public int getQuoteMaxSize() {
  return this.getIntValue("QuoteMaxSize", 300);
 }
       forum.setQuoteText(BBSCSUtil.getSpeShortString(this.getForumDetail(mainForum, false), this
         .getSysConfig().getQuoteMaxSize(), "..."));
/**
 public String getForumDetail(Forum forum, boolean forcefromfile) {
  if (Constant.POST_STORAGE_MODE == 0) {
   return forum.getDetail();//直接從對象得到!
  } else {
   if (forcefromfile) {
    return this.getForumDetailFromFile(forum);//從文件來
   } else {//從Cache來
    String detail = (String) this.getPostCache().get(forum.getId());
    if (detail == null) {//沒有的話,從文件來
     detail = this.getForumDetailFromFile(forum);
     if (StringUtils.isNotBlank(detail)) {
      postCache.add(forum.getId(), detail);
     }
    }
    return detail;
   }
  }
 }
注意這裏用了一個私有方法:
 private String getForumDetailFromFile(Forum forum) {
  File postFile = new File(forumConfig.getForumPath(forum.getBoardID(), forum.getPostTime()) + forum.getDetail());
  try {
   return FileUtils.readFileToString(postFile, Constant.CHARSET);//把文件寫成字符串!
  } catch (IOException e) {
   logger.error(e);
   return "";
  }
 }
這裏用了BBSCSUtil工具類,用於截取一定長度的引用文本內容!
public static String getSpeShortString(String s, int len, String fillstr) {
  int ilen = Math.max(0, len - fillstr.length());300-3=297!0與它比較之
  char ch = ' '
  int reallen = 0;
  for (int i = 0; i < s.length(); i++) {
   ch = s.charAt(i);
   if (((int) ch > 32) && ((int) ch < 128)) { //Ascill
    reallen++;
   } else {
    reallen += 2;
   }
  }
  if (reallen <= len) {
   return s;
  }
  StringBuffer buf = new StringBuffer();
  reallen = 0;
  for (int i = 0; i < s.length(); i++) {
   ch = s.charAt(i);
   buf.append(ch);
   if (((int) ch > 32) && ((int) ch < 128)) {
    reallen++;
   } else {
    reallen += 2;
   }
   if (reallen >= ilen) {
    return buf.append(fillstr).toString();
   }
  }
  return buf.toString();
 }
*/
      } else {       
       forum.setQuoteText(this.getForumDetail(mainForum, false));
      }
     }
    }
   } else {  //需要審覈帖子!!!!
    mainForum.setLastPostTitle(forum.getTitle());
    mainForum.setLastTime(forum.getPostTime());//不加入回覆數
    if (forum.getParentID().equals(forum.getMainID())) { // 回覆的是主帖
     if (isQuote) {
      forum.setQuoteEditType(mainForum.getEditType());
      if (this.getSysConfig().getQuoteMaxSize() > 0) {
       forum.setQuoteText(BBSCSUtil.getSpeShortString(this.getForumDetail(mainForum, false), this
         .getSysConfig().getQuoteMaxSize(), "..."));
      } else {
       forum.setQuoteText(this.getForumDetail(mainForum, false));
      }
     }
    }
   }//結束了大的if!注意兩者由於主貼有特別的edittype,纔對mainForum進行操作!
   mainForum = this.getForumDAO().saveOrUpdateForum(mainForum);//更新
   if (mainForum.getReNum() == this.getSysConfig().getForumHotRes()) { // 回覆次數達到熱貼標準,增加發帖人人緣係數
    UserInfo mui = this.getUserInfoDAO().findUserInfoById(mainForum.getUserID());
    if (mui != null) {
     mui.setUserKnow(mui.getUserKnow() + 1);
     this.getUserInfoDAO().saveUserInfo(mui);
     this.getUserInfoFileIO().writeUserFile(mui);
    }//更新用戶信息
   }
   Forum reForum = mainForum;//假的!
   if (!forum.getParentID().equals(forum.getMainID())) { // 回覆的不是主帖
    reForum = this.getForumDAO().findForumByID(forum.getParentID(), forum.getBoardID());
    if (reForum != null) {
     if (reForum.getCanNotRe() == 0) {
      reForum.setCanNotRe(1);
      reForum = this.getForumDAO().saveOrUpdateForum(reForum);//保存真正所回覆的帖子信息!
     }
     if (isQuote) {
      forum.setQuoteEditType(reForum.getEditType());
      String reDetail = this.getForumDetail(reForum, false);
      if (this.getSysConfig().getQuoteMaxSize() > 0) {
       forum.setQuoteText(BBSCSUtil.getSpeShortString(reDetail, this.getSysConfig()
         .getQuoteMaxSize(), "..."));
      } else {
       forum.setQuoteText(reDetail);
      }
     }
    }
   }

   // 處理文章標題
   if (forum.getTitle().equalsIgnoreCase(Constant.RE)) {
    if (reForum != null) {
     if (reForum.getTitle().startsWith(Constant.RE)) {
      forum.setTitle(reForum.getTitle());//如果主貼有re:,則回貼一致有!
     } else {
      if (BBSCSUtil.getSysCharsetStrLength(Constant.RE + reForum.getTitle()) > 90)
/**
public static int getSysCharsetStrLength(String txt) {
  try {
   return txt.getBytes(Constant.CHARSET).length;
  } catch (UnsupportedEncodingException ex) {
   return txt.length();
  }
 }
*/
{
       forum.setTitle(reForum.getTitle());
      } else {
       forum.setTitle(Constant.RE + reForum.getTitle());
      }
     }
    }
   }

   forum = this.getForumDAO().saveOrUpdateForum(forum);//更新下回貼!
//下面是附件的操作!
   if (Constant.POST_STORAGE_MODE == 0) {
    if (uploadFile != null) {
     String fileName = "File_" + forum.getId() + "_" + System.currentTimeMillis() + "."
       + FilenameUtils.getExtension(uploadFile.getFileName());
     String toFilePath = BBSCSUtil.getUpFilePath(forum.getBoardID(), forum.getPostTime());
     this.getForumUploadFile().saveUploadFile(toFilePath + fileName, uploadFile, this.getSysConfig());
     forum.setHaveAttachFile(1);
     forum.getAttachFileName().add(fileName);
     forum = this.getForumDAO().saveOrUpdateForum(forum);
    }
   } else {
    if (uploadFile != null) {
     String fileName = "File_" + forum.getId() + "_" + System.currentTimeMillis() + "."
       + FilenameUtils.getExtension(uploadFile.getFileName());
     String toFilePath = BBSCSUtil.getUpFilePath(forum.getBoardID(), forum.getPostTime());
     this.getForumUploadFile().saveUploadFile(toFilePath + fileName, uploadFile, this.getSysConfig());
     forum.setHaveAttachFile(1);
     forum.getAttachFileName().add(fileName);

    }

    String postFileName = "P_" + forum.getBoardID() + "_" + forum.getId() + ".txt";
    File postFile = new File(this.getForumConfig().getForumPath(forum.getBoardID(), forum.getPostTime())
      + postFileName);
    FileUtils.writeStringToFile(postFile, detail, Constant.CHARSET);
    forum.setDetail(postFileName);

    forum = this.getForumDAO().saveOrUpdateForum(forum);
   }

   if (board.getAuditPost() == 0 && board.getAddUserPostNum() == 1) { // 不需要審覈,並且版區爲增加用戶發帖數量
    ui.setArticleNum(ui.getArticleNum() + 1);
    ui.setExperience(ui.getExperience() + 1); // 回帖增加經驗值1點。
    ui = this.getUserInfoDAO().saveUserInfo(ui);
    this.getUserInfoFileIO().writeUserFile(ui);//寫入用戶文件!
   }

   if (forum.getEmailInform() != 0 || forum.getMsgInform() != 0) {
    Subscibe subs = this.getSubscibeFactory().getInstance(forum.getBoardID());
    subs.setBoardID(forum.getBoardID());
    subs.setCreateTime(new Date());
    subs.setEmailinform(forum.getEmailInform());
    subs.setMsginform(forum.getMsgInform());
    subs.setNickName(ui.getNickName());
    subs.setPostID(forum.getId());
    subs.setPostTitle(forum.getTitle());
    subs.setUserEmail(ui.getEmail());
    subs.setUserID(ui.getId());
    subs.setUserName(ui.getUserName());
    subs.setUserLocale(ui.getUserLocale());
    this.getSubscibeDAO().saveSubscibe(subs);
   }
   this.getSubscibeQueue().add(forum);
/**加入隊列中!是一個同步訪問方法:
public synchronized void add(Object o) {
  aVector.add(o);
 }
*/
   return forum;
  } catch (Exception ex) {
   logger.error(ex);
   throw new BbscsException(ex);
  }
 }
這個時候,讓我們回顧一下applicationContext.xml中的內容:
          <bean id="forumServiceTarget"
  class="com.laoer.bbscs.service.imp.ForumServiceImp">
  <property name="forumDAO">
   <ref local="forumMainDAO" />
  </property>
  <property name="userInfoDAO">
   <ref local="userInfoDAO" />
  </property>
  <property name="forumUploadFile">
   <ref local="forumUploadFile" />
  </property>
  <property name="sysListObjCache">
   <ref local="sysListObjCache" />
  </property>
  <property name="sysConfig">
   <ref local="sysConfig" />
  </property>
  <property name="userInfoFileIO">
   <ref local="userInfoFileIO" />
  </property>
  <property name="subscibeFactory">
   <ref local="subscibeFactory" />
  </property>
  <property name="subscibeDAO">
   <ref local="subscibeDAO" />
  </property>
  <property name="boardDAO">
   <ref local="boardDAO" />
  </property>
  <property name="subscibeQueue">
   <ref local="subscibeQueue" />
  </property>
  <property name="forumBuyDAO">
   <ref local="forumBuyDAO" />
  </property>
  <property name="voteDAO">
   <ref local="voteDAO" />
  </property>
  <property name="voteItemDAO">
   <ref local="voteItemDAO" />
  </property>
  <property name="commendDAO">
   <ref local="commendDAO" />
  </property>
  <property name="commendFileIO">
   <ref local="commendFileIO" />
  </property>
  <property name="forumHistoryDAO">
   <ref local="forumHistoryDAO" />
  </property>
  <property name="forumConfig">
   <ref local="forumConfig" />
  </property>
  <property name="postCache">
   <ref local="postCache" />
  </property>
 </bean>
OK!Go On!下面是創建投票帖子的,省略了後面的部分!
public Forum createVoteForum(Forum forum, Board board, Vote vote, UserInfo ui, String voteItem)
   throws BbscsException {
  try {
   String[] details = voteItem.split(" ");//投票項字符串數組!

   vote = this.getVoteDAO().saveVote(vote);//Vote保存!
   for (int i = 0; i < details.length; i++) {
    VoteItem vi = new VoteItem();
    vi.setItem(details[i]);
    vi.setItemValue(0);
    vi.setVoteID(vote.getId());
    this.getVoteItemDAO().saveVoteItem(vi);//VoteItem保存
   }
   forum.setVoteID(vote.getId());
   forum = this.getForumDAO().saveOrUpdateForum(forum);

   forum.setMainID(forum.getId());
   forum = this.getForumDAO().saveOrUpdateForum(forum);//帖子保存
....
下面這個是刪除真實帖子的文件及從數據庫中把數據刪除之!
 public void delPostReal(Forum forum) throws BbscsException {
  try {
   
   if (Constant.POST_STORAGE_MODE == 1) {
    this.getForumUploadFile().delDetailFile(forum);
   }
   this.getForumUploadFile().delUploadFile(forum);
   this.getForumDAO().removeForum(forum);
  } catch (Exception ex) {
   logger.error(ex);
   throw new BbscsException(ex);
  }
 }
其中,由delDetailFile刪除帖子內容文件,delUploadFile刪除附件文件!
 public void delDetailFile(Forum forum) throws IOException {

  File postFile = new File(this.getForumConfig().getForumPath(forum.getBoardID(), forum.getPostTime())
    + forum.getDetail());
  if (postFile.exists()) {
   FileUtils.forceDelete(postFile);
  }
 }
 public void delUploadFile(Forum f) throws IOException {
  if (f.getHaveAttachFile() == 1 && f.getAttachFileName() != null && f.getAttachFileName().size() > 0) {
   List fl = f.getAttachFileName();
   String filePath = BBSCSUtil.getUpFilePath(f.getBoardID(), f.getPostTime());
   for (int i = 0; i < fl.size(); i++) {
    File attachFile = new File(filePath + (String) fl.get(i));
    if (attachFile.exists()) {
     FileUtils.forceDelete(attachFile);
    }
    File attachFileSmall = new File(filePath + (String) fl.get(i) + Constant.IMG_SMALL_FILEPREFIX);
    if (attachFileSmall.exists()) {
     FileUtils.forceDelete(attachFileSmall);
    }
   }
  }
 }

接下來,看下複雜的方法:(delaPost(Forum forum,Board board,UserInfo ui)差不多!
public void delPosts(List forums, Board board) throws BbscsException {
  for (int i = 0; i < forums.size(); i++) {
   try {
    Forum forum = (Forum) forums.get(i);
    if (forum.getAuditing() == 0) { // 如果已近是通過審覈的帖子
     forum = this.getForumDAO().saveOrUpdateForum(forum); // 保存標註刪除,包括刪除人、時間等信息  ????
     if (forum.getIsNew() != 1) { // 如果不是主帖
      Forum mainForum = this.getForumDAO().findForumByID(forum.getMainID(), forum.getBoardID()); // 取主帖
      if (mainForum != null) {
       mainForum.setReNum(mainForum.getReNum() - 1); // 減少主帖回覆數
       if (mainForum.getLastTime() == forum.getPostTime()) { // 如果刪除的是最後一個回覆帖,要修正主帖表中最後回覆人和時間
        OrderObj[] oo = { new OrderObj("postTime", Constant.ORDER_DESC) };//查詢條件吧!OrderObj是com.laoer.bbscs.comm裏面的一個javaBean類,它有兩個屬性orderBy和ascOrDesc=Constant.ORDER_DESC;
/**
 public OrderObj(String orderBy, int ascOrDesc) {
    this.orderBy = orderBy;
    this.ascOrDesc = ascOrDesc;
  }
*/
        List l = this.getForumDAO().findForumsTopic(mainForum.getBoardID(), mainForum.getId(),
          0, 0, oo, 0, 1);//參見DAO!!!
        if (l != null && !l.isEmpty()) {
         Forum lastF = (Forum) l.get(0);
         mainForum.setLastPostNickName(lastF.getNickName());
        mainForum.setLastPostTitle(lastF.getTitle());
         mainForum.setLastPostUserName(lastF.getUserName());
         mainForum.setLastTime(lastF.getPostTime());
        }
       }
       this.getForumDAO().saveOrUpdateForum(mainForum); // 保存主帖
      }
     }
     UserInfo ui = this.getUserInfoDAO().findUserInfoById(forum.getUserID());
     if (board.getAddUserPostNum() == 1 && ui != null) { // 版區爲增加用戶發帖數量
      if (ui.getArticleNum() > 0) {
       ui.setArticleNum(ui.getArticleNum() - 1); // 減少用戶發帖數
       ui = this.getUserInfoDAO().saveUserInfo(ui);
       this.getUserInfoFileIO().writeUserFile(ui);
      }

      if (forum.getIsNew() == 1) {
       ui.setExperience(ui.getExperience() - 2); // 主帖,扣除發帖人2點經驗值
      } else {
       ui.setExperience(ui.getExperience() - 1); // 回帖,扣除發帖人1點經驗值
      }
     }
     if (forum.getElite() != 0 || forum.getCommend() != 0) { // 如果是精華或推薦
      if (forum.getElite() != 0) {
       if (ui.getArticleEliteNum() >= 1) {
        ui.setArticleEliteNum(ui.getArticleEliteNum() - 1);
       } else {
        ui.setArticleEliteNum(0);
       }
       ui.setLiterary(ui.getLiterary() - 3);
      }
      if (forum.getCommend() != 0) {
       ui.setLiterary(ui.getLiterary() - 1);
       this.getCommendDAO().removeCommend(forum.getId());
      }

      ui = this.getUserInfoDAO().saveUserInfo(ui);
      this.getUserInfoFileIO().writeUserFile(ui);
     }

     if (forum.getIsNew() == 1) { // 如果是主帖,嘗試從新帖Cache中清除,只有主貼才從Cache去之
      this.getSysListObjCache().remove(Constant.FORUM_NEW_CACHE_NAME);
     }
    } else { // 如果是尚未審覈
     forum = this.getForumDAO().saveOrUpdateForum(forum); // 保存標註刪除,包括刪除人、時間等信息
    }
   } catch (Exception ex) {
    logger.error(ex);
    throw new BbscsException(ex);
   }
  }
 }

另外:
public void delPostsReal(List ids) throws BbscsException {
  List forums = this.getForumDAO().findForumsInIds(ids);
  for (int i = 0; i < forums.size(); i++) {
   Forum forum = (Forum) forums.get(i);
   if ((System.currentTimeMillis() - forum.getDelTime()) > 7 * 24 * 3600 * 1000) {//大於7天就真正刪除哦!
    try {
     if (Constant.POST_STORAGE_MODE == 1) {
      this.getForumUploadFile().delDetailFile(forum);
     }
     this.getForumUploadFile().delUploadFile(forum);
     this.getForumDAO().removeForum(forum);
    } catch (Exception ex) {
     logger.error(ex);
     throw new BbscsException(ex);
    }
   }
  }
 }
看個查找生成分頁列表的兩個方法:
public PageList findForums(long bid, int isNew, int delSign, int auditing, int auditingAttachFile, OrderObj[] oo,
   Pages pages) {
  PageList pl = new PageList();
  if (pages.getTotalNum() == -1) {
   pages.setTotalNum(this.getForumDAO().getForumNum(bid, isNew, delSign, auditing, auditingAttachFile));
  }
  pages.executeCount();

  List l = this.getForumDAO().findForums(bid, isNew, delSign, auditing, auditingAttachFile, oo, pages.getSpage(),
    pages.getPerPageNum());
  pl.setObjectList(l);
  pl.setPages(pages);
  return pl;
 }

public PageList findForumsAll(long bid, Pages pages) {
  OrderObj oo0 = new OrderObj("isTop", Constant.ORDER_DESC);
  OrderObj oo1 = new OrderObj("lastTime", Constant.ORDER_DESC);
  OrderObj[] oo = { oo0, oo1 };
  return this.findForums(bid, -1, 0, 0, -1, oo, pages);
 }
還有重載的方法:
public List findForumsAllNew(int num) {
  OrderObj[] oo = { new OrderObj("postTime", Constant.ORDER_DESC) };
  return this.getForumDAO().findForums(-1, 1, 0, 0, -1, oo, 0, num);
 }

 public List findForumsAllNewCache(int num) {
  List l = (List) this.getSysListObjCache().get(Constant.FORUM_NEW_CACHE_NAME);
  if (l == null) {
   OrderObj[] oo = { new OrderObj("postTime", Constant.ORDER_DESC) };
   l = this.getForumDAO().findForums(-1, 1, 0, 0, -1, oo, 0, num);
   this.getSysListObjCache().add(Constant.FORUM_NEW_CACHE_NAME, l);
  }
  return l;
 }

下面是恢復帖子方法:
public void saveForumsResume(List ids, Board board) throws BbscsException {
  List forums = this.getForumDAO().findForumsInIds(ids);
  for (int i = 0; i < forums.size(); i++) {
   Forum forum = (Forum) forums.get(i);
   if (forum.getAuditing() == 0) { // 已通過審覈的帖子恢復
    forum.setDelSign(0);
    if (forum.getIndexStatus() == Constant.INDEX_STATUS_NO_INDEX_TO_DEL) {
     forum.setIndexStatus(Constant.INDEX_STATUS_NO_INDEX);
    } else if (forum.getIndexStatus() == Constant.INDEX_STATUS_DELED) {
     forum.setIndexStatus(Constant.INDEX_STATUS_NO_INDEX);
    }
    try {
     this.getForumDAO().saveOrUpdateForum(forum);
     if (!forum.getId().equals(forum.getMainID())) {
      Forum mainForum = this.getForumDAO().findForumByID(forum.getMainID(), forum.getBoardID());
      if (mainForum != null) {  //mainForum修改
       mainForum.setReNum(mainForum.getReNum() + 1);
       this.getForumDAO().saveOrUpdateForum(mainForum);
      }
     }
     if (board.getAddUserPostNum() == 1) {
      UserInfo ui = this.getUserInfoDAO().findUserInfoById(forum.getUserID());
      if (ui != null) {//用戶信息修改
       ui.setArticleNum(ui.getArticleNum() + 1);

       if (forum.getIsNew() == 1) {
        ui.setExperience(ui.getExperience() + 2); // 主帖增加2點經驗值
       } else {
        ui.setExperience(ui.getExperience() + 1); // 回帖增加1點經驗值
       }

       ui = this.getUserInfoDAO().saveUserInfo(ui);
       this.getUserInfoFileIO().writeUserFile(ui);
      }
     }
    } catch (Exception ex) {
     logger.error(ex);
     throw new BbscsException(ex);
    }
   } else { // 未通過審覈的被刪除的帖子恢復
    forum.setDelSign(0);
    try {
     this.getForumDAO().saveOrUpdateForum(forum);
    } catch (Exception ex1) {
     logger.error(ex1);
     throw new BbscsException(ex1);
    }
   }
  }
 }
 public static final int INDEX_STATUS_NO_INDEX = 0;
 public static final int INDEX_STATUS_INDEXED = 1;
 public static final int INDEX_STATUS_NEED_UPDTAE = 2;
 public static final int INDEX_STATUS_NEED_DEL = 3;
 public static final int INDEX_STATUS_DELED = 4;
 public static final int INDEX_STATUS_NO_INDEX_TO_DEL = 5;
        public static final int INDEX_STATUS_UPDATE_TO_DEL = 6;
對於其它類似,不在繼續下去了!我們將直接看DAO的實現層:(由於DAO層簡單,下面只是列出我個人認爲較有代表性的方法)
public List findForums(final long bid, final int isNew, final int delSign, final int auditing, final OrderObj[] oo) {//參數爲-1,表示此項忽略,否則爲增加相應的條件
  return getHibernateTemplate().executeFind(new HibernateCallback() {
   public Object doInHibernate(Session s) throws HibernateException {
    Criteria c = s.createCriteria(getForumClass());
    if (bid != -1) {
     c.add(Restrictions.eq("boardID", new Long(bid)));
    }
    if (isNew != -1) {
     c.add(Restrictions.eq("isNew", new Integer(isNew)));
    }
    if (delSign != -1) {
     c.add(Restrictions.eq("delSign", new Integer(delSign)));
    }
    if (auditing != -1) {
     c.add(Restrictions.eq("auditing", new Integer(auditing)));
    }
    if (oo != null && oo.length > 0) {
     for (int i = 0; i < oo.length; i++) {
      if (StringUtils.isNotBlank(oo[i].getOrderBy())) {
       if (oo[i].getAscOrDesc() == Constant.ORDER_ASC) {
        c.addOrder(Order.asc(oo[i].getOrderBy()));//Order序!!!
       }
       if (oo[i].getAscOrDesc() == Constant.ORDER_DESC) {
        c.addOrder(Order.desc(oo[i].getOrderBy()));
       }
      }
     }
    }
    return c.list();
   }
  });
 }

 public List findForumsElite(long bid, long elite, long eliteId) {
  Object[] o = { new Long(bid), new Long(elite), new Long(eliteId), new Integer(0) };
  String sql = "from " + this.getObjName() + " where boardID = ? and elite = ? and eliteID = ? and delSign = ?";
  return this.getHibernateTemplate().find(sql, o);
 }
/**
 private String getObjName() {
  return "Forum" + this.flag;
 }
 private String flag = "Main";
*/


public List findForumsRealDelAll(long bid, long atime) {
  Object[] o = { new Long(bid), new Long(atime) };
  String sql = "from " + this.getObjName() + " where boardID = ? and delSign = 1 and delTime < ?";
  return this.getHibernateTemplate().find(sql, o);
 }

 public List findForumsToHistory(long atime) {
  String sql = "from ForumMain where isNew = 1 and elite = 0 and lastTime <= ?";
  return this.getHibernateTemplate().find(sql, new Long(atime));
 }

 public long getForumNumHotTopic(long bid, int reNum, int click) {
  Object[] o = { new Long(bid), new Integer(1), new Integer(0), new Integer(0), new Integer(reNum),
    new Integer(click) };
  String sql = "select count(id) from " + this.getObjName()
    + " where boardID = ? and isNew = ? and delSign = ? and auditing = ? and (reNum >= ? or click >= ?)";
  List l = this.getHibernateTemplate().find(sql, o);
  if (l == null || l.isEmpty()) {
   return 0;
  } else {
   return ((Long) l.get(0)).longValue();
  }
 }
下面是一個關於搜索帖子的方法:
 public List getSearchList(final long bid, final String con, final String text, final int delSign,
   final int auditing, final String orderby, final int ascOrDesc, final int firstResult, final int maxResults) {
  return getHibernateTemplate().executeFind(new HibernateCallback() {
   public Object doInHibernate(Session s) throws HibernateException {
    Criteria c = s.createCriteria(getForumClass());
    c.add(Restrictions.eq("boardID", new Long(bid)));
    c.add(Restrictions.like(con, "%" + text + "%"));
    if (delSign != -1) {
     c.add(Restrictions.eq("delSign", new Integer(delSign)));
    }
    if (auditing != -1) {
     c.add(Restrictions.eq("auditing", new Integer(auditing)));
    }
    if (StringUtils.isNotBlank(orderby)) {
     if (ascOrDesc == Constant.ORDER_ASC) {
      c.addOrder(Order.asc(orderby));
     }
     if (ascOrDesc == Constant.ORDER_DESC) {
      c.addOrder(Order.desc(orderby));
     }
    }
    c.setFirstResult(firstResult);
    c.setMaxResults(maxResults);
    return c.list();
   }
  });
 }
其它的省略之!已經將主要的Forum服務給分析完了.OK!

 
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章