SpringBoot实现员工增删查改及登录功能

0 源代码

GitHub Page

1 默认访问首页功能

当不提交任何请求,即localhost:8080/时默认访问login.html

方法:
(1)重写webMvcConfigurerAdapter,在里面覆写addViewControllers函数,并用@Bean把重写的注入容器
(2)html里面静态资源引用

注意:所有的webMvcConfigurerAdapter组件会一起起作用——覆盖

config/MyConfig.java

@Bean
public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
    WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/").setViewName("login");
            registry.addViewController("/login.html").setViewName("login");
        }
    };
    return adapter;
}
<link href="#" th:href="@{/css/signin.css}" rel="stylesheet" />

解读:
@Bean等价于用xml配置

registry.addViewController("/").setViewName("login");addViewController的参数为发送了什么请求,setViewName的参数为基础名字(会被自动补充路径和.html)设置要跳转到哪,适用于页面跳转不需要或者没有任何业务逻辑处理的过程,只是单纯的路由跳转过程或者是点击一个按钮跳转到另一个页面。上面就是/和/login.html都跳到login.html页面

th:href 用于表达要跳到哪里:

@{}定义url链接 如果是需要从model中取值的话,写法为 th:href="@{${model中的name值}}"

有的时候我们不止需要从model中进行取值,还需写字符串与model中的值进行拼接,写法为th:href="@{‘字符串’+${model中的nam值}}"

2 国际化

2.1 根据浏览器语言自动切换语言

方法:
(1) 抽取(找出)页面需要的国际化消息,编辑国际化配置文件properties
(2) 把我们写的配置文件直接放在类路径下
(3) 从html中读取
(4) 设置编码

具体实现:
第一步,编写三个properties文件,分别存储默认/中文/英文的显示内容,然后在里面设置

第二步,spring自动配置好了国际化,只需要把我们写的配置文件直接放在类路径下,并命名为叫 网页基础名_语言缩写_国家缩写.properties 即可,例如: login_en_US.properties

第三步,从html中读取
对于需要修改的地方,加入th:text标签,替换<>外的本内容,#就是用来读国际化文件,{}放刚刚设置的变量名,如:

<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign in</h1>

第四步,idea- file- setting - file encoding- 选utf8和打勾,防止中文乱码

2.2 根据点击链接切换国际化,点中文按钮显示中文

方法:自己编写localResolver,加到容器中
具体实现:
(1) html修改“中文”“english”按钮的代码,修改他们点击后发送的请求:

 <a href="#" class="btn btn-sm" th:href="@{/index.html?lg=zh_CN}">中文</a>
 <a href="#" class="btn btn-sm" th:href="@{/index.html?lg=en_US}">English</a>

(2) 在component目录新建一个MyLocaleResolver.class,继承原来的LocaleResolver
在里面,根据_切分,locale对象根据切分结果存入对应的国家和语言

public class MyLocaleResolver implements LocaleResolver {

    //解析区域信息
    @Override
    public Locale resolveLocale(HttpServletRequest request) {
        String l = request.getParameter("lg");
        Locale locale = Locale.getDefault();
        if(!StringUtils.isEmpty(l)){
            String[] split = l.split("_");
            locale = new Locale(split[0], split[1]);
        }
        return locale;
    }

    @Override
    public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {

    }
}

(3)在config-MyMvcConfig.java内加入以下内容,以将刚刚写的注入容器

@Bean
public LocaleResolver localeResolver(){
    return new MyLocalResolver();
}

3 登录功能

3.1 用户名密码验证

功能:输入用户名和密码+验证密码,若密码为空,显示提示

方法:建立一个LoginController,前端发送post请求,后端处理该请求,判断用户名和密码是否正确,正确则去main.html,错误则返回login.html

LoginController.java

@Controller
public class LoginController {

