模块简介
要求
开发工具
IDEA2020.3 + MySQL.8.0.22 + tomcat9
静态效果展示
总结
代码还算干净,页面美观;但花的时间有点长;
代码仓库地址
项目代码笔记
数据库和JDBC代码
这部分代码还是蛮干净的
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/resume?useUnicode=true&characterEncoding=utf-8&serverTimezone=Asia/Shanghai
user=****
password=****
import java.io.IOException;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtil {
public static String driver;
public static String url;
public static String user;
public static String password;
static {
init();
}
/**
* 初始哈输入库连接配置
*/
public static void init() {
Properties properties = new Properties();
String configFile = "database.properties";
InputStream inputStream = JdbcUtil.class.getClassLoader().getResourceAsStream(configFile);
try {
properties.load(inputStream);
} catch (IOException e) {
e.printStackTrace();
}
driver = properties.getProperty("driver");
url = properties.getProperty("url");
user = properties.getProperty("user");
password = properties.getProperty("password");
}
/**
* 获取数据库连接
*
* @return connection
* @throws SQLException
*/
public static Connection getConnection() {
Connection connection = null;
try {
Class.forName(driver);
connection = DriverManager.getConnection(url, user, password);
} catch (ClassNotFoundException | SQLException e) {
e.printStackTrace();
}
return connection;
}
/**
* 关闭资源
*
* @param conn:数据库连接
* @param statement:处理对象
* @param resultSet:结果集
* @return 是否关闭成功 成功:true
*/
public static boolean closeResource(Connection conn, PreparedStatement statement, ResultSet resultSet) {
if (resultSet != null) {
try {
resultSet.close();
resultSet = null;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (statement!=null){
try {
statement.close();
statement = null;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if (conn!=null){
try {
conn.close();
conn = null;
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
return true;
}
/**
* 统一查询接口
*
* @param connection:数据库连接
* @param sql:待执行的sql
* @param params:sql中的参数
* @return ResultSet:返回的结果
* @throws SQLException
*/
public static ResultSet selectQuery(Connection connection,PreparedStatement statement,String sql,Object...params) {
ResultSet resultSet = null;
try {
// 记录引用
statement = connection.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
statement.setObject(i+1,params[i]);
}
resultSet = statement.executeQuery();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return resultSet;
}
/**
* 统一更新接口
*
* @param connection:数据库连接
* @param sql:待执行的sql
* @param params:sql中的参数
* @return int:影响的条数
* @throws SQLException
*/
public static int updateQuery(Connection connection,PreparedStatement statement,String sql,Object...params){
int result = 0;
try {
statement = connection.prepareStatement(sql);
for (int i = 0; i < params.length; i++) {
statement.setObject(i+1,params[i]);
}
result = statement.executeUpdate();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
return result;
}
}
项目结构
文件上传和接收
文件上传
<!--文件上传的表单必须添加这个属性 enctype-->
<form action="upload" method="post" enctype="multipart/form-data">
<input type="text" name="name" id="name"><br>
<input type="file" name="file" id="file"><br>
<input type="submit" value="上传">
</form>
接收代码
package cm.resume.util;
import cm.resume.entity.Resume;
import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileUploadException;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
public class RequestUtil {
public static Resume getResumeFromRequest(HttpServletRequest request) {
Resume resume = new Resume();
Class<Resume> clazz = Resume.class;
// -------------------- 设置路径 -----------------
String path = request.getServletContext().getRealPath("upload");
String tmpPath = path + "\\tmp";
// -------------------- 获取form的input 内容 -----------------
Map<String, String> propMap = new HashMap<>();
Map<String, FileItem> fileMap = new HashMap<>();
// 获取所有input的属性key : value
parseFileFrom(request, tmpPath, propMap, fileMap);
// -------------------- 设置resume的非文件属性 -----------------
propMap.forEach((key, value) -> {
try {
Method method = clazz.getDeclaredMethod(key, String.class);
method.setAccessible(true);
method.invoke(resume, value);
} catch (Exception e) {
e.printStackTrace();
}
});
// -------------------- 设置resume的文件属性 -----------------
fileMap.forEach((key, value) -> {
FileItem fileItem = fileMap.get(key);
// 上传的是文件,获得文件上传字段中的文件名
// 注意IE或FireFox中获取的文件名是不一样的,IE中是绝对路径,Chrome,FireFox中只是文件名。
String fileName = fileItem.getName();
if (fileName == null || fileName.isEmpty()) {
return;
}
String lastEx = fileName.substring(fileName.lastIndexOf("."));
fileName = UUID.randomUUID() + lastEx;
// 将FileItem对象中保存的主体内容保存到某个指定的文件中。
// 如果FileItem对象中的主体内容是保存在某个临时文件中,该方法顺利完成后,临时文件有可能会被清除
resume.setPicture(fileName);
try {
fileItem.write(getFile(path + "\\" + fileName));
} catch (Exception e) {
e.printStackTrace();
}
});
return resume;
}
public static void parseFileFrom(HttpServletRequest request, String path,
Map<String, String> propMap,
Map<String, FileItem> fileMap) {
if (request == null || propMap == null || fileMap == null) {
return;
}
DiskFileItemFactory factory = new DiskFileItemFactory();
//设置工厂的内存缓冲区大小,默认是10K
factory.setSizeThreshold(1024 * 1024 * 10);
//设置工厂的临时文件目录:当上传文件的大小大于缓冲区大小时,将使用临时文件目录缓存上传的文件
factory.setRepository(getFile(path));
//文件上传解析器
ServletFileUpload upload = new ServletFileUpload(factory);
//设置所有上传数据的最大值,单位字节long 1M
upload.setSizeMax(1024 * 1024 * 20);
//设置单个文件上传的最大值
upload.setFileSizeMax(1024 * 1024 * 10);
//设置编码格式
upload.setHeaderEncoding("UTF-8");
try {
//解析请求,将表单中每个输入项封装成一个FileItem对象
List<FileItem> itemList = upload.parseRequest(request);
String name = null;
for (FileItem item : itemList) {
// 得到input中的name属性的名
name = item.getFieldName();
if (item.isFormField()) {
// 得到input中的name属性的值
propMap.put(name, getProperty(item));
} else {
fileMap.put(name, item);
}
}
} catch (FileUploadException e) {
e.printStackTrace();
}
}
public static File getFile(String path) {
File file = new File(path);
// 先判断文件是否存在
// 如果文件不存在,创建文件然后判断是否创建文件是否成功
if (!file.exists() || !file.mkdirs()) {
try {
throw new IOException("文件创建失败");
} catch (IOException e) {
e.printStackTrace();
}
}
return file;
}
public static String getProperty(FileItem item) {
String value = null;
// 只处理普通输入项
if (item != null && item.isFormField()) {
try {
// 得到输入项中的值
value = item.getString("UTF-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
return value;
}
}
jQuery实现ajax
$.ajax({
type:"GET",
url:$("#path").val()+"/resume",
// url:"http://localhost:8888"+"/resume",
data:{method:"remove",resumeId:obj.attr("resumeId")},
dataType:"json",
success:function(data){
if(data.delResult == "true"){//删除成功:移除删除行
cancleBtn();
obj.parents("tr").remove();
}else if(data.delResult == "false"){//删除失败
alert("path->"+path + "resumeList.js 对不起,删除简历:【"+obj.attr("resumeName")+"】失败");
changeDLGContent("对不起,删除简历:【"+obj.attr("resumeName")+"】失败");
}else if(data.delResult == "notexist"){
alert("path->"+path + "resumeList.js 对不起,简历:【"+obj.attr("resumeName")+"】不存在");
changeDLGContent("对不起,简历:【"+obj.attr("resumeName")+"】不存在");
}
},
error:function(data){
alert("data.delResult->"+data.delResult);
alert("path--->"+path + "\tthis.url->"+this.url +"\t"+ "resumeList.js 对不起,删除失败");
changeDLGContent("对不起,删除失败369");
}
});
分页
sql代码
String sql = "select" + selectFieldsStr +
"from" + tableName + " " +
"order by create_time DESC limit ?,?";
java代码
public class Page {
/**
* 当前页码
*/
private int currentPageNo = 1;
/**
* 记录总数
*/
private int totalCount = 0;
/**
* 页面容量(一页展示的条数)
*/
private int pageSize = 0;
/**
* 页面总数
*/
private int totalPageCount = 1;
public Page() {
}
public Page(int currentPageNo, int totalCount, int pageSize) {
this.currentPageNo = currentPageNo;
this.pageSize = pageSize;
// 设置记录总数和页面总数
this.setTotalCount(totalCount);
}
public int getCurrentPageNo() {
return currentPageNo;
}
public void setCurrentPageNo(int currentPageNo) {
if (currentPageNo > 0) {
this.currentPageNo = currentPageNo;
}
}
public int getTotalCount() {
return totalCount;
}
public void setTotalCount(int totalCount) {
if(totalCount > 0){
this.totalCount = totalCount;
//设置总页数
this.setTotalCount(calculationPageCount(this.pageSize,this.totalCount));
}
}
public int getPageSize() {
return pageSize;
}
public void setPageSize(int pageSize) {
if (pageSize > 0) {
this.pageSize = pageSize;
}
}
public int getTotalPageCount() {
return totalPageCount;
}
public void setTotalPageCount(int totalPageCount) {
if (totalPageCount >= 0) {
this.totalPageCount = totalPageCount;
}
}
/**
* 计算分页参数: 页面总数 this.totalPageCount
*/
public static int calculationPageCount(int pageSize,int totalCount) {
if (pageSize == 0) {
return 0;
}
if (totalCount % pageSize == 0) {
return totalCount / pageSize;
}else if(totalCount % pageSize > 0){
return totalCount / pageSize + 1;
}else {
return 0;
}
}
}
Jsp代码
<div class="page-bar">
<ul class="page-num-ul clearfix">
<li>共${param.totalCount }条记录 ${param.currentPageNo }/${param.totalPageCount }页</li>
<c:if test="${param.currentPageNo > 1}">
<a href="javascript:page_nav(document.forms[0],1);">首页</a>
<a href="javascript:page_nav(document.forms[0],${param.currentPageNo-1});">上一页</a>
</c:if>
<c:if test="${param.currentPageNo < param.totalPageCount }">
<a href="javascript:page_nav(document.forms[0],${param.currentPageNo+1 });">下一页</a>
<a href="javascript:page_nav(document.forms[0],${param.totalPageCount });">最后一页</a>
</c:if>
</ul>
<span class="page-go-form"><label>跳转至</label>
<input type="text" name="inputPage" id="inputPage" class="page-key"/>页
<button type="button" class="page-btn"
onClick='jump_to(document.forms[0],document.getElementById("inputPage").value)'>GO</button>
</span>
</div>
使用selenium自动测试
谷歌浏览器selenium插件 + 谷歌浏览器驱动
测试示例(工具导出)
import org.junit.Test;
import org.junit.Before;
import org.junit.After;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.chrome.ChromeDriver;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.JavascriptExecutor;
import java.util.*;
public class TestdetailTest {
private WebDriver driver;
private Map<String, Object> vars;
JavascriptExecutor js;
@Before
public void setUp() {
System.setProperty(ConstValue.driverName, ConstValue.chromePath);
driver = new ChromeDriver();
js = (JavascriptExecutor) driver;
vars = new HashMap<String, Object>();
}
@After
public void tearDown() {
driver.quit();
}
@Test
public void testdetail() {
// Test name: test_detail
// Step # | name | target | value
// 1 | open | / |
driver.get("http://localhost:8888/");
// 2 | setWindowSize | 1366x768 |
driver.manage().window().setSize(new Dimension(1366, 768));
// 3 | click | linkText=简历管理 |
driver.findElement(By.linkText("简历管理")).click();
// 4 | click | css=tr:nth-child(2) .viewResume > img |
driver.findElement(By.cssSelector("tr:nth-child(2) .viewResume > img")).click();
// 5 | click | id=evaluation-box |
driver.findElement(By.id("evaluation-box")).click();
// 6 | click | id=evaluation |
driver.findElement(By.id("evaluation")).click();
// 7 | click | id=experience |
driver.findElement(By.id("experience")).click();
// 8 | click | id=skill |
driver.findElement(By.id("skill")).click();
// 9 | click | id=ethnic |
driver.findElement(By.id("ethnic")).click();
// 10 | click | name=picture |
driver.findElement(By.name("picture")).click();
// 11 | click | linkText=专业技能 |
driver.findElement(By.linkText("专业技能")).click();
// 12 | click | linkText=在校经历 |
driver.findElement(By.linkText("在校经历")).click();
// 13 | click | linkText=回到主页 |
driver.findElement(By.linkText("回到主页")).click();
// 14 | click | css=#submit-box > input |
driver.findElement(By.cssSelector("#submit-box > input")).click();
}
}