SpringBoot开发一个简单的网站

project:springboot-02!!!

一、模拟一个数据库

pojo.Department

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
    private Integer id;
    private String departmentName;

}

pojo.Employee

@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Integer gender;//0女1男
    private Department department;
    private Date birth;
}

dao.DepartmentDao

@Repository
public class DepartmentDao {
    //模拟数据库中的数据
    private static Map<Integer, Department> departments = null;
    static {
        //Map中的key代表数据库的索引,value代表索引对应的这一行记录
        departments = new HashMap<Integer, Department>();//创建一个部门数据表
        departments.put(101, new Department(101, "教学部"));
        departments.put(102, new Department(102, "市场部"));
        departments.put(103, new Department(103, "教研部"));
        departments.put(104, new Department(104, "运营部"));
        departments.put(105, new Department(105, "后勤部"));
    }

    //获取所有部门信息
    public Collection<Department> getDepartments(){
        return departments.values();
    }

    //通过id得到部门
    public Department getDepartmentById(Integer id){
        return departments.get(id);
    }
}

dao.EmployeeDao

@Repository
public class EmployeeDao {
    //模拟数据库中的数据
    private static Map<Integer, Employee> employees = null;

    @Autowired
    private DepartmentDao departmentDao;

    static {
        //Map中的key代表数据库的索引,value代表索引对应的这一行记录
        employees = new HashMap<Integer, Employee>();
        employees.put(1001, new Employee(1001, "AA", "392851349@qq.com", 0, new Department(101, "教学部"), new Date()));
        employees.put(1002, new Employee(1002, "BB", "378985149@qq.com", 0, new Department(102, "市场部"), new Date()));
        employees.put(1003, new Employee(1003, "CC", "848695349@qq.com", 1, new Department(103, "教研部"), new Date()));
        employees.put(1004, new Employee(1004, "DD", "648695349@qq.com", 1, new Department(104, "运营部"), new Date()));
        employees.put(1005, new Employee(1005, "EE", "248695349@qq.com", 1, new Department(105, "后勤部"), new Date()));
    }

    //主键自增
    private static Integer initId = 1006;

    //增加一个员工
    public void addEmployee(Employee employee){
        if(employee.getId()==null){
            employee.setId(initId++);
        }
        employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
        employees.put(employee.getId(), employee);
    }

    //查询全部员工
    public Collection<Employee> getAll(){
        return employees.values();
    }

    //通过id查询员工
    public Employee getEmployeeById(Integer id){
        return employees.get(id);
    }

    //通过id删除员工
    public void delete(Integer id){
        employees.remove(id);
    }
}

@Repository注解 

@repository跟@Service,@Compent,@Controller这4种注解是没什么本质区别,都是声明作用,取不同的名字只是为了更好区分各自的功能.

@Repository

@Repository

  • 对DepartmentDao注解上@Repository后,   
  • @Autowire
  • private DepartmentDao departmentDao;
  • 就可以这样自动注入了,而不需要new对象

二、导入静态资源

 具体资源在java后端开发文件的狂神springboot静态资源目录

  • 注意这里面的html放在templates下,css、js、图片放在static下

 三、首页


controller层:

//首页
@Controller
public class IndexController {
    @RequestMapping({"/", "/index", "/index.html"})
    //不管是访问"/", "/index", "/index.html"中的哪一个,都可以跳转到index.html页面
    public String index(){
        return "index";
    }
}
  • 在这里终于搞明白了return的作用,也就是说当请求localhost:8080/index的时候,视图控制器跳转到resources/templates/index.html。
  • return的字符串其实就是templates下的文件名

结果:

  •  但是这里的index首页中一些css样式和图片没有加进来,下一步就要把html文件中添加css、img
  • 通过thymeleaf模板

扩展MVC

除了通过上述方式跳转到主页index.html以外,还可以通过自己定义的方式去扩展mvc

config.MyMvcConfig类:

@Configuration
//@EnableWebMvc
public class MyMvcConfig implements WebMvcConfigurer{
    @Override
    public void addViewControllers(ViewControllerRegistry registry){
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");
    }
}
  • @Configuration表示是一个配置类
  • 不能加上@EnableWebMvc,加上后表示springboot 的所有默认配置都失效了,但是我们只想更改部分默认配置
  • registry.addViewController("/").setViewName("index");//访问根目录时,跳转到index.html文件

        但是这里的index首页中一些css样式和图片没有加载进来,下一步就要把html文件中添加css、img。

        通过thymeleaf模板引擎来在html加载静态资源