    @PostMapping(value ="/user/login")
    public String login(@RequestParam("username")String username,
                        @RequestParam("password")String password,
                        Map<String,Object> map){
        if(!StringUtils.isEmpty(username) && "123456".equals(password)){
            //登录成功
            return "redirect:/main.html";
        }else{
            map.put("msg", "用户名密码错误");
            return "login";
        }

    }
}

Login.html

<p style="color: red" th:text="${msg}" th:if="${not #strings.isEmpty(msg)}"></p>

<form class="form-signin" th:action="@{/user/login}" method="post">
      <input type="text"  name="username" class="form-control" placeholder="Username" th:placeholder="#{login.username}" required="" autofocus=""/>
      <input type="password" name="password" id="inputPassword" class="form-control" placeholder="Password" th:placeholder="#{login.password}" required="" />

解读:
@Controller下,return的字符串,会回到该字符串对应的html

html中加一行,判断从后台传来的msg是否为空,是空就显示msg键对应值的信息(“用户名密码错误”),这里只是用户名不为空&&密码为123456就行

前端的form表单里,发出名为/user/login的请求,后台的@PostMapping(value ="/user/login")处理名为/user/login的post请求。

前端下划线是上一部国际化设置的,读取properties的内容,加粗是定义了名字,后台由@RequestParam按名字接收发来请求里面对应的值,再判断

语法:@RequestParam(value=”参数名”,required=”true/false”,defaultValue=””)

  • value:参数名
  • required:是否包含该参数,默认为true,表示该请求路径中必须包含该参数,如果不包含就报错。
  • defaultValue:默认参数值,如果设置了该值,required=true将失效,自动为false,如果没有传该参数,就使用默认值

Redirect 防止重复提交

3.2 拦截器

功能:拦截未登录的用户访问内部页面,不然直接访问登录成功后的页面,密码就无意义了

方法:
在component文件夹下新建一个LoginHandlerInterceptor.java拦截器,继承原来的拦截器

public class LoginHandlerInterceptor implements HandlerInterceptor {

    //目标方法执行之前
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        Object user = request.getSession().getAttribute("loginUser");
        if(user!=null){
            //已经登录
            return true;
        }
        //未经过验证
        request.setAttribute("msg", "没权限请先登录");
        request.getRequestDispatcher("/index.html").forward(request, response);

        return false;
    }

    @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 {

    }
}

MyMvcConfig配置中重写拦截器方法,加入到容器中

//所有的webMvcConfigurerAdapter组件会一起起作用
@Bean //註冊到容器去
public WebMvcConfigurerAdapter webMvcConfigurerAdapter(){
    WebMvcConfigurerAdapter adapter = new WebMvcConfigurerAdapter() {
        @Override
        public void addViewControllers(ViewControllerRegistry registry) {
            registry.addViewController("/").setViewName("login");
            registry.addViewController("/index.html").setViewName("login");
            registry.addViewController("/main.html").setViewName("Dashboard");
        }
        //注册拦截器
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            //静态资源 css js img 已经做好了静态资源映射
            registry.addInterceptor(new LoginHandlerInterceptor()).addPathPatterns("/**").
                    excludePathPatterns("/index.html","/","/user/login");
        }
    };
    return adapter;
}

在LoginHandler中添加登录成功写入session

@Controller
public class LoginController {

    @PostMapping(value ="/user/login")
    public String login(@RequestParam("username")String username,
                        @RequestParam("password")String password,
                        Map<String,Object> map,
                        HttpSession session){
        if(!StringUtils.isEmpty(username) && "123456".equals(password)){
            //登录成功,防止重复提交
            session.setAttribute("loginUser", username);
            return "redirect:/main.html";
        }else{
            map.put("msg", "用户名密码错误");
            return "login";
        }

    }
}

