文章内容输出来源:拉勾教育Java高薪训练营
目标
项目的主要目标是,将SSS三个主流框架进行整合,并实现简单的用户登录,展示简历的列表、添加、编辑、删除的功能
SSS = Spring + Spring JPA + Spring MVC
整体思路
分为以下三个步骤:
1、整合Spring+Spring JPA
2、整合Spring MVC
3、用户登录示例
4、简历的CRUD功能示例
实现过程
数据库准备
- 创建一个MYSQL数据库
db_test
,创建一张简历表tb_resume
DROP DATABASE IF EXISTS db_test;
CREATE DATABASE db_test DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
CREATE TABLE `tb_resume`(
`id` int(10) NOT NULL AUTO_INCREMENT COMMENT '用户ID',
`name` varchar(200) NOT NULL COMMENT '名称',
`address` varchar(200) COMMENT '地址',
`phone` varchar(200) COMMENT '手机',
PRIMARY KEY(`id`)
)ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_bin;
创建项目
- 在IDEA上创建一个空项目,命名为
sss_demo
- 创建相关包
- 实体层:
com.yyh.sss.entity
- 数据访问层:
com.yyh.sss.dao
- 业务层:
com.yyh.sss.service
- 控制层:
com.yyh.sss.controller
- 工具类:
com.yyh.sss.utils
Spring与Spring JPA的整合
- 修改pom.xml,引入依赖
- 引入单元测试
- 引入数据库相关:连接池、数据库驱动
- 引入Spring相关:beans、context、tx、orm等
- 引入hibernate相关
- 引入jpa相关
<!--测试相关-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.2.2.RELEASE</version>
<scope>test</scope>
</dependency>
<!--数据库相关-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.13</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.47</version>
</dependency>
<!--Spring相关-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.9.5</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<!--spring对orm的支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-orm</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<!--jpa相关-->
<dependency>
<groupId>org.springframework.data</groupId>
<artifactId>spring-data-jpa</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.glassfish.web</groupId>
<artifactId>javax.el</artifactId>
<version>2.2.4</version>
<exclusions>
<exclusion>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>javax.el</groupId>
<artifactId>javax.el-api</artifactId>
<version>2.2.4</version>
</dependency>
<!--hibernate相关-->
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-core</artifactId>
<version>5.4.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-entitymanager</artifactId>
<version>5.4.0.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-validator</artifactId>
<version>5.4.0.Final</version>
</dependency>
- 创建jdbc.properties配置文件
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/db_test
jdbc.username=root
jdbc.password=root
- 创建
spring-dao.xml
配置文件
- 配置数据源
- 配置
entityManager
- 配置jpa的dao实现细节
jpa:repositories
- 配置事务
transactionManager
<beans>
<context:property-placeholder location="classpath:jdbc.properties"/>
<bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
<!--配置数据源-->
<property name="dataSource" ref="dataSource"/>
<!--配置包扫描-->
<property name="packagesToScan" value="com.yyh.sss.entity"/>
<!--指定jpa的具体实现hibernate-->
<property name="persistenceProvider">
<bean class="org.hibernate.jpa.HibernatePersistenceProvider"></bean>
</property>
<!--jpa方言配置-->
<property name="jpaDialect">
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaDialect"></bean>
</property>
<!--配置具体provider,hibearnte框架的执行细节-->
<property name="jpaVendorAdapter" >
<bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
<!--配置数据表是否自动创建-->
<property name="generateDdl" value="false"/>
<!--指定数据库的类型 -->
<property name="database" value="MYSQL"/>
<!--配置数据库的方言-->
<property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect"/>
<!--是否显示sql-->
<property name="showSql" value="true"/>
</bean>
</property>
</bean>
<!--配置jpa的dao层-->
<jpa:repositories base-package="com.yyh.sss.dao" entity-manager-factory-ref="entityManagerFactory"
transaction-manager-ref="transactionManager"/>
<!--事务管理器配置-->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
</beans>
- 创建
spring-service.xml
- 配置业务接口的扫描
- 开启事务注解
<beans>
<context:component-scan base-package="com.yyh.sss.service"/>
<!--开启事务注解-->
<tx:annotation-driven/>
</beans>
- 创建entity类
@Id
@Entity
@Table(name = "tb_resume")
public class ResumeEntity {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(name = "id")
private Long id;
@Column(name = "name")
private String name;
@Column(name = "address")
private String address;
@Column(name = "phone")
private String phone;
//ignore setter/getter
}
- 编写dao接口
public interface ResumeDao extends JpaRepository<ResumeEntity, Long>, JpaSpecificationExecutor<ResumeEntity> {
}
- 创建service接口,添加增、删、改、查方法
public interface ResumeService {
void add(ResumeEntity resume);
void update(ResumeEntity resume);
List<ResumeEntity> list();
void remove(Long id);
}
- 创建service接口的实现类
@Service
public class ResumeServiceImpl implements ResumeService {
@Autowired
private ResumeDao resumeDao;
public void add(ResumeEntity resume) {
resumeDao.save(resume);
}
public void update(ResumeEntity resume) {
resumeDao.save(resume);
}
public List<ResumeEntity> list() {
return resumeDao.findAll();
}
public void remove(Long id) {
Optional<ResumeEntity> resume = resumeDao.findById(id);
if(!resume.isPresent()) {
return;
}
resumeDao.deleteById(id);
}
}
- 创建单元测试进行测试
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"classpath:spring-dao.xml",
"classpath:spring-service.xml"})
public class ResumeServiceTest {
@Autowired
private ResumeService resumeService;
@Test
public void testAdd() {
ResumeEntity entity = new ResumeEntity();
entity.setName("李小清");
entity.setAddress("福建省厦门市集美区软件园");
entity.setPhone("18095847334");
resumeService.add(entity);
}
@Test
public void testUpdate() {
ResumeEntity entity = new ResumeEntity();
entity.setId(1L);
entity.setName("李小青");
entity.setAddress("福建省厦门市集美区软件园三期");
entity.setPhone("18095847354");
resumeService.add(entity);
}
@Test
public void testList() {
List<ResumeEntity> list = resumeService.list();
if(null != list) {
for (ResumeEntity row : list) {
System.out.println(row.toString());
}
}
}
@Test
public void testRemove() {
resumeService.remove(1L);
}
}
- 以上就整合完成了Spring+Spring JPA。跑下单元测试,验证下效果
整合Spring MVC
- 在pom.xml中添加相关依赖
- Spring MVC
- jsp和servlet相关
- jstl表达式
<!--SpringMVC-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.2.RELEASE</version>
</dependency>
<!--jsp-api&servlet-api-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
<scope>provided</scope>
</dependency>
<!--页面使用jstl表达式-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<dependency>
<groupId>taglibs</groupId>
<artifactId>standard</artifactId>
<version>1.1.2</version>
</dependency>
- 添加mvc的配置文件
spring-web.xml
- 配置controller的扫描包
- 开启mvc相关Bean注册的配置
- 配置视图解析器
<beans>
<context:component-scan base-package="com.yyh.sss.controller"/>
<mvc:annotation-driven/>
<!--配置springmvc的视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
</beans>
- 在src/main目录下创建webapp目录,接着在webapp目录下创建WEB-INF目录,在WEB-INF中创建jsp目录
- 在WEB-INF目录下创建web.xml文件,用于配置spring的监听器和mvc的DisptcherServlet
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-*.xml</param-value>
</context-param>
<!--spring框架启动-->
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!--springmvc启动-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:spring-web.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 在jsp目录下创建
login.jsp
页面,页面中添加上用户登录的form表单
<%@ page language="java" pageEncoding="UTF-8" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE html>
<html>
<body>
<h2>用户登录</h2>
<form>
<div>
<b>用户名:</b><input type="text" placeholder="请输入用户名"/>
</div>
<div>
<b>密码:</b><input type="password" placeholder="请输入用户密码"/>
</div>
<div>
<input type="button" value="登录"/>
</div>
</form>
</body>
</html>
- 在webapp目录下创建
index.jsp
默认页面,在此页面中设置跳转到登录页面
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Document</title>
<!--马上重定向到home-->
<meta http-equiv="Refresh" content="0;url=login" />
</head>
<body>
</body>
</html>
- 创建LoginController,添加上登录页面的handler方法
@Controller
public class LoginController {
@GetMapping("/login")
public String loginPage() {
return "login";
}
}
- 在pom.xml中添加tomcat的插件(或者直接在IDEA配置外部的tomcat进行访问)
<plugin>
<groupId>org.apache.tomcat.maven</groupId>
<artifactId>tomcat7-maven-plugin</artifactId>
<version>2.2</version>
<configuration>
<port>8080</port>
<path>/</path>
</configuration>
</plugin>
- 在IDEA的Maven Projects中对应项目的Plugins中启动tomcat【tomcat7/tomcat7:run】
- 启动完毕后,在浏览器访问
http://localhost:8080
,如果跳转到了登录页面,则表示整合成功
实现用户登录功能
- 用户名和密码直接硬编码,当输入用户名为admin,密码为admin时,则登录成功,否则失败
- 统一各个请求方法的返回数据,添加
Res
类
public class Res {
//是否成功
private Boolean success;
//返回的数据
private Object data;
//成功请求
public static Res ok(Object data) {
Res res = new Res();
res.success = true;
res.data = data;
return res;
}
//失败请求
public static Res fail(Object data) {
Res res = new Res();
res.success = false;
res.data = data;
return res;
}
//ignore getter/setter
}
- 在LoginController中添加
login
方法。同时添加了一个LoginVO
表示登录的传入参数实体
- 当登录成功,则在session中记录下登录用户的名称
@PostMapping("/login")
@ResponseBody
public Res login(HttpServletRequest request, @RequestBody LoginVO loginVO) {
if(null == loginVO.getName() || loginVO.getName().trim().length() == 0) {
return Res.fail("用户名不能为空");
}
if(null == loginVO.getPassword() || loginVO.getPassword().trim().length() == 0) {
return Res.fail("密码不能为空");
}
if(!loginVO.getName().equals("admin") || !loginVO.getPassword().equals("admin")) {
return Res.fail("用户名或密码错误");
}
request.getSession().setAttribute("loginUser", "admin");
return Res.ok("登录成功");
}
public class LoginVO {
//用户名
private String name;
//密码
private String password;
//ignore getter/setter
}
- 添加登录拦截器
LoginInterceptor
- 在请求处理前进行判断session中是否有相关记录,如果没有则跳转到登录页面
public class LoginInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
Object loginUser = request.getSession().getAttribute("loginUser");
if(null != loginUser) {
return true;
}
response.sendRedirect("/login");
return false;
}
}
- 在
spring-web.xml
中配置上拦截器,排除掉login的路由
<!--配置拦截器-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/login"/>
<bean class="com.yyh.sss.utils.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
- 修改LoginController的
loginPage
方法
- 增加上以下判断,如果用户已经登录了则直接跳转到简历列表
Object loginUser = request.getSession().getAttribute("loginUser");
if(null != loginUser) {
return "redirect:resumes/list";
}
- 修改
login.jsp
,实现登录的交互
<html>
<body>
<h2>用户登录</h2>
<div>
<div>
<b>用户名:</b><input id="name" type="text" placeholder="请输入用户名"/>
</div>
<div>
<b>密码:</b><input id="password" type="password" placeholder="请输入用户密码"/>
</div>
<div>
<input id="btnLogin" type="button" value="登录"/>
</div>
</div>
</body>
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script type="text/javascript">
$('#btnLogin').on('click', function() {
var name = $('#name').val().trim();
var password = $('#password').val().trim();
if(name.length == 0) {
alert('请输入用户名');
return;
}
if(password.length == 0) {
alert('请输入密码');
return;
}
var params = {
'name': name,
'password': password
};
$.ajax({
type: 'post',
url: 'login',
data: JSON.stringify(params),
contentType: 'application/json;charset=utf-8',
dataType: 'json',
success: function(result) {
if(result.success) {
window.location.href = 'resumes/list';
}else {
alert(result.data);
}
}
});
});
</script>
</html>
简历的CRUD功能实现
- 添加ResumeController的简历Controller类
- 添加resume_list.jsp页面
- 在ResumeController中添加
listPage
跳转页面、save
保存、remove
删除方法
@Controller
@RequestMapping("resumes")
public class ResumeController {
@Autowired
private ResumeService resumeService;
@GetMapping("/list")
public String list(Model model) {
List<ResumeEntity> list = resumeService.list();
model.addAttribute("resumes", list);
return "resume_list";
}
@PostMapping("/save")
@ResponseBody
public Res save(@RequestBody ResumeEntity resume) {
if(null != resume.getId()) {
resumeService.update(resume);
}else {
resumeService.add(resume);
}
return Res.ok("保存成功");
}
@DeleteMapping("/{id}/remove")
@ResponseBody
public Res remove(@PathVariable Long id) {
resumeService.remove(id);
return Res.ok("删除成功");
}
}
- 在resume_list.jsp页面中对简历数据进行迭代,使用table进行展示数据,对每行数据添加一栏编辑、删除操作,并在列表上方添加上新增简历按钮
<%@ page language="java" pageEncoding="UTF-8" %>
<%@page isELIgnored="false" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%
String path = request.getContextPath();
String basePath = request.getScheme()+"://"+request.getServerName()+":"+request.getServerPort()+path+"/";
%>
<!DOCTYPE html>
<html>
<style type="text/css">
table{border-collapse: collapse;text-align: center;}
table td, table th{border: 1px solid #cad9ea;color: #666;height: 30px;}
table thead th{background-color: #CCE8EB;width: 100px;}
table tr:nth-child(odd){background: #fff;}
table tr:nth-child(even) {background: #F5FAFA;}
</style>
<body>
<h2>简历列表</h2>
<div><input id="btnAdd" type="button" value="新增简历"/></div>
<hr/>
<div id="resumeForm" style="display: none;">
<input id="id" type="hidden" value=""/>
<p><b>用户名称:</b><input id="name" type="text" placeholder="请输入名称" width="300px"/></p>
<p><b>用户地址:</b><input id="address" type="text" placeholder="请输入地址" width="300px"/></p>
<p><b>用户手机:</b><input id="phone" type="text" placeholder="请输入手机" width="300px"/></p>
<p><input id="btnSave" type="button" value="保存"/> <input id="btnCancel" type="button" value="取消"/></p>
<hr/>
</div>
<div>
<table>
<thead>
<tr>
<td>用户ID</td>
<td>用户名称</td>
<td>用户地址</td>
<td>用户手机</td>
<td>操作</td>
</tr>
</thead>
<tbody>
<c:forEach var="row" items="${resumes}">
<tr>
<td>${row.id}</td>
<td>${row.name}</td>
<td>${row.address}</td>
<td>${row.phone}</td>
<td>
<a class="btnEdit" href="javascript:void(0);">编辑</a>
<a class="btnDelete" href="javascript:void(0);" data-id="${row.id}">删除</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</body>
</html>
- 在resume_list.jsp页面中添加上js的交互操作,实现简历表单的展示/隐藏、保存以及删除操作的ajax请求
<script src="http://libs.baidu.com/jquery/2.0.0/jquery.min.js"></script>
<script type="text/javascript">
$('#btnAdd').on('click', function () {
$('#resumeForm').css('display', "block");
});
$('#btnCancel').on('click', function () {
$('#resumeForm').css('display', "none");
});
$('#btnSave').on('click', function() {
var name = $('#name').val().trim();
var phone = $('#phone').val().trim();
var address = $('#address').val().trim();
var id = $('#id').val().trim();
if(name.length == 0) {
alert('请输入名称');
return;
}
if(address.length == 0) {
alert('请输入地址');
return;
}
if(phone.length == 0) {
alert('请输入手机');
return;
}
var params = {
'name': name,
'address': address,
'phone': phone,
'id': id
};
$.ajax({
type: 'post',
url: '/resumes/save',
data: JSON.stringify(params),
contentType: 'application/json;charset=utf-8',
dataType: 'json',
success: function(result) {
if(result.success) {
window.location.reload();
}else {
alert(result.data);
}
}
});
});
$('.btnEdit').on('click', function() {
var $tds = $(this).parents("tr").find('td');
console.log($tds);
$('#id').val($tds.eq(0).text());
$('#name').val($tds.eq(1).text());
$('#address').val($tds.eq(2).text());
$('#phone').val($tds.eq(3).text());
$('#resumeForm').css('display', "block");
});
$('.btnDelete').on('click', function() {
var id = $(this).data('id');
if(id > 0) {
if(confirm('是否确认删除?')) {
$.ajax({
type: 'delete',
url: '/resumes/'+id+'/remove',
data: {},
success: function(result) {
if(result.success) {
window.location.reload();
}else {
alert(result.data);
}
}
})
}
}
});
</script>
6.演示的效果
-
登录页面
-
简历列表