BBS一

小型BBS
该系统是 Struts Ibatis 结合运用的一个实例,通过做一个小型的 bbs 论坛,了解如何结合使用 Struts Ibatis 。业务逻辑层与数据层的实现如下:
一.系统的功能 :
用户:注册,登录
文章:发贴,回复,编辑,浏览
二.系统的架构
采用三层架构: 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 自带的 junit jar 包。 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_name password 均加索引,但
这样每次更新和删除时就会新创建一个索引,影响效率。所以一般情况下如果不是出现在 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;
}
}
 
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值