解读:
为login函数的参数增加一个HttpSession类型变量session,将用户名密码正确的用户的username保存到叫loginUsersession里面,后台调用getAttribute函数获取loginUser的值,如果没有的话说明刚刚没有存进来,也就说明没有登录成功,则又设置一个msg给前端展示,前端根据${msg}读到msg对应的值,打印出”没权限请先登录”的提示消息,而且还要调用分发器,当作他们发出了/index.html这个请求,而根据MyMvcConfig的设置registry.addViewController("/index.html").setViewName("login");,这个请求会去到login.html,也就是密码错误将会重新回到登录页面而且登录页面会多一句“没权限”的提示。

注释:
(1)

session.setAttribute("sessionName",Object);

用来设置session值

  • sessionName是名称
  • object是你要保存的对象。
session.getAttribute("sessionName");

用来得到对应名称的session值,即得到object对象,注意需要进行类型转换!

这两个共同把自己要的数据放在session里面传来传去

(2)拦截器:
在我的config里覆写addInterceptors函数,new LoginHandlerInterceptor()把拦截器引入,覆写的目的是实现后面的addPathPatterns("/**")对所有请求都拦截,但是(excludePathPatterns)排除了一些连接请求的拦截。

登录成功后,刷新的话是重新提交user/login请求,会重新提交表单,因此最好是使用重定向

4 抽取公共片段

功能:侧边栏当点击进入员工列表时,员工列表变蓝,原来的变灰

方法:将侧边栏提取成公共片段存入新的bar.html,并定义参数activeUri,再在dashboar/list等有侧边栏的地方引用,引用的时候读取参数,根据参数的内容确定显示方式

具体方法:
把侧边栏放到一个公共的bar.html
commos.bar.html里,员工管理那一行的代码:

<li class="nav-item">
    <a class="nav-link"
       th:class="${activeUri}=='emps'?'nav-link active':'nav-link'"
       href="https://getbootstrap.com/docs/4.1/examples/dashboard/#" th:href="@{/emps}">
        <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="feather feather-users"><path d="M17 21v-2a4 4 0 0 0-4-4H5a4 4 0 0 0-4 4v2"></path><circle cx="9" cy="7" r="4"></circle><path d="M23 21v-2a4 4 0 0 0-3-3.87"></path><path d="M16 3.13a4 4 0 0 1 0 7.75"></path></svg>
        员工管理
    </a>
</li>

dashboard那行:

<li class="nav-item">
<a class="nav-linkactive"
th:class="${activeUri}=='main.html'?'nav-linkactive':'nav-link'"
href="https://getbootstrap.com/docs/4.1/examples/dashboard/#"th:href="@{/main.html}">
<svgxmlns="http://www.w3.org/2000/svg"width="24"height="24"viewBox="002424"fill="none"stroke="currentColor"stroke-width="2"stroke-linecap="round"stroke-linejoin="round"class="featherfeather-home"><path d="M39l9-797v11a22001-22H5a22001-2-2z"></path><polylinepoints="92291215121522"></polyline></svg>
Dashboard<spanclass="sr-only">(current)</span>
</a>
</li>

在dashboard.html(主页)引入侧边栏时:

<div th:replace="commons/bar :: sidebar(activeUri='main.html')"></div>

而在list.html(员工列表)引入侧边栏时:

<div th:replace="commons/bar::sidebar(activeUri='emps')"></div>

解读:

抽取公共片段:
~{templatename::fragmentname}
模板名::片段名称 footer::copy

比如在原始地方:
Footer.html

<div id="footid" th:fragment="copy">xxx</div>

引用的地方:

<div th:insert=~{footer::copy}></div>

而引用也有三种方法。
如:

<div th:insert="footer :: copy"></div>

这段原始代码,将会根据引用的不同分别变成:

th:insert :加个外层标签 +1

 <div>
        <footer>
            &copy; 2011 The Good Thymes Virtual Grocery
        </footer>
    </div>

th:replace :完全替换 1

<footer>
   		&copy; 2011 The Good Thymes Virtual Grocery
    </footer>

th:include:就替换里面的内容 -1

