BBS一

本文介绍了一个基于Struts与Ibatis的小型BBS论坛系统开发过程,包括系统架构、主要功能模块的设计与实现,以及单元测试方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

小型BBS
该系统是StrutsIbatis结合运用的一个实例,通过做一个小型的bbs论坛,了解如何结合使用StrutsIbatis。业务逻辑层与数据层的实现如下:
一.系统的功能:
用户:注册,登录
文章:发贴,回复,编辑,浏览
二.系统的架构
采用三层架构:WEB层,业务逻辑层,DAO层,在讲解三层体系架构时需要先明白什么是横向划分?什么是纵向划分?
横向划分:也就是按所谓的MVC分层划分。该系统就采用这种结构。
纵向划分:也就是所谓的模块划分。
如下图所示:
 
三.系统的实现
首先做业务逻辑层(采用的是Java工程)
1.      设计domain (域对象)
l        User.java id 用户的编号;logonName(登录的用户名)在系统中是不允许重复的;nickName昵称,它是一个对业务逻辑无影响的字段;password用户密码。具体代码略。
l        Article.java
该文件是封装文章的一个实体bean,代码如下:
package com.sample.domain;
import java.util.Date;
public class Article {
private int id; //文章的id
private String title; //文章的标题
private String content;//文章内容
private Date issue; //文章发布的日期
private int parentId; //发帖时用到的属性指向主贴,即针对那个文章进行评论的
private User author;//作者
public User getAuthor() {
        return author;
}
       public void setAuthor(User author) {
        this.author = author;
}
public String getContent() {
        return content;
}
public void setContent(String content) {
        this.content = content;
}
public int getId() {
        return id;
}
public void setId(int id) {
        this.id = id;
}
public Date getIssue() {
        return issue;
}
public void setIssue(Date issue) {
        this.issue = issue;
}
public int getParentId() {
        return parentId;
}
public void setParentId(int parentId) {
        this.parentId = parentId;
}
public String getTitle() {
        return title;
}
public void setTitle(String title) {
        this.title = title;
}
}
2.      业务层接口
l        Interface UserService 代码如下:
public interface UserService {
    // 注册新用户
    public void regist(User user);
    // 用户登录
    public User logon(String username, String password);
    // 浏览用户信息
    public User getUserById(int id);
}
注意:方法名做到见名知意,方便理解。
l        Interface ArticleService 代码如下:
public interface ArticleService {
   /**
     * 发贴
     * @param article 要发表的文章
     */
public void addArticle(Article article);
    /**
     * 根据id号取得相应文章
     * @param artId 文章的id
     * @return 代表文章的对象
     */
public Article getArticleById(int artId);
    /**
     * 删除文章
     * @param artId 要删除的文章的id
     * @param opreator 操作者(用于判断是否有删除权限)
     */
public void delArticle(int artId, User operator);
    /**
     * 编辑文章
     * @param art 要编辑的文章
     * @param operator 操作者(用于判断是否有编辑权限)
     */
public void updateArticle(Article art, User operator);
    /**
     * 获得跟帖的列表
     * @param parentid 原帖的文章id
     * @return 原帖的跟贴列表
     */
public List<Article> getChildrenArticles(int parentId);
//获得帖子列表
public List<Article> getArticles();
}
3.      数据访问层接口(DAO
l        Inerface UserDao 代码如下:
public interface UserDao {
    /**
     * 增加用户
     * @param user 要增加的新用户
     */
public void addUser(User user);
    /**
     * 根据用户名查找用户
     * @param logonName 要查找的用户名
     * @return 如果找到了,返回该用户;如果没找到,返回null
     */
 public User getUserByUsername(String logonName);
    /**
     * 根据用户名和密码查找用户
     * @param logonName 要查找的用户名
     * @param password 要查找的用户的密码
     * @return 如果找到了,返回该用户;如果没找到,返回null
     */
  public User getUserByUsernameAndPassword(String logonName, String password);
  /**
  * 根据id查找用户
  * @param id 要查找的用户id
  * @return 如果找到了,返回该用户;如果没找到,返回null
  */
 public User getUserById(int userId);
}
l        Interface ArticleDao 代码如下:
public interface ArticleDao {
  /**
  * 增加文章
  * @param article 要发表的文章
  */
public void addArticle(Article article);
/**
  * 根据id号取得相应文章
  * @param artId 文章的artId
 * @return 代表文章的对象
  */
public Article getArticleById(int artId);
/**
  * 删除文章
  * @param artId 要删除的文章的id
  */
public void delArticle(int artId);
/**
 * 编辑文章
 * @param art 要编辑的文章
  */
public void updateArticle(Article art);
/**
 * 获得跟帖的列表
 * @param parentId 原帖的文章id
 * @return 原帖的跟贴列表
 */
public List<Article> getChildrenArticles(int parentId);
}
4.      业务逻辑层的实现类
l        UserServiceImpl 程序代码如下:
   public class UserServiceImpl implements UserService {
            private UserDao userDao;
            public UserServiceImpl(UserDao userDao) {
                      this.userDao = userDao;
             }
        // 浏览用户信息
            public User getUserById(int userId) {
       // 没有需要特殊处理的逻辑,直接委托DAO去做即可
               return this.userDao.getUserById(userId);
            }
       // 用户登录
           public User logon(String logonName, String password) {
               User user = this.userDao.getUserByLogonNameAndPassword(logonName,
                            password);
       /* 判断返回值的操作,是一种面向过程的方式。在面向对象程序设计中,尽量采用抛异常的方式。这样做的好处是使上层代码可以将正常流程与异常流程分开。
       */
                      if (user == null)
                     throw new ServiceException("用户名或密码错误!");
               return user;
           }
       //注册新用户
           public void regist(User user) {
       // 首先检查该用户名是否已被占用
               User u = this.userDao.getUserByLogonName(user.getLogonName());
               if (u != null)
                     throw new ServiceException("登录名重复!");
               this.userDao.addUser(user);
           }
}
l        UserServiceTest
该类是一个单元测试类,用来测试用户的注册,登录等。使用时需首先导入junit发布包中的jar包,或导入eclipse自带的junitjar包。junit写测试用例最重要的好处就是节约时间,及早发现问题,以降低修复问题的成本。程序代码如下:
public class UserServiceTest extends TestCase {
                UserService userService;
public UserServiceTest(String arg0) {
super(arg0);
                userService = new UserServiceImpl(new UserDaoIbatisImpl());
}
public void testRegist() {
                User user = this.regist("1");
                assertTrue(user.getId() > 0);
                try {
         // 测试重复注册
                userService.regist(user);
        // 如果没有抛异常,进而走到下一行代码,则说明测试失败。
fail("注册重复的登录名!");
                } catch (ServiceException e) {
                // 如果抛出异常,则表明测试通过(能够检测得到用户名重复)
                     }
                }
    //测试是否是注册用户
public void testLogon() {
this.regist("2");
                User user = this.userService.logon("logonName2", "password2");
                assertTrue(user.getId() > 0);
                try {
                     this.userService.logon("logonName", "password");
                     fail("登录错误!");
                     } catch (ServiceException e) {
                            // right;
                     }
                }
public void testGetUserById() {
                User user = this.regist("3");
                User u = this.userService.getUserById(user.getId());
                assertEquals(user.getLogonName(), u.getLogonName());
                }
  private User regist(String post) {
              User user = new User();
              user.setLogonName("logonName" + post);
              user.setNickName("nickName" + post);
              user.setPassword("password" + post);
              userService.regist(user);
              return user;
             }
}
/*该类用来创建一个模拟的DAO。因为实际开发的时候,不同层的开发人员不可能互相等待对方的代码都开发完之后,再去测试自己的代码,所以需要构造一个模拟的DAO*/
class MockUserDao implements UserDao {
private Map<Integer, User> users = new HashMap<Integer, User>();
private int id = 1;
public void addUser(User user) {
user.setId(id++);
System.out.println(users);
users.put(user.getId(), user);
System.out.println(users);
                }
public User getUserById(int userId) {
return users.get(userId);
                }
   public User getUserByLogonName(String logonName) {
for (Iterator<User> iter = users.values().iterator(); iter.hasNext();) {
                     User user = iter.next();
                     if (user.getLogonName().equals(logonName))
                            return user;
                     }
                     return null;
                }
public User getUserByLogonNameAndPassword(String logonName, String password) {
for (Iterator<User> iter = users.values().iterator(); iter.hasNext();) {
                     User user = iter.next();
                      if (user.getLogonName().equals(logonName)&& user.getPassword().equals(password))
                      return user;
                     }
                     return null;
                }
}
l        ArticleServiceImpl
   代码略类似UserServiceImpl
5.      DAO的实现类
l        设计数据表
根据需求数据库采用MySQL,数据表的结构如下所示:
5.1 用户表(user
字段
字段类型
描述
id
int
用户编号
logon_name
varchar
用户名
nick_name
varchar
用户昵称
password
varchar
用户密码
 
   5.2 帖子表(article
 
字段
字段类型
描述
id
int
帖子编号
title
varchar
帖子标题
content
text
帖子内容
parent_id
int
主贴的编号
issue
date
帖子发表日期
author_id
int
作者的编号
 
设计表时注意以下细节:
(1)    列的命名一般是两个单词用下划线连接。
(2)    为了提高查询效率用户表中logon_namepassword均加索引,但
这样每次更新和删除时就会新创建一个索引,影响效率。所以一般情况下如果不是出现在where语句后的字段就不要加索引。
3对业务逻辑无影响的字段添加外键可以实现级联操作,以便让数据库自己去维护数据的完整性。如果不建外键,那么也可以通过程序在业务逻辑层中维护, 以提高性能。
l        UserDaoIbatisImpl
该类是利用ibatis组件构建的userdao的一个实现类来完成与后台数据的交互。其中SqlMapClient这个类是利用自己定义的工具类IbatisUtils去获取而不是通过外层类来传递,以避免耦合。IbatisUtils代码如下:
final class IbatisUtils {
    private static SqlMapClient sqlMap = null;
    // static代码块在初始化时自动执行
    static {
        String res = "cn/pf/tbbs/dao/impl/SqlMapConfig.xml";
        Reader reader = null;
        try {
        /*下面这句话会产生异常。但是不要catch,以防初始化的时候,上层不知道下层发生了什么事情。
        */
          reader = Resources.getResourceAsReader(res);
          sqlMap = SqlMapClientBuilder.buildSqlMapClient(reader);
        } catch (IOException e) {
            throw new RuntimeException(e.getMessage(), e);
        } finally {
            if (reader != null) {
               try {
                   reader.close();
                } catch (IOException e) {
                 throw new RuntimeException(e.getMessage(), e);
                }
            }
        }
    }
    static SqlMapClient getSqlMapClient() {
        return sqlMap;
    }
    private IbatisUtils() {
    }
}
UserDaoIbatisImpl的代码如下:
import com.sample.exception.DataAccessException;
public class UserDaoIbatisImpl implements UserDao {
    private SqlMapClient sqlMap = IbatisUtils.getSqlMapClient();
    public void addUser(User user) {
     try {
           this.sqlMap.insert("addUser", user);
           } catch (SQLException e) {
/*不要直接抛出sql异常,而是抛出一个自定义的异常使得分层隔离。因为sql异常是针对数据库的,如果抛出,上层就必须捕获这种sql异常,从而造成上层对底层具体实现的耦合。
*/
           throw new DataAccessException(e.getMessage(), e);
           }
    }
    public User getUserById(int userId) {
           User user = null;
           try {
                  user = (User) this.sqlMap.queryForObject("getUserById", userId);
           } catch (SQLException e) {
                  throw new DataAccessException(e.getMessage(), e);
           }
           return user;
    }
    public User getUserByLogonName(String logonName) {
           User user = null;
           try {
                  user = (User) this.sqlMap.queryForObject("getUserByLogonName",
                                logonName);
            } catch (SQLException e) {
                  throw new DataAccessException(e.getMessage(), e);
           }
           return user;
    }
    public User getUserByLogonNameAndPassword(String logonName, String password) {
                 User user = null;
            try {
                     User u = new User();
                     u.setLogonName(logonName);
                     u.setPassword(password);
                     user = (User) this.sqlMap.queryForObject(
                                "getUserByLogonNameAndPassword", u);
               } catch (SQLException e) {
                   throw new DataAccessException(e.getMessage(), e);
               }
            return user;
       }
}
l        ArticleDaoIbatisImpl
该类是数据层ArticleDao的实现类,通过调用SqlMapClient的各种方法来实现对文章的添加,删除,查询和修改。代码如下:
public class ArticleDaoIbatisImpl implements ArticleDao {
private SqlMapClient sqlMap = IbatisUtils.getSqlMapClient();
public void addArticle(Article article) {
        try {
               this.sqlMap.insert("addArticle", article);
           } catch (SQLException e) {
               throw new DataAccessException(e.getMessage(), e);
           }
    }
      public void delArticle(int artId) {
        try {
               this.sqlMap.delete("delArticle", artId);
        } catch (SQLException e) {
               throw new DataAccessException(e.getMessage(), e);
        }
 }
      public Article getArticleById(int artId) {
        Article art = null;
        try {
               art = (Article) this.sqlMap.queryForObject("getArticleById", artId);
        } catch (SQLException e) {
               throw new DataAccessException(e.getMessage(), e);
        }
        return art;
}
@SuppressWarnings("unchecked")
public List<Article> getChildrenArticles(int parentId) {
        List<Article> arts = new ArrayList<Article>();
        try {
               arts = (List<Article>) this.sqlMap.queryForList(
                             "getChildrenArticles", parentId);
        } catch (SQLException e) {
               throw new DataAccessException(e.getMessage(), e);
        }
        return arts;
}
public void updateArticle(Article art) {
        try {
               this.sqlMap.update("updateArticle", art);
        } catch (SQLException e) {
               throw new DataAccessException(e.getMessage(), e);
        }
}
@SuppressWarnings("unchecked")
public List<Article> getArticles() {
        List<Article> arts = new ArrayList<Article>();
        try {
               arts = (List<Article>) this.sqlMap
                             .queryForList("getArticles", null);
        } catch (SQLException e) {
               throw new DataAccessException(e.getMessage(), e);
        }
        return arts;
}
}
 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值