我们仍然将分析处于service包中,首分析下上次没有分析的ForumArchiveService:(它只有一个方法)
public interface ForumArchivesService {
 public void createForumArchives() throws BbscsException;
}
看applicationContext.xml中:
<bean id="forumArchivesServiceTarget"
  class="com.laoer.bbscs.service.imp.ForumArchivesServiceImp">
  <property name="forumHistoryDAO">
   <ref bean="forumHistoryDAO" />
/**
<bean id="forumHistoryDAO"
  class="com.laoer.bbscs.dao.hibernate.ForumHibernateDAO">
  <constructor-arg>
   <value>History</value>
  </constructor-arg>
  <property name="sessionFactory">
   <ref local="sessionFactory" />
  </property>
 </bean>
而以前讲过的forumMain:
<bean id="forumMainDAO"
  class="com.laoer.bbscs.dao.hibernate.ForumHibernateDAO">
  <constructor-arg>
   <value>Main</value>
  </constructor-arg>
  <property name="sessionFactory">
   <ref local="sessionFactory" />
  </property>
 </bean>
对于这个forumHistoryDAO,其实它只不过是一个ForumHibernateDAO的引用而已,只不过加了个History构造参数!
public ForumHibernateDAO(String flag) {
  super();
  this.flag = flag; //flag不是main了,而是History了!!!那后面的
 private String getObjName() {
  return "Forum" + this.flag;//ForumHistory!
 }

 private Class getForumClass() {
  return Constant.FORUM_CLASS_MAP.get(this.flag); //ForumHistory.class!
 }
*/
  </property>
  <property name="forumArchivesDAO">
   <ref bean="forumArchivesDAO" /> /**com.laoer.bbscs.dao.jdbc.ForumArchivesJDBCDAO,其实这个JDBC操作由
this.getJdbcTemplate().update(sql, o);完成的!!!注意需导入org.springframework.jdbc.core.support.JdbcDaoSupport;
public void saveForumArchives(long bid, Forum f) {
  String sql = "insert into bbscs_forumarchives_"
    + (bid % 10)  
    + " (ParentID, MainID, BoardID, BoardName, ReNum, Face, UserID, UserName, NickName, Title, Detail, Sign, ArtSize, Click, PostTime, LastTime, IPAddress, IsNew, Elite, EliteID, Agree, BeAgainst, CanNotDel, CanNotRe, Commend, DelSign, DelUserID, DelUserName, DelTime, DelIP, Amend, DoEliteName, DoEliteTime, HaveAttachFile, AttachFileName, LastPostUserName, LastPostTitle, LastPostNickName, IsTop, IsLock, Auditing, AuditingAttachFile, IsVote, IsHidden, IsHiddenValue, EditType, QuoteText, QuoteEditType, PostType, TitleColor, UserBlog, IndexStatus, EmailInform, MsgInform, VoteID, TagID, TagName, IsGuest, PreviewAttach, ID) values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";  //由bid决定写入10个表中的其中一个!!!
  Object[] o = new Object[] { f.getParentID(), f.getMainID(), f.getBoardID(), f.getBoardName(), f.getReNum(),
    f.getFace(), f.getUserID(), f.getUserName(), f.getNickName(), f.getTitle(), f.getDetail(), f.getSign(),
    f.getArtSize(),....};
  this.getJdbcTemplate().update(sql, o);
 }

 private String attachFileName2String(List attachFileName) {  //将List--->String
  String text = "";
  if (attachFileName != null) {
   for (int i = 0; i < attachFileName.size(); i++) {
    String fileName = (String) attachFileName.get(i);
    text = text + fileName + ",";
   }
   if (text.endsWith(",")) {
    text = text.substring(0, (text.length() - 1));
   }
  }
  return text;
 }
*/
  </property>


  <property name="boardService">
   <ref bean="boardService" />
  </property>
  <property name="tempConfiguration">
   <ref bean="tempConfiguration" />
  </property>
  <property name="forumConfig">
   <ref bean="forumConfig" />
  </property>
  <property name="sysConfig">
   <ref bean="sysConfig" />
  </property>
 </bean>
在其实现类ForumArchiveServiceImp中注入了forumHistoryDAO,boardService,tempConfiguration,forumConfig,SysConfig等其它服务和DAO...其中,tempConfiguration是一个freemarker.template下面的Configuration对象.
public void createForumArchives() throws BbscsException {
  for (int y = 12; y >= 3; y--) {
   Calendar cld = Calendar.getInstance();
   cld.set(Calendar.HOUR_OF_DAY, 0);
   cld.set(Calendar.MINUTE, 0);
   cld.set(Calendar.SECOND, 0);
   cld.set(Calendar.MILLISECOND, 0);
   cld.add(Calendar.MONTH, -y);//三个月之前的任意月,注意这个add方法会改变年份,若y为12,则是一年前的时间,若为3,则是(相对现在)三个月前的时间
   logger.info("计算时间点:" + Util.formatDateTime(cld.getTime()));
/**
public static String formatDateTime(Date date) {
  SimpleDateFormat outFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
  return outFormat.format(date);
 }
*/
   long atime = cld.getTimeInMillis();//long型时间atime
   cld.add(Calendar.MONTH, -1);//一个月之前
   String month = Util.formatDate(cld.getTime(), "yyyy-MM");
   logger.info("存档月份:" + month);//若现在是2007-7,则这里是2007-6

   List blist = this.getBoardService().findBoardsByParentID(0, 1, -1, Constant.FIND_BOARDS_BY_ORDER);//以前讲过,从最顶层找起!useStat=1,hidden=-1,0
//Criteria c.addOrder(Order.asc("orders"));
   for (int i = 0; i < blist.size(); i++) {
    Board b = (Board) blist.get(i);
    if (b.getBoardType() == 3) {
     this.createForumArchivesFile(b, atime, month);
    }
    List bclist = this.getBoardService().findBoardsByParentID(b.getId(), 1, -1,
      Constant.FIND_BOARDS_BY_ORDER);//各大版块的子版!
    for (int j = 0; j < bclist.size(); j++) {
     Board bc = (Board) bclist.get(j);
     if (bc.getBoardType() == 3) {
      this.createForumArchivesFile(bc, atime, month);
     }//我认为这个方法少了对子版的子版的深度保存!!!
    }
   }
  }
 }
