学web后台开发已经有一段时间了,是时候做一点小总结。前段时间学习了一个小项目——论坛系统,是基于SPRing+Struts1+Hibernate框架开发的,虽然Struts1框架已逐渐比较少人用了,但作为初学者我觉得还是有必要了解一下。在实现完这个小系统后,我想把它改成基于Struts2实现的,毕竟Struts2比Struts1优秀得多。但了解到目前很多企业公司都采用了Spring+SpringMVC+Mybatis的组合框架,所以后来我把这个系统改成了基于Spring+SpringMVC+Mybatis实现的,SSM比SSH好就不用我多说了。下面先介绍基于SSH实现的,想看基于SSM实现的可直接看下一篇文章。
整个工程目录以及数据库表如下图:
代码下载
运行效果:
界面虽然丑了点,但我们这里只专注于后台开发,界面就交给前端吧!下面UI的代码就不贴出来了,自己下载吧。
本系统包括5个模块:用户模块、版面类型模块、版面模块、帖子模块和回帖模块。
用户模块:包括用户的注册、登录、注销、以及查看用户资料等功能。发表帖子、发表回帖、设置版主等都需要用到用户信息。注册时将记录用户的注册ip、注册时间等,登录时将记录用户的登录IP、登录时间等。版面类别模块:本系统为二级结构,即先创建版面类别,然后才创建版面。本模块包括创建版面类别,列出所有的类别、首页等。首页将列出所有的版面类别、各类别下对应的版面、帖子总数、回帖总数、最后发表的帖子或者回复、版主等。版面模块:包括创建版面、设置版主,列出所有本版的帖子等。帖子列表包括帖子标题、作者、发表时间、回帖数、人气、最后回复人、最后回复时间等。
帖子模块 :包括发表帖子、浏览帖子等。发表帖子时记录发表帖子的IP等;浏览帖子时分页列出所有的回帖,并更新帖子的人气(浏览次数)等。
回帖模块:包括发表回帖等。
package bean;@MappedSuperclass //实体类父类public class BaseBean { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Integer id; @Version //版本列,hibernate自动维护该列 private Integer version; private boolean deleted; @Temporal(value = TemporalType.TIMESTAMP) private Date dateCreated;}2.2、Person类代码
Person类为用户信息,属性boardsAdministrated代表该Person管理的版面,也就是版主。一个版面可以有多个版主,一个版主也可以管理多个版面,所以Person与Board是多对多的关系。package bean;@Entity@Tablepublic class Person extends BaseBean { private String account; private String passWord; private String sex; private String name; private String birthday; private String email; private String ipCreated; @Temporal(value = TemporalType.TIMESTAMP) private Date dateLastActived; private String ipLastActived; @ManyToMany(mappedBy = "administrators") private Set<Board> boardsAdministrated = new HashSet<Board>();}2.3、Category类代码
Category类为版面类型package bean;@Entity@Table@org.hibernate.annotations.Entitypublic class Category extends BaseBean { private String name; @OneToMany(mappedBy = "category") private List<Board> boards = new ArrayList<Board>();}2.4、Board类代码
Board类代表版面,Board有两个一对多的属性lastThread与lastReply,以及一个多对多的属性administrators。lastThread和lastReply分别保存最后发表的帖子及回帖。package bean;@Entity@Tablepublic class Board extends BaseBean { @ManyToOne @JoinColumn(name = "category_id") private Category category; private String name; private String description; private int threadCount; private int replyCount; @ManyToOne @JoinColumn(name = "last_reply_id") private Reply lastReply; @ManyToOne @JoinColumn(name = "last_thread_id") private Thread lastThread; @ManyToMany(cascade = CascadeType.ALL) @JoinTable(name = "board_administrator", joinColumns = { @JoinColumn(name = "board_id") }, inverseJoinColumns = { @JoinColumn(name = "person_id") }) private Set<Person> administrators = new HashSet<Person>();}2.5、Thread类代码
Thread类代表帖子,其中内容比较大,因此配置为延迟加载。package bean;@Entity@Table@org.hibernate.annotations.Entitypublic class Thread extends BaseBean { @ManyToOne @JoinColumn(name = "board_id") private Board board; private String title; @Basic(fetch = FetchType.LAZY) @Column(columnDefinition="longtext") private String content; @ManyToOne @JoinColumn(name = "author_id") private Person author; private String ipCreated; private int hit; @ManyToOne @JoinColumn(name = "author_last_replied_id") private Person authorLastReplied; @Temporal(TemporalType.TIMESTAMP) private Date dateLastReplied; private boolean readonly; private boolean topped; private int replyCount;}2.6、Reply类代码
Reply代表回帖package bean;@Entity@Table@org.hibernate.annotations.Entitypublic class Reply extends BaseBean { @ManyToOne @JoinColumn(name = "thread_id") private Thread thread; private String title; @Basic(fetch = FetchType.LAZY) private String content; @ManyToOne @JoinColumn(name = "author_id") private Person author; private int floor; private String ipCreated;}2.7、数据库与Hibernate配置
在配置前先在MySQL中创建一个名为forum的数据库,Hibernate配置在Spring的配置文件applicationContext.xml中,由Spring管理,采用hbm2ddl策略为update,启动时会自动生成对应的表。<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close"> <!-- 定义数据源 --> <property name="driverClassName" value="com.mysql.jdbc.Driver"></property> <property name="url" value="jdbc:mysql://localhost:3306/forum?characterEncoding=UTF-8"></property> <property name="username" value="root"></property> <property name="password" value="5845201314"></property> </bean> <bean id="sessionFactory" class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" destroy-method="destroy"> <!-- SessionFactory实现类 --> <property name="dataSource" ref="dataSource"></property> <property name="annotatedClasses"> <!-- 配置实体类 --> <list> <value>bean.Board</value> <value>bean.Category</value> <value>bean.Person</value> <value>bean.Reply</value> <value>bean.Thread</value> </list> </property> <property name="hibernateProperties"> <!-- Hibernate属性 --> <props> <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop> <prop key="hibernate.show_sql">true</prop> <prop key="hibernate.hbm2ddl.auto">update</prop> <prop key="hibernate.current_session_context_class">thread</prop> </props> </property> </bean> <bean id="hibernateTransactionmanager" class="org.springframework.orm.hibernate3.HibernateTransactionManager"> <!-- Hibernate事务管理器 --> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <bean id="hibernateTransactionAttributeSource" class="org.springframework.transaction.interceptor.NameMatchTransactionAttributeSource"> <!-- 事务管理规则 --> <property name="properties"> <!-- 具有事务管理的方法名 --> <props> <prop key="*">PROPAGATION_REQUIRED</prop> <!-- 所有方法都加事务 --> </props> </property> </bean> ......</beans>3、DAO层设计
DAO层负责与数据库打交道,它把java Bean分拆成SQL语句并执行。个人觉得在ORM框架的基础上再封装一个DAO会更加方便。3.1、IDAO代码
由于Hibernate能操作所有的实体类,因此只定义一个DAO层即可。遵循接口编程原则,IDAO接口定义了最基本的DAO操作,同时也引入泛型,方便实现不同的实体类。package dao;public interface IDao<T> { public T find(Class<T> clazz, int id); public void create(T baseBean); public void save(T baseBean); public void delete(T baseBean); public List<T> list(String hql); public int getTotalCount(String hql, Object... params); public List<T> list(String hql, int firstResult, int maxSize, Object... params); public Query createQuery(String hql);}3.2、DaoImpl代码
DaoImpl继承了Spring提供的HibernateDaoSupport类,该类主要提供两个方法:public final HibernateTemplate getHibernateTemplate();public final void setSessionFactory(SessionFactory sessionFactory);其中setSessionFactory方法接收来自Spring的依赖注入,接收了配置在Spring的SessionFactory实例,getHibernateTemplate方法用来利用该实例生成Session,再生成HibernateTemplate来完成数据库的访问。package dao;public class DaoImpl<T> extends HibernateDaoSupport implements IDao<T> { @SuppressWarnings("unchecked") public T find(Class<T> clazz, int id) { return (T) getHibernateTemplate().get(clazz, id); } public void create(T baseBean) { getHibernateTemplate().persist(baseBean); } public Query createQuery(String hql) { return getSession().createQuery(hql); } public void delete(T baseBean) { getHibernateTemplate().delete(baseBean); } @SuppressWarnings("unchecked") public List<T> list(String hql) { return getHibernateTemplate().find(hql); } public int getTotalCount(String hql, Object... params) { Query query = createQuery(hql); for (int i = 0; params != null && i < params.length; i++) query.setParameter(i + 1, params[i]); Object obj = createQuery(hql).uniqueResult(); return ((Long) obj).intValue(); } @SuppressWarnings("unchecked") public List<T> list(String hql, int firstResult, int maxResults,Object... params) { Query query = createQuery(hql); for (int i = 0; params != null && i < params.length; i++) query.setParameter(i + 1, params[i]); List<T> list = createQuery(hql).setFirstResult(firstResult).setMaxResults(maxResults).list(); return list; } public void save(T baseBean) { getHibernateTemplate().save(baseBean); }}4、Service层设计
业务代码集中在Service层,Action中调用Service完成业务逻辑,Service调用DAO完成数据存取。4.1、IService接口
IService接口是所有Service的基类,定义了最基本的Service层方法。package Service;public interface IService<T> { public T find(Class<T> clazz, int id); public void create(T baseBean); public void save(T baseBean); public void delete(T baseBean); public List<T> list(String hql); public int getTotalCount(String hql, Object... params); public List<T> list(String hql, int firstResult, int maxSize, Object... params);}4.2、ServiceImpl实现
ServiceImpl实现了IService接口的方法,由于各个实体类的create()方法有不同的业务逻辑,因此create()方法定义为abstract的,有各自的Service单独实现。由于含有抽象方法,类必须是抽象类!IDAO对象将由Spring通过IoC注射进来。package Service;public abstract class ServiceImpl<T extends BaseBean> implements IService<T> { protected IDao<T> dao; public IDao<T> getDao() { return dao; } public void setDao(IDao<T> dao) { this.dao = dao; } public T find(Class<T> clazz, int id) { return dao.find(clazz, id); } public abstract void create(T baseBean); public void delete(T baseBean) { baseBean.setDeleted(true); dao.save(baseBean); } public int getTotalCount(String hql, Object... params) { return dao.getTotalCount(hql, params); } public void save(T baseBean) { dao.save(baseBean); } public List<T> list(String hql) { return dao.list(hql); } public List<T> list(String hql, int firstResult, int maxSize, Object... params) { return dao.list(hql, firstResult, maxSize, params); }}5、Action层设计
为了便于统一风格,统一控制,所有Action都继承自ForumAction基类,所有的ActionForm都继承自ForumForm基类。5.1、ForumForm基类
ForumForm类继承了Struts的ActionForm,封装了action、title属性,action用于区分不同的执行方式,title代表页面的标题。package form;public class ForumForm extends ActionForm { private String action; private String title; public String getAction() { return action; } public void setAction(String action) { this.action = action; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; }}5.2、ForumAction基类
ForumAction类继承了DispatchAction分发器,使用Action作为分发器的参数。各模块的Action需要继承该类,并实现各自的list()方法。package action;public abstract class ForumAction extends DispatchAction { protected Log log = LogFactory.getLog(getClass()); @Override public ActionForward execute(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) throws Exception { ForumForm forumForm = (ForumForm) form; forumForm.setTitle("轻量级 Java EE 论坛程序"); System.out.println("execute action:"+forumForm.getAction()); if (forumForm.getAction() == null|| forumForm.getAction().trim().length() == 0) { return this.list(mapping, form, request, response); } return super.execute(mapping, form, request, response); } public abstract ActionForward list(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response)throws Exception;}6、用户模块
6.1、IPersonService接口
IPersonService接口继承IService接口,还需要定义自己单独的方法。package Service;public interface IPersonService<T extends Person> extends IService<T> { /** 根据帐号查找用户 */ public T findPersonByAccount(String account); /** 根据帐号、密码查找用户 */ public T getPerson(String account, String password);}6.2、PersonServiceImpl实现
PersonServiceImpl实现IPersonService接口,还需要实现自己的方法,创建用户时,密码需要经过加密后才保存到数据库,md5工具类可下载源代码查看。package Service;public class PersonServiceImpl<T extends Person> extends ServiceImpl<T> implements IPersonService<T> { @SuppressWarnings("unchecked") @Override public T findPersonByAccount(String account) { List<T> person = this.getDao().createQuery(" select p from Person p "+ " where lower(p.account) = lower(:account) and deleted = false ") .setParameter("account", account.trim()).list(); if (person.size() > 0) return person.get(0); return null; } @SuppressWarnings("unchecked") @Override public T getPerson(String account, String password) { List<T> list = this.getDao().createQuery(" select p from Person p where p.account = :account "+ " and p.password = :password and p.deleted = false ") .setParameter("account", account).setParameter("password",MD5Util.calc(password)).list(); if (list.size() > 0) return list.get(0); return null; } @Override public void create(T person) { if (findPersonByAccount(person.getAccount()) != null) throw new RuntimeException("帐号 " + person.getAccount() + " 已经存在。"); person.setPassword(MD5Util.calc(person.getPassword())); this.getDao().create(person); }}6.3、PersonForm代码
PersonForm还拥有person和password属性,省略set和get方法package form;public class PersonForm extends ForumForm { private Person person = new Person(); private String password; public ActionErrors validate(ActionMapping mapping,HttpServletRequest request) { return null; } public void reset(ActionMapping mapping, HttpServletRequest request) { }}6.4、PersonAction代码——用户
PersonAction完成用户模块的注册、登录、注销、查看用户资料等。先看注册相关代码。personService对象将由Spring注射进来,注册时会把用户的id以及account保存到Session中,方便下次登录时不需要进入登录界面。package action;public class PersonAction extends ForumAction { private IPersonService<Person> personService; //默认方法 返回到注册页面 public ActionForward list(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { return this.initAdd(mapping, form, request, response); } public ActionForward initAdd(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { PersonForm personForm = (PersonForm) form; personForm.setTitle("用户注册"); return mapping.findForward("add"); } public ActionForward add(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { PersonForm personForm = (PersonForm) form; personForm.setTitle("用户注册"); Person person = personForm.getPerson(); person.setIpCreated(request.getRemoteAddr()); person.setIpLastActived(request.getRemoteAddr()); person.setDateCreated(new Date()); person.setDateLastActived(new Date()); if (person.getAccount() == null|| person.getAccount().trim().length() == 0) { request.setAttribute("message", "请输入帐号"); return this.initAdd(mapping, form, request, response); } if (person.getPassword() == null|| person.getPassword().trim().length() == 0|| !person.getPassword().equals(personForm.getPassword())) { request.setAttribute("message", "密码不一致"); return this.initAdd(mapping, form, request, response); } try { personService.create(person); PersonUtil.setPersonInf(request, response, person); request.setAttribute("message", "注册成功"); return new ActionForward("success", "/jsp/person/success.jsp",false); } catch (Exception e) { request.setAttribute("message", "注册失败,原因:" + e.getMessage()); return this.initAdd(mapping, form, request, response); } } public IPersonService<Person> getPersonService() { return personService; } public void setPersonService(IPersonService<Person> personService) { this.personService = personService; }}6.5、PersonAction配置
在struts-config.xml中需要添加PersonAction的配置代码,并且必须配置了controller后,Spring才能管理Action。之后几个模块的配置类似如下,就不再介绍了,拦截器与异常捕捉代码,可下载查看。<?xml version="1.0" encoding="UTF-8"?><!DOCTYPE struts-config PUBLIC "-//Apache Software Foundation//DTD Struts Configuration 1.3//EN" "http://struts.apache.org/dtds/struts-config_1_3.dtd"><struts-config> <form-beans > <form-bean name="personForm" type="form.PersonForm"></form-bean> </form-beans> <global-exceptions> <exception key="login" type="java.lang.Exception" handler="exception.ForumExceptionHandler"></exception> </global-exceptions> <global-forwards /> <action-mappings> <action attribute="personForm" path="/person" name="personForm" input="/jsp/person/listPerson.jsp" scope="request" parameter="action" type="action.PersonAction"> <!-- DispatchAction是通过请求参数来选择方法 --> <forward name="add" path="/jsp/person/addPerson.jsp"></forward> <forward name="list" path="/jsp/person/listPerson.jsp"></forward> </action> </action-mappings> <controller processorClass="org.springframework.web.struts.DelegatingRequestProcessor"></controller> <!-- 把Struts的Action交给Spring代理 --> <message-resources parameter="com.yourcompany.struts.ApplicationResources" /></struts-config>配置applicationContext.xml文件<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.1.xsd"> //结合前面代码 <bean id="dao" class="dao.DaoImpl"> <property name="sessionFactory" ref="sessionFactory"></property> </bean> <bean id="personService" class="org.springframework.transaction.interceptor.TransactionProxyFactoryBean"> <!-- 事务工厂代理类 --> <property name="transactionManager" ref="hibernateTransactionmanager"></property> <property name="target"> <!-- 被管理的对象,匿名Bean --> <bean class="Service.PersonServiceImpl"> <property name="dao" ref="dao"></property> </bean> </property> <property name="transactionAttributeSource" ref="hibernateTransactionAttributeSource"></property> </bean> <bean id="loginInterceptor" class="org.springframework.aop.support.NameMatchMethodPointcutAdvisor"> <!-- 方法前拦截器 --> <property name="advice"> <bean class="interceptor.LoginInterceptor" /> </property> <property name="mappedName" value="*"></property> </bean> <bean name="/person" class="org.springframework.aop.framework.ProxyFactoryBean"> <property name="interceptorNames"> <list> <value>loginInterceptor</value> </list> </property> <!-- 被拦截的对象 --> <property name="target"> <bean class="action.PersonAction"> <property name="personService" ref="personService"></property> </bean> </property> </bean></beans>注册界面的效果如下:6.6、PersonAction代码——实现用户登录
package action;public class PersonAction extends ForumAction { private IPersonService<Person> personService; //结合前面相关代码 public ActionForward initLogin(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { PersonForm personForm = (PersonForm) form; personForm.setTitle("用户登录"); return new ActionForward("login", "/jsp/person/login.jsp", false); } public ActionForward login(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response)throws Exception { PersonForm personForm = (PersonForm) form; personForm.setTitle("用户登录"); Person person = personService.getPerson(personForm.getPerson().getAccount(), personForm.getPerson().getPassword()); if (person == null) throw new AccountException("用户名密码错误"); PersonUtil.setPersonInf(request, response, person); person.setIpLastActived(request.getRemoteAddr()); person.setDateLastActived(new Date()); personService.save(person); request.setAttribute("message", "欢迎回来"); return new ActionForward("success", "/jsp/person/success.jsp", false); } public ActionForward logout(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response)throws Exception { PersonForm personForm = (PersonForm) form; personForm.setTitle("用户注销"); request.getSession(true).setAttribute(PersonUtil.PERSON_INFO, null); return new ActionForward("login", "/jsp/person/login.jsp", false); }}登录效果如下6.7、PersonAction代码——查看用户资料
package action;public class PersonAction extends ForumAction { private IPersonService<Person> personService; //结合前面代码 public ActionForward view(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response)throws Exception { PersonForm personForm = (PersonForm) form; personForm.setTitle("查看用户资料"); Person person = personService.find(Person.class, personForm.getPerson().getId()); request.setAttribute("person", person); return new ActionForward("view", "/jsp/person/viewPerson.jsp", false); }}运行效果:7、版面类型模块
7.1、ICategoryService接口
ICategoryService不需要特别的方法,因此没有定义任何操作package Service;public interface ICategoryService<T extends Category> extends IService<T> {}7.2、CategoryServiceImpl实现
CategotyServiceImpl只需要实现ServiceImpl的create()即可。package Service;public class CategoryServiceImpl<T extends Category> extends ServiceImpl<T> implements ICategoryService<T> { @Override public void create(T category) { if (dao.createQuery(" from Category c where c.name = :name and c.deleted = false ").setParameter("name", category.getName()).list().size() > 0) throw new RuntimeException("类别 " + category.getName() + " 已经存在。"); dao.create(category); }}7.3、CategoryForm代码
package form;public class CategoryForm extends ForumForm { private Category category = new Category(); public Category getCategory() { return category; } public void setCategory(Category category) { this.category = category; } public ActionErrors validate(ActionMapping mapping,HttpServletRequest request) { return null; } public void reset(ActionMapping mapping, HttpServletRequest request) { }}7.4、CategoryAction代码——实现浏览类别
浏览类别时会显示类别的版面、版面的帖子数、回帖数、最后发表的帖子等。package action;public class CategoryAction extends ForumAction { private ICategoryService<Category> categoryService; public ActionForward list(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { CategoryForm categoryForm = (CategoryForm) form; List<Category> categoryList = categoryService.list(" from Category where deleted = false ", 0, Integer.MAX_VALUE,null); request.setAttribute("categoryList", categoryList); return new ActionForward("list", "/jsp/category/listCategory.jsp",false); } public ICategoryService<Category> getCategoryService() { return categoryService; } public void setCategoryService(ICategoryService<Category> categoryService) { this.categoryService = categoryService; }}实现效果如图:7.5、CategoryAction代码——实现添加类别
添加类别只是纯粹地将类别保存进数据库package action;public class CategoryAction extends ForumAction { private ICategoryService<Category> categoryService; public ActionForward initAdd(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { CategoryForm categoryForm = (CategoryForm) form; categoryForm.setTitle("添加类别"); return new ActionForward("add", "/jsp/category/addCategory.jsp", false); } public ActionForward add(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { CategoryForm categoryForm = (CategoryForm) form; categoryForm.setTitle("添加类别"); Category category = categoryForm.getCategory(); category.setDateCreated(new Date()); categoryService.create(category); request.setAttribute("category", category); return new ActionForward("add", "/jsp/category/success.jsp", false); }}运行效果:8、版面模块
版面模块包括添加版面、浏览版面帖子列表、设置版主等。8.1、IBoradService接口
设置版主利用Hibernate的Cascade自动实现,因此不需要定义特别的操作。package Service;public interface IBoardService<T extends Board> extends IService<T> {}8.2、BoardServiceImpl实现
package Service;public class BoardServiceImpl<T extends Board> extends ServiceImpl<T> implements IBoardService<T> { @Override public void create(T board) { if (dao.createQuery(" from Board b where b.deleted = false and b.name = :name ").setParameter("name", board.getName().trim()).list().size() > 0) throw new RuntimeException("版面 " + board.getName() + " 已经存在。"); dao.create(board); }}8.3、BoardForm代码
package form;public class BoardForm extends ForumForm { private Category category = new Category(); private Board board = new Board(); private int[] adminId; public ActionErrors validate(ActionMapping mapping,HttpServletRequest request) { return null; } public void reset(ActionMapping mapping, HttpServletRequest request) { } //省略getter和setter方法}8.4、BoardAction代码——实现浏览版面
浏览版面会以分页的形式列出所有的帖子,分页使用Pagination类实现,放在uitl工具包里。package action;public class BoardAction extends ForumAction { private ICategoryService<Category> categoryService; private IBoardService<Board> boardService; private IThreadService<Thread> threadService; private IPersonService<Person> personService; @Override @SuppressWarnings("all") public ActionForward list(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { BoardForm boardForm = (BoardForm) form; Board board = boardService.find(Board.class, boardForm.getBoard().getId()); boardForm.setBoard(board); int totalCount = threadService.getTotalCount(" select count(t) from Thread t "+ " where t.deleted = false and t.board.id = "+ board.getId(), null); Pagination pagination = new Pagination(request, response); pagination.setRecordCount(totalCount); List<Thread> threadList = threadService.list(" select t from Thread t "+ " where t.deleted = false and t.board.id = " + board.getId() + " order by t.dateLastReplied desc ", pagination.getFirstResult(), pagination.getPageSize(), null); request.setAttribute("board", board); request.setAttribute("pagination", pagination); request.setAttribute("threadList", threadList); boardForm.setTitle("帖子列表 - 版面:" + board.getName()); return new ActionForward("list", "/jsp/thread/listThread.jsp", false); } //省略setter和getter方法}实现效果:8.5、BoardAction代码——实现版面添加
版面添加时需要选择类别,因此需要将所有耳朵类别显示出来。package action;public class BoardAction extends ForumAction { private ICategoryService<Category> categoryService; private IBoardService<Board> boardService; private IThreadService<Thread> threadService; private IPersonService<Person> personService; public ActionForward initAdd(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { BoardForm boardForm = (BoardForm) form; boardForm.setTitle("添加版面"); List<Category> categoryList = categoryService.list(" from Category c where c.deleted = false "); request.setAttribute("categoryList", categoryList); return new ActionForward("add", "/jsp/board/addBoard.jsp", false); } public ActionForward add(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { BoardForm boardForm = (BoardForm) form; boardForm.setTitle("添加版面"); Category category = categoryService.find(Category.class, boardForm.getCategory().getId()); Board board = boardForm.getBoard(); board.setCategory(category); board.setDateCreated(new Date()); boardService.create(board); request.setAttribute("category", category); return new ActionForward("success", "/jsp/board/success.jsp", false); }}实现效果:8.6、BoardAction代码——设置版主
设置版主要显示版面的信息、待选的版主列表,并且现在的版主要处于选中状态。package action;public class BoardAction extends ForumAction { private ICategoryService<Category> categoryService; private IBoardService<Board> boardService; private IThreadService<Thread> threadService; private IPersonService<Person> personService; public ActionForward initSetAdmin(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { BoardForm boardForm = (BoardForm) form; Board board = boardService.find(Board.class, boardForm.getBoard().getId()); List<Person> personList = personService.list(" from Person p where p.deleted = false "); int[] adminId = new int[board.getAdministrators().size()]; int i = 0; for (Iterator<Person> it = board.getAdministrators().iterator(); it.hasNext(); i++) { Person p = it.next(); adminId[i] = p.getId(); } boardForm.setAdminId(adminId); request.setAttribute("board", board); request.setAttribute("personList", personList); boardForm.setTitle("设置管理员 - 版面:" + board.getName()); return new ActionForward("success", "/jsp/board/setAdmin.jsp", false); } public ActionForward setAdmin(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { BoardForm boardForm = (BoardForm) form; Board board = boardService.find(Board.class, boardForm.getBoard().getId()); board.getAdministrators().clear(); int[] adminId = boardForm.getAdminId(); for (int i = 0; adminId != null && i < adminId.length; i++) { Person person = personService.find(Person.class, adminId[i]); board.getAdministrators().add(person); } boardService.save(board); boardForm.setTitle("设置管理员 - 版面:" + board.getName()); return new ActionForward("success", "/jsp/board/success.jsp", false); }}实现效果:9、帖子模块
帖子模块包括浏览帖子与发表帖子,浏览帖子会以分页的形式列出所有回帖,发表帖子时允许编辑HTML代码。9.1、IThreadService接口
浏览帖子时需要统计帖子的点击率package Service;public interface IThreadService<T extends Thread> extends IService<T> { public void updateHit(Integer threadId);}9.2、ThreadServiceImpl实现
发表帖子时需要更新版面的信息,包括帖子数、最后发表的帖子、作者、时间等package Service;public class ThreadServiceImpl<T extends Thread> extends ServiceImpl<T> implements IThreadService<T> { @Override @SuppressWarnings("all") public void create(T thread) { dao.create(thread); int totalCount = dao.getTotalCount(" select count(t) from Thread t "+ " where t.deleted = false and t.board.id = "+ thread.getBoard().getId(), null); dao.createQuery(" update Board b "+ " set b.lastThread.id = :lastThreadId, b.lastReply.id = null, threadCount = :threadCount "+ " where b.id = :boardId ").setParameter( "lastThreadId", thread.getId()).setParameter("boardId",thread.getBoard().getId()).setParameter("threadCount",totalCount).executeUpdate(); } public void updateHit(Integer threadId) { dao.createQuery(" update Thread t set t.hit = t.hit + 1 where t.id = :id ").setParameter("id", threadId).executeUpdate(); }}9.3、ThreadForm代码
package form;public class ThreadForm extends ForumForm { private Thread thread = new Thread(); private Board board = new Board(); public ActionErrors validate(ActionMapping mapping,HttpServletRequest request) { return null; } public void reset(ActionMapping mapping, HttpServletRequest request) { } //省略setter和getter方法}9.4、ThreadAction代码——实现浏览帖子
浏览帖子时以分页的形式显示所有的回帖,并更新人气package action;public class ThreadAction extends ForumAction { private IThreadService<Thread> threadService; private IPersonService<Person> personService; private IBoardService<Board> boardService; private IReplyService<Reply> replyService; @Override @SuppressWarnings("all") public ActionForward list(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { return this.view(mapping, form, request, response); } @SuppressWarnings("all") public ActionForward view(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { ThreadForm threadForm = (ThreadForm) form; Thread thread = threadService.find(Thread.class, (int) threadForm.getThread().getId()); int totalCount = replyService.getTotalCount(" select count(r) from Reply r "+ " where r.deleted = false and r.thread.id = " + threadForm.getThread().getId(), null); Pagination pagination = new Pagination(request, response); pagination.setRecordCount(totalCount); List<Reply> replyList = replyService.list(" from Reply r where r.deleted = false and r.thread.id = "+ threadForm.getThread().getId() + " order by r.id asc ", pagination.getFirstResult(),pagination.getPageSize(), null); threadService.updateHit(thread.getId()); request.setAttribute("category", thread.getBoard().getCategory()); request.setAttribute("board", thread.getBoard()); request.setAttribute("thread", thread); request.setAttribute("pagination", pagination); request.setAttribute("replyList", replyList); threadForm.setTitle("浏览帖子 - 标题:" + thread.getTitle()); return new ActionForward("list", "/jsp/thread/viewThread.jsp", false); } //省略setter和getter方法}实现效果:9.5、ThreadAction代码——实现发表帖子
发表帖子时需要用到HTML编辑器,本系统采用tinyMCE,tinyMCE是js实现的编辑器,功能十分强大,而且使用十分简单,只需要在JSP中引入即可。本系统把textArea变成HTML编辑器。package action;public class ThreadAction extends ForumAction { private IThreadService<Thread> threadService; private IPersonService<Person> personService; private IBoardService<Board> boardService; private IReplyService<Reply> replyService; public ActionForward initAdd(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { ThreadForm threadForm = (ThreadForm) form; Board board = boardService.find(Board.class, threadForm.getBoard().getId()); threadForm.setBoard(board); request.setAttribute("board", board); threadForm.setTitle("发表帖子 - 版面:" + board.getName()); return new ActionForward("add", "/jsp/thread/addThread.jsp", false); } public ActionForward add(ActionMapping mapping, ActionForm form, HttpServletRequest request, HttpServletResponse response) { ThreadForm threadForm = (ThreadForm) form; Board board = boardService.find(Board.class, threadForm.getBoard().getId()); PersonInfo personInfo = PersonUtil.getPersonInfo(request, response); Person person = personService.find(Person.class, personInfo.getId()); Thread thread = threadForm.getThread(); thread.setBoard(board); thread.setAuthor(person); thread.setAuthorLastReplied(null); thread.setDateCreated(new Date()); thread.setDateLastReplied(new Date()); thread.setDeleted(false); thread.setIpCreated(request.getRemoteAddr()); thread.setReadonly(false); thread.setReplyCount(0); thread.setTopped(false); threadService.create(thread); threadForm.setTitle("发表帖子 - 版面:" + board.getName()); return this.list(mapping, form, request, response); }}运行效果:10、回帖模块
回帖模块只有回帖一个功能。10.1、IReplyService接口
package Service;public interface IReplyService<T extends Reply> extends IService<T> {}10.2、ReplyServiceImpl实现
发表回帖时,需要更新版面最后发表的回帖、作者、时间,并更新帖子最后回复的回帖、作者、时间,以及回帖总数。package Service;public class ReplyServiceImpl<T extends Reply> extends ServiceImpl<T> implements IReplyService<T> { @Override public void create(T reply) { dao.create(reply); // 更新帖子最后回复、最后回复日期、作者、回帖数 int count = dao.getTotalCount(" select count(r) from Reply r "+ " where r.deleted = false and r.thread.id = "+ reply.getThread().getId(), null); dao.createQuery(" update Thread t "+ " set t.authorLastReplied.id = :authorLastReplied, "+ " t.dateLastReplied = :dateLastReplied, " + " t.replyCount = :replyCount "+ " where t.id = :threadId ").setParameter("authorLastReplied", reply.getAuthor().getId()).setParameter( "dateLastReplied", reply.getDateCreated()).setParameter("replyCount", count).setParameter("threadId",reply.getThread().getId()).executeUpdate(); // 更新版面的最后发表数、最后发表时间 int replyCount = dao.getTotalCount(" select count(r) from Reply r "+ " where r.deleted = false " + " and r.thread.board.id = "+ reply.getThread().getBoard().getId(), null); dao.createQuery(" update Board b " + " set b.lastThread.id = null, "+ " b.lastReply.id = :lastReplyId, " + " b.replyCount = :replyCount "+ " where b.id = :boardId ").setParameter( "lastReplyId", reply.getId()).setParameter("boardId",reply.getThread().getBoard().getId()).setParameter("replyCount", replyCount).executeUpdate(); int floor = dao.getTotalCount(" select count(r) from Reply r "+ " where r.thread.id = " + reply.getThread().getId(), null); // 回帖处于第几楼 reply.setFloor(floor); dao.save(reply); }}10.3、ReplyForm代码
package form;public class ReplyForm extends ForumForm { private Thread thread = new Thread(); private Reply reply = new Reply(); public ActionErrors validate(ActionMapping mapping,HttpServletRequest request) { return null; } public void reset(ActionMapping mapping, HttpServletRequest request) { } //省略setter和getter方法}10.4、ReplyAction代码——实现发表回帖
package action;public class ReplyAction extends ForumAction { private IPersonService<Person> personService; private IThreadService<Thread> threadService; private IReplyService<Reply> replyService; public ActionForward list(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { ReplyForm replyForm = (ReplyForm) form; return null; } public ActionForward initAdd(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { ReplyForm replyForm = (ReplyForm) form; Thread thread = threadService.find(Thread.class, (int) replyForm.getThread().getId()); request.setAttribute("category", thread.getBoard().getCategory()); request.setAttribute("board", thread.getBoard()); request.setAttribute("thread", thread); replyForm.setTitle("回复帖子 - 标题:" + thread.getTitle()); return new ActionForward("add", "/jsp/reply/addReply.jsp", false); } public ActionForward add(ActionMapping mapping, ActionForm form,HttpServletRequest request, HttpServletResponse response) { ReplyForm replyForm = (ReplyForm) form; Thread thread = threadService.find(Thread.class, (int) replyForm.getThread().getId()); PersonInfo personInfo = PersonUtil.getPersonInfo(request, response); Person person = personService.find(Person.class, personInfo.getId()); Reply reply = replyForm.getReply(); reply.setThread(thread); reply.setDateCreated(new Date()); reply.setDeleted(false); reply.setAuthor(person); replyService.create(reply); request.setAttribute("category", thread.getBoard().getCategory()); request.setAttribute("board", thread.getBoard()); request.setAttribute("thread", thread); request.setAttribute("reply", reply); replyForm.setTitle("回复帖子 - 标题:" + thread.getTitle()); return new ActionForward("success", "/jsp/reply/success.jsp", false); } //省略setter和getter方法}实现效果:
新闻热点
疑难解答