简单的CMS项目

一、项目介绍

  1. CMS项目介绍

    项目的主要功能:包含后台图片管理,后台职位管理,用户登录,用户注册,用户邮箱激活,前台展示,用户登录,高级查询。
    项目的主要技术:
    web框架spring+springmvc+springjdbc
    前端框架:bootstrap
    职位和轮播图的CRUD
    分页对象—显示职位和轮播管理信息
    富文本编辑器wangeditor—优化相关展示信息
    页面静态化技术freemarker—优化访问服务器过多,效率低
    缓存技术ehcache----优化访问服务器过多,效率低
    高级查询技术—简单提供职位的精确查询
    邮箱激活技术
    Spring拦截器—后台访问安全

二. 项目的搭建和实现

开发工具:eclipse 
服务器:Tomcat7.0.57
调试浏览器:火狐
数据库:mysql

	
 1.采用maven项目结构:

![maven项目结构](https://img-blog.csdnimg.cn/20190223231042318.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzYzNjI5MQ==,size_16,color_FFFFFF,t_70)	2.项目分层
dao层----数据层,与数据库做交互
service层---业务层,处理前台逻辑
web层---处理前台请求,返回页面
![项目分层](https://img-blog.csdnimg.cn/20190224224957765.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80MzYzNjI5MQ==,size_16,color_FFFFFF,t_70)	

3.项目配置

采用Spring+SpringMVC+Springjdbc
web.xml配置


  cms
  
    index.html
    index.htm
    index.jsp
    default.html
    default.htm
    default.jsp
  
 
  
  	characterEncoding
  	org.springframework.web.filter.CharacterEncodingFilter
  	
  		encoding
  		UTF-8
  	
  
  
  	characterEncoding
  	/*
  
   
  
  	springmvc
  	org.springframework.web.servlet.DispatcherServlet
  	 
  	
  		contextConfigLocation
  		classpath:springmvc-servlet.xml
  	
  	1
  	
  
  
  
  	springmvc
  	/
  

SpringMVC——servlet配置










 
	 	
	 	
 

    
		
			2000000000
		
   



applicationContext.xml配置



	
	
	
		
		
		
		
	
	
	
		
	
	
	
		
		
			
			
			
			
			
			
			
			
			
			
			
			
			
		
			
			
		
	


数据库的配置:db.properties

driverClassName=com.mysql.jdbc.Driver
url=jdbc:mysql://localhost:3306/test
username=
password=

缓存的配置:ehcache.xml



	
    

	
			
			
			
            
			
            
			
            
			
            
			
            
			
            
            
           
           
           
           
           
            


4.相关内容的实现

1.职位的CRUD

职位的CRUD使用Springjdbc+Springmvc

难点:地址的显示
需求:前端的地址需要由后台查询
思路:将地址封装,而jobs对象只存储地址的编号,数据库专门建立地址表,并提供职位与地址的视图查询

package cn.xxx.domain;

/**
 * @author peng
 *存储工作地址的字段
 */
public class Address {
	
	//地址的id 
	private Long address_id;
	//地址的名字
	private String address;
	//父id
	private Integer pid;
	@Override
	public String toString() {
		return "Address [address_id=" + address_id + ", address=" + address + ", pid=" + pid + "]";
	}
	public Address() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Long getAddress_id() {
		return address_id;
	}
	public void setAddress_id(Long address_id) {
		this.address_id = address_id;
	}
	public String getAddress() {
		return address;
	}
	public void setAddress(String address) {
		this.address = address;
	}
	public Integer getPid() {
		return pid;
	}
	public void setPid(Integer pid) {
		this.pid = pid;
	}
	public Address(Long address_id, String address, Integer pid) {
		super();
		this.address_id = address_id;
		this.address = address;
		this.pid = pid;
	}
	
	
}

domain层:

package cn.itsource.domain;

import java.io.Serializable;
import java.util.Date;


/**
 * @author peng
 *工作类
 */
public class Jobs  implements Serializable{
	//工作的编号
	private Integer id;
	//工作标题
	private String title;
	//多表查询的address
	private String address;
	//工作地址编号
	private Long address_id;
	//招聘人数
	private Integer jobnum;
	//薪资
	private Integer treatment;
	//工作描述
	private String describes;
	//工作要求
	private String requires;
	
	private String htmlurl;
	//工作类型:全职or兼职
	private Integer positiontype;
	//是否启用
	private Boolean isenabled;
	//上传时间
	private Date inputdate=new Date();
	
	public Jobs() {
		super();
		// TODO Auto-generated constructor stub
	}

	@Override
	public String toString() {
		return "Jobs [id=" + id + ", title=" + title + ", address=" + address + ", address_id=" + address_id
				+ ", jobnum=" + jobnum + ", treatment=" + treatment + ", describes=" + describes + ", requires="
				+ requires + ", htmlurl=" + htmlurl + ", positiontype=" + positiontype + ", isenabled=" + isenabled
				+ ", inputdate=" + inputdate + "]";
	}

	public Jobs(Integer id, String title, String address, Long address_id, Integer jobnum, Integer treatment,
			String describes, String requires, String htmlurl, Integer positiontype, Boolean isenabled,
			Date inputdate) {
		super();
		this.id = id;
		this.title = title;
		this.address = address;
		this.address_id = address_id;
		this.jobnum = jobnum;
		this.treatment = treatment;
		this.describes = describes;
		this.requires = requires;
		this.htmlurl = htmlurl;
		this.positiontype = positiontype;
		this.isenabled = isenabled;
		this.inputdate = inputdate;
	}

	public Integer getId() {
		return id;
	}

	public void setId(Integer id) {
		this.id = id;
	}

	public String getTitle() {
		return title;
	}

	public void setTitle(String title) {
		this.title = title;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

	public Long getAddress_id() {
		return address_id;
	}

	public void setAddress_id(Long address_id) {
		this.address_id = address_id;
	}

	public Integer getJobnum() {
		return jobnum;
	}

	public void setJobnum(Integer jobnum) {
		this.jobnum = jobnum;
	}

	public Integer getTreatment() {
		return treatment;
	}

	public void setTreatment(Integer treatment) {
		this.treatment = treatment;
	}

	public String getDescribes() {
		return describes;
	}

	public void setDescribes(String describes) {
		this.describes = describes;
	}

	public String getRequires() {
		return requires;
	}

	public void setRequires(String requires) {
		this.requires = requires;
	}

	public String getHtmlurl() {
		return htmlurl;
	}

	public void setHtmlurl(String htmlurl) {
		this.htmlurl = htmlurl;
	}

	public Integer getPositiontype() {
		return positiontype;
	}

	public void setPositiontype(Integer positiontype) {
		this.positiontype = positiontype;
	}

	public Boolean getIsenabled() {
		return isenabled;
	}

	public void setIsenabled(Boolean isenabled) {
		this.isenabled = isenabled;
	}

	public Date getInputdate() {
		return inputdate;
	}

	public void setInputdate(Date inputdate) {
		this.inputdate = inputdate;
	}
	
	
	

	
	
}

dao层:

package cn.xxx.dao;

import java.util.List;

import cn.xxx.domain.Images;
import cn.xxx.domain.Jobs;
import cn.xxx.pagelist.PageList;
import cn.xxx.query.SqlCondition;

public interface IJobsDao {
	
	/**显示全部职位*/
	List queryAll();
	
	/**添加职位*/
	void add(Jobs job);
	
	/**删除职位 通过id*/
	void delete(Long id);
	
	/**查询某个职位 通过id*/
	Jobs queryOne(Long id);
	
	/**分页查询*/
	PageList queryByPage(SqlCondition sqlCondition);
	
	/**更新某个职位 通过id*/
	void update(Jobs job);
	
	/**高级查询*/
	List queryByCondition(SqlCondition sqlCondition);
}

Service层

package cn.xxx.service;

import java.util.List;

import cn.xxx.domain.Images;
import cn.xxx.domain.Jobs;
import cn.xxx.pagelist.PageList;
import cn.xxx.query.SqlCondition;

public interface IJobsService {
	List queryAll();
	void add(Jobs job);
	void delete(Long id);
	Jobs queryOne(Long id);
	/**分页查询*/
	PageList queryByPage(SqlCondition sqlCondition);
	/**更新*/
	void update(Jobs job);
	/**高级查询*/
	List queryByCondition(SqlCondition sqlCondition);
}

package cn.xxx.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.xxx.dao.IJobsDao;
import cn.xxx.domain.Jobs;
import cn.xxx.myutils.CacheUtils;
import cn.xxx.pagelist.PageList;
import cn.xxx.query.SqlCondition;
import cn.xxx.service.IJobsService;
import net.sf.ehcache.CacheManager;
@Service
public class JobsServiceImpl implements IJobsService {
	/**用来判断是否需要新增缓存 默认为flase不需要缓存
	 * 新增 删除 修改 需要更新缓存
	 * 
	 * */
	private Boolean isEnableCache=false;
	@Autowired
	private IJobsDao jobsDaoImpl;
	@Override
	public List queryAll() {
		return jobsDaoImpl.queryAll();
	}
	@Override
	public void add(Jobs Jobs) {
		jobsDaoImpl.add(Jobs);
		isEnableCache=true;
	}
	@Override
	public void delete(Long id) {
		jobsDaoImpl.delete(id);
		isEnableCache=true;
	}
	@Override
	public Jobs queryOne(Long id) {
		return jobsDaoImpl.queryOne(id);
	}
	@Override
	public PageList queryByPage(SqlCondition sqlCondition) {
		PageList queryByPage = null;
		//创建缓存管理者
		CacheManager cacheManager = CacheManager.create();
		//每次不同的存入key
		String key="key_"+sqlCondition.getCurrentPage();
		//缓存名称
		String cacheName="jobsCache";
		if(sqlCondition.getTitle()!=null&&!"".equals(sqlCondition.getTitle())){
			/**
			 * 使用高级查询 将缓存重新清理
			 */
			sqlCondition.setIsEnableCache(true);
		}
		if(sqlCondition.getPositiontype()!=null&&!"".equals(sqlCondition.getPositiontype())&&sqlCondition.getPositiontype()!=2){
			sqlCondition.setIsEnableCache(true);
		}
		//静态化操作
		/**取出缓存*/
		queryByPage = (PageList) CacheUtils.getCacheData(cacheManager, key, cacheName);
		/**如果未取出数据,进行查询存储数据到缓存*/
		if(queryByPage==null||isEnableCache||sqlCondition.getIsEnableCache()){
			//如果标识符为ture表名内容已修改需要更新缓存
			
			if(isEnableCache){
				//删除原来缓存数据
				cacheManager.getCache(cacheName).removeAll();
			}
			queryByPage = jobsDaoImpl.queryByPage(sqlCondition);
			CacheUtils.setCacheData(cacheManager, key, queryByPage, cacheName);
			isEnableCache=false;
		}
		return queryByPage;
	}
	@Override
	public void update( Jobs job) {
		// TODO Auto-generated method stub
		jobsDaoImpl.update(job);
		isEnableCache=true;
	}
	@Override
	public List queryByCondition(SqlCondition sqlCondition) {
		// TODO Auto-generated method stub
		return jobsDaoImpl.queryByCondition(sqlCondition);
	}

}

2.轮播图的CRUD
难点:图片的上传和删除
需求:用户可以自己上传图片到后台,选择是否在显示
思路:在前台提交的form表单设置图片属性,图片对象提供图片这个对象,在上传的时候,在服务器目录下存储图片,同时随机生成文件名(防止文件名重复),而在数据库保存图片名称和相对路径,绝对路径通过请求对象拿到,取出和删除都通过构建含有路径和文件名的文件对象来删除,注意删除要调用gc释放资源
domian:

package cn.xxx.domain;

import java.util.Date;

import org.springframework.web.multipart.MultipartFile;

public class Images {
	/**图片名称*/
	private String imagesName;
	/**图片id*/
	private Long id;
	/**图片信息*/
	private String info;
	/**图片上传日期*/
	private Date imagesDate;
	/**图片是否启用*/
	private Boolean imagesIsEnable;//true是启用
	/**图片地址*/
	private String storePath;
	/**图片存储对象*/
	private MultipartFile multiPartFile;
	public Images() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Images(String imagesName, Long id, String info, Date imagesDate, Boolean imagesIsEnable, String storePath,
			MultipartFile multiPartFile) {
		super();
		this.imagesName = imagesName;
		this.id = id;
		this.info = info;
		this.imagesDate = imagesDate;
		this.imagesIsEnable = imagesIsEnable;
		this.storePath = storePath;
		this.multiPartFile = multiPartFile;
	}
	@Override
	public String toString() {
		return "Images [imagesName=" + imagesName + ", id=" + id + ", info=" + info + ", imagesDate=" + imagesDate
				+ ", imagesIsEnable=" + imagesIsEnable + ", storePath=" + storePath + ", multiPartFile=" + multiPartFile
				+ "]";
	}
	public String getImagesName() {
		return imagesName;
	}
	public void setImagesName(String imagesName) {
		this.imagesName = imagesName;
	}
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getInfo() {
		return info;
	}
	public void setInfo(String info) {
		this.info = info;
	}
	public Date getImagesDate() {
		return imagesDate;
	}
	public void setImagesDate(Date imagesDate) {
		this.imagesDate = imagesDate;
	}
	public Boolean getImagesIsEnable() {
		return imagesIsEnable;
	}
	public void setImagesIsEnable(Boolean imagesIsEnable) {
		this.imagesIsEnable = imagesIsEnable;
	}
	public String getStorePath() {
		return storePath;
	}
	public void setStorePath(String storePath) {
		this.storePath = storePath;
	}
	public MultipartFile getMultiPartFile() {
		return multiPartFile;
	}
	public void setMultiPartFile(MultipartFile multiPartFile) {
		this.multiPartFile = multiPartFile;
	}
	
}

Dao:

package cn.xxx.dao;

import java.util.List;

import cn.xxx.domain.Images;
import cn.xxx.pagelist.PageList;
import cn.xxx.query.SqlCondition;

public interface IImagesDao {
	
	/**显示照片*/
	List queryAll();
	
	/**添加照片*/
	void add(Images images);
	
	/**删除照片*/
	void delete(Long id);
	
	/**查询一个*/
	Images queryOne(Long id);
	
	/**分页查询*/
	PageList queryByPage(SqlCondition sqlCondition);
	
	/**更新照片*/
	void update(Images images);
}

package cn.xxx.dao.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import cn.xxx.dao.IImagesDao;
import cn.xxx.domain.Images;
import cn.xxx.pagelist.PageList;
import cn.xxx.query.SqlCondition;
@Repository
public class ImagesDaoImpl implements IImagesDao{
	//使用Spring的jdbc对象
	@Autowired
	private JdbcTemplate jdbcTemplate;

	//查询所有的方法
	@Override
	public List queryAll() {
		String sql="select * from t_images";
		return this.jdbcTemplate.query(sql, new BeanPropertyRowMapper(Images.class));
	}

	//新增方法
	@Override
	public void add(Images images) {
		String sql="insert into t_images(imagesName,info,imagesDate,imagesIsEnable,storePath)values(?,?,?,?,?)";
		this.jdbcTemplate.update(sql, 
						    images.getImagesName(),
						    images.getInfo(),
						    images.getImagesDate(),
						    images.getImagesIsEnable(),
						    images.getStorePath());
	}
	
	//删除方法
	@Override
	public void delete(Long id) {
		String sql="delete from t_images where id=?";
		this.jdbcTemplate.update(sql, id);
	}

	//查询一条方法
	@Override
	public Images queryOne(Long id) {
		String sql="select * from t_images where id=?";
		
		return this.jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(Images.class), id);
	}

	//分页查询方法
	@Override
	public PageList queryByPage(SqlCondition sqlCondition) {
		
		//分页查询所有条数
		String countSql="select count(*) from t_images";
		Integer totalCount = this.jdbcTemplate.queryForObject(countSql, Integer.class);
		
		//当前页
		Integer currentPage = sqlCondition.getCurrentPage();
		//每页展示的数据条数
		Integer pageSize = sqlCondition.getPageSize();
		//设置分页对象
		PageList pageList = new PageList<>(currentPage,pageSize,totalCount);

		//分页查询数据
		Integer beginIndex=(currentPage-1)*pageSize;
		String sql="select * from t_images limit "+beginIndex+","+pageSize;
		List data = this.jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(Images.class));
		pageList.setData(data);
		return pageList;
	}

	//更新方法
	@Override
	public void update(Images images) {
		String sql="update  t_images set info=?,imagesIsEnable=? where id=?";
		this.jdbcTemplate.update(sql, 
						    images.getInfo(),
						    images.getImagesIsEnable(),
						    images.getId()
							);
	}

}

service层

package cn.xxx.service;

import java.util.List;

import cn.xxx.domain.Images;
import cn.xxx.pagelist.PageList;
import cn.xxx.query.SqlCondition;

public interface IImagesService {
	List queryAll();
	void add(Images images);
	void delete(Long id);
	Images queryOne(Long id);
	/**分页查询*/
	PageList queryByPage(SqlCondition sqlCondition);
	/**更新照片*/
	void update(Images images);
}

package cn.xxx.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.xxx.dao.IImagesDao;
import cn.xxx.domain.Images;
import cn.xxx.pagelist.PageList;
import cn.xxx.query.SqlCondition;
import cn.xxx.service.IImagesService;
@Service
public class ImagesServiceImpl implements IImagesService {

	@Autowired
	private IImagesDao imagesDaoImpl;
	@Override
	public List queryAll() {
		return imagesDaoImpl.queryAll();
	}
	@Override
	public void add(Images images) {
		imagesDaoImpl.add(images);
	}
	@Override
	public void delete(Long id) {
		imagesDaoImpl.delete(id);
	}
	@Override
	public Images queryOne(Long id) {
		return imagesDaoImpl.queryOne(id);
	}
	@Override
	public PageList queryByPage(SqlCondition sqlCondition) {
		// TODO Auto-generated method stub
		return imagesDaoImpl.queryByPage(sqlCondition);
	}
	@Override
	public void update(Images images) {
		// TODO Auto-generated method stub
		imagesDaoImpl.update(images);
	}

}

3.分页的实现
难点:分页的实现
需求:每次查询数据库降所有数据展示出来,阅读不便,而且数据量大,内存紧张
思路:采用真分页的方式,每次查询按照分页条件,进行分页查询展示,提供分页对象(包含当前页,总数据,每页展示的条数,可以得到下一页,上一页等),分页条件对象,注意分页返回的集合为泛型,谁使用就规定泛型。
分页对象:PageList

package cn.xxx.pagelist;

import java.io.Serializable;
import java.util.List;

public class PageList implements Serializable{
	//分页查询的数据
	private List data;
	//第一页码
	private Integer firstPage;
	//前页码
	private Integer prePage;
	//下一页码
	private Integer nextPage;
	//最后页码
	private Integer lastPage;
	//总页数
	private Integer totalPage;
	//总数据量
	private Integer totalCount;
	//当前页码
	private Integer currentPage;
	//当前页面显示数据条数
	private Integer pageSize;

	public List getData() {
		return data;
	}

	public void setData(List data) {
		this.data = data;
	}

	public Integer getFirstPage() {
		return firstPage;
	}

	public void setFirstPage(Integer firstPage) {
		this.firstPage = firstPage;
	}

	public Integer getPrePage() {
		return prePage;
	}

	public void setPrePage(Integer prePage) {
		this.prePage = prePage;
	}

	public Integer getNextPage() {
		return nextPage;
	}

	public void setNextPage(Integer nextPage) {
		this.nextPage = nextPage;
	}

	public Integer getLastPage() {
		return lastPage;
	}

	public void setLastPage(Integer lastPage) {
		this.lastPage = lastPage;
	}

	public Integer getTotalPage() {
		return totalPage;
	}

	public void setTotalPage(Integer totalPage) {
		this.totalPage = totalPage;
	}

	public Integer getTotalCount() {
		return totalCount;
	}

	public void setTotalCount(Integer totalCount) {
		this.totalCount = totalCount;
	}

	public Integer getCurrentPage() {
		return currentPage;
	}

	public void setCurrentPage(Integer currentPage) {
		this.currentPage = currentPage;
	}

	public Integer getPageSize() {
		return pageSize;
	}

	public void setPageSize(Integer pageSize) {
		this.pageSize = pageSize;
	}

	@Override
	public String toString() {
		return "PageList [data=" + data + ", firstPage=" + firstPage + ", prePage=" + prePage + ", nextPage=" + nextPage
				+ ", lastPage=" + lastPage + ", totalPage=" + totalPage + ", totalCount=" + totalCount
				+ ", currentPage=" + currentPage + ", pageSize=" + pageSize + "]";
	}

	public PageList() {
		super();
		// TODO Auto-generated constructor stub
	}

	/**提供一个有参的构造方法对字段赋值*/
	public PageList(Integer currentPage,Integer pageSize,Integer totalCount ) {
		//传入当前页码
		this.currentPage=currentPage;
		//传入每页页面展示的条数
		this.pageSize=pageSize;
		//传入总的数据量
		this.totalCount=totalCount;
		
		//计算第一页码
		this.firstPage=1;
		
		//计算最后页码
		this.lastPage= totalCount%pageSize==0?totalCount/pageSize:totalCount/pageSize+1;
		
		//计算总页码
		this.totalPage=this.lastPage;
		
		//计算前页码
		this.prePage= currentPage==1?currentPage:currentPage-1;
		
		//计算下页码
		this.nextPage = currentPage

分页条件:SQLCondition

package cn.xxx.query;

import java.util.ArrayList;
import java.util.List;

/**
 * @author peng
 *定义分页的首页 每页展示的数据量
 */
/**
 * @author peng
 *
 */
public class SqlCondition {
	//高级查询的title
	private String title;
	//高级查询positiontype
	private Integer positiontype;
	//首页
	private Integer currentPage=1;
	//每页数据量
	private Integer pageSize=4;
	//提供内置标识符 用来解决高级查询问题
	private Boolean isEnableCache=false;
	
	
	public Boolean getIsEnableCache() {
		return isEnableCache;
	}

	public void setIsEnableCache(Boolean isEnableCache) {
		this.isEnableCache = isEnableCache;
	}

	public SqlCondition(Integer currentPage, Integer pageSize) {
		super();
		this.currentPage = currentPage;
		this.pageSize = pageSize;
	}
	
	@Override
	public String toString() {
		return "SqlCondition [title=" + title + ", positiontype=" + positiontype + ", currentPage=" + currentPage
				+ ", pageSize=" + pageSize + "]";
	}

	public SqlCondition() {
		super();
		// TODO Auto-generated constructor stub
	}
	public Integer getCurrentPage() {
		return currentPage;
	}
	public void setCurrentPage(Integer currentPage) {
		this.currentPage = currentPage;
	}
	public Integer getPageSize() {
		return pageSize;
	}
	public void setPageSize(Integer pageSize) {
		this.pageSize = pageSize;
	}
	public String getTitle() {
		return title;
	}
	public void setTitle(String title) {
		this.title = title;
	}
	public Integer getPositiontype() {
		return positiontype;
	}
	public void setPositiontype(Integer positiontype) {
		this.positiontype = positiontype;
	}
	
	/**解决where还是and问题
	 * SQL方案一:
	 * where 1=1
	 *缺点:会把索引失效 查询效率低
	 *
	 */
	public String whereCondition1(){
		String whereSql="where 1=1";
		if(this.title!=null&&!"".equals(this.title)){
			whereSql+=" and  title like'%"+this.title+"%'";
		}
		if(this.positiontype!=null&&!"".equals(this.positiontype)){
			whereSql+=" and positiontype="+this.positiontype;
		}
		return whereSql;
	}
	/**
	 * @return
	 * 方案二:
	 * 将数据装入list 进行遍历
	 * 第一个为where 其他为and
	 */
	public String whereCondition2(){
		String whereSql="";
		List whereConditionList = new ArrayList<>();
		if(this.title!=null&&!"".equals(this.title)){
			whereSql ="title like '%"+this.title+"%'";
			whereConditionList.add(whereSql);
		}
		if(this.positiontype!=null&&!"".equals(this.positiontype)){
			whereSql ="positiontype="+this.positiontype;
			whereConditionList.add(whereSql);
		}
		if(whereConditionList!=null){
			for (int i = 0; i < whereConditionList.size(); i++) {
				if(i==0){
					whereSql+=" where "+whereConditionList.get(i);
				}else{
					whereSql+=" and "+whereConditionList.get(i);
				}
			}
		}
		return whereSql;
	}
	/**
	 * @return
	 * 方案三:使用String方法replaceFirst("and", "where")
	 */
	public String whereCondition(){
		String whereSql="";
	
		if(this.title!=null&&!"".equals(this.title)){
			whereSql+=" and title like '%"+this.title+"%'";
			
		}
		if(this.positiontype!=null&&!"".equals(this.positiontype)&&this.positiontype!=2){
			whereSql+=" and positiontype="+this.positiontype;
			
		}
		return whereSql.replaceFirst("and", "where");
	}
	/**
	 * @return
	 * 采用一个标识符
	 */
	public String whereCondition3(){
		String whereSql="";
		Boolean isWhere=true;
		if(this.title!=null&&!"".equals(this.title)){
			if(isWhere){
				whereSql+=" where ";
				isWhere=false;
			}else{
				whereSql+=" and ";
			}
			whereSql+="like title='%"+this.title+"%'";
		}
		if(this.positiontype!=null&&!"".equals(this.positiontype)){
			if(isWhere){
				whereSql+=" where ";
				isWhere=false;
			}else{
				whereSql+=" and ";
			}
			whereSql+="positiontype="+this.positiontype;
		}
		return whereSql;
	}
	
}

4.页面静态化技术
难点;页面静态化技术
需求:每次前端在访问前台职位详情,都需要查询数据库,如果同时访问量大,那么数据库压力太大
思路:将前台职位详情的内容静态化处理(freemarker),提供模板,数据用freemarker,生成静态化页面(.html),存储在一起,当职位的详情发生改变时,将静态化内容,删除,并重新生成静态化页面(.html)
需要的jar:
freemarker.jar
join_us_details.ftl模板:

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>



	
		
		
		
		
		物流校招
		
		
		

		
		
		
	

	
		
		
		
		
		
		
	

静态化

	//增加静态化页面
	public String addHtml(HttpServletRequest req,Jobs job) throws Exception{
			//* 1.导入jar包freemarker.jar
			//* 2.创建一个配置对象 1.传递一个版本 
				Configuration cfg = new Configuration(Configuration.VERSION_2_3_28);
				
			//* 3.设置模板加载路径 
				String realPath = req.getServletContext().getRealPath("/template");
				File file=new File(realPath);
				cfg.setDirectoryForTemplateLoading(file);
			//* 4.设置一个模板编码
				cfg.setDefaultEncoding("UTF-8");
			//* 5.获取一个模板对象
				Template template = cfg.getTemplate("join_us_details.ftl");
			//* 6.获取一个数据 通过对象
			
			//7.生成文件
				String newName=UUID.randomUUID().toString()+".html";
				PrintWriter pw = new PrintWriter(new File(file,newName));
				template.process(job, pw);
				pw.close();
				//释放资源
				System.gc();
				return newName;
	}
	

controller的调用:

package cn.xxx.web;

import java.io.File;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
import java.util.UUID;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import com.sun.org.glassfish.gmbal.ParameterNames;

import cn.xxx.domain.Address;
import cn.xxx.domain.Jobs;
import cn.xxx.domain.User;
import cn.xxx.pagelist.PageList;
import cn.xxx.query.SqlCondition;
import cn.xxx.service.IAddressService;
import cn.xxx.service.IJobsService;
import cn.xxx.service.impl.AddressServiceImpl;
import freemarker.template.Configuration;
import freemarker.template.Template;

/**
 * @author peng
 *轮播管理后台
 */
@Controller
@RequestMapping("/jobs")
public class JobsController {
	
	//注入图片服务对象调用底层方法
	@Autowired
	private IJobsService jobsServiceImpl;
	@Autowired
	private IAddressService addressServiceImpl;
	//主页请求
	@RequestMapping("/main")
	public String mainJobs(){
		return "forward:/WEB-INF/views/index.jsp";
	}
	//查询数据库 返回数据库的存储图片数据的集合 显示在主页上
	/**分页查询*/
	@RequestMapping("/list")
	public String listJobs(Model model,SqlCondition sqlCondition){
		//将高级查询禁用
		sqlCondition.setTitle(null);
		sqlCondition.setPositiontype(null);
		PageList pageList = jobsServiceImpl.queryByPage(sqlCondition);
		
		model.addAttribute("pageList", pageList);
		//屏蔽视图处理器 跳转到查询主页
		return "forward:/WEB-INF/jobs/jobs.jsp";
	}
	//-1----》新增标识
	//编辑界面请求  根据id判断是进新增或者修改界面
	@RequestMapping("/add")
	public String addJobs(Model model){
		List
address = addressServiceImpl.queryAll(); model.addAttribute("address", address); return "forward:/WEB-INF/jobs/jobs_add.jsp"; } @RequestMapping("/edict/{id}") public String edictJobs(Model model,@PathVariable("id") Long id,SqlCondition sqlCondition){ Jobs job = jobsServiceImpl.queryOne(id); List
address = addressServiceImpl.queryAll(); model.addAttribute("address", address); model.addAttribute("job", job); return "forward:/WEB-INF/jobs/jobs_edict.jsp"; } //保存请求 @RequestMapping("/save") public String saveJobs(HttpServletRequest req,Jobs job,SqlCondition sqlCondition) throws Exception{ if(!"".equals(job.getId())&&job.getId()!=null){ /**修改*/ String name=jobsServiceImpl.queryOne(Long.valueOf(job.getId()+"")).getHtmlurl(); String realPath=req.getServletContext().getRealPath("/template"); File file=new File(realPath,name); file.delete(); //得到静态化界面名字 String htmlurl = this.addHtml(req,job); //将值赋值给job对象 job.setHtmlurl(htmlurl); //更新数据库 jobsServiceImpl.update(job); }else{ System.out.println(11111); /**新增*/ //得到静态化界面名字 String htmlurl = this.addHtml(req,job); //将值赋值给job对象 job.setHtmlurl(htmlurl); //添加到数据库 jobsServiceImpl.add(job); } return "forward:/jobs/list"; } //增加静态化页面 public String addHtml(HttpServletRequest req,Jobs job) throws Exception{ //* 1.导入jar包freemarker.jar //* 2.创建一个配置对象 1.传递一个版本 Configuration cfg = new Configuration(Configuration.VERSION_2_3_28); //* 3.设置模板加载路径 String realPath = req.getServletContext().getRealPath("/template"); File file=new File(realPath); cfg.setDirectoryForTemplateLoading(file); //* 4.设置一个模板编码 cfg.setDefaultEncoding("UTF-8"); //* 5.获取一个模板对象 Template template = cfg.getTemplate("join_us_details.ftl"); //* 6.获取一个数据 通过对象 //7.生成文件 String newName=UUID.randomUUID().toString()+".html"; PrintWriter pw = new PrintWriter(new File(file,newName)); template.process(job, pw); pw.close(); //释放资源 System.gc(); return newName; } //删除请求 @RequestMapping("/delete/{id}") public String deleteJobs(@PathVariable("id")Long id,HttpServletRequest req){ Jobs job=jobsServiceImpl.queryOne(id); String name=job.getHtmlurl(); String realPath=req.getServletContext().getRealPath("/template"); File file=new File(realPath,name); file.delete(); jobsServiceImpl.delete(id); return "forward:/jobs/list"; } }

5.缓存技术
难点:缓存的应用
需求:解决每次查询都需要重复去数据库查询效率低效问题
思路:使用缓存存储数据,先查询缓存中是否有数据,有用缓存的,没有查询数据库,而将新查询的数据保存到缓存中,注意删除,新增,修改都需要通过标识符重新查询的数据库
带来的问题:高级查询和普通查询共用一个查询service方法,那么高级查询之前应该将标识符改正为从数据库查询
使用的jar包:
简单的CMS项目_第1张图片
缓存工具类

package cn.itsource.myutils;

import java.util.List;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;



/**
 * @author peng
 *缓存工具
 */
public class CacheUtils {
	/**
	 * @param cacheManager
	 * @param key
	 * @param object
	 * @param cacheName
	 * 存储方法
	 */
	public static void setCacheData(CacheManager cacheManager,String key,Object object,String cacheName){
			//拿到缓存管理对象
			cacheManager = CacheManager.create();
			//准备数据
			//拿到缓存对象 存储数据
			Cache cache = cacheManager.getCache(cacheName);
			Element element=new Element(key, object);
			cache.put(element);
	}
	/**
	 * @param cacheManager
	 * @param key
	 * @param cacheName
	 * @return
	 * 取出方法
	 */
	public static Object getCacheData(CacheManager cacheManager,String key,String cacheName){
		Object elementValue=null;
		try {
			
			Cache cache = cacheManager.getCache(cacheName);
			Element element = cache.get(key);
			elementValue = element.getObjectValue();
		} catch (Exception e) {
			// TODO: handle exception
			return elementValue;
		}
		return elementValue;
	}
}

package cn.xxx.service.impl;

import java.util.List;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.xxx.dao.IJobsDao;
import cn.xxx.domain.Jobs;
import cn.xxx.myutils.CacheUtils;
import cn.xxx.pagelist.PageList;
import cn.xxx.query.SqlCondition;
import cn.xxx.service.IJobsService;
import net.sf.ehcache.CacheManager;
@Service
public class JobsServiceImpl implements IJobsService {
	/**用来判断是否需要新增缓存 默认为flase不需要缓存
	 * 新增 删除 修改 需要更新缓存
	 * 
	 * */
	private Boolean isEnableCache=false;
	@Autowired
	private IJobsDao jobsDaoImpl;
	@Override
	public List queryAll() {
		return jobsDaoImpl.queryAll();
	}
	@Override
	public void add(Jobs Jobs) {
		jobsDaoImpl.add(Jobs);
		isEnableCache=true;
	}
	@Override
	public void delete(Long id) {
		jobsDaoImpl.delete(id);
		isEnableCache=true;
	}
	@Override
	public Jobs queryOne(Long id) {
		return jobsDaoImpl.queryOne(id);
	}
	@Override
	public PageList queryByPage(SqlCondition sqlCondition) {
		PageList queryByPage = null;
		//创建缓存管理者
		CacheManager cacheManager = CacheManager.create();
		//每次不同的存入key
		String key="key_"+sqlCondition.getCurrentPage();
		//缓存名称
		String cacheName="jobsCache";
		if(sqlCondition.getTitle()!=null&&!"".equals(sqlCondition.getTitle())){
			/**
			 * 使用高级查询 将缓存重新清理
			 */
			sqlCondition.setIsEnableCache(true);
		}
		if(sqlCondition.getPositiontype()!=null&&!"".equals(sqlCondition.getPositiontype())&&sqlCondition.getPositiontype()!=2){
			sqlCondition.setIsEnableCache(true);
		}
		//静态化操作
		/**取出缓存*/
		queryByPage = (PageList) CacheUtils.getCacheData(cacheManager, key, cacheName);
		/**如果未取出数据,进行查询存储数据到缓存*/
		if(queryByPage==null||isEnableCache||sqlCondition.getIsEnableCache()){
			//如果标识符为ture表名内容已修改需要更新缓存
			
			if(isEnableCache){
				//删除原来缓存数据
				cacheManager.getCache(cacheName).removeAll();
			}
			queryByPage = jobsDaoImpl.queryByPage(sqlCondition);
			CacheUtils.setCacheData(cacheManager, key, queryByPage, cacheName);
			isEnableCache=false;
		}
		return queryByPage;
	}
	@Override
	public void update( Jobs job) {
		// TODO Auto-generated method stub
		jobsDaoImpl.update(job);
		isEnableCache=true;
	}
	@Override
	public List queryByCondition(SqlCondition sqlCondition) {
		// TODO Auto-generated method stub
		return jobsDaoImpl.queryByCondition(sqlCondition);
	}

}

6.高级查询
难点:高级查询的实现
需求:需要模糊查询职位的名称和是否全职
思路:通过多条件SQL语句进行后台查询,将SQL条件封装在分页条件中,关键处理第一个条件需要语句开始为where 而其他为and,使用String替换第一个的方法。
问题:出现缓存与高级查询不兼容问题

package cn.itsource.myutils;

import java.util.List;

import net.sf.ehcache.Cache;
import net.sf.ehcache.CacheManager;
import net.sf.ehcache.Element;



/**
 * @author peng
 *缓存工具
 */
public class CacheUtils {
	/**
	 * @param cacheManager
	 * @param key
	 * @param object
	 * @param cacheName
	 * 存储方法
	 */
	public static void setCacheData(CacheManager cacheManager,String key,Object object,String cacheName){
			//拿到缓存管理对象
			cacheManager = CacheManager.create();
			//准备数据
			//拿到缓存对象 存储数据
			Cache cache = cacheManager.getCache(cacheName);
			Element element=new Element(key, object);
			cache.put(element);
	}
	/**
	 * @param cacheManager
	 * @param key
	 * @param cacheName
	 * @return
	 * 取出方法
	 */
	public static Object getCacheData(CacheManager cacheManager,String key,String cacheName){
		Object elementValue=null;
		try {
			
			Cache cache = cacheManager.getCache(cacheName);
			Element element = cache.get(key);
			elementValue = element.getObjectValue();
		} catch (Exception e) {
			// TODO: handle exception
			return elementValue;
		}
		return elementValue;
	}
}

7.邮箱激活技术
难点:邮箱的激活
需求:用户注册后,需要激活后才能使用
思路:注册后,自动根据用户提供的邮箱地址,发送到邮箱中,点击传来UUID,将用户是否可用的字段设置为可用。

package cn.itsource.service.impl;

import java.util.Properties;
import java.util.UUID;

import javax.mail.Message;
import javax.mail.NoSuchProviderException;
import javax.mail.Session;
import javax.mail.Transport;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import cn.itsource.dao.IUserDao;
import cn.itsource.domain.User;
import cn.itsource.service.IUserService;
@Service
public class UserServiceImpl implements IUserService {
	
	@Autowired
	private IUserDao UserDaoImpl;
	@Override
	public void add(User user) throws Exception {
				//生成一个uuid
				user.setUUID((UUID.randomUUID().toString()));
				UserDaoImpl.add(user);
				
				//发送邮件
				Properties prop = new Properties();
				prop.setProperty("mail.host", "127.0.0.1");
				prop.setProperty("mail.transport.protocol", "smtp");
				prop.setProperty("mail.smtp.auth", "true");
				// 使用JavaMail发送邮件的5个步骤
				// 1、创建session
				Session session = Session.getInstance(prop);
				// 开启Session的debug模式,这样就可以查看到程序发送Email的运行状态
				session.setDebug(true);
				// 2、通过session得到transport对象
				Transport ts = session.getTransport();
				// 3、使用邮箱的用户名和密码连上邮件服务器,发送邮件时,发件人需要提交邮箱的用户名和密码给smtp服务器,用户名和密码都通过验证之后才能够正常发送邮件给收件人。
				ts.connect("127.0.0.1", "a", "a");
				// 4、创建邮件
				Message message = createSimpleMail(session,user.getUUID(),user);
				// 5、发送邮件
				ts.sendMessage(message, message.getAllRecipients());
				ts.close();
	}
	public  MimeMessage createSimpleMail(Session session,String uuid,User user) throws Exception {
		// 创建邮件对象
		MimeMessage message = new MimeMessage(session);
		// 指明邮件的发件人
		message.setFrom(new InternetAddress("[email protected]"));
		// 指明邮件的收件人,现在发件人和收件人是一样的,那就是自己给自己发
		message.setRecipient(Message.RecipientType.TO, new InternetAddress(user.getEmail()));
		// 邮件的标题
		message.setSubject("登录激活");
		// 邮件的文本内容
		message.setContent("亲爱的"+user.getUsername()+"!验证用户注册", "text/html;charset=UTF-8");
		// 返回创建好的邮件对象
		return message;
	}
	@Override
	public User queryOneUser(User user) {
		
		return UserDaoImpl.queryOneUser(user);
	}
	@Override
	public void updateUser(String UUID) {
		// TODO Auto-generated method stub
		UserDaoImpl.updateUser(UUID);
	}

}

8.Spring拦截
拦截无登录的后台访问请求
需求:用户需要有效的登录才能访问到后台管理
思路:使用Spring自带的拦截器,配置需要拦截的路径,和放行的路径,在拦截方法中,判断是否存在成功登录的session才放行
拦截器的配置



	
	
	
		
		
		
		
	
	
	
		
	
	
	
		
		
			
			
			
			
			
			
			
			
			
			
			
			
			
			
		
			
			
		
	


拦截器:

package cn.itsource.domain;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

public class MyInterceptor implements HandlerInterceptor {
	@Override
	public void afterCompletion(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, Exception arg3)
			throws Exception {
		// TODO Auto-generated method stub
		System.out.println("DispatcherServlet完结之后调用");
	}	
	@Override
	public void postHandle(HttpServletRequest arg0, HttpServletResponse arg1, Object arg2, ModelAndView arg3)
			throws Exception {
		// TODO Auto-generated method stub
		System.out.println("处理完请求之后");
	}
	@Override
	public boolean preHandle(HttpServletRequest req, HttpServletResponse resp, Object obj2) throws Exception {
		HttpSession session = req.getSession();
		Object obj = session.getAttribute("user");
		if (obj==null) {
			System.out.println("没有登录");
			//登录放行
			req.getRequestDispatcher("/login.jsp").forward(req, resp);
			
			return false;  //执行之后,不用放行
		}
		return true;
	}
	
}

登录controller

package cn.itsource.web;

import javax.servlet.http.HttpServletRequest;
import javax.swing.plaf.synth.SynthSpinnerUI;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import cn.itsource.domain.User;
import cn.itsource.service.IUserService;

@Controller
public class LoginController {
	@Autowired
	private IUserService userService;
	@RequestMapping("/login")
	public String login(HttpServletRequest req,User user){
		String randomcode = req.getParameter("random");
		/**校验验证码*/
		if(randomcode==null||"".equals(randomcode)){
			req.setAttribute("error", "亲,验证码不能为空");
			return "forward:/login.jsp";
		}
		if(!randomcode.equals(req.getSession().getAttribute("randomcode_IN_SESSION"))){
			req.setAttribute("error", "亲,验证码输入错误");
			return "forward:/login.jsp";
		}
		System.out.println(user);
		//先判断是否有此用户 无该用户返回登录页面
		//查询存在的用户在数据库的对象
		User userForLogin = userService.queryOneUser(user);
		if(userForLogin!=null&&userForLogin.getUUID()!=null&&!"".equals(userForLogin.getUUID())){
			//判断该用户有权限进行登录功能
			if(userForLogin.getIsEnable()){
				req.getSession().setAttribute("user", userForLogin);
				return "forward:/main/index";
			}
			req.setAttribute("error", "亲,你的用户还未激活,请尽快进行激活");
			return "forward:/login.jsp";
		}
		req.setAttribute("error", "亲,你的用户未注册或密码错误");
		return "forward:/login.jsp";
	}
	//注册方法,发送邮件
	@RequestMapping("/signup")
	public String signup(HttpServletRequest req,User user) throws Exception{
		userService.add(user);
		req.setAttribute("error", "亲,请尽快完成邮件注册,请先进行注册");
		return "forward:/login.jsp";
	}
	//邮件发送后 点击完成注册方法
	@RequestMapping("/update")
	public String update(HttpServletRequest req,User user){
		System.out.println(user.getUUID());
		userService.updateUser(user.getUUID());
		return "forward:/login.jsp";
	}
	
}

9.富文本编辑器
富文本编辑器的使用
需求:在添加说明时,需要更多的组件完成文本的输入
思路:使用富文本编辑器

package cn.itsource.web;

import javax.servlet.http.HttpServletRequest;
import javax.swing.plaf.synth.SynthSpinnerUI;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import cn.itsource.domain.User;
import cn.itsource.service.IUserService;

@Controller
public class LoginController {
	@Autowired
	private IUserService userService;
	@RequestMapping("/login")
	public String login(HttpServletRequest req,User user){
		String randomcode = req.getParameter("random");
		/**校验验证码*/
		if(randomcode==null||"".equals(randomcode)){
			req.setAttribute("error", "亲,验证码不能为空");
			return "forward:/login.jsp";
		}
		if(!randomcode.equals(req.getSession().getAttribute("randomcode_IN_SESSION"))){
			req.setAttribute("error", "亲,验证码输入错误");
			return "forward:/login.jsp";
		}
		System.out.println(user);
		//先判断是否有此用户 无该用户返回登录页面
		//查询存在的用户在数据库的对象
		User userForLogin = userService.queryOneUser(user);
		if(userForLogin!=null&&userForLogin.getUUID()!=null&&!"".equals(userForLogin.getUUID())){
			//判断该用户有权限进行登录功能
			if(userForLogin.getIsEnable()){
				req.getSession().setAttribute("user", userForLogin);
				return "forward:/main/index";
			}
			req.setAttribute("error", "亲,你的用户还未激活,请尽快进行激活");
			return "forward:/login.jsp";
		}
		req.setAttribute("error", "亲,你的用户未注册或密码错误");
		return "forward:/login.jsp";
	}
	//注册方法,发送邮件
	@RequestMapping("/signup")
	public String signup(HttpServletRequest req,User user) throws Exception{
		userService.add(user);
		req.setAttribute("error", "亲,请尽快完成邮件注册,请先进行注册");
		return "forward:/login.jsp";
	}
	//邮件发送后 点击完成注册方法
	@RequestMapping("/update")
	public String update(HttpServletRequest req,User user){
		System.out.println(user.getUUID());
		userService.updateUser(user.getUUID());
		return "forward:/login.jsp";
	}
	
}

三、总结

本项目多个地方值得优化,如登录和注册,地址的查询都可以换方式更简单的实现,主要锻炼了Spring框架的使用,和前后端交互的认识

你可能感兴趣的:(cms)