基于SSM框架搭建的论坛系统


第一次来CSDN发表文章,这个编辑器还不怎么会用,所以文章排版可能不怎么好看,大家见谅。
这个项目是作为今年毕业设计和答辩项目的来做的,用了大概两三个星期,论坛核心框架搭建的倒是挺快,零零碎碎的小功能还有页面设计是我最花时间的地方,因为老师没怎么教过javascript,还有一些前端框架的应用,所以我基本上是在边学边做。现在答辩过了,也没什么事,就把这个项目分享给大家,有兴趣的同学可以参考一下,也希望有大佬指点我一二。

页面展示:

主页

在这里插入图片描述

帖子页面

在这里插入图片描述

论坛数据库设计

在做项目之前我也在网上看过其他人的一些作品,但他们的论坛系统真的比较简陋,就是简单的楼层1、2、3、4。。。往下排,不可以在楼层下面回复,页面也很简单,与现在主流的贴吧论坛差距很大。既然没人来,大佬又不屑做,那我就来搞一个。
众所周知论坛应该分为三个层级,大板块——帖子——楼层,而楼层又可以详细分为普通楼层和回复楼层,这样想的话思路就比较清晰了,一个简单的包含关系而已,重点是数据库该如何表示,不多说,上图:
数据库ER图
对于本论坛的功能需求分析,再根据上述数据库的E-R模型图,如下为本数据库的各个表以及其字段:

1.用户表:用于存储所有用户及管理员信息。

用户表(tb_users)
在这里插入图片描述
主键:uid、email;
外键:无;
解释:uid(用户id),username(用户昵称),sex(性别),email(电子邮箱),power(权限),password(密码),registertime(注册时间),underwrite(个性签名),birthdata(出生日期),experence(经验),headphoto(头像);
用户表中,采用双主键分别是uid和email,为的是防止用户登入信息重复造成数据库混乱登入功能BUG。
2.板块表:用于存储板块信息。
板块表(tb_boards)
在这里插入图片描述
主键:bid;
外键:user_id—uid(tb_users);
解释:bid(板块id),user_id(用户id),boardname(板块名称),boardintroduction(板块介绍),boardclassfication(板块分类),boardimage(图示);
与tb_users绑定外键,方便展示板块时一起带回板块创建者信息。
3.帖子表:用于存储板块信息。如表3-3所示。
帖子表(tb_posts)
在这里插入图片描述
主键:pid;
外键:user_id—uid(tb_users)、board_id—bid(tb_board);
解释:pid(帖子id),user_id(用户id),board_id(板块id),posttitle(帖子标题),posttime(帖子发表时间),postheat(帖子热度),posttype(帖子分类);
4.楼层表:用于帖子中的楼层信息。
楼层表(tb_floors)
在这里插入图片描述
主键:fid;
外键:user_id—uid(tb_users)、post_id(tb_posts);
解释:fid(楼层id),user_id(用户id),post_id(帖子id),floornum(楼层层数),floortime(楼层回复时间),floorcontent(回复内容);
5.回复楼层表:用于存储楼层中的回复信息。
回复楼层表(tb_refloors)
在这里插入图片描述
主键:refid;
外键:user_id—uid(tb_users)、reuser_id—uid(tb_users)、floor_id—fid(tb_floor);
解释:refid(回复楼层id),user_id(用户id),reuser_id(回复人id),floor_id(楼层id),refloorcontent(回复内容),refloortime(回复时间),messagestate(回复状态);

SSM框架搭建

本想跳过这步,但是很多同学可能对框架还不是太熟悉,自己搭的不一定与我的项目耦合,那就重零开始搭建框架。首先说一下我这边的配置:Tomcat 8.0;MyEclipse 2014;JDK 1.7;MySQL 5.7;
不一样也不要紧,配置路径那边自己调。
新建项目GameBBS,我这里直接把目录结构截图了吧,你们看着建:

在这里插入图片描述
com.ve.config:存放Web、Spring的配置文件;
com.ve.controller:存放控制层的处理逻辑;
com.ve.mapper:存放对应的数据库会话;
com.ve.po:存放数据模型;
com.ve.service:存放服务类处理逻辑,方便controller调用;
com.ve.utils:存放一些用到的工具类,比如说随机生成验证码;

值得一提的是我的框架配置用的是全注解配置,并不是传统的xml,对这个感兴趣的同学可以去这个大佬博客看一下:使用全注解配置Spring MVC

这三个是框架必须的配置文件

RootConfig.java

package com.ve.config;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSource;
import org.apache.ibatis.logging.LogFactory;
import org.apache.ibatis.session.Configuration;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.mapper.MapperScannerConfigurer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.transaction.annotation.EnableTransactionManagement;

import com.ve.service.ServiceHook;