1、在html中添加thymeleaf命名空间

<html lang="en" xmlns:th="http://www.thymeleaf.org">

2、 更改html中的超链接:

<link href="asserts/css/bootstrap.min.css" rel="stylesheet">
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">

3、......

        在application.properties中关闭模板引擎的缓存,否则更改的html会因为缓存而无效

spring.thymeleaf.cache=false

四、首页的国际化

1、首先在File-setting-Editor-File Encodings中确保都是utf8,否则会乱码

2、在resources下新建一个i18n文件夹(全称:internationalization,i和n之间有18个字母)

      新建三个名为:login.properties、login_en_US.properties、login_zh_CN.properties的配置文件,springboot会根据文件名自动识别其功能,并把它们自动合并在Resource Bundle 'login'

 login.properties(默认)

login.tip=请登录
login.password=密码
login.remember=记住我
login.username=用户名
login.btn=登录

login_en_US.properties(英文)

login.tip=Please sign in
login.password=password
login.remember=remember me
login.username=user name
login.btn=sign in

login_zh_CN.properties(中文)

login.tip=请登录
login.password=密码
login.remember=记住我
login.username=用户名
login.btn=登录

3、application.properties中添加配置:

#国际化的配置文件所在地
spring.messages.basename=i18n.login

4、修改对应的index.html文件

 ...............省略下面过程

五、首页的登录功能

功能描述:

        首页输入用户名和密码,如果正确,则页面跳转,如果不正确,那么提示密码错误。


首先来看首页提交的表单部分:

  •  form标签的action属性表示表单提交后处理表单的url地址,用thymeleaf改写前这个form是这样的:但是这样只是静态的,无法和服务器交互
    <form class="form-signin" action="dashboard.html">

    注意thymeleaf中url用@{}来表示,改写后表示表单提交后,                                                    将跳转到localhost:8080/user/login请求

  • 要实现登录功能,input标签必须有name属性,这个name属性不仅仅是别名的作用,还作为可与服务器交互数据的HTML元素的服务器端的标示,比如input、select、textarea、和button的name属性都有这个功能。我们可以在服务器端根据其Name通过@RequestParam取得元素提交的值


controller层:

在controller文件夹下新建一个类:

@Controller
public class LoginController {
    @RequestMapping("/user/login")
    public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model){
        //获取到提交的表单的username、password后,进行具体的业务(这里是验证用户名密码是否正确业务)
        if(username!=null && password.equals("123456")){
            return "dashboard";//登录成功,跳转到dashboard.html页面
        }else{
            //显示登陆失败
            //通过model向页面传递值
            model.addAttribute("msg","用户名密码错误!");
            return "index";
        }
    }
}
  • 这里要结合index.html的表单部分来看
  • form表单提交后,<form class="form-signin" th:action="@{/user/login}">,这个表示会产生一个localhost:8080/user/login的请求,从而这个请求会被上面这个controller层的login方法接收。(action="@{/user/login}和@RequestMapping("/user/login")相互对应)这里你也可以仔细品味一下@RequestMapping,翻译为中文就是请求映射,把一个请求映射到另外一个路径上
  • @RequestParam("username") String username 就表示把name="username"的标签的内容(这里指的是input标签中,我们从网页输入的内容)获取到,并且就是这个String username
  • Model model的作用是向前端页面传递值。
    model.addAttribute("msg","用户名密码错误!");//即向下面这个p标签的msg传递值
  • <p th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>
  • p标签的作用是,如果msg不为空(即msg为用户名密码错误!),就将这个p标签显示出来,否则不显示(用户名密码正确)。
  • 用户名密码错误时:

  •  用户名密码正确时:

  •  但是注意上面这个url:

http://localhost:8080/user/login?username=ZY2002529&password=123456

  • 这个url并不是这个页面的地址:localhost:8080/dashboard.html
  • 而是一个请求(从“?username=ZY2002529&password=123456”这些参数就可以很明显看出来)
  • 因此就要介绍一下请求转发和重定向的区别了!

@RequestMapping注解所在的方法

  1. 如果是return " ****** "; 那么是请求转发
  2. 如果是return " redirect:/ ***** "; 是重定向

总结重定向和请求转发的区别



  • 请求转发:直接调用跳转的页面,让它返回,但是对于浏览器来说,地址栏不变,地址栏仍然是请求


  • 请求重定向:重定向是发一个302的状态码给浏览器,浏览器自己去请求跳转的网页,url会改变,request数据不带到重定向的方法中

 重定向的方式进入dashboard.html页面

  •  上述方式是错误的,会404,找不到dashboard.html,没搞明白为啥!????????

  • 必须在扩展MVC配置类中加入这个才能正确跳转到dashboard.html 

六、登录拦截器 

        登录拦截器的作用就是必须在你登录了以后才可以进入到http://localhost:8080/dashboard,否则你直接输入这个网址也可以进入。


首先需要使用session

@Controller
public class LoginController {
    @RequestMapping("/user/login")
    public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model, HttpSession session){
        //获取到提交的表单的username、password后,进行具体的业务(这里是验证用户名密码是否正确业务)
        if(username!=null && password.equals("123456")){
            session.setAttribute("loginUser", username);
            return "redirect:/dashboard";//登录成功,跳转到dashboard.html页面
        }else{
            //显示登陆失败
            //通过model向页面传递值
            model.addAttribute("msg","用户名密码错误!");
            return "index";
        }
    }
}
  • 在login方法参数中加入 HttpSession session参数
  • 当验证密码通过后,给session赋值: session.setAttribute("loginUser", username); 也就是给别名为loginUser的Session赋值为username

