该项目源码来自B站编程不良人,项目为员工管理系统。数据库十分简单,只有登录用户表和员工表。本篇目的在于一步步分析项目及其源码,快速带领大家走进springboot项目的完成,从上至下学习知识点,可达到事半功倍的效果。
项目源码提取在文末!!!
本项目解读涉及复习到的的技术栈包含:html、thymeleaf、springboot、mybatis、mysql、spring、javaWeb(session)
前言所涉及知识点较为基础全面,基础好的小伙伴可直接跳过或者略读。
spring的一大特点便是面向注解开放,将建立的类注册为bean交给spring容器管理其的使用调度,所以在复习springboot知识点前,先复习各个注解对应的应用。
@Component
:通用的注解,可标注任意类为 Spring
组件。如果一个 Bean 不知道属于哪个层,可以使用@Component
注解标注。@Repository
: 对应持久层即 Dao 层,主要用于数据库相关操作。@Service
: 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。@Controller
: 对应 Spring MVC 控制层,主要用户接受用户请求并调用 Service 层返回数据给前端页面。解析:我们一般使用 @Autowired
注解让 Spring 容器帮我们自动装配 bean。要想把类标识成可用于 @Autowired
注解自动装配的 bean 的类,可以采用以上注解实现。
@value
(常用)使用 @Value("${property}")
读取比较简单的配置信息:
@Value("${wuhan2020}")
String wuhan2020;
@ConfigurationProperties
(常用)通过@ConfigurationProperties
读取配置信息并与 bean 绑定。
@Component
@ConfigurationProperties(prefix = "library")
static class Book {
String name;
String description;
}
你可以像使用普通的 Spring bean 一样,将其注入到类中使用。
PropertySource
(不常用)@PropertySource
读取指定 properties 文件
@Component
@PropertySource("classpath:website.properties")
class WebSite {
@Value("${url}")
private String url;
}
@RestController
@RestController
注解是@Controller和
@ResponseBody
的合集,表示这是个控制器 bean,并且是将函数的返回值直接填入HTTP响应体中,是REST风格的控制器。
现在都是前后端分离,说实话我已经很久没有用过@Controller
。如果你的项目太老了的话,就当我没说。
单独使用 @Controller
不加 @ResponseBody
的话一般使用在要返回一个视图的情况,这种情况属于比较传统的 Spring MVC 的应用,对应于前后端不分离的情况。@Controller
+@ResponseBody
返回 JSON 或 XML 形式数据
即
@Controller
返回一个页面
@RestController
返回json或xml形式数据
@Controller
+@ResponseBody
返回JSON 或 XML 形式数据
@SpringBootApplication
我们可以把 @SpringBootApplication
看作是 @Configuration
、@EnableAutoConfiguration
、@ComponentScan
注解的集合。
根据 SpringBoot 官网,这三个注解的作用分别是:
@EnableAutoConfiguration
:启用 SpringBoot 的自动配置机制@ComponentScan
:扫描被@Component
(@Service
,@Controller
)注解的 bean,注解默认会扫描该类所在的包下所有的类。@Configuration
:允许在 Spring 上下文中注册额外的 bean 或导入其他配置类@Autowired
自动导入对象到类中,被注入进的类同样要被 Spring 容器管理比如:Service 类注入到 Controller 类中。
我们一般使用 @Autowired
注解让 Spring 容器帮我们自动装配 bean。要想把类标识成可用于 @Autowired
注解自动装配的 bean 的类,可以采用以下注解实现:
@Component
:通用的注解,可标注任意类为 Spring
组件。如果一个 Bean 不知道属于哪个层,可以使用@Component
注解标注。@Repository
: 对应持久层即 Dao 层,主要用于数据库相关操作。@Service
: 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。@Controller
: 对应 Spring MVC 控制层,主要用户接受用户请求并调用 Service 层返回数据给前端页面。@Mapper
:为dao接口添加注释
@MapperScan
:为xxxApplication启动类指定Mapper映射文件的位置,若Mapper存储在src/main/resources/mapper/xxxMapper.xml则不需要加该注解
基本
<html>…html> 定义 HTML 文档
<head>…head> 文档的信息
<meta> HTML 文档的元信息
<title>…title> 文档的标题
<link> 文档与外部资源的关系
<style>…style> 文档的样式信息
<body>…body> 可见的页面内容
注释
文本
<h1>...h1> 标题字大小(h1~h6)
<b>...b> 粗体字
<strong>...strong> 粗体字(强调)
<i>...i> 斜体字
<em>...em> 斜体字(强调)
<center>…center> 居中文本
<ul>…ul> 无序列表
<ol>…ol> 有序列表
<li>…li> 列表项目
<a href=”…”>…a> 超链接
<font> 定义文本字体尺寸、颜色、大小
<sub> 下标
<sup> 上标
<br> 换行
<p> 段落
图形
<img src=’”…”> 定义图像
<hr> 水平线
<del> 加删除线
表格
<table>…table> 定义表格
<th>…th> 定义表格中的表头单元格
<tr>…tr> 定义表格中的行
<td>…td> 定义表格中的单元
其它
<form>…form> 定义供用户输入的 HTML 表单
<frame> 定义框架集的窗口或框架
要实现Controller返回数据给页面,Spring MVC 提供了以下几种途径(只列举常见的):
方式一:通过org.springframework.web.servlet.ModelAndView实现
@RequestMapping(value="/view/{userId}/use/ModelAndView", method=RequestMethod.GET)
private ModelAndView getUserInfo(@PathVariable("userId") Integer userId){
User user = userService.getUserById(userId);
return new ModelAndView("userinfo", "user", user);
}
ModelAndView:将视图和数据封装成ModelAndView对象,作为方法的返回值,数据最终会存到HttpServletRequest对象中
方式二:通过org.springframework.ui.Model实现
@RequestMapping(value="/view/{userId}/use/Model", method=RequestMethod.GET)
private String getUserInfo(@PathVariable("userId") Integer userId, Model model){
User user = userService.getUserById(userId);
model.addAttribute("user", user);
return "userinfo";
}
Model对象:通过给方法添加引用Model对象入参,直接通过addAttribute往Model对象添加属性值。
方式三:直接将数据存到HttpSession,让页面可以获取
@RequestMapping(value="/view/{userId}/use/HttpSession", method=RequestMethod.GET)
private String getUserInfo(@PathVariable("userId") Integer userId, HttpSession session){
User user = userService.getUserById(userId);
session.setAttribute("user", user);
return "userinfo";
}
Session对象:通过给方法添加引用Session对象入参,直接通过setAttribute往Session对象添加属性值。
Cookie 可以让服务端程序跟踪每个客户端的访问,但是每次客户端的访问都必须传回这些 Cookie,如果 Cookie 很多,这无形地增加了客户端与服务端的数据传输量,而 Session 的出现正是为了解决这个问题。
同一个客户端每次和服务端交互时,不需要每次都传回所有的 Cookie 值,而是只要传回一个 ID,这个 ID 是客户端第一次访问服务器的时候生成的,而且每个客户端是唯一的。这样每个客户端就有了一个唯一的 ID,客户端只要传回这个 ID 就行了,这个 ID 通常是 NANE 为 JSESIONID 的一个 Cookie。
当访问服务器否个网页的时候,会在服务器端的内存里开辟一块内存,这块内存就叫做session,而这个内存是跟浏览器关联在一起的。这个浏览器指的是浏览器窗口,或者是浏览器的子窗口,意思就是,只允许当前这个session对应的浏览器访问,就算是在同一个机器上新启的浏览器也是无法访问的。而另外一个浏览器也需要记录session的话,就会再启一个属于自己的session。
Session可实现共享数据,会话之间的数据不能共享
服务器会给每一个用户(浏览器)创建一个Session,一个Session独占一个浏览器,一个Session对应一个ID,所以需要创建一个Cookie对象,并设置其有效时间。这样当用户关闭浏览器,重新打开浏览器访问该页面时,服务器也能找到之前为用户创建的Session对象。
//新建Session
//用户第1次访问的时候,自动创建会话
//获取Session的创建时间
session.getCreationTime();
//获取Session
HttpSession session = req.getSession();
//给Session储存信息
session.setAttribute("name","pl");//将值与名称关联后存储到当前的HttpSession的对象中
//获取Session的ID
String id = session.getId();
//从HttpSession对象获取Session的信息
String name =(String) session.getAttribute("name");
System.out.println(name);
//返回客户端上一次发送Session请求的时间
session.getLastAccessedTime();
//删除HttpSession对象对应的信息
session.removeAttribute("name");
//强制注销session
session.invalidate();
关键字 | 功能介绍 | 案例 | |||
---|---|---|---|---|---|
th:id | 替换id |
|
|||
th:text | 文本替换 |
|
|||
th:utext | 支持html的文本替换 |
|
|||
th:object | 替换对象 |
|
|||
th:value | 属性赋值 |
|
|||
th:with | 变量赋值运算 |
|
|||
th:style | 设置样式 | th:style="'display:' + @{(${sitrue} ? 'none' : 'inline-block')} + ''" |
|||
th:onclick | 点击事件 | th:onclick="'getCollect()'" |
|||
th:each | 属性赋值 | tr th:each="user,userStat:${users}"> |
|||
th:if | 判断条件 |
|
|||
th:unless | 和th:if判断相反 | Login |
|||
th:href | 链接地址 | Login /> |
|||
th:switch | 多路选择 配合th:case 使用 |
|
|||
th:case | th:switch的一个分支 |
|
|||
th:fragment | 布局标签,定义一个代码片段,方便其它地方引用 |
|
|||
th:include | 布局标签,替换内容到引入的文件 | /> |
|||
th:replace | 布局标签,替换整个标签到引入的文件 |
|
|||
th:selected | selected选择框 选中 | th:selected="(${xxx.id} == ${configObj.dd})" |
|||
th:src | 图片类地址引入 |
|
|||
th:inline | 定义js脚本可以使用变量 |
|
|||
th:action | 表单提交的地址 |
|
|||
th:remove | 删除某个属性 |
|
@{}的使用:用于获取链接地址。
${}的使用:和常规的使用差不多,用于字符串的替换或对象获取或属性绑定等,具体作用根据标签而定
@{}和${}的结合使用:在a标签的href中直接直接写对应值会导致解析失败
@{/admin/xxx/{id}(input(id=$(type))}
一共两个数据库,分别是用户和员工,其中的字段如下,就不必多说啦。
CREATE TABLE `user` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`username` VARCHAR(40) DEFAULT NULL COMMENT '用户名',
`realname` VARCHAR(60) DEFAULT NULL COMMENT '真实姓名',
`password` VARCHAR(40) DEFAULT NULL COMMENT '密码',
`gender` TINYINT(1) UNSIGNED DEFAULT NULL COMMENT '性别',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
CREATE TABLE `employee` (
`id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(60) DEFAULT NULL COMMENT '员工姓名',
`salary` DOUBLE(10,2) DEFAULT NULL COMMENT '员工工资',
`birthday` DATETIME DEFAULT NULL COMMENT '员工生日',
`photo` VARCHAR(200) DEFAULT NULL COMMENT '头像路径',
PRIMARY KEY (`id`)
) ENGINE=INNODB DEFAULT CHARSET=utf8mb4;
整合Mybatis要添加mysql-connecter-java和mybatis-spring-boot-starter的依赖
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<version>5.1.38version>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.1.4version>
dependency>
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Employee {
private Integer id;
private String name;
private Double salary;
private Date birthday;
private String photo;//头像路径
}
===========================================
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private Integer id;
private String username;
private String realname;
private String password;
private Boolean gender;
}
这里使用了偷懒插件lombok
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
<version>1.18.10version>
dependency>
使用@Data是对应建立getter、setter、tostring方法,剩下两个分别是无参构造器(@NoArgsConstructor)和有参构造器(@AllArgsConstructor)
一个数据库表对应一个操作
public interface EmployeeDao {
//员工列表
List<Employee> lists();
//保存员工信息
void save(Employee employee);
//根据id查询一个
Employee findById(Integer id);
//更新员工信息
void update(Employee employee);
//删除员工信息
void delete(Integer id);
}
public interface UserDao {
//根据用户查询用户
User findByUserName(String username);
//保存用户信息
void save(User user);
}
注释解释:
@Repository和@Controller、@Service、@Component的作用差不多,都是把对象交给spring管理。@Repository用在持久层的接口上,这个注解是将接口的一个实现类交给spring管理。
为什么有时候我们不用@Repository来注解接口,我们照样可以注入到这个接口的实现类呢?
1、spring配置文件中配置了MapperScannerConfigurer这个bean,它会扫描持久层接口创建实现类并交给spring管理。
2、接口上使用了@Mapper注解或者springboot中主类上使用了@MapperScan注解,和MapperScannerConfigurer作用一样。
注:所以Dao层可以不使用@Repository注解,虽然idea会报警告,提示找不到这个bean,直接忽略即可。
文件放置在resource目录下,这样springboot会自动扫描配置映射文件
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.baizhi.dao.EmployeeDao">
<select id="lists" resultType="Employee">
select id,name,salary,birthday,photo from `employee`
select>
<insert id="save" parameterType="Employee" useGeneratedKeys="true" keyProperty="id">
insert into `employee` values (#{id},#{name},#{salary},#{birthday},#{photo})
insert>
<select id="findById" parameterType="Integer" resultType="Employee">
select id,name,salary,birthday,photo from `employee`
where id = #{id}
select>
<update id="update" parameterType="Employee" >
update `employee` set name=#{name},salary=#{salary},birthday=#{birthday},photo=#{photo}
where id = #{id}
update>
<delete id="delete" parameterType="Integer">
delete from `employee` where id = #{id}
delete>
mapper>
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.baizhi.dao.UserDao">
<select id="findByUserName" parameterType="String" resultType="User">
select id,username,realname,password,gender from `user`
where username = #{username}
select>
<insert id="save" parameterType="User" useGeneratedKeys="true" keyProperty="id">
insert into `user` values(#{id},#{username},#{realname},#{password},#{gender})
insert>
mapper>
1、@Mapper注解:
作用:在接口类上添加了@Mapper,在编译之后会生成相应的接口实现类
添加位置:接口类上面
如果想要每个接口都要变成实现类,那么需要在每个接口类上加上@Mapper注解,比较麻烦,解决这个问题用@MapperScan
2、@MapperScan
作用:指定要变成实现类的接口(即上述所创建的Dao)所在的包,然后包下面的所有接口在编译之后都会生成相应的实现类
添加位置:是在Springboot启动类上面添加,
@SpringBootApplication
@MapperScan("com.plord.demo.dao")
public class EmsThymeleafApplication {
public static void main(String[] args) {
SpringApplication.run(EmsThymeleafApplication.class, args);
}
}
在整合Mybatis时,我们书写了sql语句的调用接口和对应的Mapper文件。
但在实际开发中,我们不能在controller控制层去直接调用Dao接口,应在service层中去实现Dao接口对应的操作。因为在具体工程中,service承担大部分功能,还包含除开CURD的操作,所以整合完Mybatis,可以先将对应的数据库操作方法在service中实例化。
public interface EmployeeService {
//员工列表方法
List<Employee> lists();
//保存员工信息
void save(Employee employee);
//根据id查询一个
Employee findById(Integer id);
//更新员工信息
void update(Employee employee);
//删除员工信息
void delete(Integer id);
}
public interface UserService {
//注册用户
void register(User user);
//用户登录
User login(String username, String password);
}
impl实现其service接口,并置于Dao实现类,以便后期实现各种功能。此处impl包含功能具体实现将在后面对应项目实现模块逐步进行实现讲解。
@Service
@Transactional
public class EmployeeServiceImpl implements EmployeeService{
private EmployeeDao employeeDao;
@Autowired
public EmployeeServiceImpl(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
}
@Service
@Transactional
public class UserServiceImpl implements UserService{
private UserDao userDao;
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
}
注解记忆强化:
@Autowired
自动导入对象到类中,被注入进的类同样要被 Spring 容器管理比如此处:Dao实现类注入到service类中。
@Service
:
对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。
@Transactional
是声明式事务管理编程中使用的注解
添加位置:
1)接口实现类或接口实现方法上,而不是接口类中。
2)访问权限:public 的方法才起作用。@Transactional 注解应该只被应用到 public 方法上,这是由 Spring AOP 的本质决定的。
系统设计:将标签放置在需要进行事务管理的方法上,而不是放在所有接口实现类上:只读的接口就不需要事务管理,由于配置了@Transactional就需要AOP拦截及事务的处理,可能影响系统性能,所以此处不应该对service进行事务管理,要具体到特定的方法不然影响性能。
用户注册需要从前端提交表单申请到后台进行处理,然后将数据提交至数据库中。所以流程为:
从登录页面跳转:
<a href="/register">还没有账号,立即注册a>
注册页面(简化保留关键html代码):
<form th:action="@{/user/register}" method="post">
<table cellpadding="0" cellspacing="0" border="0"
class="form_table">
<input type="text" class="inputgri" name="username" />
<input type="text" class="inputgri" name="realname" />
<input type="password" class="inputgri" name="password" />
男
<input type="radio" class="inputgri" name="gender" value="1" checked="checked"/>
女
<input type="radio" class="inputgri" name="gender" value="0"/>
<input type="submit" class="button" value="立即注册 »" />
table>
form>
对应的表单如上,三个输入栏,一个双选栏
action属性的作用
form标签中有一个action属性,该属性的作用是:
提交表单后,将表单数据发送到指定位置“@{/user/register}”,即传到对应controller中映射的(user/register)方法中去。
@Controller
@RequestMapping("/user")
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
/**
* 用户注册
*/
@RequestMapping("/register")
public String register(User user){
log.debug("用户名: {},真实姓名: {},密码: {},性别: {},",user.getUsername(),user.getRealname(),user.getPassword(),user.getGender());
try {
//注册用户
userService.register(user);
} catch (RuntimeException e) {
e.printStackTrace();
return "redirect:/register"; //注册失败回到注册
}
return "redirect:/login"; //注册成功跳转到登录
}
}
redirect:/xxx
代表重定向,即跳转到指定xxx页面public class UserServiceImpl implements UserService{
private UserDao userDao;
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override
public void register(User user) {
//1.根据用户名查询数据库中是否存在改用户
User userDB = userDao.findByUserName(user.getUsername());
//2.判断用户是否存在
if(!ObjectUtils.isEmpty(userDB)) throw new RuntimeException("当前用户名已被注册!");
//3.注册用户&明文的密码加密 特点: 相同字符串多次使用md5就行加密 加密结果始终是一致
String newPassword = DigestUtils.md5DigestAsHex(user.getPassword().getBytes(StandardCharsets.UTF_8));
user.setPassword(newPassword);
userDao.save(user);
}
}
用户登录与注册原理相似,简单说。
<form th:action="@{/user/login}" method="post">
<table cellpadding="0" cellspacing="0" border="0" class="form_table">
<p>
用户名:
<input type="text" class="inputgri" name="username" />
p>
<p>
密码:
<input type="password" class="inputgri" name="password" />
p>
<p>
<input type="submit" class="button" value="点我登录 »" />
p>
<p>
<a href="/register">还没有账号,立即注册a>
p>
table>
form>
如上图所示,两个输入栏和一个登录按钮(表单提交按钮)
@Controller
@RequestMapping("user")
public class UserController {
private static final Logger log = LoggerFactory.getLogger(UserController.class);
private UserService userService;
@Autowired
public UserController(UserService userService) {
this.userService = userService;
}
/**
* 用户登录
*/
@RequestMapping("login")
public String login(String username,String password,HttpSession session){
log.debug("本次登录用户名: {}",username);
log.debug("本地登录密码: {}",password);
try {
//1.调用业务层进行登录
User user = userService.login(username,password);
//2.保存用户信息
session.setAttribute("user",user);
} catch (Exception e) {
e.printStackTrace();
return "redirect:/login";//登录失败回到登录界面
}
return "redirect:/employee/lists";//登录成功之后,跳转到查询所有员工信息控制器路径
}
}
@Service
@Transactional
public class UserServiceImpl implements UserService{
private UserDao userDao;
@Autowired
public UserServiceImpl(UserDao userDao) {
this.userDao = userDao;
}
@Override//xiaochen 123 ====> xiaochen xxed
public User login(String username, String password) {
//1.根据用户名查询用户
User user = userDao.findByUserName(username);
if(ObjectUtils.isEmpty(user)) throw new RuntimeException("用户名不正确!");
//2.比较密码
String passwordSecret = DigestUtils.md5DigestAsHex(password.getBytes(StandardCharsets.UTF_8));
if(!user.getPassword().equals(passwordSecret)) throw new RuntimeException("密码输入错误!");
return user;
}
}
要获取员工列表信息要经过如下逻辑步骤:
@Controller
@RequestMapping("employee")
public class EmployeeController {
private static final Logger log = LoggerFactory.getLogger(EmployeeController.class);
private EmployeeService employeeService;
@Value("${photo.file.dir}")
private String realpath;
@Autowired
public EmployeeController(EmployeeService employeeService) {
this.employeeService = employeeService;
}
/**
* 员工列表
**/
@RequestMapping("lists")
public String lists(Model model) {
log.debug("查询所有员工信息");
List<Employee> employeeList = employeeService.lists();
model.addAttribute("employeeList", employeeList);
return "emplist";
}
}
@Service
@Transactional
public class EmployeeServiceImpl implements EmployeeService{
private EmployeeDao employeeDao;
@Autowired
public EmployeeServiceImpl(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
@Override
public List<Employee> lists() {
return employeeDao.lists();
}
}
欢迎
<span th:if="${session.user!=null}" th:text="${session.user.username}">span>
<span th:if="${session.user==null}" >游客span>!
<table class="table">
<tr class="table_header">
<td>
编号
td>
<td>
姓名
td>
<td>
头像
td>
<td>
工资
td>
<td>
生日
td>
<td>
操作
td>
tr>
<tr th:each="employee,state:${employeeList}" th:class="${state.odd?'row1':'row2'}">
<td>
<span th:text="${employee.id}">span>
td>
<td>
<span th:text="${employee.name}">span>
td>
<td>
<img th:src="@{/}+${employee.photo}" width="60">
td>
<td>
<span th:text="${employee.salary}">span>
td>
<td>
<span th:text="${#dates.format(employee.birthday,'yyyy/MM/dd')}">span>
td>
<td>
<a href="javascript:;" th:onclick="'deleteEmployee('+${employee.id}+')'">删除a>
<a th:href="@{/employee/detail(id=${employee.id})}">更新a>
td>
tr>
${}
来获取model中的变量th:each="employee,state:${employeeList}"
代表从模板上下文获取employeeList变量,用employee作为装载个体<form th:action="@{/employee/save}" method="post" enctype="multipart/form-data">
<table cellpadding="0" cellspacing="0" border="0" class="form_table">
<tr>
<td valign="middle" align="right">
姓名:
td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="name" />
td>
tr>
<tr>
<td valign="middle" align="right">
头像:
td>
<td valign="middle" align="left">
<input type="file" width="" name="img" />
td>
tr>
<tr>
<td valign="middle" align="right">
工资:
td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="salary" />
td>
tr>
<tr>
<td valign="middle" align="right">
生日:
td>
<td valign="middle" align="left">
<input type="text" class="inputgri" name="birthday" />
td>
tr>
table>
<p>
<input type="submit" class="button" value="确认添加" />
<input type="submit" class="button" value="返回列表" />
p>
form>
@Controller
@RequestMapping("employee")
public class EmployeeController {
private static final Logger log = LoggerFactory.getLogger(EmployeeController.class);
private EmployeeService employeeService;
@Value("${photo.file.dir}")
private String realpath;
@Autowired
public EmployeeController(EmployeeService employeeService) {
this.employeeService = employeeService;
}
//上传头像方法
private String uploadPhoto(MultipartFile img, String originalFilename) throws IOException {
String fileNamePrefix = new SimpleDateFormat("yyyyMMddHHmmssSSS").format(new Date());
String fileNameSuffix = originalFilename.substring(originalFilename.lastIndexOf("."));
String newFileName = fileNamePrefix + fileNameSuffix;
img.transferTo(new File(realpath, newFileName));
return newFileName;
}
/**
* 保存员工信息
* 文件上传: 1.表单提交方式必须是post 2.表单enctype属性必须为 multipart/form-data
* @return
*/
@RequestMapping("save")
public String save(Employee employee, MultipartFile img) throws IOException {
log.debug("姓名:{}, 薪资:{}, 生日:{} ", employee.getName(), employee.getSalary(), employee.getBirthday());
String originalFilename = img.getOriginalFilename();
log.debug("头像名称: {}", originalFilename);
log.debug("头像大小: {}", img.getSize());
log.debug("上传的路径: {}", realpath);
//1.处理头像的上传&修改文件名称
String newFileName = uploadPhoto(img, originalFilename);
//2.保存员工信息
employee.setPhoto(newFileName);//保存头像名字
employeeService.save(employee);
return "redirect:/employee/lists";//保存成功跳转到列表页面
}
}
@Service
@Transactional
public class EmployeeServiceImpl implements EmployeeService{
private EmployeeDao employeeDao;
@Autowired
public EmployeeServiceImpl(EmployeeDao employeeDao) {
this.employeeDao = employeeDao;
}
@Override
public void save(Employee employee) {
employeeDao.save(employee);
}
}
后续的员工更新与删除无非就是表单提交到controller,controller调用service的方法,然后service类中的dao方法被调用达成数据库的连接,执行mysql数据库的更新操作。这也是CRUD的基本操作,所以到这里就结束啦。想要源码的朋友就自行提取(源码来自编程不良人):
链接:https://pan.baidu.com/s/1aCOl6ZtX09kDv5sfsXEKKA
提取码:KKKK