/**
 *  相当于spring,需要扫描service层,mapper层,可以配置相应的东西(数据源,会话工厂,事务管理等等)
 *  
 * **/
@org.springframework.context.annotation.Configuration
//相当于spring 扫描service层
@ComponentScan(basePackageClasses={ServiceHook.class})
//启动事务管理
@EnableTransactionManagement
public class RootConfig {
	
	
	//配置数据源
	@Bean
	public DataSource buildDataSource() {
		BasicDataSource ds = new BasicDataSource();
		ds.setDriverClassName("com.mysql.jdbc.Driver");
		ds.setUrl("jdbc:mysql://localhost:3306/db_bbs");
		ds.setUsername("root");
		ds.setPassword("123");
		return ds;
	}
	
	//配置log4日志
	@Bean
	public Configuration buildMyBatisConfig() {
		LogFactory.useLog4J2Logging();
		Configuration configuration = new Configuration();
		return configuration;
	}
	//配置会话工厂
	@Bean(name="sqlSessionFactory")
	public SqlSessionFactoryBean buildSessionFactory(
			DataSource datasource, Configuration config) {
		SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
		factory.setDataSource(datasource);
		factory.setConfiguration(config);
		return factory;
	}
	
	//利用会话工厂扫描mapper包(扫描mapper层)
	@Bean
	public MapperScannerConfigurer buildMybatisMapperSacnConfig() {
		MapperScannerConfigurer msc = new MapperScannerConfigurer();
		msc.setSqlSessionFactoryBeanName("sqlSessionFactory");
		msc.setBasePackage("com.ve.mapper");
		return msc;
	}
	
	//开启事务
	@Bean
	public DataSourceTransactionManager transactionManager( 
	        BasicDataSource datasource) {
	    return  new DataSourceTransactionManager(datasource);
	}
}

WebConfig.java

package com.ve.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.ViewResolver;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
import org.springframework.web.servlet.view.InternalResourceViewResolver;
import com.ve.controller.ControllerHook;

/**
 * webconfig 类似于 springmvc 需要启动webmvc , 扫描想应的包(controller) *
 **/

@Configuration
// 启动Mvc
@EnableWebMvc
// 扫描handler层
@ComponentScan(basePackageClasses = { ControllerHook.class })
public class WebConfig extends WebMvcConfigurerAdapter {

	// 用视图解析器,配置前后缀
	@Bean
	public ViewResolver resolver() {

		InternalResourceViewResolver vr = new InternalResourceViewResolver();
		vr.setPrefix("/WEB-INF/views/");
		vr.setSuffix(".jsp");
        
		return vr;

	}

	// 前端网页静态资源加载
	@Override
	public void addResourceHandlers(ResourceHandlerRegistry registry) {
		// TODO Auto-generated method stub
		super.addResourceHandlers(registry);
		registry.addResourceHandler("/framework/**").addResourceLocations(
				"/framework/");
		registry.addResourceHandler("/resources/**").addResourceLocations(
				"/resources/");
	}

}

WebInit.java

package com.ve.config;

import javax.servlet.Filter;

import org.springframework.context.annotation.Configuration;
import org.springframework.web.filter.CharacterEncodingFilter;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;

@Configuration
public class WebInit extends
		AbstractAnnotationConfigDispatcherServletInitializer {

	@Override
	protected Class<?>[] getRootConfigClasses() {
		// TODO Auto-generated method stub
		return new Class<?>[] { RootConfig.class };
	}

	@Override
	protected Class<?>[] getServletConfigClasses() {
		// TODO Auto-generated method stub
		return new Class<?>[] { WebConfig.class };
	}

	@Override
	protected String[] getServletMappings() {
		// TODO Auto-generated method stub
		return new String[] { "/" };
	}

	@Override
	protected Filter[] getServletFilters() {
		// TODO 自动生成的方法存根

		return new Filter[] { new CharacterEncodingFilter("UTF-8") };

	}

}

数据库配置什么的在RootConfig中根据自己需要改,其他一般没什么要变动的。

配置po模型

这个我觉得没什么好讲的,就是简单的get,set方法,我就以user为例吧,其他的你们根据数据库表来建。

UserModel.java

package com.ve.po;