<div>
        &copy; 2011 The Good Thymes Virtual Grocery
    </div>

5 增删查改

1、GET请求会向数据库发索取数据的请求,从而来获取信息,该请求就像数据库的select操作一样,只是用来查询一下数据,不会修改、增加数据,不会影响资源的内容,即该请求不会产生副作用。无论进行多少次操作,结果都是一样的。

2、PUT请求是向服务器端发送数据的,从而改变信息,该请求就像数据库的update操作一样,用来修改数据的内容,但是不会增加数据的种类等,也就是说无论进行多少次PUT操作,其结果并没有不同。

3、POST请求,同PUT请求类似,都是向服务器端发送数据的,但是该请求会改变数据的种类等资源,就像数据库的insert操作一样,会创建新的内容。几乎目前所有的提交操作都是用POST请求的。

4、DELETE请求顾名思义,就是用来删除某一个资源的,该请求就像数据库的delete操作。

1、POST /url 创建
2、DELETE /url/xxx 删除
3、PUT /url/xxx 更新
4、GET /url/xxx 查看

5.1 查看员工:

新建一个EmployeeController类,获取员工信息,发送到前端

@Controller
public class EmployeeController {

    @Autowired
    EmployeeDao employeeDao;
    /**
     * 查询所有员工返回列表页面
     */
    @GetMapping(value = "/emps")
    public String list(Model model){
        Collection<Employee> employees = employeeDao.getAll();
        model.addAttribute("emps",employees);
        return "emp/list";
    }
}

前端接收员工集合,并用each循环,展示

<table class="table table-striped table-sm">
    <thead>
    <tr>
        <th>#</th>
        <th>lastName</th>
        <th>email</th>
        <th>gender</th>
        <th>department</th>
        <th>birth</th>
        <th>操作</th>
    </tr>
    </thead>
    <tbody>
        <tr th:each="emp:${emps}">
            <td th:text="${emp.id}">1</td>
            <td th:text="${emp.lastName}">1</td>
            <td th:text="${emp.email}">1</td>
            <td th:text="${emp.gender}">1</td>
            <td th:text="${emp.department.departmentName}">1</td>
            <td th:text="${#dates.format(emp.birth,'yyyy-MM-dd HH:mm:ss')}">1</td>
            <td>
                <button class="btn btn-sm btn-primary">编辑</button>
                <button class="btn btn-sm btn-danger">删除</button>
            </td>
        </tr>
    </tbody>
</table>

model.addattribute()可以往前台传数据(可以传对象,List),前端通过 ${}可以获取到,类似于request.setAttribute("sts",sts)

引入dao层,利用dao层的getAll函数将所有员工的信息返回到一个employee类型的collection里,用emps把获得到的集合传到前端

@GetMapping(value = "/emps")负责处理名为emps的GET请求

return让页面跳转到emp文件夹下的list.html

前端的th:each,对每个emps里的emp进行遍历,下面将文本内容替换成emp的id,email等信息(employee的dao层定义了employee有哪些元素)

5.2 新增员工

功能1:
在员工列表页面点击添加按钮,跳转到新增员工信息页面

页面跳转:
EmployeeController中添加addEmpPage方法

@GetMapping(value = "/emp")
public String toAddPage(Model model){
    //来到添加页面,查出所有部门显示
    Collection<Department> depts = departmentDao.getDepartments();
    model.addAttribute("depts",depts);
    return "emp/add";
}

当前端发送emp请求时,该函数处理叫emp的get请求,方法为先获取到所有的部门信息并放到depts里,跳转到emp文件夹下的add.html

新增员工信息页面add.html:

<form>
    <!-- LastName -->
    <div class="form-group">
        <label for="LastName">LastName</label>
        <input type="text" class="form-control" id="LastName"  placeholder="LastName">
    </div>
    <!-- Email -->
    <div class="form-group">
        <label for="Email">Email</label>
        <input type="email" class="form-control" id="Email"  placeholder="zhangsan@163.com">
    </div>
    <!--gender-->
    <div class="form-group">
        <label >Gender</label><br/>
        <div class="form-check form-check-inline">
            <input class="form-check-input" type="radio" name="gender" value="1">
            <label class="form-check-label" ></label>
        </div>
        <div class="form-check form-check-inline">
            <input class="form-check-input" type="radio" name="gender" value="0">
            <label class="form-check-label" ></label>
        </div>
    </div>
    <!-- department -->
    <div class="form-group">
        <label for="exampleFormControlSelect1">department</label>
        <select class="form-control" id="exampleFormControlSelect1">
            <option th:each="dept:${depts}" th:value="${dept.id}" th:text="${dept.departmentName}"></option>
        </select>
    </div>
    <!--Birth-->
    <div class="form-group">
        <label for="birthDate">Birth</label>
        <input type="text" class="form-control" id="birthDate" placeholder="2012-12-12">
    </div>
    <button type="submit" class="btn btn-primary">添 加</button>
</form>

注意,这里的department的选择内容,是后台通过employee的dao层,获取所有部门的名称再用depts变量传到前端,供用户选择。用了option标签,value为部门id,显示的文本为部门名称

功能2:
在新增员工信息页面填写好新员工的信息,点击按钮回到员工列表页面

方法:新建一个postMapping的方法用来接受页面的添加POST请求,修改添加页面,添加name属性,点击按钮向后台发送一个emp名字的post请求,用dao层的save函数将新employee的信息存储起来,之后重定向到emps请求(也就是list.html),此时遍历员工信息的时候就会展示出来。

页面:

<form th:action="@{/emp}" method="post">
    <!-- LastName -->
    <div class="form-group">
        <label for="LastName">LastName</label>
        <input type="text" class="form-control" id="LastName" name="lastName" placeholder="LastName">
    </div>
    <!-- Email -->
    <div class="form-group">
        <label for="Email">Email</label>
        <input type="email" class="form-control" id="Email"  name="email" placeholder="zhangsan@163.com">
    </div>
    <!--gender-->
    <div class="form-group">
        <label >Gender</label><br/>
        <div class="form-check form-check-inline">
            <input class="form-check-input" type="radio" name="gender" value="1">
            <label class="form-check-label" ></label>
        </div>
        <div class="form-check form-check-inline">
            <input class="form-check-input" type="radio" name="gender" value="0">
            <label class="form-check-label" ></label>
        </div>
    </div>
    <!-- department -->
    <div class="form-group">
        <label >department</label>
        <select class="form-control"  name="department.id">
            <option th:each="dept:${depts}" th:value="${dept.id}" th:text="${dept.departmentName}"></option>
        </select>
    </div>
    <div class="form-group">
        <label for="birthDate">Birth</label>
        <input type="text" class="form-control" id="birthDate" placeholder="2012-12-12" name="birth">
    </div>
    <button type="submit" class="btn btn-primary">添 加</button>
</form>

后台controller新增函数

@PostMapping(value = "/emp")
public String addEmp(Employee employee){
    employeeDao.save(employee);
    //来到员工列表页面、redirect:重定向到一个地址,forward转发到一个地址
    return "redirect:/emps";
}

修改页面第一行表示,该表单会像后台发送emp的post请求。

name="email"这种,是用name来给employee对应的属性赋值。

5.3 修改员工信息:到修改页面,修改按钮

给编辑按钮写上跳转链接,跳转到emp/emp.id请求

<td>
    <a  href="#" th:href="@{/emp/}+${emp.id}" class="btn btn-sm btn-primary">编辑</a>
    <button class="btn btn-sm btn-danger">删除</button>
</td>

增加跳转到员工页面的controller,接收emp/emp.id的拼接请求,通过发送来的id,找到对应的emp对象,通过addAttribute发送给前端,然后跳转到add页面(这里add和编辑用的都是add.html)