@SuppressWarnings("unchecked")
 private void createForumArchivesFile(Board b, long atime, String month) throws BbscsException {
  if (!BBSCSUtil.ArchivesMonthInFile(b.getId(), month)) { //所在月没存过!
/**

 public static List<String> getArchivesMonths(long bid) {
  List<String> l = new ArrayList<String>();
  File archivesMonthsFile = new File(Constant.ROOTPATH + "archives/archivesmonths-" + bid + ".txt");
  String month = "";
  try {
   month = FileUtils.readFileToString(archivesMonthsFile, Constant.CHARSET);
  } catch (IOException e) {
   return l;
  }
  String[] months = month.split(",");
  if (months.length > 0) {
   for (int i = (months.length - 1); i >= 0; i--) {
    if (StringUtils.isNotBlank(months[i])) {
     l.add(months[i]);
    }
   }
  }
  return l;  //archivesMonthsFile-bid.txt中存的是这个bid的存档时间!
 }

 public static boolean ArchivesMonthInFile(long bid, String month) {
  List<String> l = getArchivesMonths(bid);
  for (String m : l) {  //JDK5.0
   if (m.equalsIgnoreCase(month)) {
    return true;
   }
  }
  return false;
 }
*/
   try {
    this.getTempConfiguration().setDirectoryForTemplateLoading(
      new File(Constant.ROOTPATH + Constant.FTL_PATH));//public static String FTL_PATH = "WEB-INF/templates/";
    this.getTempConfiguration().setDefaultEncoding(Constant.CHARSET);
    this.getTempConfiguration().setNumberFormat("0.##########");
    Locale locale = new Locale("zh", "CN");//前者指语言后者是国家
    this.getTempConfiguration().setLocale(locale);
    Template temp = this.getTempConfiguration().getTemplate("archivesPostMain.ftl");//产生一个Template!!!
我们用记事本等工具打开它
/**
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>帖子存档--${boardName}(${month})</title>
<link href="../../../css/archiveslist.css" rel="stylesheet" type="text/css" />
</head>

<body>
<div id="forums">
<table cellpadding="0" cellspacing="0">
  <caption>${month}</caption>
  <tr class="trt">
   <th class="name" scope="col">主题</th>
   <th scope="col">回复</th>
   <th scope="col">作者</th>
   <th scope="col">发帖时间</th>
  </tr>
  <#list mainlist as fm>
  <tr>
    <td class="name1"><a href="${fm["postdir"]}${fm["id"]}.html" target="_blank">${fm["title"]}</a></td>
 <td>[+${fm["renum"]}]</td>
    <td>${fm["username"]}</td>
    <td>${fm["postTime"]}</td>
  </tr>
  </#list>
  <tr class="trp">
    <td colspan="4" class="name">分页:${pagebreak}</td>
  </tr>
</table>
</div>
</body>
</html>
*/
    long total = this.getForumHistoryDAO().getForumNumBeforeDate(b.getId(), atime);//历史帖子数(注: 
历史帖是3个月以前的帖子,系统每天会把3个月以前的帖子写入历史帖。)

    if (total > 0) { //有过时(超过3个月的)帖子时,把month写入
     BBSCSUtil.writeMonthToFile(b.getId(), month);//month是存储时间而已
    }

    int allPage = (int) Math.ceil((total + 40 - 1) / 40);
/**
Math.ceil(x):比x大的最小值。  
  Math.round(x):四舍五入。  
  Math.floor(x):比x小的最大值。  
  Math.round(x)返回long型,其余的返回double   型。
*/
    logger.info(b.getBoardName() + " total:" + total + " allPage:" + allPage);

    for (int i = 1; i <= allPage; i++) {
     int spage = (i - 1) * 40;开始记录数编号
     List l = this.getForumHistoryDAO().findForumsBeforeDate(b.getId(), atime, spage, 40);
/**
public List findForumsBeforeDate(final long bid, final long atime, final int firstResult, final int maxResults) {
  final String sql = "from " + this.getObjName()
    + " where boardID = ? and isNew = 1 and postTime < ? order by postTime desc";
  return getHibernateTemplate().executeFind(new HibernateCallback() {
   public Object doInHibernate(Session s) throws HibernateException, SQLException {
    Query query = s.createQuery(sql);
    query.setLong(0, bid);
    query.setLong(1, atime);
    query.setFirstResult(firstResult);
    query.setMaxResults(maxResults);

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

*/
     List mainlist = new ArrayList();
     for (int j = 0; j < l.size(); j++) {
      Forum f = (Forum) l.get(j); //Forum通用BEAN!
      Map pmap = new HashMap();
      pmap.put("id", f.getMainID());
      pmap.put("title", f.getTitle());
      pmap.put("renum", f.getReNum());
      pmap.put("username", f.getUserName());
      pmap.put("postTime", Util.formatDate6(new Date(f.getPostTime())));
/**
 public static String formatDate6(Date myDate) {
  SimpleDateFormat formatter = new SimpleDateFormat("yyyy-MM-dd HH:mm");
  String strDate = formatter.format(myDate);
  return strDate;
 }
*/
      pmap.put("postdir", Util.formatDate4(new Date(f.getPostTime())) + "/" + (f.getPostTime() % 100)
        + "/");
/**
public static String formatDate4(Date myDate) {
  SimpleDateFormat formatter = new SimpleDateFormat("yyyyMMdd");
  String strDate = formatter.format(myDate);
  return strDate;
 }
*/
      mainlist.add(pmap); //用于历史帖列表显示吧!所以不用内容!

      List fl = this.getForumHistoryDAO().findForumsTopic(b.getId(), f.getId(), 0, 0, -1,
        new OrderObj[] { new OrderObj("postTime", Constant.ORDER_ASC) });//以它为主帖,返回按postTime的回复List
      List topiclist = new ArrayList();
      for (int x = 0; x < fl.size(); x++) {
       Forum tf = (Forum) fl.get(x);
       Map tmap = new HashMap();
       tmap.put("id", tf.getMainID());
       tmap.put("title", tf.getTitle());
       tmap.put("username", tf.getUserName());
       tmap.put("posttime", Util.formatDateTime(new Date(tf.getPostTime())));
       tmap.put("content", this.getForumDetail(tf, b).toString());
/**
private StringBuffer getForumDetail(Forum f, Board board) {
  StringBuffer sb = new StringBuffer();
  if (StringUtils.isNotBlank(f.getQuoteText())) {
   sb.append("<blockquote class="quote1"><strong>");
   sb.append("引用");
   sb.append(":</strong><br />");
   if (f.getQuoteEditType() == 0) {
    sb.append(BBSCSUtil.filterText(f.getQuoteText(), (board.getAllowHTML() == 1),
      (board.getAllowUBB() == 1), true));
   } else {
    sb.append(BBSCSUtil.filterScript(f.getQuoteText()));
   }
   sb.append("</blockquote>");
  }
  if (f.getHaveAttachFile() != 0 && f.getAttachFileName() != null && !f.getAttachFileName().isEmpty()) {
   sb.append(this.getAttachFile(f)); //有附件!!!
  } else {
   sb.append("<div id="upfile");
   sb.append(f.getId());
   sb.append("" class="font5" style="display:none"></div>");
  }

  if (f.getIsVote() == 0) {
   String detail = this.getForumDetail(f);
   if (f.getEditType() == 0) {
    sb.append(BBSCSUtil.filterText(detail, (board.getAllowHTML() == 1), (board.getAllowUBB() == 1), true));
   } else {
    sb.append(BBSCSUtil.filterScript(detail));
   }
  } else {
   if (f.getEditType() == 0) {
    sb.append(BBSCSUtil.filterText(f.getDetail(), (board.getAllowHTML() == 1), (board.getAllowUBB() == 1),
      true));
   } else {
    sb.append(BBSCSUtil.filterScript(f.getDetail()));
   }
  }
  return sb;
 }
对于getAttachFile()方法我们省略分析之,我们看下:
public static String filterText(String sign, boolean useHTML, boolean useUBB, boolean useSmile) {
  if (!useHTML) {
    sign = TextUtils.htmlEncode(sign);
  }
  if (useUBB) {
   sign = getUBB2HTML(sign);
  }
  if (useSmile) {
   sign = replaceSmile(sign);
  }
  sign = sign.replaceAll(" ", "<BR/>");
  sign = filterScript(sign);
  return sign;
 }
public static String getFileTypeIcon(String fileExt) {
  String fileTypeIcon = (String) Constant.ICON_MAP.get(fileExt);
  if (fileTypeIcon == null) {
   fileTypeIcon = "default.icon.gif";
  }
  return fileTypeIcon;
 }
需要注意的是ICON_MAP在Constant中有定义值了!
*/
       topiclist.add(tmap);
       if (x != 0) { //非第一个帖,就是mainID是自己的帖子!
        this.forumArchivesDAO.saveForumArchives(b.getId(), tf);//存档!
        this.getForumHistoryDAO().removeForum(tf);//从历史帖中删除之
       }
      }

      Template temptopic = this.getTempConfiguration().getTemplate("archivesPostTopic.ftl");
/**下面是这个ftl的部分代码:
<body>
<div id="topic">
<#list topiclist as fm>
<div class="postdiv" id="post${fm["id"]}">
  <div class="title" id="title${fm["id"]}">${fm["title"]}</div>
  <div class="author" id="author${fm["id"]}">作者:${fm["username"]} (发表时间:${fm["posttime"]})</div>
  <div class="content" id="content${fm["id"]}">
  ${fm["content"]}
  </div>
</div>
</#list>
<div align="center" class="postdiv">
<script type="text/javascript"><!--
google_ad_client = "pub-9505761087047507";
google_ad_width = 728;
google_ad_height = 90;
google_ad_format = "728x90_as";
google_ad_type = "text";
google_ad_channel = "";
//-->
</script>
<script type="text/javascript"
  src="http://pagead2.googlesyndication.com/pagead/show_ads.js">
</script>
</div>
</div>
</body>
</html>
*/

      OutputStream out = new FileOutputStream(BBSCSUtil.getArchivesPostTopicPath(b.getId(), month, f
        .getPostTime())
        + f.getId() + ".html");
/**
 public static String getArchivesPostMainListWebPath(long bid, String month) {
  StringBuffer sb = new StringBuffer();
  sb.append("archives/");
  sb.append(bid);
  sb.append("/");
  sb.append(month);
  sb.append("/");
  return sb.toString();
 }
public static String getArchivesPostTopicPath(long bid, String month, long posttime) {
  StringBuffer sb = new StringBuffer();
  sb.append(Constant.ROOTPATH);
  sb.append(getArchivesPostMainListWebPath(bid, month));
  sb.append(Util.formatDate4(new Date(posttime)));
  sb.append("/");
  sb.append((posttime % 100));
  sb.append("/");
  File ft = new File(sb.toString());
  if (!ft.exists()) {
   ft.mkdirs();
  }
  return sb.toString();
 }
*/
      Writer writer = new OutputStreamWriter(out, Constant.CHARSET);
      Map root = new HashMap();
      root.put("topoctitle", f.getTitle());
      root.put("topiclist", topiclist);

      temptopic.process(root, writer);
      writer.flush();//处理f存档!

      this.forumArchivesDAO.saveForumArchives(b.getId(), f);//保存好f
      this.getForumHistoryDAO().removeForum(f);//从历史帖中删除之
     }
     StringBuffer sb = new StringBuffer();
     for (int x = 1; x <= allPage; x++) {
      sb.append("[<a href="");
      sb.append(x);
      sb.append(".html">");
      if (x == i) {
       sb.append("<strong>");
       sb.append(x);
       sb.append("</strong>");
      } else {
       sb.append(x);
      }
      sb.append("</a>] ");
     }

     OutputStream out = new FileOutputStream(BBSCSUtil.getArchivesPostMainListPath(b.getId(), month) + i
       + ".html"); //i是页码,如果有30页的话,那就是30个文件了!
     Writer writer = new OutputStreamWriter(out, Constant.CHARSET);
     Map root = new HashMap();
     root.put("boardName", b.getBoardName());
     root.put("month", month);
     root.put("mainlist", mainlist);
     root.put("pagebreak", sb.toString());

     temp.process(root, writer);//这些参数与文件中的参数一致!
     writer.flush();
    }

   } catch (Exception e) {
    logger.error(e);
    throw new BbscsException(e);
   }
  }
 }
对于上面的部分可以查看历史帖选项查看效果!

接下来是FriendFactory,是有个方法public Friend getInstance(String userId);用于实例化Friend对象用!进入Friend Bean:(注意其实现了可序列化接口implements Serializable与BookMark一样,其实后面的Note,Subscibe都一样,bean包下还有Friend0~9个与Friend内容一样的BEAN,它们主要是为了拆表准备的,普通版本用不到)
 private String id;
 private String userID;
 private String userName;
 private String friendID;
 private String friendName;
 private String friendComment;//介绍
 private int isBlack;//是否加入黑名单
我们看其Friend.hbm.xml:
<hibernate-mapping package="com.laoer.bbscs.bean">
  <class name="Friend" table="bbscs_friend">
    <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="UserName" length="20" name="userName" not-null="true" type="string"/>
    <property column="FriendID" length="40" name="friendID" not-null="true" type="string"/>
    <property column="FriendName" length="20" name="friendName" not-null="true" type="string"/>
    <property column="FriendComment" length="2000" name="friendComment" type="string"/>//length=2000?--->对应表中的`FriendComment` text, //说明
    <property column="IsBlack" length="1" name="isBlack" type="int"/>//`IsBlack` tinyint(1) default Ɔ'
  </class>
</hibernate-mapping>
看服务层的方法:
public Friend saveFriend(Friend f) throws BbscsException;
public Friend findFriendByID(String id, String ownId);
public Friend findFriendByName(String fname, String ownId);//根据朋友名、自己的ID取得
public long getFriendNum(String ownId, int isBlack);
public List findFriends(String ownId, int isBlack);
public void removeFriend(Friend f) throws BbscsException;
public void removeFriend(String id, String ownId) throws BbscsException;
public void friendIDsToFile(String ownId);//好友列表ID保存至文件
public List fileToFriendIDs(String ownId);
public List findFriendIds(String ownId, int isBlack);
进入service.imp层,首先是
public class FriendFactoryImp
    implements FriendFactory {
  public FriendFactoryImp() {
  }
   public synchronized Friend getInstance(String userId) {
    return new Friend();//同步方法!!实例化Friend对象
  }
}
而另外的FriendsFactoryImp实现,需要注意的是它也实现了FriendFactory接口:
public synchronized Friend getInstance(String userId) {
    try {
      return (Friend) Class.forName(BBSCSUtil.getClassName("Friend", userId)).newInstance();
    }
/**
 public static String getClassName(String className, String userID) {
  int num = Math.abs(userID.hashCode());
  className = Constant.BEANPERFIX + className + (num % 10);
  return className; //Friend0~~~~9
 }
public static String getClassName(String className, String userID, int modnum) {
  int num = Math.abs(userID.hashCode());
  className = Constant.BEANPERFIX + className + (num % modnum);
  return className;
 }//modnum由setModnum具体得到
*/
    catch (ClassNotFoundException ex) {
      logger.error(ex);
      return null;
    }
    catch (IllegalAccessException ex) {
      logger.error(ex);
      return null;
    }
    catch (InstantiationException ex) {
      logger.error(ex);
      return null;
    }
  }
我们看其主要的实现方法:
/**
 * 好友列表ID保存至文件
 */
  public void friendIDsToFile(String ownId) {
    List l = this.getFriendDAO().findFriends(ownId, 0);
    StringBuffer sb = new StringBuffer();
    Friend f;
    for (int i = 0; i < l.size(); i++) {
      f = (Friend) l.get(i);
      sb.append(f.getFriendID());
      sb.append(",");
    }
    File toFile = new File(this.getUserConfig().getUserFilePath(ownId) + Constant.USER_FRIEND_FILE);
//public static final String USER_FRIEND_FILE = "UserFriendFile.txt";
    try {
  FileUtils.writeStringToFile(toFile, sb.toString(), Constant.CHARSET);
 } catch (IOException e) {
  logger.error(e);
 }
  }
  
  public List fileToFriendIDs(String ownId) {
    List<String> l = new ArrayList<String>();
    File fromFile = new File(this.getUserConfig().getUserFilePath(ownId) + Constant.USER_FRIEND_FILE);
    try {
  String fids = FileUtils.readFileToString(fromFile, Constant.CHARSET);
  String[] ids = fids.split(",");//分割出来!
  if (ids != null) {
    for (int i = 0; i < ids.length; i++) {
      //System.out.println(ids[i]);
      l.add(ids[i]);
    }
  }
 } catch (IOException e) {
  logger.error(e);
 }
    return l;
  }

  @SuppressWarnings("unchecked")
public List findFriendIds(String ownId, int isBlack) {
   List l = this.getFriendDAO().findFriendIds(ownId, isBlack);
   if (l.isEmpty()) {   //填充List
    l.add("0");
   }
   return l;
  }

Friend DAO接口中提供如下方法:
 public Friend saveFriend(Friend f);
 public Friend findFriendByID(String id, String ownId);
 public Friend findFriendByName(String fname, String ownId);
 public long getFriendNum(String ownId, int isBlack);
 public List findFriends(String ownId, int isBlack);
 public void removeFriend(Friend f);
 public void removeFriend(String id, String ownId);
 public List findFriendIds(String ownId, int isBlack);
对于DAO的实现FriendHibernateDAO省略.由于上次忘记分析BookMarksHibernateDAO(拆表用的),这里我们详细分析一下FriendsHibernateDAO:(有一个属性private int modNum,而这个服务类其实也不用了)
public Friend findFriendByID(String id, String ownId) {
  StringBuffer sb = new StringBuffer();
  sb.append("from Friend");
  sb.append(BBSCSUtil.getTableID(ownId, this.getModNum()));
/**
 public static int getTableID(String userID, int num) {
  int absNum = Math.abs(userID.hashCode());
  return (int) (absNum % num); //0~~~9
 }
 public static int getTableID(long bid, int num) {
  return (int) (bid % num);
 }

*/
  sb.append(" where id = ? and userID = ?");
  Object[] o = { id, ownId };
  List l = this.getHibernateTemplate().find(sb.toString(), o);
  if (l == null || l.isEmpty()) {
   return null;
  } else {
   return (Friend) l.get(0);
  }

 }
其它方法省略之.我们来看看Friend0.java:
public class Friend0 extends Friend implements Serializable {
  private static final long serialVersionUID = 8915456842365368615L;
 public Friend0() {
  super();
 }
}
<hibernate-mapping package="com.laoer.bbscs.bean">
  <class name="Friend0" table="bbscs_friend_0">//现在没这个表了!5555
    <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="UserName" length="20" name="userName" not-null="true" type="string"/>
    <property column="FriendID" length="40" name="friendID" not-null="true" type="string"/>
    <property column="FriendName" length="20" name="friendName" not-null="true" type="string"/>
    <property column="FriendComment" length="2000" name="friendComment" type="string"/>
    <property column="IsBlack" length="1" name="isBlack" type="int"/>
  </class>
</hibernate-mapping>

接下来是LoginErrorService:
public LoginError saveLoginError(LoginError loginError) throws BbscsException;
public LoginError findLoginErrorByID(String id);
public LoginError findLoginErrorByUserID(String userID);
public List findLoginErrorsOutTime(long atime);//取得超过指定时间的LoginError列表
public void removeLoginErrorsOutTime(long atime) throws BbscsException;//删除超过指定时间的LoginError对象
public LoginError createLoginError(String userID) throws BbscsException;//创建或取得一个已有的LoginError对象
public boolean isCanNotLogin(String userID);//15分钟之内登录错误次数超过5次,不能登录
对于bean:
  private String id;
  private String userID;
  private int errorTimes;//登录时间
  private long loginTime;//错误次数
其hbm.xml文件中:
<hibernate-mapping package="com.laoer.bbscs.bean">
  <class name="LoginError" table="bbscs_loginerror">
    <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="ErrorTimes" length="11" name="errorTimes" not-null="true" type="int"/>
    <property column="LoginTime" length="20" name="loginTime" type="long"/>//long型的时间
  </class>
</hibernate-mapping>
对于服务的实现层,主要还是由DAO去处理的,除了以下两个方法:
//创建或取得一个已有的LoginError对象
 public LoginError createLoginError(String userID) throws BbscsException {
    LoginError loginError = this.getLoginErrorDAO().findLoginErrorByUserID(userID);
    if (loginError == null) {
      loginError = new LoginError();
      loginError.setErrorTimes(1);
      loginError.setLoginTime(System.currentTimeMillis());
      loginError.setUserID(userID);
    }
    else {
      loginError.setErrorTimes(loginError.getErrorTimes() + 1);//加1
      loginError.setLoginTime(System.currentTimeMillis());
    }
    try {
      return this.getLoginErrorDAO().saveLoginError(loginError);//保存
    }
    catch (Exception ex) {
      logger.error(ex);
      throw new BbscsException(ex);
    }
  }

  /**
   * 15分钟之内登录错误次数超过5次,不能登录,也就是15分钟后进行再进行登录不会执行本方法为true值,不过,再次登录错误的话,重新计算机loginError的LoginTime和ErrorTimes值!
      */
  public boolean isCanNotLogin(String userID) {
    LoginError loginError = this.getLoginErrorDAO().findLoginErrorByUserID(userID);
    if (loginError != null) {
      if ( (System.currentTimeMillis() - loginError.getLoginTime()) <= 15 * 60000) {
        if (loginError.getErrorTimes() >= 5) {
          return true;
        }
      }
    }
    return false;
  }
而DAO提供如下方法:(应该是根据serive imp中的需要而定吧!个人意见)
public LoginError saveLoginError(LoginError loginError);
public LoginError findLoginErrorByID(String id);
public LoginError findLoginErrorByUserID(String userID);
public List findLoginErrorsOutTime(long atime);
public void removeLoginErrorsOutTime(long atime);
public void removeLoginError(LoginError loginError);
DAO实现省略(下同)

下个是NoteService:(先看bean和hbm.xml)
        private String id;
 private String fromID;
 private String fromUserName;
 private String fromNickName;
 private String toID;
 private String toUserName;
 private String toNickName;
 private int noteType;//留言类型
 private String noteContext;
 private Date createTime;//`CreateTime` datetime NOT NULL default 񟍰-00-00 00:00:00', 创建时间
 private int isNew;//`IsNew` tinyint(1) default Ƈ', 已读标志
 private int needRe; //需要回执
 private String noteTitle;
        private int isRe;//回复标志
 private int sysMsg; //系统留言标志
注意它用实现了Serializable接品,在bean包中也有许多Note0...Note9及Note0.hbm.xml...Note9.hbm.xml --->  <class name="Note9" table="bbscs_note_9">
在service层也有NoteFactory,在serivce imp层NoteFactoryImp和NotesFactoryImp两种不同的实例化Note对象的同步方法,在DAO实现层也有NotesHibernateDAO用于对相应table(如:bbscs_note_9)的操作,这些与Friend类似,而Subscibe类也差不多如此,因此省略之..
OK,我们继续看Note.hbm.xml:
<hibernate-mapping package="com.laoer.bbscs.bean">
  <class name="Note" table="bbscs_note">
    <id name="id" column="ID" type="string" unsaved-value="null">
      <generator class="uuid"/>
    </id>
    <property column="FromID" length="40" name="fromID" not-null="true" type="string"/>
    <property column="FromUserName" length="20" name="fromUserName" not-null="true" type="string"/>
    <property column="FromNickName" length="60" name="fromNickName" not-null="true" type="string"/>
    <property column="ToID" length="40" name="toID" not-null="true" type="string"/>
    <property column="ToUserName" length="20" name="toUserName" not-null="true" type="string"/>
    <property column="ToNickName" length="60" name="toNickName" not-null="true" type="string"/>
    <property column="NoteType" length="1" name="noteType" not-null="true" type="int"/>
    <property column="NoteTitle" length="150" name="noteTitle" not-null="true" type="string"/>
    <property column="NoteContext" name="noteContext" not-null="true" type="text"/>//直接text类型,OK!
    <property column="CreateTime" name="createTime" not-null="true" type="timestamp"/>//timstamp类型
    <property column="IsNew" length="1" name="isNew" type="int"/>
    <property column="NeedRe" length="1" name="needRe" type="int"/>
    <property column="IsRe" length="1" name="isRe" type="int"/>
    <property column="SysMsg" length="1" name="sysMsg" type="int"/>
  </class>
</hibernate-mapping>
我们继续看NoteService中的公开方法:
public Note saveNote(Note note) throws BbscsException;
public Note[] createNote(Note inbodNote, Note outboxNote) throws BbscsException;//创建纸条
public Note findNoteByIDFromID(String id, String fromID);
public Note findNoteByIDToID(String id, String toID);
public long getNoteAllNumOutBox(String fromID);//取得发件箱纸条数量
public PageList findNotesOutBox(final String fromId, Pages pages);
public long getNoteAllNumInBox(String toID);//取得收件箱纸条数量
public PageList findNotesInBox(final String toID, Pages pages);
public long getNoteNumInBoxByIsNew(String toID, int isNew);
public List findNotesInIDsOutBox(final String fromId, final List values);//取得发件箱中指定ID的Note列表
public List findNotesInIDsOutBox(final String fromId, final List values);
public List findNotesInIDsInBox(final String toId, final List values);
public void removeNote(Note note) throws BbscsException;
public void removeAllOutBox(String fromID) throws BbscsException;
public void removeAllInBox(String toID) throws BbscsException;
public void removeByIDFromID(String id, String fromID) throws BbscsException;//根据ID和发送者ID删除Note对象
public void removeByIDToID(String id, String toID) throws BbscsException;
public void removeInIDsFromID(List values, String fromID) throws BbscsException;
public void removeInIDsToID(List values, String toID) throws BbscsException;
我们看实现层,createNote创建纸条,返回一个Note数组:
public Note[] createNote(Note inbodNote, Note outboxNote) throws BbscsException {
    try {
      inbodNote = this.getNoteDAO().saveNote(inbodNote);
      outboxNote = this.getNoteDAO().saveNote(outboxNote);
      Note[] note = {inbodNote, outboxNote};
      return note;
    }
    catch (Exception ex) {
      logger.error(ex);
      throw new BbscsException(ex);
    }
  }
由于大部分方法直接给DAO层去完成,我们直接看DAO实现层:(NoteHibernateDAO)
首先是查询HQL定义
private static final String LOAD_BY_ID_FROMID = "from Note where id = ? and fromID = ?";
 private static final String LOAD_BY_ID_TOID = "from Note where id = ? and toID = ?";
 private static final String GET_ALL_NUM_OUTBOX =
     "select count(*) from Note where fromID = ? and noteType = ?";
  private static final String LOADS_OUTBOX =
      "from Note where fromID = ? and noteType = ? order by createTime desc";
  private static final String GET_ALL_NUM_INBOX =
      "select count(*) from Note where toID = ? and noteType = ?";
  private static final String LOADS_INBOX =
      "from Note where toID = ? and noteType = ? order by createTime desc";
  private static final String GET_NUM_INBOX_BY_ISNEW =
      "select count(*) from Note where toID = ? and noteType = ? and isNew = ?";
  private static final String LODAS_INIDS_OUTBOX =
      "from Note where id in (:ids) and fromID = :fromId";
  private static final String LODAS_INIDS_INBOX = "from Note where id in (:ids) and toID = :toId";
  private static final String REMOVE_ALL_OUTBOX =
      "delete from Note where fromID = ? and noteType = ?";
  private static final String REMOVE_ALL_INBOX = "delete from Note where toID = ? and noteType = ?";
  private static final String REMOVE_BY_ID_FROMID = "delete from Note where id = ? and fromID = ?";
  private static final String REMOVE_BY_ID_TOID = "delete from Note where id = ? and toID = ?";
  private static final String REMOVE_INIDS_FROMID =
      "delete from Note where id in (:ids) and fromID = :fromId";
  private static final String REMOVE_INIDS_TOID =
      "delete from Note where id in (:ids) and toID = :toId";
下面是取得发件箱纸条数量的方法:
 public long getNoteAllNumOutBox(String fromID) {
    Object[] o = {fromID, new Integer(0)}; //noteType=0
    List l = this.getHibernateTemplate().find(GET_ALL_NUM_OUTBOX, o);
    if (l == null || l.isEmpty()) {
      return 0;
    }
    else {
      return ( (Long) l.get(0)).longValue();
    }
  }
取得发件箱Note分页列表:
public List findNotesOutBox(final String fromId, final int firstResult, final int maxResults) {
    return getHibernateTemplate().executeFind(new HibernateCallback() {
      public Object doInHibernate(Session s) throws HibernateException, SQLException {
        Query query = s.createQuery(LOADS_OUTBOX);
        query.setString(0, fromId);
        query.setInteger(1, 0);
        query.setFirstResult(firstResult);
        query.setMaxResults(maxResults);

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

我们看PermissionService,先进入BEAN(权限信息),它有五个属性:
 private Long id;// default Ɔ'
 private String permissionName; //权限名称 default ''
 private String resource;//权限资源(URI)default NULL
 private String action;//动作,Action default NULL
 private int typeID;//权限类型,default `0`
这里我发现在这个系统的数据库表设计中它尽量的提供了default值!!!(不过也可能是导出时工具生成的)
<hibernate-mapping package="com.laoer.bbscs.bean">
  <class name="Permission" table="bbscs_permission">
    <id name="id" column="ID" type="long" unsaved-value="undefined">
      <generator class="assigned"/> //自动增加键!
    </id>
    <property column="PermissionName" length="255" name="permissionName" not-null="true" type="string"/>
    <property column="PermissionResource" length="255" name="resource" type="string"/>
    <property column="Action" length="255" name="action" type="string"/>
    <property column="TypeID" length="2" name="typeID" not-null="true" type="int"/>
  </class>
</hibernate-mapping>
这次我们来看看数据库表Permission,发现它是一个基本固定不变的定义表,注意: 
权限类型有4种,普通权限(0) 特殊权限(1) 版区普通权限(2) 版区特殊权限(3),这里都为0
122  XXXX(PermissionName)     /amdinUOTimeSet  *                       0
209  XXXX(由于我本地乱码)     /userFace        index,uppage,up,delme   0
OK!看方法:
  public Permission savePermission(Permission permission) throws BbscsException;
  public Permission updatePermission(Permission permission) throws BbscsException;
  public Permission findPermissionByID(long id);
  public List findPermissionsAll();
  public List findPermissionsByTypeID(int typeID);
  public List findPermissionnIDs(List ids);
PermissionServiceImp实现之,首先引入了userPermissionCache,以便在保存和更新后清理缓存数据!
我们直接进入PermissionHibernateDAO:
private static final String LOADS_ALL = "from Permission order by id";
  private static final String LOADS_BY_TYPEID = "from Permission where typeID = ? order by id";
  private static final String LOADS_IN_IDS = "from Permission where id in (:ids)";
我们只看一个方法:
public List findPermissionnIDs(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 list = query.list();
        return list;
      }
    });
  }

OK,Next  RoleService,先看其BEAN先,它有四个属性:
 private Integer id;
 private String roleName;
 private int typeID;
 private List permissions = new ArrayList();
其Role-mysql.hbm.xml:
<hibernate-mapping package="com.laoer.bbscs.bean">
  <class name="Role" table="bbscs_role">
    <id name="id" column="ID" type="int" unsaved-value="null">
      <generator class="identity"/> //自动增长!
    </id>
    <property column="RoleName" length="255" name="roleName" not-null="true" type="string"/>
    <property column="TypeID" length="2" name="typeID" not-null="true" type="int"/>
    <property column="Permissions" name="permissions" type="com.laoer.bbscs.ext.hibernate.SplitList"/>//用了自定义类型!
  </class>
</hibernate-mapping>
我们看下数据库bbscs_role中的记录,可见它也基本上是一本固定的配置表而已:
1  ???(乱码)     1      MEMO(401,402,403,404,405,406,407,408,409,410,411,412,413,.....)
4  ???(乱码)     0      MEMO(126,127,128,129,130)
我们看方法:
public Role saveRole(Role role) throws BbscsException;
public Role findRoleByID(int id);
public List findRolesAll();
public List findRolesByTypeID(int typeID);//根据tpyeID取得Role列表
public List findRolesInIDs(List ids);//根据指定IDs取得Role对象列表
public void removeRole(Role role) throws BbscsException;
进入其实现层,首先也用了userPermissionCache,大部分方法仍有DAO处理:
  public Role saveRole(Role role) throws BbscsException {
    try {
      role = this.getRoleDAO().saveRole(role);
      this.clearPermissionCache();
      return role;
    }
    catch (Exception ex) {
      logger.error(ex);
      throw new BbscsException(ex);
    }
  }
  private void clearPermissionCache() {   //私有方法
    if (Constant.USE_PERMISSION_CACHE) {//public static final boolean USE_PERMISSION_CACHE = true;
      this.getUserPermissionCache().removeAll();
    }
  }
对于DAO及其实现层我们不分析下去.

好,我们到了SessionService,这个接口中并没有import任何service,bean(当然也没有.hbm.xml),应该是完全用于session的操作的吧.我们直接看方法:
 public void saveSession(String id, String key, Object value);
 public void saveSession(String id, Map session);
 public Map getSession(String id);
 public Object getSession(String id, String key);
 public void removeSession(String id);
我们在com.laoer.bbscs.serivce.config包中也发现了还有SessionConfig没有介绍之,其实它也是个用于得到目录的配置服务类,先注入safePath后:
 public String getSessionPath(String id) {
  StringBuffer sb = new StringBuffer();
  sb.append(this.getSafePath());
  if (!this.getSafePath().endsWith("/")) {
   sb.append("/");
  }
  sb.append("session/");
  sb.append(BBSCSUtil.getStringHashCode(id) % 100);
  sb.append("/");
  String tmp = "";
  if (id.length() > 5) {
   tmp = id.substring(0, 5);
  } else {
   tmp = id;
  }
  sb.append(BBSCSUtil.getStringHashCode(tmp) % 100);
  sb.append("/");
  sb.append(id);
  sb.append("/");
  File ft = new File(sb.toString());
  if (!ft.exists()) {
   ft.mkdirs();
  }
  return sb.toString(); //safe/session/34/23/2342342/
 }
其中用到了com.laoer.bbscs.comm.*:
public static int getStringHashCode(String txt) {
  int t = 0;
  if (txt != null) {
   char[] chars = txt.toCharArray();
   for (int i = 0; i < chars.length; i++) {
    t = t + (int) chars[i];
   }
  }
  return t;
 }
至于SessionService和SessionConfig的具体用处可能是在web层使用吧!有待考虑之.

我们来到SubscibeService(帖子订阅服务):(我们不分析关于拆表的任何代码,有关请看Friend相关类) 
        private String id;
 private String userID; //用户ID
 private String userName;
        private String nickName;
 private String postID;//帽子ID
 private String postTitle;
 private long boardID;//版区ID
 private int emailinform;//Email发送标志
 private int msginform;//留言发送标志
 private String userEmail;
 private Date createTime;
 private String userLocale;//用户Locale信息
<hibernate-mapping package="com.laoer.bbscs.bean">
  <class name="Subscibe" table="bbscs_subscibe">
    <id name="id" column="ID" type="string" unsaved-value="null">
      <generator class="uuid"/>  //UUID类型!!
    </id>
    <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="NickName" length="60" name="nickName" not-null="true" type="string"/>
    <property column="PostID" length="60" name="postID" not-null="true" type="string"/>
    <property column="PostTitle" length="150" name="postTitle" type="string"/>
    <property column="BoardID" length="13" name="boardID" not-null="true" type="long"/>
    <property column="Emailinform" length="1" name="emailinform" type="int"/>
    <property column="Msginform" length="1" name="msginform" type="int"/>
    <property column="UserEmail" length="255" name="userEmail" type="string"/>
    <property column="CreateTime" name="createTime" not-null="true" type="timestamp"/>//时间截
    <property column="UserLocale" length="20" name="userLocale" type="string"/>
  </class>
</hibernate-mapping>
好了,我们看SubscibeService中的服务公开方法:
public Subscibe saveSubscibe(Subscibe subscibe) throws BbscsException;
public

评论
发表评论

您还没有登录,请登录后发表评论

sgwood
搜索本博客
存档
最新评论