public class UserModel {
private int uid;
private String username;
private int sex;  /* 0=男,1=女 */
private String email;
private int power; /* 0=板块管理,1=大会员,2=普通会员 */
private String password;
private String registertime;
private String underwrite;
private String birthdate;  /*年龄由出生日期计算得出,不需要用户填写,用户填写的出生日期转换为整形储存,取出时分段计算取值*/
private int experience;   /*发帖,回帖增加经验值,用户等级有经验值分层判定,这里只记录总经验值,等级显示由页面计算显示*/
private String headphoto;
public int getUid() {
	return uid;
}
public void setUid(int uid) {
	this.uid = uid;
}
public String getUsername() {
	return username;
}
public void setUsername(String username) {
	this.username = username;
}
public int getSex() {
	return sex;
}
public void setSex(int sex) {
	this.sex = sex;
}
public String getEmail() {
	return email;
}
public void setEmail(String email) {
	this.email = email;
}
public int getPower() {
	return power;
}
public void setPower(int power) {
	this.power = power;
}
public String getPassword() {
	return password;
}
public void setPassword(String password) {
	this.password = password;
}
public String getRegistertime() {
	return registertime;
}
public void setRegistertime(String registertime) {
	this.registertime = registertime;
}
public String getUnderwrite() {
	return underwrite;
}
public void setUnderwrite(String underwrite) {
	this.underwrite = underwrite;
}
public String getBirthdate() {
	return birthdate;
}
public void setBirthdate(String birthdate) {
	this.birthdate = birthdate;
}
public int getExperience() {
	return experience;
}
public void setExperience(int experience) {
	this.experience = experience;
}
public String getHeadphoto() {
	return headphoto;
}
public void setHeadphoto(String headphoto) {
	this.headphoto = headphoto;
}

}

配置mapper类

mapper主要是负责po与数据库的交互,这里主要是存放SQL语句和调用方法。

PostMapper.java

package com.ve.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import com.ve.po.PostModel;

public interface PostMapper {
	@Select("select * from tb_posts where board_id = #{bid} order by pid desc limit #{startPos},#{pageSize}")
	/* post和user的组合查询 */
	@Results({ @Result(column = "user_id", property = "user", one = @One(select = "com.ve.mapper.AccountMapper.selectbyuid")) })
	public List<PostModel> postlist(@Param("bid") int bid,
			@Param("startPos") int startPos, @Param("pageSize") int pageSize);

	@Insert("insert into tb_posts (user_id,board_id,posttitle,posttype) values(#{user_id},#{board_id},#{posttitle},#{posttype})")
	public int newpost(PostModel pmodel);

	@Select("select @@identity")
	// 查询最新生成记录的id
	public int returnid();

	@Select("SELECT COUNT(*) FROM tb_posts where board_id = #{bid}")
	public int getpostCount(int bid);/* 某板块帖子总数 */

	@Select("select * from tb_posts where posttitle like #{search}")
	@Results({ @Result(column = "user_id", property = "user", one = @One(select = "com.ve.mapper.AccountMapper.selectbyuid")) })
	public List<PostModel> searchpost(String search);/* 帖子标题的模糊查询 */
}

组合查询语句一对一,一对多,我只会用一对一,一对多不知道该怎么具体操作,在页面拿数据的时候拿不出来,希望知道的大佬可以点拨一下。

ReFloorMapper.java

package com.ve.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import com.ve.po.ReFloorModel;

public interface ReFloorMapper {
	@Select("select * from tb_refloors where floor_id in(select fid from tb_floors where post_id =#{pid})")
	@Results({
			@Result(column = "user_id", property = "user", one = @One(select = "com.ve.mapper.AccountMapper.selectbyuid")),
			@Result(column = "reuser_id", property = "reuser", one = @One(select = "com.ve.mapper.AccountMapper.selectbyuid")) })
	public List<ReFloorModel> refloorlist(int pid); /* 组合查询,定位帖子里所有的回复 */

	@Insert("insert into tb_refloors (user_id,reuser_id,floor_id,refloorcontent) values (#{user_id},#{reuser_id},#{floor_id},#{refloorcontent})")
	public int reply(ReFloorModel rfmodel);
}

就是回复楼层,我觉得应该在查询普通楼层的时候将回复楼层数据一起带出来的,可惜我不知道怎么用语句写出这种关系,所以只能用这种笨方法一股脑的先把所有fid相关的回复楼层全找出来,在页面中再排序。

FloorMapper

package com.ve.mapper;

import java.util.List;

import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.One;
import org.apache.ibatis.annotations.Result;
import org.apache.ibatis.annotations.Results;
import org.apache.ibatis.annotations.Select;

import com.ve.po.FloorModel;

public interface FloorMapper {
	@Select("select * from tb_floors where post_id = #{pid}")
	@Results({ @Result(column = "user_id", property = "user", one = @One(select = "com.ve.mapper.AccountMapper.selectbyuid")) })
	public List<FloorModel> floorlist(int pid); /* 列出楼层 */

	@Insert("insert into tb_floors (user_id,post_id,floornum,floorcontent) values (#{user_id},#{post_id},#{floornum},#{floorcontent})")
	public int postreply(FloorModel fmodel); /* 楼层回复写入数据库 */
}

源代码已上传,感兴趣的同学可以看一下https://download.csdn.net/download/qq_39249005/10828545

  • 22
    点赞
  • 155
    收藏
    觉得还不错? 一键收藏
  • 14
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值