@GetMapping(value = "/emp/{id}")
public String toEditPage(@PathVariable("id") Integer id ,Model model){
    Employee emp = employeeDao.getEmpById(id);
    Collection<Department> departments = departmentDao.getDepartments();
    model.addAttribute("emp",emp);
    model.addAttribute("depts",departments);
    return "emp/add";
}

修改add页面,让他能兼容增加和修改

 <form th:action="@{/emp}" method="post">
        <!--发送put请求-->
        <!--1.SpringMVC配置HiddenHttpMethodFilter
            2.页面创建一个post表单
            3.创建一个 input name_method 值就是我们请求的方式-->
        <input type="hidden" name="_method" value="put" th:if="${emp!=null}">

        <input type="hidden" name="id" th:value="${emp.id}" th:if="${emp!=null}">
        <!-- LastName -->
        <div class="form-group">
            <label for="LastName">LastName</label>
            <input type="text" class="form-control" id="LastName" name="lastName" placeholder="LastName" th:value="${emp!=null}?${emp.lastName}">
        </div>
        <!-- Email -->
        <div class="form-group">
            <label for="Email">Email</label>
            <input type="email" class="form-control" id="Email"  name="email" placeholder="zhangsan@163.com" th:value="${emp!=null}?${emp.email}">
        </div>
        <!--gender-->
        <div class="form-group">
            <label >Gender</label><br/>
            <div class="form-check form-check-inline">
                <input class="form-check-input" type="radio" name="gender" value="1" th:checked="${emp!=null}?${emp.gender}==1">
                <label class="form-check-label" ></label>
            </div>
            <div class="form-check form-check-inline">
                <input class="form-check-input" type="radio" name="gender" value="0" th:checked="${emp!=null}?${emp.gender}==0">
                <label class="form-check-label" ></label>
            </div>
        </div>
        <!-- department -->
        <div class="form-group">
            <label >department</label>
            <select class="form-control"  name="department.id" >
                <option th:selected="${emp!=null}?${dept.id == emp.department.id}" th:each="dept:${depts}" th:value="${dept.id}" th:text="${dept.departmentName}"></option>
            </select>
        </div>
        <div class="form-group">
            <label for="birthDate">Birth</label>
            <input type="text" class="form-control" id="birthDate" placeholder="2012-12-12" name="birth" th:value="${emp!=null}?${#dates.format(emp.birth,'yyyy-MM-dd HH:mm:ss')}">
        </div>
        <button type="submit" class="btn btn-primary" th:text="${emp!=null}?'修改':'添加'">添 加</button>
    </form>
</main>

若emp不为空,说明是修改,则提交put请求

对于名字的显示,如果emp不为空,则显示他原来的,否则显示空(但是有一个placeholder所以看起来有内容),其余邮件等同理

最下面的按钮也是,根据emp为不为空,显示编辑or添加

5.4 删除:

员工列表页的删除按钮:

<button th:attr="del_uri=@{/emp/}+${emp.id}"  class="btn btn-sm btn-danger deleteBtn">
    删除
</button>

form表单, name-value这一对,对应着_method和delete,_method的值delete就是请求方式

<form id="deleteEmpForm" method="post">
        <input type="hidden" name="_method" value="delete">
</form>

写js,提交请求
return false;禁用btn提交效果

<script>
    $(".deleteBtn").click(function () {
        $("#deleteEmpForm").attr("action",$(this).attr("del_uri")).submit();
        return false;
    })
</script>

controller中新建函数,处理delete请求
@PathVariable("id")接收请求路径中占位符的值
调用dao层的函数,根据id删除对应的员工后,重定向仍回到员工列表页面

@DeleteMapping(value = "/emp/{id}")
public String deleteEmp(@PathVariable("id") Integer id){
    employeeDao.deleteEmpById(id);
    return "redirect:/emps";
}
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值