什么是session和cookie?

Session:记录一系列状态

        Session与cookie功能效果相同。Session与Cookie的区别在于Session是记录在服务端的,而Cookie是记录在客户端的。

解释session:当访问服务器否个网页的时候,会在服务器端的内存里开辟一块内存,这块内存就叫做session,而这个内存是跟浏览器关联在一起的。这个浏览器指的是浏览器窗口,或者是浏览器的子窗口,意思就是,只允许当前这个session对应的浏览器访问,就算是在同一个机器上新启的浏览器也是无法访问的。而另外一个浏览器也需要记录session的话,就会再开启一个属于自己的session

原理:HTTP协议是非连接性的,取完当前浏览器的内容,然后关闭浏览器后,链接就断开了,而没有任何机制去记录取出后的信息。而当需要访问同一个网站的另外一个页面时(就好比如在第一个页面选择购买的商品后,跳转到第二个页面去进行付款)这个时候取出来的信息,就读不出来了。所以必须要有一种机制让页面知道原来页面的session内容。

问题:如何知道浏览器和这个服务器中的session是一一对应的呢?又如何保证不会去访问其它的session呢?

原理解答:就是当访问一个页面的时候给浏览器创建一个独一无二的号码,也给同时创建的session赋予同样的号码。这样就可以在打开同一个网站的第二个页面时获取到第一个页面中session保留下来的对应信息(理解:当访问第二个页面时将号码同时传递到第二个页面。找到对应的session。)。这个号码也叫sessionID,session的ID号码,session的独一无二号码。
————————————————
版权声明:本文为CSDN博主「广小白」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/weixin_42217767/article/details/92760353


在config包下新建一个类:LoginHandlerInterceptor

public class LoginHandlerInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        //登录成功后,应该会有用户的session
        Object loginUser = request.getSession().getAttribute("loginUser");//获取别名为loginUser的session中存储的内容
        if(loginUser==null){
            request.setAttribute("msg", "没有权限,请先登录");//给msg赋值,这个msg是index.html中的参数
            request.getRequestDispatcher("/index.html").forward(request, response);
            return false;
        }else{
            return true;
        }
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}
  • 在SpringMVC 或者SpringBoot中定义一个拦截器是比较非常简单,主要有两种方式:
    • 1、类要实现Spring 的HandlerInterceptor 接口
    • 2、类继承实现了HandlerInterceptor 接口的类,例如 已经提供的实现了HandlerInterceptor 接口的抽象类HandlerInterceptorAdapter
  • HandlerInterceptor:Handler(处理程序)Interceptor(拦截器),即拦截器处理程序
  • HandlerInterceptor的三个方法

    • preHandle:该方法将在请求处理之前进行调用,只有该方法返回true,才会继续执行后续的Interceptor和Controller,当返回值为true 时就会继续调用下一个Interceptor的preHandle 方法,如果已经是最后一个Interceptor的时候就会是调用当前请求的Controller方法;预处理,可以进行编码、安全控制、权限校验等处理;(上面红字的“请求”就是,/user/login请求,即前端的表单提交后发送的一个请求,“请求处理之前”也就是被LoginController的login方法返回页面return处理之前)

    • postHandle:在业务处理器处理请求执行完成后,生成视图之前执行。

    • afterCompletion:在DispatcherServlet完全处理完请求后被调用,可用于清理资源等。返回处理(已经渲染了页面);

  • request.getRequestDispatcher是请求转发的意思,具体可以看下面这个博客,写的很好请求重定向、请求转发、请求包含的特点与区别详解


