参考的代码和资源链接:https://blog.csdn.net/qq_45173404/article/details/108934414?spm=1001.2014.3001.5501
学习视频:B站 狂神说Java – https://www.bilibili.com/video/BV1PE411i7CV
学习文档: 微信公众号 狂神说 –https://mp.weixin.qq.com/mp/homepage?__biz=Mzg2NTAzMTExNg==&hid=1&sn=3247dca1433a891523d9e4176c90c499&scene=18&uin=&key=&devicetype=Windows+10+x64&version=63020170&lang=zh_CN&ascene=7&fontgear=2
SpringBoot其实就是去整合了Spring、spring mvc框架,整合了里面的配置信息,“约定大于配置”。
xxxAutoConfiguration 完成自动配置文件,给容器中导入非常多的自动配置类。即给容器中导入配置好的组件 bean信息。
使用时可以直接拿来使用,开箱即用。【不用再向原来mybatis、spring中那样写xml配置文件了】
内嵌了服务器,比如Tomcat、Jetty。简化了web项目开发,不用再自己启动配置tomcat
单体应用架构转化为微服务架构风格。
最重要的就是 SpringBoot自动装配原理了。
@SpringBootApplication
public class Springboot03WebApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot03WebApplication.class, args);
}
}
开启SpringBoot应用的注解是 @SpringBootApplication,它里面包括:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
......
}
配置文件:
自己定义 *.yaml 或者 *.properties 配置文件。然后可以去修改SpringBoot中默认的配置信息。
yaml语法:key: 空格 value
server:
port: 8081
#选择要激活那个环境块
spring:
profiles:
active: prod
---
server:
port: 8082
spring:
profiles: dev # 设置环境的名称
---
server:
port: 8084
spring:
profiles: prod # 设置环境的名称
person:
name: alzn
age: 18
happy: true
birth: 2002/01/01
maps: {al: last name, zn: first name}
lists:
- learning
- running
- dancing
dog:
name: xiaohu
age: 6
利用注解开发自定义配置文件,
// 如果你想要 div 一些定制的功能,只要写这个组件,然后把它交给springboot,springboot就会帮我们装配
// 扩展 springmvc 围绕着核心 dispatchservlet
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
// ViewResolver 实现了视图解析器接口的类。 我们就可以把这个当作视图解析器
@Bean //放到bean中。通过方法注册一个bean,这里的返回值就Bean的类型,方法名就是bean的id!
public ViewResolver myViewResolver(){
return new MyViewResolver();
}
//我们写一个静态内部类,视图解析器就需要实现ViewResolver接口
private static class MyViewResolver implements ViewResolver{
@Override
public View resolveViewName(String s, Locale locale) throws Exception {
return null;
}
}
}
thymeleaf模板引擎:同样也是为了简化web开发,直接继承 thymeleaf模板引擎。
模板引擎的作用就是帮助我们写了一个页面模板,比如有些值是动态的,我们写一些表达式。而这些值,从哪来呢,就是我们在后台封装一些数据。然后把这个模板和这个数据交给我们模板引擎,模板引擎按照我们这个数据帮你把这表达式解析、填充到我们指定的位置,然后把这个数据最终生成一个我们想要的内容给我们写出去,这就是我们这个模板引擎,不管是jsp还是其他模板引擎,都是这个思想。
在使用SpringBoot进行开发的时候,自己要解决的、参与的工作是:
@EnableWebMvc // 这个就是导入了一个类: DelegatingWebMvcConfiguration: 从容器中获取所有的 webmvcConfig
@EnableWebMvc 将 WebMvcConfigurationSupport 组件 导入进来了;而导入的 WebMvcConfigurationSupport 只是SpringMVC最基本的功能!此时我们全面接管SpringMVC。
jar:webapp
自动装配:
Springboot到底帮我们配置了什么? 我们能不能进行修改? 能修改哪些东西?能不能扩展?
xxxxAutoConfigurartion:自动配置类;给容器中添加组件
xxxxProperties: 封装配置文件中相关属性;
其实SpringBoot的东西用起来非常简单,因为SpringBoot最大的特点就是自动装配。
使用SpringBoot的步骤:
1、创建一个SpringBoot应用,选择我们需要的模块,SpringBoot就会默认将我们的需要的模块自动配置好
2、手动在配置文件中配置部分配置项目就可以运行起来了
3、专注编写业务代码,不需要考虑以前那样一大堆的配置了。
我们在利用 Springboot的时候,能够利用和修改哪些配置,以及增加配置文件呢? 如下所示,我们最终是要通过这两个去进行 环境配置:
我们在进行 springboot web 开发要解决的问题:
参考的代码和资源链接:https://blog.csdn.net/qq_45173404/article/details/108934414?spm=1001.2014.3001.5501
新建一个SpringBoot项目:springboot-03-system。
选择pom依赖文件:SpringWeb 和Thymeleaf模板引擎
创建不存在的静态资源目录 public 和 resources,便于添加我们想要自定义的静态资源。
SpringBoot中的静态资源导入:【SpringBoot中,SpringMVC的web配置都在 WebMvcAutoConfiguration 这个配置类里面】
- 两种映射规则:“classpath:/META-INF/resources/”、“classpath:/resources/”、“classpath:/static/”、“classpath:/public/”
- 自定义静态资源路径
所有的 /webjars/** , 都需要去 classpath:/META-INF/resources/webjars/ 找对应的资源
静态资源文件:
把静态资源文件导入到对应文件夹中:
创建一个伪数据库,进行数据库的模拟。
导入lombok,便于使用注解开发:
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
实体类:pojo
创建实体类:Department 部门表
package com.al.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
// 部门表
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
private Integer id;
private String departmentName;
}
员工表:
package com.al.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
// 员工表
@Data
@NoArgsConstructor
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender; // 0:女 1:男。 用数字表示性别男女
private Department department;
private Date birth;
public Employee(Integer id, String lastName, String email, Integer gender, Department department, Date birth) {
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.department = department;
// 默认的创建日期
//this.birth = birth;
this.birth = new Date();
}
}
创建dao层:dao层,负责底层数据的操作。
Bean的注解开发:
@Component 通常是通过类路径扫描来⾃动侦测以及⾃动装配到Spring容器中。@Component 表明会将这个类交给 Spring管理装配,它与@Bean的不同之处在于,它作用在类上。注册一个类的实例到Spring IOC 容器。需要的时候,从里面去获取。
为了更好的进行分层,在 web 开发中,会按照 mvc 三层架构分层。Spring可以使用其它三个注解,功能一样,目前使用哪一个功能都一样。
- @Controller:web层
- @Service:service层
- @Repository:dao层
Bean的自动装配配置:
- @Autowired: 自动装配 通过类型,名字
- 如果@Autowired 不能唯一自动装配属性,则还需要通过 @Qualifier(value = “xxx”)
- @Nullable: 字段标注了这个注解,表示这个字段可以为 null
- @Resource:自动装配 通过名字, 类型。先去匹配byName,后去进行byType。
部门dao类:
DepartmentDao:将此类注入到Spring IOC容器中,dao层使用注解 @Repository。
模拟数据库中的部门数据。采用map表示。
Map
package com.al.dao;
import com.al.pojo.Department;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
// 部门dao
@Repository
public class DepartmentDao {
// 模拟数据库表中的数据
private static Map<Integer, Department> departments = null;
static {
// 创建一个部门表
departments = new HashMap<>();
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, "礼部"));
departments.put(106, new Department(106, "工部"));
}
// 获取所有的部门信息
public Collection<Department> getDepartments(){
return departments.values();
}
// 通过id获取部门信息
public Department getDepartmentById(Integer id){
return departments.get(id);
}
}
员工dao类:
EmployeeDao类:需要去继承DepartmentDao 这个部门dao,获取这样的一个类实例,所以
使用注解@Autowired 获取DapartmentDao的一个实例。
模拟数据库中的员工表数据。采用map表示:private static Map
package com.al.dao;
import com.al.pojo.Department;
import com.al.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import java.util.Collection;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
// 员工Dao
@Repository
public class EmployeeDao {
// 模拟数据库表中的员工数据信息
private static Map<Integer, Employee> employees = null;
// 员工所属部门
@Autowired
private static DepartmentDao departmentDao;
static {
employees = new HashMap<>();
employees.put(1001, new Employee(1001, "AAA", "[email protected]", 1, new Department(101, "户部"), new Date()));
employees.put(1002, new Employee(1002, "BBB", "[email protected]", 0, new Department(102, "兵部"), new Date()));
employees.put(1003, new Employee(1003, "CCC", "[email protected]", 1, new Department(103, "吏部"), new Date()));
employees.put(1004, new Employee(1004, "DDD", "[email protected]", 0, new Department(104, "刑部"), new Date()));
employees.put(1005, new Employee(1005, "EEE", "[email protected]", 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 deleteEmployeeByID(int id) {
employees.remove(id);
}
}
首页控制,使导入的templates中的静态资源有效。利用该自定义的静态资源,需要提供静态资源配置文件。
在控制器 controller中,进行首页控制,IndexController:
package com.al.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class IndexController {
@RequestMapping({"/", "/index.html"}) //这是一个集合
public String index(){
return "index";
}
}
不过最好我们采用这种方式去进行对根目录下的访问:【采用自定义配置 资源文件的方式】
在主程序同级目录下新建 config 包用来存放自己的配置类:
package com.al.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
}
}
启动浏览器测试,观察界面:界面可以访问,但是不理想
想要直接去访问 template 文件夹路径下的静态资源,需要引入模板引擎。这里采用 SpringBoot中的 thymeleaf 。
创建有效的静态资源路径 文件夹:而其中的classpath:/META-INF/resource/ 这个就对应着上面的那个webjars 资源路径。
"classpath:/META-INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"
classpath对应的文件夹等级就是 resources 资源文件夹。
我们创建以下的几个文件夹,都能够存放我们的静态文件:【可以直接获取该路径下的静态资源文件】
上述测试的时候,发现页面中的图片没有加载出来,且css和js的样式并没有显示。 这是因为 想要直接去访问 template 文件夹路径下的静态资源,需要引入模板引擎。
在SpringBoot中,推荐使用Thymeleaf
作为模板引擎,我们将其中的语法改为Thymeleaf
,所有页面的静态资源都需要使用模板引擎接管,里面的动态数据由后端提供。前面页面需要符合 thymeleaf语法。
Selection Variable Expressions: *{…}:选择表达式:和${}在功能上是一样;
Message Expressions: #{…}:获取国际化内容
Link URL Expressions: @{…}:定义URL;
Fragment Expressions: ~{…}:片段引用表达式
在index.html页面中:
引入thymeleaf:
<html lang="en" xmlns:th="http://www.thymeleaf.org">
第一个/
代表项目的classpath,也就是这里的resources
目录。 直接获取 “classpath:/static/” 路径下的资源文件:
thymeleaf语法中 定义URL的方式:@{}
index.html:
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Signin Template for Bootstraptitle>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/signin.css}" rel="stylesheet">
head>
<body class="text-center">
<form class="form-signin" action="dashboard.html">
<img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="" width="72" height="72">
<h1 class="h3 mb-3 font-weight-normal">Please sign inh1>
<label class="sr-only">Usernamelabel>
<input type="text" class="form-control" placeholder="Username" required="" autofocus="">
<label class="sr-only">Passwordlabel>
<input type="password" class="form-control" placeholder="Password" required="">
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me"> Remember me
label>
div>
<button class="btn btn-lg btn-primary btn-block" type="submit">Sign inbutton>
<p class="mt-5 mb-3 text-muted">© 2017-2018p>
<a class="btn btn-sm">中文a>
<a class="btn btn-sm">Englisha>
form>
body>
html>
测试访问结果:
其它页面进行同样的修改。
配置首页的时候:可以在这里自己自行定义 目录, 此时 localhost:8080失效,必须是 localhost:8080/al
server.servlet.context-path=/al
此时的测试结果:ulr请求路径必须是:http://localhost:8080/al/
首先在 setting -> FileEncodings中 必须要确保编码为 UTF-8, 不然写的东西会乱码。
在静态资源中创建一个 i18n文件,国际化:
在这其中分别创建两个配置文件:
命名方式是下划线的组合:文件名_
语言_
国家.properties;
以此方式命名,IDEA会帮我们识别这是个国际化配置包,自动绑定在一起转换成如下的模式:
login.properties 和 login_zh_CN.properties, 然后IDEA会自动帮你合并:
如果再添加其它配置文件:选择Resource Bundle ‘login’,右键点击添加即可:
此时只需要输入区域名即可创建成功,比如输入 en_US,就会自动识别
此时的i18n文件下的配置资源文件:
在编写login资源配置文件时:可以选择左下角的 Resource Bundle进行可视化编写:
然后对这种提示,去分别做语言配置:在输入框中写入相应的语言提示
login.properties:
login.btn=登录
login.password=密码
login.remember=记住我
login.tip=请登录
login.username=用户名
login_zh_CN.properties:
login.btn=登录
login.password=密码
login.remember=记住我
login.tip=请登录
login.username=用户名
login_en_US.properties:
login.btn=login
login.password=password
login.remember=remember
login.tip=Please sigin in
login.username=suaername
那么如何让我们配置的这些生效呢?
在Spring程序中,国际化主要是通过ResourceBundleMessageSource这个类来实现的
Spring Boot通过MessageSourceAutoConfiguration为我们自动配置好了管理国际化资源文件的组件
查看源码MessageSourceAutoConfiguration,
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(name = AbstractApplicationContext.MESSAGE_SOURCE_BEAN_NAME, search = SearchStrategy.CURRENT)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@Conditional(ResourceBundleCondition.class)
@EnableConfigurationProperties
public class MessageSourceAutoConfiguration {
private static final Resource[] NO_RESOURCES = {};
@Bean
@ConfigurationProperties(prefix = "spring.messages")
public MessageSourceProperties messageSourceProperties() {
return new MessageSourceProperties();
}
@Bean
public MessageSource messageSource(MessageSourceProperties properties) {
ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
if (StringUtils.hasText(properties.getBasename())) {
messageSource.setBasenames(StringUtils
.commaDelimitedListToStringArray(StringUtils.trimAllWhitespace(properties.getBasename())));
}
if (properties.getEncoding() != null) {
messageSource.setDefaultEncoding(properties.getEncoding().name());
}
messageSource.setFallbackToSystemLocale(properties.isFallbackToSystemLocale());
Duration cacheDuration = properties.getCacheDuration();
if (cacheDuration != null) {
messageSource.setCacheMillis(cacheDuration.toMillis());
}
messageSource.setAlwaysUseMessageFormat(properties.isAlwaysUseMessageFormat());
messageSource.setUseCodeAsDefaultMessage(properties.isUseCodeAsDefaultMessage());
return messageSource;
}
...
}
关于 messageSource 方法中的参数:MessageSourceProperties properties。
MessageSourceProperties 类:
public class MessageSourceProperties {
/**
* Comma-separated list of basenames (essentially a fully-qualified classpath
* location), each following the ResourceBundle convention with relaxed support for
* slash based locations. If it doesn't contain a package qualifier (such as
* "org.mypackage"), it will be resolved from the classpath root.
*/
private String basename = "messages";
/**
* Message bundles encoding.
*/
private Charset encoding = StandardCharsets.UTF_8;
...
}
注释的意思:
* 逗号分隔的基本名称列表(本质上是完全限定的类路径 location),每个都遵循 ResourceBundle 约定,并轻松支持
*基于斜线的位置。 如果它不包含包限定符(例如"org.mypackage"),它将从类路径根目录中解析。
意思是:如果你不在springboot配置文件中指定以.分隔开的国际化资源文件名称的话,它默认会去类路径下找messages.properties作为国际化资源文件。
自定义国际化资源文件,因此我们需要在SpringBoot配置文件application.properties中加入以下配置指定我们配置文件的名称
#自定义配置:国际化资源文件
spring.messages.basename=i18n.login
Message Expressions: #{…}:获取国际化内容
对于国际化 message如何写:thymeleaf 中 表示message 的语法为 #{…}
<label>
<input type="checkbox" value="remember-me" th:text="#{login.remember}">
<input type="checkbox" value="remember-me"> [[#{login.remember}]]
label>
修改的地方:【对应着前端页面显示的地方】
此时的index.html 代码:
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Signin Template for Bootstraptitle>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/signin.css}" rel="stylesheet">
head>
<body class="text-center">
<form class="form-signin" action="dashboard.html">
<img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="" width="72" height="72">
<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign inh1>
<label class="sr-only">Usernamelabel>
<input type="text" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
<label class="sr-only">Passwordlabel>
<input type="password" class="form-control" th:placeholder="#{login.password}" required="">
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me" th:text="#{login.remember}">
label>
div>
<button class="btn btn-lg btn-primary btn-block" type="submit">[[#{login.btn}]]button>
<p class="mt-5 mb-3 text-muted">© 2017-2018p>
<a class="btn btn-sm">中文a>
<a class="btn btn-sm">Englisha>
form>
body>
html>
此时的测试:首页:
如何通过按钮 进行中英文的转换?在index.html
页面中可以看到两个标签:
<a class="btn btn-sm">中文a>
<a class="btn btn-sm">Englisha>
在标签中加入跳转链接,点击后进行响应跳转,获取对应的信息资源参数:
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文a>
<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">Englisha>
Spring源码中关于国际化的类有:
Locale
:代表地区,每一个Locale对象都代表了一个特定的地理、政治和文化地区LocaleResolver
:地区解析器分析:
消息语言转换的类:AcceptHeaderLocaleResolver,实现接口 LocaleResolver
LocaleResolver:
public interface LocaleResolver {
Locale resolveLocale(HttpServletRequest request);
void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale);
}
在WebMvcAutoConfiguration 类中寻找 LocaleResolver:
@Override
@Bean
@ConditionalOnMissingBean(name = DispatcherServlet.LOCALE_RESOLVER_BEAN_NAME)
public LocaleResolver localeResolver() {
//如果用户配置了,则使用用户配置好的
if (this.webProperties.getLocaleResolver() == WebProperties.LocaleResolver.FIXED) {
return new FixedLocaleResolver(this.webProperties.getLocale());
}
//用户没有配置,则使用默认的
AcceptHeaderLocaleResolver localeResolver = new AcceptHeaderLocaleResolver();
localeResolver.setDefaultLocale(this.webProperties.getLocale());
return localeResolver;
}
经过寻找后发现只要完成了 接口实现类 LocalResolver,就能完成 地区语言转换。该方法就是获取LocaleResolver
地区对象解析器:
在消息语言转换的类 AcceptHeaderLocaleResolver中:
public class AcceptHeaderLocaleResolver implements LocaleResolver {
private final List<Locale> supportedLocales = new ArrayList<>(4);
@Nullable
private Locale defaultLocale;
......
@Override
public Locale resolveLocale(HttpServletRequest request) {
Locale defaultLocale = getDefaultLocale();
if (defaultLocale != null && request.getHeader("Accept-Language") == null) {
return defaultLocale;
}
Locale requestLocale = request.getLocale();
List<Locale> supportedLocales = getSupportedLocales();
if (supportedLocales.isEmpty() || supportedLocales.contains(requestLocale)) {
return requestLocale;
}
Locale supportedLocale = findSupportedLocale(request, supportedLocales);
if (supportedLocale != null) {
return supportedLocale;
}
return (defaultLocale != null ? defaultLocale : requestLocale);
}
......
@Override
public void setLocale(HttpServletRequest request, @Nullable HttpServletResponse response, @Nullable Locale locale) {
throw new UnsupportedOperationException(
"Cannot change HTTP accept header - use a different locale resolution strategy");
}
}
我们自定义一个地区解析器,继承实现LocaleResolver 接口,使我们的自定义国际化资源生效。去实现,前端界面 Index.html中点击链接传递参数,使得按钮生效,完成中英文切换。
index.html:
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文a>
<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">Englisha>
自定义地区配置解析器:MyLocaleResolver,完成实现接口LocalResolver。
package com.al.config;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
// 地区解析器:获取链接上携带的参数信息
public class MyLocaleResolver implements LocaleResolver {
// 解析请求
@Override
public Locale resolveLocale(HttpServletRequest request) {
// 获取请求中的国家化参数
String language = request.getParameter("l");
// 默认的地区
Locale locale = Locale.getDefault();
//如果请求的链接参数不为空,携带了国际化参数
if (!StringUtils.isEmpty(language)) {
String[] split = language.split("_");//zh_CN(语言_地区)
locale = new Locale(split[0], split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
我们的国际化 message 写完后, 为了使地区配置解析器的信息生效。该组件需要放入到 bean中。
在配置类 MvcConofig 中添加bean:
package com.al.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
}
// 将自定义的国际化配置放入 Spring容器中,使组件生效
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
}
重启项目进行访问测试。
点击中文按钮,url路径为:http://localhost:8080/index.html?l=zh_CN
点击英文按钮,url路径为:http://localhost:8080/index.html?l=en_US
在登录页面,即首页去实现登录, 实现跳转到 dashboard.html 页面。
name
属性,便于传参DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Signin Template for Bootstraptitle>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/signin.css}" rel="stylesheet">
head>
<body class="text-center">
<form class="form-signin" th:action="@{/user/login}">
<img class="mb-4" th:src="@{/img/bootstrap-solid.svg}" alt="" width="72" height="72">
<h1 class="h3 mb-3 font-weight-normal" th:text="#{login.tip}">Please sign inh1>
<label class="sr-only">Usernamelabel>
<input type="text" name="username" class="form-control" th:placeholder="#{login.username}" required="" autofocus="">
<label class="sr-only">Passwordlabel>
<input type="password" name="password" class="form-control" th:placeholder="#{login.password}" required="">
<p style="color: red" th:text="${msg}">p>
<div class="checkbox mb-3">
<label>
<input type="checkbox" value="remember-me" th:text="#{login.remember}">
label>
div>
<button class="btn btn-lg btn-primary btn-block" type="submit">[[#{login.btn}]]button>
<p class="mt-5 mb-3 text-muted">© 2017-2018p>
<a class="btn btn-sm" th:href="@{/index.html(l='zh_CN')}">中文a>
<a class="btn btn-sm" th:href="@{/index.html(l='en_US')}">Englisha>
form>
body>
html>
我们创建一个控制器用于 响应这个请求 servlet,完成跳转。
LoginController 控制器:
package com.al.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
@Controller
public class LoginController {
@RequestMapping("/user/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model){
if (!StringUtils.isEmpty(username) && "123456".equals(password)){
return "dashboard";
}else {
model.addAttribute("msg", "用户名或密码错误");
return "index";
}
}
}
然后我们在index.html
首页中加一个标签用来显示controller返回的错误信息:
<p style="color: red" th:text="${msg}">p>
测试结果:
在密码不对的情况下:http://localhost:8080/user/login?username=ahoh&password=123
url路径里面显示了用户的名字和密码,这暴漏了用户的信息。我们编写一个映射 去解决这个问题。
在自定义的配置类MyMvcConfig 中添加映射。在url=/main.html时,页面为 登录后的页面 dashboard.html
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
registry.addViewController("/main.html").setViewName("dashboard");
}
// 将自定义的国际化配置放入 Spring容器中,使组件生效
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
}
对应的,我们需要在 登录控制器 LoginController中进行重定向:redirect:/main.html
@Controller
public class LoginController {
@RequestMapping("/user/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model){
if (!StringUtils.isEmpty(username) && "123456".equals(password)){
return "redirect:/main.html";
}else {
model.addAttribute("msg", "用户名或密码错误");
return "index";
}
}
}
测试:输入正确的用户名和密码登陆成功后,浏览器不再泄露信息。
但此时的结果虽然可以在登录后的后台界面进去了,但是你直接在url路径输入 /main.html 也能进去, 这就需要 拦截器了。
创建一个登录拦截器 LoginHandlerInterceptor。
拦截器去完成登录拦截的功能:【用户登录后,后台会得到用户的信息;如果没有登录,则不会有任何的用户信息】。
LoginController控制器:
@Controller
public class LoginController {
@RequestMapping("/user/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model, HttpSession session){
if (!StringUtils.isEmpty(username) && "123456".equals(password)){
session.setAttribute("LoginUser", username);
//return "dashboard";
return "redirect:/main.html"; // 重定向
}else {
model.addAttribute("msg", "用户名或密码错误");
return "index";
}
}
}
登录拦截器 LoginHandlerInterceptor:
编写一个自定义的登录拦截器 LoginHandlerInterceptor,继承HandlerInterceptor接口
package com.al.config;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 用户如果登录成功了,应该获取到用户的 session 信息
Object session = request.getSession().getAttribute("LoginUser");
if (session == null) {
request.setAttribute("msg", "权限不够,请先登录");
request.getRequestDispatcher("/index.html").forward(request, response);
return false;
} else {
return true;
}
}
}
注册拦截器 LoginHandlerInterceptor 到spring 容器中,Srping Boot配置中添加自定义配置资源
定义完拦截器后,我们需要进行在 bean中进行 注册配置:
在MyMvcConfig配置类中,重写关于拦截器的方法,添加自定义的拦截器
屏蔽:要拦截的以及不要拦截的 路径 资源,
需要放行的:.excludePathPatterns(“/index.html”, “/”, “/user/login”, “/css/", "/js/”, “/img/**”)。才能将页面完全导入进来,页面加载出来
package com.al.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
registry.addViewController("/main.html").setViewName("dashboard");
}
// 将自定义的国际化配置放入 Spring容器中,使组件生效
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginHandlerInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/index.html", "/", "/user/login", "/css/**", "/js/**", "/img/**");
}
}
如果不放行静态资源,页面无法完全导入:
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginHandlerInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/index.html", "/", "/user/login");
}
修改后测试:此时直接访问http://localhost:8080/main.html 显示权限不够:
重新写入username 和password,进入到dashboard页面。
如果再重新直接访问 http://localhost:8080/main.html ,也可以直接进入。因为此时的 session 里面存入了用户的信息,拦截器会放行通过。
目标,功能:点击dashboard.html页面中的Customers 按钮跳转到 list.html列表,展示所有的员工信息。
我们想要 点击员工管理,就去跳转到 员工管理的界面:
dashboard.html页面的代码修改:
修改后:
<li class="nav-item">
<a class="nav-link" 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>
同样修改list.html
对应该的代码为上述代码:
员工管理控制器:EmployeeController:
package com.al.controller;
import com.al.dao.EmployeeDao;
import com.al.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Collection;
// 员工管理信息 controller
@Controller
public class EmployeeController {
@Autowired
private EmployeeDao employeeDao;
@RequestMapping("/emps")
public String list(Model model){
Collection<Employee> employees = employeeDao.getAll();
model.addAttribute("emps",employees);
return "emps/list"; // 返回到 list界面
}
}
测试:点击Customers
,成功跳转到/emps
改进的地方:
对于这些公共的地方, 我们创建一个 commons文件夹去进行存放, commons.html 实现代码复用。
commons.html 公共部分的复用代码:顶部导航栏与侧边导航栏:
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
<a class="navbar-brand col-sm-3 col-md-2 mr-0" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Company
namea>
<input class="form-control form-control-dark w-100" type="text" placeholder="Search" aria-label="Search">
<ul class="navbar-nav px-3">
<li class="nav-item text-nowrap">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">Sign outa>
li>
ul>
nav>
<nav class="col-md-2 d-none d-md-block bg-light sidebar" th:fragment="siderbar">
<div class="sidebar-sticky">
<ul class="nav flex-column">
<li class="nav-item">
<a class="nav-link active" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<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-home">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z">path>
<polyline points="9 22 9 12 15 12 15 22">polyline>
svg>
Dashboard <span class="sr-only">(current)span>
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<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-file">
<path d="M13 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V9z">path>
<polyline points="13 2 13 9 20 9">polyline>
svg>
Orders
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<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-shopping-cart">
<circle cx="9" cy="21" r="1">circle>
<circle cx="20" cy="21" r="1">circle>
<path d="M1 1h4l2.68 13.39a2 2 0 0 0 2 1.61h9.72a2 2 0 0 0 2-1.61L23 6H6">path>
svg>
Products
a>
li>
<li class="nav-item">
<a class="nav-link" 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>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<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-bar-chart-2">
<line x1="18" y1="20" x2="18" y2="10">line>
<line x1="12" y1="20" x2="12" y2="4">line>
<line x1="6" y1="20" x2="6" y2="14">line>
svg>
Reports
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<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-layers">
<polygon points="12 2 2 7 12 12 22 7 12 2">polygon>
<polyline points="2 17 12 22 22 17">polyline>
<polyline points="2 12 12 17 22 12">polyline>
svg>
Integrations
a>
li>
ul>
<h6 class="sidebar-heading d-flex justify-content-between align-items-center px-3 mt-4 mb-1 text-muted">
<span>Saved reportsspan>
<a class="d-flex align-items-center text-muted"
href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<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-plus-circle">
<circle cx="12" cy="12" r="10">circle>
<line x1="12" y1="8" x2="12" y2="16">line>
<line x1="8" y1="12" x2="16" y2="12">line>
svg>
a>
h6>
<ul class="nav flex-column mb-2">
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<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-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Current month
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<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-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Last quarter
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<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-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Social engagement
a>
li>
<li class="nav-item">
<a class="nav-link" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<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-file-text">
<path d="M14 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V8z">path>
<polyline points="14 2 14 8 20 8">polyline>
<line x1="16" y1="13" x2="8" y2="13">line>
<line x1="16" y1="17" x2="8" y2="17">line>
<polyline points="10 9 9 9 8 9">polyline>
svg>
Year-end sale
a>
li>
ul>
div>
nav>
html>
删除掉 list.html 和 dashboard.html 中的侧边栏和顶部导航栏的代码:
dashboard.html中删除后剩下的:
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Dashboard Template for Bootstraptitle>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/dashboard.css}" rel="stylesheet">
<style type="text/css">
/* Chart.js */
@-webkit-keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
@keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
.chartjs-render-monitor {
-webkit-animation: chartjs-render-animation 0.001s;
animation: chartjs-render-animation 0.001s;
}
style>
head>
<body>
<div class="container-fluid">
<div class="row">
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<div class="chartjs-size-monitor" style="position: absolute; left: 0px; top: 0px; right: 0px; bottom: 0px; overflow: hidden; pointer-events: none; visibility: hidden; z-index: -1;">
<div class="chartjs-size-monitor-expand" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;">
<div style="position:absolute;width:1000000px;height:1000000px;left:0;top:0">div>
div>
<div class="chartjs-size-monitor-shrink" style="position:absolute;left:0;top:0;right:0;bottom:0;overflow:hidden;pointer-events:none;visibility:hidden;z-index:-1;">
<div style="position:absolute;width:200%;height:200%;left:0; top:0">div>
div>
div>
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pb-2 mb-3 border-bottom">
<h1 class="h2">Dashboardh1>
<div class="btn-toolbar mb-2 mb-md-0">
<div class="btn-group mr-2">
<button class="btn btn-sm btn-outline-secondary">Sharebutton>
<button class="btn btn-sm btn-outline-secondary">Exportbutton>
div>
<button class="btn btn-sm btn-outline-secondary dropdown-toggle">
<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-calendar"><rect x="3" y="4" width="18" height="18" rx="2" ry="2">rect><line x1="16" y1="2" x2="16" y2="6">line><line x1="8" y1="2" x2="8" y2="6">line><line x1="3" y1="10" x2="21" y2="10">line>svg>
This week
button>
div>
div>
<canvas class="my-4 chartjs-render-monitor" id="myChart" width="1076" height="454" style="display: block; width: 1076px; height: 454px;">canvas>
main>
div>
div>
<script type="text/javascript" src="asserts/js/jquery-3.2.1.slim.min.js" >script>
<script type="text/javascript" src="asserts/js/popper.min.js" >script>
<script type="text/javascript" src="asserts/js/bootstrap.min.js" >script>
<script type="text/javascript" src="asserts/js/feather.min.js" >script>
<script>
feather.replace()
script>
<script type="text/javascript" src="asserts/js/Chart.min.js" >script>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
datasets: [{
data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
lineTension: 0,
backgroundColor: 'transparent',
borderColor: '#007bff',
borderWidth: 4,
pointBackgroundColor: '#007bff'
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: false
}
}]
},
legend: {
display: false,
}
}
});
script>
body>
html>
list.html 中删除后剩下的:
DOCTYPE html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
<meta name="description" content="">
<meta name="author" content="">
<title>Dashboard Template for Bootstraptitle>
<link th:href="@{/css/bootstrap.min.css}" rel="stylesheet">
<link th:href="@{/css/dashboard.css}" rel="stylesheet">
<style type="text/css">
/* Chart.js */
@-webkit-keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
@keyframes chartjs-render-animation {
from {
opacity: 0.99
}
to {
opacity: 1
}
}
.chartjs-render-monitor {
-webkit-animation: chartjs-render-animation 0.001s;
animation: chartjs-render-animation 0.001s;
}
style>
head>
<body>
<div class="container-fluid">
<div class="row">
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<h2>Section titleh2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>#th>
<th>Headerth>
<th>Headerth>
<th>Headerth>
<th>Headerth>
tr>
thead>
<tbody>
<tr>
<td>1,001td>
<td>Loremtd>
<td>ipsumtd>
<td>dolortd>
<td>sittd>
tr>
<tr>
<td>1,002td>
<td>amettd>
<td>consecteturtd>
<td>adipiscingtd>
<td>elittd>
tr>
<tr>
<td>1,003td>
<td>Integertd>
<td>nectd>
<td>odiotd>
<td>Praesenttd>
tr>
<tr>
<td>1,003td>
<td>liberotd>
<td>Sedtd>
<td>cursustd>
<td>antetd>
tr>
<tr>
<td>1,004td>
<td>dapibustd>
<td>diamtd>
<td>Sedtd>
<td>nisitd>
tr>
<tr>
<td>1,005td>
<td>Nullatd>
<td>quistd>
<td>semtd>
<td>attd>
tr>
<tr>
<td>1,006td>
<td>nibhtd>
<td>elementumtd>
<td>imperdiettd>
<td>Duistd>
tr>
<tr>
<td>1,007td>
<td>sagittistd>
<td>ipsumtd>
<td>Praesenttd>
<td>mauristd>
tr>
<tr>
<td>1,008td>
<td>Fuscetd>
<td>nectd>
<td>tellustd>
<td>sedtd>
tr>
<tr>
<td>1,009td>
<td>auguetd>
<td>sempertd>
<td>portatd>
<td>Mauristd>
tr>
<tr>
<td>1,010td>
<td>massatd>
<td>Vestibulumtd>
<td>laciniatd>
<td>arcutd>
tr>
<tr>
<td>1,011td>
<td>egettd>
<td>nullatd>
<td>Classtd>
<td>aptenttd>
tr>
<tr>
<td>1,012td>
<td>tacititd>
<td>sociosqutd>
<td>adtd>
<td>litoratd>
tr>
<tr>
<td>1,013td>
<td>torquenttd>
<td>pertd>
<td>conubiatd>
<td>nostratd>
tr>
<tr>
<td>1,014td>
<td>pertd>
<td>inceptostd>
<td>himenaeostd>
<td>Curabiturtd>
tr>
<tr>
<td>1,015td>
<td>sodalestd>
<td>ligulatd>
<td>intd>
<td>liberotd>
tr>
tbody>
table>
div>
main>
div>
div>
<script type="text/javascript" src="asserts/js/jquery-3.2.1.slim.min.js">script>
<script type="text/javascript" src="asserts/js/popper.min.js">script>
<script type="text/javascript" src="asserts/js/bootstrap.min.js">script>
<script type="text/javascript" src="asserts/js/feather.min.js">script>
<script>
feather.replace()
script>
<script type="text/javascript" src="asserts/js/Chart.min.js">script>
<script>
var ctx = document.getElementById("myChart");
var myChart = new Chart(ctx, {
type: 'line',
data: {
labels: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"],
datasets: [{
data: [15339, 21345, 18483, 24003, 23489, 24092, 12034],
lineTension: 0,
backgroundColor: 'transparent',
borderColor: '#007bff',
borderWidth: 4,
pointBackgroundColor: '#007bff'
}]
},
options: {
scales: {
yAxes: [{
ticks: {
beginAtZero: false
}
}]
},
legend: {
display: false,
}
}
});
script>
body>
html>
测试,此时访问dashboard.html 或者 list.html(/emps)页面的结果:http://localhost:8080/emps
没有了侧边栏和顶部导航栏
插入侧边栏和导航栏:
<div th:replace="~{commons/commons::topbar}">div>
<div th:replace="~{commons/commons::siderbar}">div>
测试:登陆后,成功导入侧边栏和导航栏。
那么对于 高亮而言:就是 active 这里的动作:
commons页面:
commons.html
相应标签部分利用thymeleaf接收参数active
,利用三元运算符判断决定是否高亮 <li class="nav-item">
<a th:class="${active=='dashboard.html'?'nav-link active':'nav-link'}" href="http://getbootstrap.com/docs/4.0/examples/dashboard/#">
<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-home">
<path d="M3 9l9-7 9 7v11a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2z">path>
<polyline points="9 22 9 12 15 12 15 22">polyline>
svg>
Dashboard <span class="sr-only">(current)span>
a>
li>
...
<li class="nav-item">
<a th:class="${active=='list.html'?'nav-link active':'nav-link'}" 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.html页面:
<div th:replace="~{commons/commons::siderbar(active='dashboard.html')}">div>
同样在list.html
的侧边栏标签传递参数active
为list.html
:
<div th:replace="~{commons/commons::siderbar(active='list.html')}">div>
测试:
此时点击员工管理,员工管理高亮,成功。
修改 list.html 页面,展示员工的信息。
员工管理控制器 EmployeeController:
package com.al.controller;
import com.al.dao.EmployeeDao;
import com.al.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Collection;
// 员工管理信息 controller
@Controller
public class EmployeeController {
@Autowired
private EmployeeDao employeeDao;
@RequestMapping("/emps")
public String list(Model model){
Collection<Employee> employees = employeeDao.getAll();
model.addAttribute("emps",employees);
return "emps/list"; // 返回到 list界面
}
}
修改表格的展示信息:获取参数的信息表示:【从emps中获取,遍历表示】
<td th:text="${emp.getId()}">td>
<td>[[${emp.getLastName()}]]td>
上述修改后,重新启动程序,可以展示员工的信息列表。不过此时的
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<h2>Section titleh2>
<div class="table-responsive">
<table class="table table-striped table-sm">
<thead>
<tr>
<th>idth>
<th>lastNameth>
<th>emailth>
<th>genderth>
<th>departmentth>
<th>birthth>
<th>操作th>
tr>
thead>
<tbody>
<tr th:each="emp:${emps}">
<td th:text="${emp.getId()}">td>
<td>[[${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="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}">td>
<td>
<a class="btn btn-sm btn-primary">编辑a>
<a class="btn btn-sm btn-danger">删除a>
td>
tr>
tbody>
table>
div>
main>
小结:
在登录过程中:
在展示员工列表的时候:
提取页面公共部分,代码复用。
<nav class="navbar navbar-dark sticky-top bg-dark flex-md-nowrap p-0" th:fragment="topbar">
<div th:replace="~{commons/commons::topbar}">div>
员工信息在列表中循环/遍历获取 进行展示。
员工管理的增加:
在员工信息展示列表 list.html 界面中添加 员工增加(add)选项,和对应的url请求路径:
<h2><a class="btn btn-sm btn-success" th:href="@{/add}">添加员工a>h2>
效果:
编写 add 添加员工这一请求的 controller。
@GetMapping("/add")
public String toAddPage(Model model) {
//查出所有的部门信息,添加到departments中,用于前端接收
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments", departments);
return "emps/add";//返回到添加员工页面
}
在这里,toAddPage函数传递 部门信息给前端,便于在添加员工信息时 去选择所在部门信息。
此时的department 信息 传递给前端, 在前端显示出来所有部门的信息。 add.thml 页面的修改:
add.html :
list.html
中的内容,把员工表格信息展示的部分修改为:
<div th:replace="~{commons/commons::topbar}">div>
<div class="container-fluid">
<div class="row">
<div th:replace="~{commons/commons::siderbar(active='list.html')}">div>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<div class="form-group">
<label>LastNamelabel>
<input type="text" name="lastName" class="form-control" placeholder="lastname:zsr">
div>
<div class="form-group">
<label>Emaillabel>
<input type="email" name="email" class="form-control" placeholder="email:[email protected]">
div>
<div class="form-group">
<label>Genderlabel><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>
<div class="form-group">
<label>departmentlabel>
<select class="form-control" name="department.id">
<option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}">option>
select>
div>
<div class="form-group">
<label>Birthlabel>
<input type="text" name="date" class="form-control" placeholder="birth:yyyy/MM/dd">
div>
<button type="submit" class="btn btn-primary">添加button>
main>
div>
div>
点击添加员工,成功跳转到add.html
页面:
在前面增加员工实现的功能,已经完成了:
那么增加员工实现 功能完成后,跳转页面到 list.html中,展示所有的员工信息。
add.html:
<div th:replace="~{commons/commons::topbar}">div>
<div class="container-fluid">
<div class="row">
<div th:replace="~{commons/commons::siderbar(active='list.html')}">div>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<form th:action="@{/add}" method="post">
<div class="form-group">
<label>LastNamelabel>
<input type="text" name="lastName" class="form-control" placeholder="lastname:zsr">
div>
<div class="form-group">
<label>Emaillabel>
<input type="email" name="email" class="form-control" placeholder="email:[email protected]">
div>
<div class="form-group">
<label>Genderlabel><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>
<div class="form-group">
<label>departmentlabel>
<select class="form-control" name="department.id">
<option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}">option>
select>
div>
<div class="form-group">
<label>Birthlabel>
<input type="text" name="date" class="form-control" placeholder="birth:yyyy/MM/dd">
div>
<button type="submit" class="btn btn-primary">添加button>
form>
main>
div>
div>
编写对应的controller,同样在EmployeeController
中添加一个方法addEmp
用来处理点击添加按钮
的操作
package com.al.controller;
import com.al.dao.DepartmentDao;
import com.al.dao.EmployeeDao;
import com.al.pojo.Department;
import com.al.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Collection;
// 员工管理信息 controller
@Controller
public class EmployeeController {
@Autowired
private EmployeeDao employeeDao;
@Autowired
private DepartmentDao departmentDao;
@RequestMapping("/emps")
public String list(Model model){
Collection<Employee> employees = employeeDao.getAll();
model.addAttribute("emps",employees);
return "emps/list"; // 返回到 list界面
}
@GetMapping("/add")
public String toAddPage(Model model) {
//查出所有的部门信息,添加到departments中,用于前端接收
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments", departments);
return "emps/add";//返回到添加员工页面
}
@PostMapping("/add")
public String addEmp(Employee employee){
System.out.println("save=>" +employee);
employeeDao.addEmployee(employee); //添加一个员工
return "redirect:/emps";//重定向到/emps,刷新列表,返回到list页面
}
}
重启程序,进行测试,选择添加员工进入到 add.html界面:【注意:此时的日期是yyyy/MM/dd 的形式】
点击添加选项,url请求 /add,resful风格的 Post请求提交方式,响应的控制器是 addEmp,完成员工信息添加,跳转到list.html界面。展示的所有员工信息为:这时候,发现错误,无法跳转到 list.html界面。错误的原因:
在dao层的 EmployeeDao 发生了错误:
// 增加一个员工
public void addEmployee(Employee employee){
if (employee.getId() == null){
employee.setId(initId++);
}
employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId()));
employees.put(employee.getId(), employee);
System.out.println(employee);
}
经过测试发现:
关于 departmentDao 中的方法无法调用。???错误还没解决掉
但是如果有问题的话,在EmployeeController 控制器 中的 toAddPage里面的
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments", departments);
这两行代码应该生效才对。 还是不对劲。…
界面中,关于员工信息列表展示的时候,操作栏添加了 编辑 和 删除 的选项。同样的,对于编辑选项:【和添加员工信息的步骤类似】
点击编辑选项,
<a class="btn btn-sm btn-primary" th:href="@{/emp/{id}(id=${emp.getId()})}">编辑a>
响应 url请求,获取前端传递过来的员工 id 信息,跳转到 update.html 页面,展示员工 目前的信息:
@GetMapping("/emp/{id}")
public String toUpdatePage(@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 "emps/update";
}
这里的信息展示 和 add.html页面类似,复制进行修改:
不能每次修改一次员工信息,就让id+1, 需要id隐藏域标签
<input type="hidden" name="id" th:value="${emp.getId()}">
显示员工的原有部门信息,以及所有的部门信息
<select class="form-control" name="department.id">
<option th:selected="${emp.getDepartment().getId() == department.getId()}" th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}">option>
select>
规定一下显示的日期格式
<input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}" type="text" name="birth" class="form-control" placeholder="birth:yyyy/MM/dd">
update.html:
<div th:replace="~{commons/commons::topbar}">div>
<div class="container-fluid">
<div class="row">
<div th:replace="~{commons/commons::siderbar(active='list.html')}">div>
<main role="main" class="col-md-9 ml-sm-auto col-lg-10 pt-3 px-4">
<form th:action="@{/updateEmp}" method="post">
<input type="hidden" name="id" th:value="${emp.getId()}">
<div class="form-group">
<label>LastNamelabel>
<input th:value="${emp.getLastName()}" type="text" name="lastName" class="form-control" placeholder="lastname:zsr">
div>
<div class="form-group">
<label>Emaillabel>
<input th:value="${emp.getEmail()}" type="email" name="email" class="form-control" placeholder="email:[email protected]">
div>
<div class="form-group">
<label>Genderlabel><br/>
<div class="form-check form-check-inline">
<input th:checked="${emp.getGender()==1}" 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 th:checked="${emp.getGender()==0}"class="form-check-input" type="radio" name="gender" value="0">
<label class="form-check-label">女label>
div>
div>
<div class="form-group">
<label>departmentlabel>
<select class="form-control" name="department.id">
<option th:selected="${emp.getDepartment().getId() == department.getId()}" th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}">option>
select>
div>
<div class="form-group">
<label>Birthlabel>
<input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}" type="text" name="birth" class="form-control" placeholder="birth:yyyy/MM/dd">
div>
<button type="submit" class="btn btn-primary">修改button>
form>
main>
div>
div>
员工信息修改后, 需要重新跳转 或者重定向到 员工列表的页面。
在 update.html 这个修改员工信息页面完成后, 提交动作需要执行 发送 更新页面完成这个请求, 然后才能重定向到员工列表页面:
update.html:
<form th:action="@{/updateEmp}" method="post">
对应的控制器 Controller:
修改
按钮的请求。同样在EmployeeController
中添加一个方法EditEmp
用来处理edit
页面点击添加的操作 @PostMapping("/updateEmp")
public String updataEmp(Employee employee){
employeeDao.addEmployee(employee);//添加一个员工
return "redirect:/emps";//添加完成重定向到/emps,刷新列表
}
进行测试,进入添加页面,填写相关信息
点击修改,返回list.html页面:【我取消掉关于employeeDao中增加员工中的departmentDao 才有如下结果】
同样的错误:?????????????????????????????
关于 departmentDao 中的方法无法调用。???错误还没解决掉
但是如果有问题的话,在EmployeeController 控制器 中的 toAddPage里面的
Collection<Department> departments = departmentDao.getDepartments();
model.addAttribute("departments", departments);
这两行代码应该生效才对。 还是不对劲。…
对于操作栏的删除,响应删除请求,url请求对应的controller,直接返回list.html界面。
删除选项增加超链接:【和编辑选项类似,超链接属性 href,且传递参数应携带员工id】
<a class="btn btn-sm btn-danger" th:href="@{/delemp/{id}(id=${emp.getId()})}">删除a>
EmployeeController 控制器:
响应url请求 /delemp/{id},移除员工信息,返回重定向到 list.html 页面。
@GetMapping("/delemp/{id}")
public String deleteEmp(@PathVariable("id")Integer id, Model model){
employeeDao.deleteEmployeeByID(id);
return "redirect:/emps";//添加完成重定向到/emps,刷新列表
}
测试,成功删除。
只需要在templates
目录下新建一个error
包,然后将404.html
放入其中,报错SpringBoot就会自动找到这个页面。
员工管理系统进行注销,即移除掉 session。
注销的功能在 commons.html文件中:
href
属性,实现点击发起请求/user/logout
<li class="nav-item text-nowrap">
<a class="nav-link" th:href="@{/user/logout}">Sign out</a>
</li>
控制器controller,响应 /user/logout 请求,在LoginController
中编写对应的方法,清除session,并重定向到首页。
@RequestMapping("/user/logout")
public String logout(HttpSession session) {
session.invalidate();
return "redirect:/index.html";
}
测试,重启程序,成功注销。
如何写一个网站呢?
前端
设计数据库【数据库表的设计、持久性、保护、备份】
后端操作数据库。【事务、设计模式,提高开发效率的】
后端和前端的数据交互。数据接口对接:json,对象 all in one
对于这个员工管理系统,整合mybatis去完成对数据库操作。
数据库排序规则:
建立的数据库表:
department 部门表:
SHOW CREATE DATABASE school;
create table `department`(
`id` int(4) not null auto_increment comment'部门id',
`departmentName` VARCHAR(30) NOT NULL DEFAULT '机密部门' COMMENT '姓名',
primary key(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
insert into `department`(`id`,`departmentName`)
values(101,'户部'),(102,'兵部'),(103,'吏部'),(104,'刑部'),(105,'礼部'),(106,'工部')
外键
概念:如果公共关键字在一个关系中是主关键字,那么这个公共关键字被称为另一个关系的外键。由此可见,外键表示了两个关系之间的相关联系。以另一个关系的外键作主关键字的表被称为主表,具有此外键的表被称为主表的从表。
在实际操作中,将一个表的值放入第二个表来表示关联,所使用的值是第一个表的主键值(在必要时可包括复合主键值)。此时,第二个表中保存这些值的属性称为外键(foreign key)。
作用:保持数据一致性,完整性,主要目的是控制存储在外键表中的数据,约束。使两张表形成关联,外键只能引用外表中的列的值或使用空值。
创建表的时候指定外键的约束:
方式一:在创建表的时候,增加约束(麻烦,复杂) CREATE TABLE IF NOT EXISTS `school`.`grade` ( `gradeid` INT(10) NOT NULL auto_increment COMMENT '年级编号', `gradename` VARCHAR(10) not null COMMENT '年级名字', PRIMARY KEY(`gradeid`) )ENGINE=INNODB DEFAULT CHARSET=utf8 CREATE TABLE if not EXISTS`student` ( `id` int(4) unsigned zerofill NOT NULL AUTO_INCREMENT COMMENT '学号', `name` varchar(20) NOT NULL DEFAULT '匿名' COMMENT '姓名', `pwd` varchar(20) NOT NULL DEFAULT '123456' COMMENT '密码', `sex` varchar(2) NOT NULL DEFAULT '男' COMMENT '性别', `gradeid` INT(10) NOT NULL auto_increment COMMENT '年级编号', `birthday` datetime DEFAULT NULL COMMENT '出生日期', `address` varchar(60) DEFAULT NULL COMMENT '住址', `email` varchar(50) DEFAULT NULL COMMENT '邮箱', PRIMARY KEY (`id`), KEY `FK_gradeid` (`gradeid`), CONSTRAINT `FK_gradeid` FOREIGN KEY (`gradeid`) REFERENCES `grade` (`gradeid`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8
创建表之后再去修改:
方式二:创建表之后再创建外键关系 alter table `student` add constraint `FK_gradeid` foreign key(`gradeid`) references `grade` (`gradeid`); -- alter table `表名` -- add constraint `约束名` foreign key(作为外键的列) references 哪个表(哪个字段);
删除有外键关系的表的时候,必须要先删除引用别人的表(从表),再删除自己的表()。即先删除子表,再删除主表。
以上的操作都是物理外键,数据库级别的外键,,我们不建议使用,避免数据库过多造成困扰。
最佳实践
- 数据库就是单纯的表,只用来存数据,只有行和列
- 我们想使用多张表的数据,想使用外键,程序去实现
员工表:employee
create table `employee`( `id` INT(4) NOT NULL AUTO_INCREMENT COMMENT'员工id', `lastName` VARCHAR(30) NOT NULL COMMENT '姓名', `email` VARCHAR(30) NOT NULL DEFAULT '*.com' COMMENT '邮箱', `gender` INT(4) NOT NULL DEFAULT '0' COMMENT '性别', `department` INT(4) NOT NULL COMMENT '部门id', `birth`DATETIME DEFAULT NULL COMMENT '生日', primary key(`id`), key `FK_id`(`department`), constraint `FK_id` foreign key(`department`) references `department`(`id`) )ENGINE=INNODB DEFAULT CHARSET=utf8 insert into `employee`(`id`,`lastName`,`email`,`gender`,`department`,`birth`) values(1001,'AAA','[email protected]',0,101,'2020-04-05'),(1002,'BBB','[email protected]',0,102,'2020-04-06'), (1003,'CCC','[email protected]',1,103,'2020-05-05'),(1004,'DDD','[email protected]',1,104,'2020-10-05'), (1005,'EEE','[email protected]',1,103,'2020-11-05'),(1006,'FFF','[email protected]',0,106,'2021-04-05')
新建一个SpringBoot项目:springboot-employee-management-system-mybatis。
选择的springBoot配置有 web、JDBC、MySQL的:
注意:我们需要导入 thymeleaf 模板引擎的maven依赖:
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
项目建立之后,将原来的静态资源文件,导入到现在的这个项目中。
对数据库配置文件进行连接的时候,导入 JDBC、mysql、Mybatis的maven依赖。
pom.xml :
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0modelVersion>
<parent>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-parentartifactId>
<version>2.6.7version>
<relativePath/>
parent>
<groupId>com.ALgroupId>
<artifactId>springboot-employee-management-system-mybatisartifactId>
<version>0.0.1-SNAPSHOTversion>
<name>springboot-employee-management-system-mybatisname>
<description>Employee Management System Mybatis for Spring Bootdescription>
<properties>
<java.version>1.8java.version>
properties>
<dependencies>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-jdbcartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-webartifactId>
dependency>
<dependency>
<groupId>mysqlgroupId>
<artifactId>mysql-connector-javaartifactId>
<scope>runtimescope>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-testartifactId>
<scope>testscope>
dependency>
<dependency>
<groupId>org.projectlombokgroupId>
<artifactId>lombokartifactId>
dependency>
<dependency>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-starter-thymeleafartifactId>
dependency>
<dependency>
<groupId>org.mybatis.spring.bootgroupId>
<artifactId>mybatis-spring-boot-starterartifactId>
<version>2.2.2version>
dependency>
<dependency>
<groupId>com.alibabagroupId>
<artifactId>druidartifactId>
<version>1.1.21version>
dependency>
<dependency>
<groupId>log4jgroupId>
<artifactId>log4jartifactId>
<version>1.2.17version>
dependency>
dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.bootgroupId>
<artifactId>spring-boot-maven-pluginartifactId>
plugin>
plugins>
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
build>
project>
数据库配置资源文件:
我使用 application.properties 去进行数据信息配置 。
application.properties 资源配置文件:
# 自定义ul请求目录
#server.servlet.context-path=/al
#自定义配置:国际化资源文件
spring.messages.basename=i18n.login
# 数据库配置信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
进行测试,建立数据库连接成功。
测试类文件:
package com.AL;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
@SpringBootTest
class SpringbootEmployeeManagementSystemMybatisApplicationTests {
// DI 注入数据源。从spring IOC 容器中获取数据配置信息
@Autowired
DataSource dataSource;
@Test
void contextLoads() throws SQLException {
// 查看默认的数据源: com.zaxxer.hikari.HikariDataSource
System.out.println(dataSource.getClass());
// 获得数据库连接:
Connection connection = dataSource.getConnection()
System.out.println(connection); // HikariProxyConnection@522082506 wrapping com.mysql.cj.jdbc.ConnectionImpl@57f847af
//DruidDataSource druidDataSource = (DruidDataSource) dataSource;
//System.out.println("druidDataSource 数据源最大连接数:" + druidDataSource.getMaxActive());
//System.out.println("druidDataSource 数据源初始化连接数:" + druidDataSource.getInitialSize());
connection.close(); // 关闭连接
}
}
显示出了默认的数据源:
创建实体类:Department 和 Employee 类。
Department:
package com.AL.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
// 部门表
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Department {
private Integer id;
private String departmentName;
}
Employee:
package com.AL.pojo;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
// 员工表
@Data
@NoArgsConstructor
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender; // 0:女 1:男。 用数字表示性别男女
private Department department;
private Date birth;
public Employee(Integer id, String lastName, String email, Integer gender, Department department, Date birth) {
this.id = id;
this.lastName = lastName;
this.email = email;
this.gender = gender;
this.department = department;
// 默认的创建日期
//this.birth = birth;
this.birth = new Date();
}
}
在这里,为了和上面的区分开来,创建Mapper目录以及对应Mapper接口、Mapper接口映射文件,完成对数据库的操作。
DepartmentMapper.java
package com.AL.mapper;
import com.AL.pojo.Department;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
//@Mapper : 表示本类是一个 MyBatis 的 Mapper
@Mapper
@Repository
public interface DepartmentMapper {
// 获取所有部门信息
List<Department> getDepartments();
// 通过id获得部门
Department getDepartment(Integer id);
}
DepartmentMapper.xml:关于DepartmentMapper 接口的映射文件,实现类。 完成对数据库的操作。
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.AL.mapper.DepartmentMapper">
<select id="getDepartments" resultType="com.al.pojo.Department">
select * from department;
select>
<select id="getDepartment" resultType="Department" parameterType="int">
select * from department where id = #{id};
select>
mapper>
在配置文件中,整合myabtis。绑定关于dao层的接口和接口实现类 映射文件:
application.properties:
# 数据库配置信息
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.url=jdbc:mysql://localhost:3306/springboot?serverTimezone=UTC&useUnicode=true&characterEncoding=utf-8
# 整合mybatis
mybatis.type-aliases-package=com.AL.pojo
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
关于DepartmentController 的代码:
package com.AL.controller;
import com.AL.mapper.DepartmentMapper;
import com.AL.pojo.Department;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class DepartmentController {
@Autowired
DepartmentMapper departmentMapper;
// 查询全部部门
@GetMapping("/getDepartments")
public List<Department> getDepartments(){
return departmentMapper.getDepartments();
}
// 查询全部部门
@GetMapping("/getDepartment/{id}")
public Department getDepartment(@PathVariable("id") Integer id){
return departmentMapper.getDepartment(id);
}
}
启动主程序进行测试,发现错误:
org.thymeleaf.exceptions.TemplateInputException: Error resolving template [index], template might not exist or might not be accessible by any of the configured Template Resolvers
检查后,错误的原因是 thymeleaf 的依赖重复了,写了两遍。
在新建项目之后,去进行关于 controller 层的测试,发现了错误:
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'departmentController': Unsatisfied dependency expressed through field 'departmentMapper';
错误的意思是:创建名为“departmentController”的bean时出错:通过字段“departmentMapper”表示的依赖关系不满足;
检查发现,是我的DepartmentMapper.xml中的代码写错了,修改后:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.AL.mapper.DepartmentMapper">
<select id="getDepartments" resultType="com.AL.pojo.Department">
select * from department;
select>
<select id="getDepartment" resultType="com.AL.pojo.Department" parameterType="int">
select * from department where id = #{id};
select>
mapper>
启动主程序,进行测试:http://localhost:8080/getDepartments
对于根据 id 访问部门信息:http://localhost:8080/getDepartment/102
那么对于员工信息Employee 的CRUD操作,同样和部门信息类似,去建立dao层、controller进行测试,完成相关的操作。
EmployeeMapper:
package com.AL.mapper;
import com.AL.pojo.Employee;
import org.apache.ibatis.annotations.Mapper;
import org.springframework.stereotype.Repository;
import java.util.List;
//@Mapper : 表示本类是一个 MyBatis 的 Mapper
@Mapper
@Repository
public interface EmployeeMapper {
// 获取所有员工信息
List<Employee> getEmployees();
// 新增一个员工
int addEmployee(Employee employee);
// 通过id获得员工信息
Employee getEmployeeById(Integer id);
// 通过id删除员工
int deleteEmployeeByID(Integer id);
}
EmployeeMapper.xml:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.AL.mapper.EmployeeMapper">
<select id="getEmployees" resultType="com.AL.pojo.Employee">
select * from employee
select>
<delete id="deleteEmployeeByID" parameterType="int">
delete from employee where id=#{id}
delete>
mapper>
package com.AL.controller;
import com.AL.mapper.EmployeeMapper;
import com.AL.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
@RestController
public class EmployeeController {
@Autowired
EmployeeMapper employeeMapper;
// 获取所有员工信息
@GetMapping("/getEmployees")
public List<Employee> getEmployees(){
return employeeMapper.getEmployees();
}
// 通过id删除员工
@GetMapping("/delete/{id}")
public int delete(@PathVariable("id") Integer id){
return employeeMapper.deleteEmployeeByID(id);
}
}
测试,访问 http://localhost:8080/getEmployees ,获取的所有 员工信息列表为:
[{"id":1001,"lastName":"AAA","email":"[email protected]","gender":0,"department":101,"birth":"2020-04-05T00:00:00.000+00:00"},{"id":1002,"lastName":"BBB","email":"[email protected]","gender":0,"department":102,"birth":"2020-04-06T00:00:00.000+00:00"},{"id":1003,"lastName":"CCC","email":"[email protected]","gender":1,"department":103,"birth":"2020-05-05T00:00:00.000+00:00"},{"id":1004,"lastName":"DDD","email":"[email protected]","gender":1,"department":104,"birth":"2020-10-05T00:00:00.000+00:00"},{"id":1005,"lastName":"EEE","email":"[email protected]","gender":1,"department":103,"birth":"2020-11-05T00:00:00.000+00:00"},{"id":1006,"lastName":"FFF","email":"[email protected]","gender":0,"department":106,"birth":"2021-04-05T00:00:00.000+00:00"}]
可以发现,能够成功的显示员工所有的信息。不过 我们会想要的是关于部门的具体信息,而不仅仅是部门id。对于这种的解决方法,可以采用resultMap,映射的方法去解决。
在这里修改的时候发现,为了更好的 方便的展示信息,需要在实体类 Employee 中绑定部门Department 类的信息。如下所示:
Employee:
package com.AL.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.Date;
// 员工表
@Data
@NoArgsConstructor
@AllArgsConstructor
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender; // 0:女 1:男。 用数字表示性别男女
private Integer department; //原有的属性,对应数据库的 department id, 但对应的想要的是一个表
private Date birth;
private Department eDepartment; // 冗余设计
}
多对一: 关联。 一对多:集合。
如何让员工表格和 部门表格去建立连接, 关联 起来。 需要用到map 以及 association。
思路:
1.查询所有的员工信息
2.根据查询出来的员工信息的 department(即部门id),寻找对应的部门信息
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.AL.mapper.EmployeeMapper">
<resultMap id="EmployeeDepartment" type="Employee">
<result property="id" column="id"/>
<result property="lastName" column="LastName"/>
<result property="email" column="email"/>
<result property="gender" column="gender"/>
<result property="birth" column="birth"/>
<association property="eDepartment" column="department" javaType="Department" select="getDepartment"/>
resultMap>
<select id="getDepartment" resultType="Department">
select * from department where id = #{id}
select>
<select id="getEmployees" resultMap="EmployeeDepartment">
select * from employee
select>
<delete id="deleteEmployeeByID" parameterType="int">
delete from employee where id=#{id}
delete>
mapper>
这时,重新启动程序进行测试,成功显示 department的所有信息。
分析:
association property=“eDepartment” column=“department” javaType=“Department” select=“getDepartment” 在这里面的 property 对应实体类中的属性,column对应数据库表中的字段名,javaType表示多对一(该字段对应的Java类型)。从这里去绑定部门 Department 的信息。
将查询到的结果进行封装到对象的属性中,重新进行展示。
<resultMap id="EmployeeDepartment2" type="Employee">
<result property="id" column="eid"/>
<result property="lastName" column="lastName"/>
<result property="email" column="email"/>
<result property="gender" column="gender"/>
<result property="birth" column="birth"/>
<association property="eDepartment" javaType="Department">
<result property="id" column="did"/>
<result property="departmentName" column="dname"/>
association>
resultMap>
<select id="getEmployees" resultMap="EmployeeDepartment2">
select e.id as eid,lastName,email,gender,birth,d.id as did,d.departmentName as dname
from department d,employee e
where d.id = e.department
select>
启动程序,进行测试,可以成功的查询到所有员工的信息以及对应的部门id和部门名称。
[{"id":1001,"lastName":"AAA","email":"[email protected]","gender":0,"birth":"2020-04-05T00:00:00.000+00:00","edepartment":{"id":101,"departmentName":"户部"}},{"id":1002,"lastName":"BBB","email":"[email protected]","gender":0,"birth":"2020-04-06T00:00:00.000+00:00","edepartment":{"id":102,"departmentName":"兵部"}},{"id":1003,"lastName":"CCC","email":"[email protected]","gender":1,"birth":"2020-05-05T00:00:00.000+00:00","edepartment":{"id":103,"departmentName":"吏部"}},{"id":1004,"lastName":"DDD","email":"[email protected]","gender":1,"birth":"2020-10-05T00:00:00.000+00:00","edepartment":{"id":104,"departmentName":"刑部"}},{"id":1005,"lastName":"EEE","email":"[email protected]","gender":1,"birth":"2020-11-05T00:00:00.000+00:00","edepartment":{"id":103,"departmentName":"吏部"}},{"id":1006,"lastName":"FFF","email":"[email protected]","gender":0,"birth":"2021-04-05T00:00:00.000+00:00","edepartment":{"id":106,"departmentName":"工部"}}]
分析:【自己的理解】
在这里的
里面的 eDepartment 是Employee实体类的一个属性,这个属性对应的其实是多个字段(一个类Department)。 那么在这种 resulMap的map映射里面,将这些查询到的结果 字段值 重新封装到一个对应的表里面,对应着 想要的对象属性,然后去进行展示。
对于剩下的CRUD操作代码的完善。
EmployeeMapper.xml:
DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.AL.mapper.EmployeeMapper">
<resultMap id="EmployeeDepartment2" type="Employee">
<result property="id" column="eid"/>
<result property="lastName" column="lastName"/>
<result property="email" column="email"/>
<result property="gender" column="gender"/>
<result property="birth" column="birth"/>
<association property="eDepartment" javaType="Department">
<result property="id" column="did"/>
<result property="departmentName" column="dname"/>
association>
resultMap>
<select id="getEmployees" resultMap="EmployeeDepartment2">
select e.id as eid,lastName,email,gender,birth,d.id as did,d.departmentName as dname
from department d,employee e
where d.id = e.department
select>
<insert id="addEmployee" parameterType="Employee">
insert into employee (lastName,email,gender,department,birth)
values (#{lastName},#{email},#{gender},#{department},#{birth});
insert>
<select id="getEmployeeById" parameterType="int" resultMap="EmployeeDepartment2">
select e.id as eid,lastName,email,gender,birth,d.id as did,d.departmentName as dname
from department d,employee e
where d.id = e.department and e.id = #{id}
select>
<delete id="deleteEmployeeByID" parameterType="int">
delete from employee where id=#{id}
delete>
mapper>
EmployeeController:
package com.AL.controller;
import com.AL.mapper.DepartmentMapper;
import com.AL.mapper.EmployeeMapper;
import com.AL.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
import java.util.Date;
import java.util.List;
@RestController
public class EmployeeController {
@Autowired
EmployeeMapper employeeMapper;
@Autowired
DepartmentMapper departmentMapper;
// 获取所有员工信息
@GetMapping("/getEmployees")
public List<Employee> getEmployees(){
return employeeMapper.getEmployees();
}
@GetMapping("/add")
public int save(){
Employee employee = new Employee();
employee.setLastName("alzn");
employee.setEmail("[email protected]");
employee.setGender(1);
employee.setEDepartment(departmentMapper.getDepartment(101));
employee.setBirth(new Date());
return employeeMapper.addEmployee(employee);
}
// 通过id获得员工信息
@GetMapping("/get/{id}")
public Employee getById(@PathVariable("id") Integer id){
return employeeMapper.getEmployeeById(id);
}
// 通过id删除员工
@GetMapping("/delete/{id}")
public int delete(@PathVariable("id") Integer id){
return employeeMapper.deleteEmployeeByID(id);
}
}
重新启动程序,进行测试,成功展示员工的信息以及对应的部门信息。
将前面的项目里面的资源配置文件复制到此时项目里面:
关于url请求拦截、静态资源放行的配置文件和用户登录验证的控制器、首页显示控制器:
首页显示控制器:
@Controller
public class IndexController {
@RequestMapping({"/", "/index.html"}) //这是一个集合
public String index(){
return "index";
}
}
去测试,看能否访问到该页面信息,去让用户进行登录。
错误信息:
Cannot find template location: classpath:/templates/ (please add some templates or check your Thymeleaf configuration)
org.thymeleaf.exceptions.TemplateInputException: Error resolving template [index], template might not exist or might not be accessible by any of the configured Template Resolvers
可以发现编译后的 targets里面没有我们的静态资源配置文件 templates:
这属于静态资源导出时的问题,我们查找后,对pom.xml 配置资源文件进行修改:
<resources>
<resource>
<directory>src/main/javadirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.xmlinclude>
includes>
<filtering>falsefiltering>
resource>
<resource>
<directory>src/main/resourcesdirectory>
<includes>
<include>**/*.propertiesinclude>
<include>**/*.yamlinclude>
<include>**/*.xmlinclude>
<include>**/*.htmlinclude>
<include>**/*.jsinclude>
<include>**/*.cssinclude>
includes>
<filtering>falsefiltering>
resource>
resources>
我们使静态资源能够导出,此时再进行测试的结果:
用户登录控制器和拦截器配置:
package com.Al.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpSession;
@Controller
//@RestController
public class LoginController {
@RequestMapping("/user/login")
public String login(@RequestParam("username") String username, @RequestParam("password") String password, Model model, HttpSession session){
if (!StringUtils.isEmpty(username) && "123456".equals(password)){
session.setAttribute("LoginUser", username);
//return "dashboard";
return "redirect:/main.html"; // 重定向
}else {
model.addAttribute("msg", "用户名或密码错误");
return "index";
}
}
@RequestMapping("/user/logout")
public String logout(HttpSession session) {
session.invalidate();
return "redirect:/index.html";
}
}
package com.AL.config;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class LoginHandlerInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
// 用户如果登录成功了,应该获取到用户的 session 信息
Object session = request.getSession().getAttribute("LoginUser");
if (session == null) {
request.setAttribute("msg", "权限不够,请先登录");
request.getRequestDispatcher("/index.html").forward(request, response);
return false;
} else {
return true;
}
}
}
地域解析器配置:
package com.AL.config;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.LocaleResolver;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
// 地区解析器:获取链接上携带的参数信息
public class MyLocaleResolver implements LocaleResolver {
// 解析请求
@Override
public Locale resolveLocale(HttpServletRequest request) {
// 获取请求中的国家化参数
String language = request.getParameter("l");
// 默认的地区
Locale locale = Locale.getDefault();
//如果请求的链接参数不为空,携带了国际化参数
if (!StringUtils.isEmpty(language)) {
String[] split = language.split("_");//zh_CN(语言_地区)
locale = new Locale(split[0], split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
MVC配置信息:
package com.AL.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
registry.addViewController("/main.html").setViewName("dashboard");
}
// 将自定义的国际化配置放入 Spring容器中,使组件生效
@Bean
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginHandlerInterceptor())
.addPathPatterns("/**")
.excludePathPatterns("/index.html", "/", "/user/login", "/css/**", "/js/**", "/img/**");
}
}
启动程序,进行测试,发现能够成功登录,且也可以进行拦截没有用户session的url请求。
在整合mybatis时测试的控制器,直接返回json形式的数据。只管展示。
在这里,为了和原先模拟数据库显示前端界面,需要对控制器中后端传递给前端数据 的代码进行修改。
在controller包下创建 EmployeeControllerDemo02 控制器。查询员工所有的信息,后端程序将结果封装在 Model 中的 emps 中,传递给前端。前端的emps 进行foreach 进行展示员工的信息。
EmployeeControllerDemo02:
package com.AL.controller;
import com.AL.mapper.DepartmentMapper;
import com.AL.mapper.EmployeeMapper;
import com.AL.pojo.Employee;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.List;
// 员工管理信息
@Controller
public class EmployeeControllerDemo02 {
@Autowired
EmployeeMapper employeeMapper;
@Autowired
DepartmentMapper departmentMapper;
@RequestMapping("/emps")
public String list(Model model){
List<Employee> employeeList = employeeMapper.getEmployees();
model.addAttribute("emps",employeeList);
return "emps/list"; // 返回到 list界面
}
}
前端界面显示 员工的信息。
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender; // 0:女 1:男。 用数字表示性别男女
private Integer department; //原有的属性,对应数据库的 department id, 但对应的想要的是一个表
private Date birth;
private Department eDepartment; // 冗余设计
}
create table `employee`(
`id` INT(4) NOT NULL AUTO_INCREMENT COMMENT'员工id',
`lastName` VARCHAR(30) NOT NULL COMMENT '姓名',
`email` VARCHAR(30) NOT NULL DEFAULT '*.com' COMMENT '邮箱',
`gender` INT(4) NOT NULL DEFAULT '0' COMMENT '性别',
`department` INT(4) NOT NULL COMMENT '部门id',
`birth`DATETIME DEFAULT NULL COMMENT '生日',
primary key(`id`),
key `FK_id`(`department`),
constraint `FK_id` foreign key(`department`) references `department`(`id`)
)ENGINE=INNODB DEFAULT CHARSET=utf8
而前面的,我们编写的dao层中的 EmployeeMapper.xml中查询所有的员工信息,返回的结果为:
<resultMap id="EmployeeDepartment2" type="Employee">
<result property="id" column="eid"/>
<result property="lastName" column="lastName"/>
<result property="email" column="email"/>
<result property="gender" column="gender"/>
<result property="birth" column="birth"/>
<association property="eDepartment" javaType="Department">
<result property="id" column="did"/>
<result property="departmentName" column="dname"/>
association>
resultMap>
<select id="getEmployees" resultMap="EmployeeDepartment2">
select e.id as eid,lastName,email,gender,birth,d.id as did,d.departmentName as dname
from department d,employee e
where d.id = e.department
select>
我们需要在前端中修改的部分就是关于 获取部门信息 重新进行展示的部分。如下所示:
list.html :
<tr th:each="emp:${emps}">
<td th:text="${emp.getId()}">td>
<td>[[${emp.getLastName()}]]td>
<td th:text="${emp.getEmail()}">td>
<td th:text="${emp.getGender()==0?'女':'男'}">td>
<td th:text="${emp.getEDepartment().getDepartmentName()}">td>
<td th:text="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm:ss')}">td>
<td>
<a class="btn btn-sm btn-primary" th:href="@{/emp/{id}(id=${emp.getId()})}">编辑a>
<a class="btn btn-sm btn-danger" th:href="@{/delemp/{id}(id=${emp.getId()})}">删除a>
td>
tr>
测试,访问员工管理界面,查看是否成功显示所有的 员工信息。
增加员工实现的步骤:
EmployeeControllerDemo02:控制器中的 toadd 和add 方法,去响应对应着请求url
@GetMapping("/add")
public String toAddPage(Model model) {
//查出所有的部门信息,添加到departments中,用于前端接收
Collection<Department> departments = departmentMapper.getDepartments();
model.addAttribute("departments", departments);
return "emps/add";//返回到添加员工页面
}
@PostMapping("/add")
public String addEmp(Employee employee){
System.out.println("save=>" +employee);
employeeMapper.addEmployee(employee); //添加一个员工
return "redirect:/emps";//重定向到/emps,刷新列表,返回到list页面
}
add.html:员工信息添加,重点注意的是 部门信息展示
<form th:action="@{/add}" method="post">
<div class="form-group">
<label>LastNamelabel>
<input type="text" name="lastName" class="form-control" placeholder="lastname:zsr">
div>
<div class="form-group">
<label>Emaillabel>
<input type="email" name="email" class="form-control" placeholder="email:[email protected]">
div>
<div class="form-group">
<label>Genderlabel><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>
<div class="form-group">
<label>departmentlabel>
<select class="form-control" name="department">
<option th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}">option>
select>
div>
<div class="form-group">
<label>Birthlabel>
<input type="text" name="birth" class="form-control" placeholder="birth:yyyy/MM/dd">
div>
<button type="submit" class="btn btn-primary">添加button>
form>
测试,查看跳转到 添加员工界面:
点击添加选项,成功重定向到 list.html页面,展示所有员工信息:
解决了这里的之后,那么在前面的那里,员工添加 信息 出错的原因,是不是也是这个呢?
我需要对springboot-03-system 项目中的add.html 的前端传递部门信息进行修改。查看这个问题
修改员工的信息:和增加员工信息的步骤类似。
EmployeeControllerDemo02:
@GetMapping("/emp/{id}")
public String toUpdatePage(@PathVariable("id")Integer id, Model model){
//查出原来的数据
Employee employee = employeeMapper.getEmployeeById(id);
model.addAttribute("emp", employee);
//查出所有部门的信息
Collection<Department> departments = departmentMapper.getDepartments();
model.addAttribute("departments", departments);
return "emps/update";
}
@PostMapping("/updateEmp")
public String updataEmp(Employee employee){
employeeMapper.addEmployee(employee);//添加一个员工
return "redirect:/emps";//添加完成重定向到/emps,刷新列表
}
update.html:
<div class="form-group">
<label>departmentlabel>
<select class="form-control" name="department">
<option th:selected="${emp.getEDepartment().getId() == department.getId()}" th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}">option>
select>
div>
进行测试,查看是否能够修改员工信息:
修改后重定向到 list.html 页面:
直接删除员工信息,重定向到 list.html页面。
EmployeeControllerDemo02:
@GetMapping("/delemp/{id}")
public String deleteEmp(@PathVariable("id")Integer id, Model model){
employeeMapper.deleteEmployeeByID(id);
return "redirect:/emps";//添加完成重定向到/emps,刷新列表
}
测试,删除员工。成功完成。