接下来需要在主配置文件MyMvcConfig中进行配置
 

@Configuration
//@EnableWebMvc
public class MyMvcConfig implements WebMvcConfigurer{//实现WebMvcConfigurer接口的类会被springboot认为是配置类
    @Override
    public void addViewControllers(ViewControllerRegistry registry){
        registry.addViewController("/").setViewName("index");
        registry.addViewController("/index").setViewName("index");
        registry.addViewController("/index.html").setViewName("index");

        registry.addViewController("/dashboard.html").setViewName("dashboard");
        registry.addViewController("/dashboard").setViewName("dashboard");
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").excludePathPatterns("/index", "/", "/user/login", "/css/**", "/img/**", "/js/**");
    }
}
  • 添加一个拦截器,把LoginHandlerInterceptor类的对象传参进去
  • addPathPatterns("/**"):表示拦截所有请求
  • excludePathPatterns("/index", "/", "/user/login", "/css/**", "/img/**", "/js/**"):表示排除以下请求

添加拦截器成功后,如果不登录,直接访问http://localhost:8080/dashboard就会:

登录成功后,会发现请求中多了一个sessionId

 如果你登录后把页面关闭了,再去直接访问http://localhost:8080/dashboard的时候会发现竟然也可以直接打开,是因为session的存在。如果把浏览器所有历史记录包括cookie和session删除,就不能直接打开了。

 七、展示员工列表

 对应的dashboard.html:

	<li class="nav-item">
	    <a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
			员工管理
	    </a>
    </li>
  • href对应的是点击后跳转的地址连接(其实是一个请求),这个请求被Controller接收

修改为:

	<li class="nav-item">
	    <a class="nav-link" th:href="@{/employee}">
			员工管理
	    </a>
    </li>

对应controller层 

@Controller
public class EmployeeController {
    @Autowired
    EmployeeDao employeeDao;

    @RequestMapping("/employee")
    public String selectAllEmployees(Model model){
        Collection<Employee> AllEmployees = employeeDao.getAll();
        model.addAttribute("emps", AllEmployees);
        return "/employee/list";
    }
}
  • 点击员工管理,controller接收/employee请求,利用model向前端传递值,返回/employee/list页面

未修改前的/employee/list:

 修改/employee/list.html

				<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
					<h2>Section title</h2>
					<div class="table-responsive">
						<table class="table table-striped table-sm">
							<thead><!--thead是table head-->
								<tr>
									<th>id</th>
									<th>lastname</th>
									<th>email</th>
									<th>gender</th>
									<th>department</th>
									<th>birth</th>
								</tr>
							</thead>
							<tbody><!--table 部分-->
								<tr th:each="emp:${emps}">
									<td th:text="${emp.getId()}"></td>
									<td th:text="${emp.getLastName()}"></td>
									<td th:text="${emp.getEmail()}"></td>
									<td th:text="${emp.getGender()}"></td>
									<td th:text="${emp.getDepartment().getDepartmentName()}"></td>
									<td th:text="${emp.getBirth()}"></td>
								</tr>
							</tbody>
						</table>
					</div>
				</main>
  • <tr th:each="emp:${emps}">中的emps是EmployeeController中的model.addAttribute("emps", AllEmployees); each代表对集合emps进行遍历,emp是emps的别名

 上面的页面的问题是1、gender,应该把01改为男女 2、后面应该添加对员工的删除操作

							<thead><!--thead是table head-->
								<tr>
									<th>id</th>
									<th>lastname</th>
									<th>email</th>
									<th>gender</th>
									<th>department</th>
									<th>birth</th>
									<th>操作</th>
								</tr>
							</thead>
							<tbody><!--table 部分-->
								<tr th:each="emp:${emps}">
									<td th:text="${emp.getId()}"></td>
									<td th:text="${emp.getLastName()}"></td>
									<td th:text="${emp.getEmail()}"></td>
									<td th:text="${emp.getGender()==0?'女':'男'}"></td>
									<td th:text="${emp.getDepartment().getDepartmentName()}"></td>
									<td th:text="${emp.getBirth()}"></td>
									<td>
										<button class="btn btn-sm btn-primary">编辑</button>
										<button class="btn btn-sm btn-danger">删除</button>
									</td>
								</tr>
							</tbody>

修改后:

 这一节还有几个小问题我没弄:

  1. 菜单栏点击高亮
  2. 员工birth的Date日期形式转化 
  3. 不同页面公共的部分应该抽取出来,便于后期维护

八、添加员工

在员工表单的顶部设置 添加员工 按钮

<h2>
	<a class="btn btn-sm btn-success" th:href="@{/add}">添加员工</a>
</h2>
  • class="btn btn-sm btn-success"  是按钮的样式
  • 点击后发送请求: /add

点击添加员工按钮后我们应该跳转到一个添加员工的页面/employee/add.html

    @GetMapping("/add")
    public String addEmployee(){
        return "/employee/add";
    }
  • @GetMapping和@RequestMapping的区别是前者只能处理get请求,                                    同理还有@PostMapping 
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
            <form class="form-horizontal">
                <div class="form-group">
                    <label>id</label>
                    <div class="col-sm-10">
                        <input class="form-control" name="id" placeholder="1000">
                    </div>
                </div>
                <div class="form-group">
                    <label>lastName</label>
                    <div class="col-sm-10">
                        <input class="form-control" name="lastName" placeholder="韩">
                    </div>
                </div>
                <div class="form-group">
                    <label>email</label>
                    <div class="col-sm-10">
                        <input class="form-control" name="email" placeholder="392851349@qq.com">
                    </div>
                </div>
                <label>gender</label>
                <select class="form-control" name="gender">
                    <option>男</option>
                    <option>女</option>
                </select>

                <label>department</label>
                <select class="form-control" name="department">
                    <option>1</option>
                    <option>2</option>
                    <option>3</option>
                    <option>4</option>
                    <option>5</option>
                </select>

                <div class="form-group">
                    <label>birth</label>
                    <div class="col-sm-10">
                        <input class="form-control" name="birth" placeholder="1998/09/22">
                    </div>
                </div>

                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-10">
                        <button type="submit" class="btn btn-default">添加</button>
                    </div>
                </div>
            </form>
        </main>

 接下来有一个问题是,添加员工时,在部门下拉框那我们需要知道有哪些部门,因为部门是可以动态添加删除的

改写:

    @Autowired
    DepartmentDao departmentDao;
    @GetMapping("/add")
    public String addEmployee(Model model){
        //先需要查询出来所有的部门名称
        Collection<Department> departments = departmentDao.getDepartments();
        model.addAttribute("departments", departments);
        return "/employee/add";
    }
<label>department</label>
<select class="form-control" name="department">
     <option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
</select>
  • text属性是这个下拉框的option标签显示出来的内容
  • value属性时这个下拉框的option标签所携带的值,
  • 设置value的目的是因为创建员工,为其添加对应的部门时,需要知道部门id才可以查询到部门对象
  • 通过thymeleaf的th:each来遍历后端传递过来的departments


  •  给表单加上action
<form class="form-horizontal" th:action="@{/add}" method="post">
  • form的method是post,所以 controller层用:@PostMapping("/add"),表示只能接收到post方式的/add请求
    @PostMapping("/add")
    public String add(Employee employee){
        //添加员工的操作
        employeeDao.addEmployee(employee);
        System.out.println(employee);
        //表单提交后,需要重定向到展示员工列表的页面
        return "redirect:/employee";//重定向到/employee请求,即selectAllEmployees这个方法
    }
  • 提交表单后,先调用后台的添加员工方法,再重定向(重新查询一下全部员工列表) 

但是前端表单里输入的信息,springboot是怎么与employee对象的各个属性一一对应的?

add.html中表单部分:

        <main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
            <form class="form-horizontal" th:action="@{/add}" method="post">

                <div class="form-group">
                    <label>lastName</label>
                    <div class="col-sm-10">
                        <input class="form-control" name="lastName" placeholder="韩">
                    </div>
                </div>
                <div class="form-group">
                    <label>email</label>
                    <div class="col-sm-10">
                        <input class="form-control" name="email" placeholder="392851349@qq.com">
                    </div>
                </div>
                <label>gender</label>
                <select class="form-control" name="gender">
                    <option th:value="1">男</option>
                    <option th:value="0">女</option>
                </select>

                <label>department</label>
                <select class="form-control" name="department.id">
                    <option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
                </select>

                <div class="form-group">
                    <label>birth</label>
                    <div class="col-sm-10">
                        <input class="form-control" name="birth" placeholder="1998/09/22">
                    </div>
                </div>

                <div class="form-group">
                    <div class="col-sm-offset-2 col-sm-10">
                        <button type="submit" class="btn btn-default">添加</button>
                    </div>
                </div>
            </form>
        </main>
  • name属性功不可没, 表单中的name属性要与实体类的属性名称一致。这样springboot就能将表单的input标签的输入与实体类的属性一一对应。
  • 在Springboot进行数据封装时,提交到后台的数据都是String型,对于基本的数据类型会自动转化
  • 实体类employee的字段必须包含表单中所有input的name对应的值,并且在类中为对应的字段生成getter 与setter方法
  • id不需要在前端填写,因为设置了自增
  • placeholder属性表示框内提示的信息

下面重点说一说两个<select>下拉框中选择的内容是怎么与对象employee的属性一一匹配的

<select class="form-control" name="gender">
     <option th:value="1">男</option>
     <option th:value="0">女</option>
</select>
  •  会把所选中的<option>中的value值与对应对象的gender属性匹配
<select class="form-control" name="department.id">
      <option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>
</select>
  • 会把  th:value="${department.getId()}"  中的值传递给  name="department.id"  ,注意这里department的id属性虽然是private,但是也必须这么写,不能写成 department.getId();
public class Employee {
    private Integer id;
    private String lastName;
    private String email;
    private Integer gender;//0女1男
    private Department department;
    private Date birth;
}

        观察employee类就看出来,如果表单上输入与employee属性一一对应的话,上面表单里应该提交的是Department department,那为什么提交的是department.id。

        纠正一个上面的错误观点,不是要求表单上输入与employee属性一一对应,而是表单上输入与name属性的内容一一对应。

再来看下面:

    @PostMapping("/add")
    public String add(Employee employee){
        //添加员工的操作
        employeeDao.addEmployee(employee);
        System.out.println(employee);
        //表单提交后,需要重定向到展示员工列表的页面
        return "redirect:/employee";//重定向到/employee请求,即selectAllEmployees这个方法
    }
  • 注意add方法有个Employee employee参数, 这个对象就是前端传过来的

employeeDao的addEmployee方法: 

    //增加一个员工
    public void addEmployee(Employee employee){
        if(employee.getId()==null){
            employee.setId(initId++);
        }
        employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
        employees.put(employee.getId(), employee);
    }
  • 从表单上获取的department.id被传入到addEmployee方法中去
    employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));

 九、修改员工信息

功能:点击对应员工的 编辑 按钮,进入编辑员工界面


list.html: 

    //跳转到编辑员工信息界面
    @RequestMapping("/edit")
    public String ToEdit(){
        return "/employee/edit";
    }

 

 但是在controller跳转到edit.html页面之前,必须先要获取到这个被编辑的员工的信息,展示到edit.html中。


 所以上面的代码需要调整为:

list.html:

  •  向后台发送请求的同时,发送当前待编辑对象的id参数,这样的话后台就可以根据employee的id,把这个employee的信息查询出来,显示到编辑页面edit.html
    //跳转到编辑员工信息界面
    @GetMapping("/edit/{id}")
    public String ToEdit(@PathVariable("id")Integer id, Model model){
        //编辑员工的页面要先展示这个员工的信息
        Employee employee = employeeDao.getEmployeeById(id);
        model.addAttribute("emp", employee);
        Collection<Department> departments = departmentDao.getDepartments();
        model.addAttribute("departments", departments);
        return "/employee/edit";
    }
  •   @GetMapping("/edit/{id}")中的{id}是占位符,与th:href="@{/edit/}+${emp.id}"中的emp.id对应
  • @PathVariable("id")Integer id是在获取前端数据,Model是给前端传数据

  • @PathVariable的使用参考:参考1  参考2   参考3

  • model.addAttribute("emp", employee);和model.addAttribute("departments", departments);中的"emp"、"departments"都是只在return的html页面内可以获取到的,其他页面获取不到

edit.html

  • <input>中的th:value="${emp.getLastName()}"表示:框内显示的默认值
  • <option>中的 th:selected="${表达式}",表示如果表达式为true,那么就默认选中该下拉框

 


剩下就是提交该表单的业务了

<form class="form-horizontal" th:action="@{/edit}" method="post">
  •  提交表单,发送 /edit 请求
  • 该请求被下面controller接收
    //员工信息修改提交
    @RequestMapping("/edit")
    public String edit(Employee employee){
        employeeDao.addEmployee(employee);
        return "redirect:/employee";
    }
  • 添加该员工,再重定向到/employee请求 

但是上面的edit方法有一个问题是提交的员工不是修改原来的,而是增加了一个新的员工:

 解决办法:

在表单中加上上id的标签

<div class="col-sm-10">
      <input type="hidden" th:value="${emp.getId()}" class="form-control" name="id">
</div>

 并设置隐藏,用户不能修改,这样的话新添加的employee就会覆盖原来的(从employeeDao中能看出来为啥这样可以覆盖原来的)


十、删除员工

list.html 

    //删除员工
    @RequestMapping("/delete/{id}")
    public String delete(@PathVariable("id")Integer id){
        employeeDao.delete(id);
        return "redirect:/employee";
    }

不多赘述

十一、404页面

 springboot中,只需要在templates文件下添加一个名为404.html的页面,在404的时候会自动定位到这个页面并显示。

  • 11
    点赞
  • 60
    收藏
    觉得还不错? 一键收藏
  • 5
    评论
好的,我可以为你生成一个简单的商店订单功能的Spring Boot代码。以下是一些步骤: 1. 首先,你需要创建一个Spring Boot项目。你可以使用Spring Initializr来创建一个基本的Spring Boot项目,添加Web和JPA依赖。 2. 在创建好的项目中,你需要创建一个实体类来表示订单。可以在src/main/java目录下创建一个名为Order的类,代码如下: ``` @Entity @Table(name = "orders") public class Order { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String customerName; @OneToMany(mappedBy = "order") private List<OrderItem> orderItems; // getters and setters } ``` 这个实体类表示了一个订单,其中包含了订单ID、客户姓名和订单项等属性。订单项是另一个实体类,后面会提到。 3. 接下来,你需要创建一个OrderItem类,表示订单中的一个条目。可以在src/main/java目录下创建一个名为OrderItem的类,代码如下: ``` @Entity @Table(name = "order_items") public class OrderItem { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private String productName; @Column(nullable = false) private BigDecimal price; @ManyToOne @JoinColumn(name = "order_id") private Order order; // getters and setters } ``` 这个实体类表示了一个订单中的一个条目,包含了商品名称、价格和所属订单等属性。其中,`@ManyToOne`注解表示一个订单条目对应一个订单,多个订单条目可以对应同一个订单。 4. 接下来,你需要创建一个OrderRepository类来操作订单和订单项。可以在src/main/java目录下创建一个名为OrderRepository的接口,代码如下: ``` public interface OrderRepository extends JpaRepository<Order, Long> { } ``` 这个接口继承了Spring Data JPA提供的`JpaRepository`接口,可以用来对订单进行增删改查操作。 5. 最后,你可以创建一个OrderController类来处理订单相关的请求。可以在src/main/java目录下创建一个名为OrderController的类,代码如下: ``` @RestController @RequestMapping("/orders") public class OrderController { @Autowired private OrderRepository orderRepository; @PostMapping public Order createOrder(@RequestBody Order order) { return orderRepository.save(order); } @GetMapping("/{id}") public Order getOrder(@PathVariable Long id) { return orderRepository.findById(id) .orElseThrow(() -> new RuntimeException("Order not found")); } @GetMapping public List<Order> getAllOrders() { return orderRepository.findAll(); } } ``` 这个控制器类包含了三个方法,分别对应创建订单、获取订单和获取所有订单的请求。其中,`@PostMapping`和`@GetMapping`注解分别表示POST和GET请求,`@RequestBody`注解表示请求体中的JSON数据会被转换成Order对象,`@PathVariable`注解表示请求路径中的参数会绑定到方法的参数上。 以上是一个简单的商店订单功能的Spring Boot代码,你可以根据需要进行修改和扩展。希望可以帮助到你!
评论 5
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值