赞
踩
参考的代码和资源链接: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.projectlombok</groupId>
<artifactId>lombok</artifactId>
</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<Integer, Department> departments; // Integer代替数据库表id,Department表示数据库表中部门的具体信息。
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<Integer, Employee> employees = null;
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", "1356207897@qq.com", 1, new Department(101, "户部"), new Date())); employees.put(1002, new Employee(1002, "BBB", "1356207897@qq.com", 0, new Department(102, "兵部"), new Date())); employees.put(1003, new Employee(1003, "CCC", "1356207897@qq.com", 1, new Department(103, "吏部"), new Date())); employees.put(1004, new Employee(1004, "DDD", "1356207897@qq.com", 0, new Department(104, "刑部"), new Date())); employees.put(1005, new Employee(1005, "EEE", "1356207897@qq.com", 1, new Department(105, "礼部"), new Date())); } // 主键自增 private static Integer initId = 1006; // 增加一个员工 public void addEmployee(Employee employee){ if (employee.getId() == null){ employee.setId(initId++); } employee.setDepartment(departmentDao.getDepartmentById(employee.getDepartment().getId())); employees.put(employee.getId(), employee); } // 查询所有员工信息 public Collection<Employee> getAll(){ return employees.values(); } // 通过id查询员工信息 public Employee getEmployeeById(Integer id){ return employees.get(id); } //通过id删除员工 public void 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 Bootstrap</title> <!-- Bootstrap core CSS --> <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet"> <!-- Custom styles for this template --> <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 in</h1> <label class="sr-only">Username</label> <input type="text" class="form-control" placeholder="Username" required="" autofocus=""> <label class="sr-only">Password</label> <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 in</button> <p class="mt-5 mb-3 text-muted">© 2017-2018</p> <a class="btn btn-sm">中文</a> <a class="btn btn-sm">English</a> </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 Bootstrap</title> <!-- Bootstrap core CSS --> <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet"> <!-- Custom styles for this template --> <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 in</h1> <label class="sr-only">Username</label> <input type="text" class="form-control" th:placeholder="#{login.username}" required="" autofocus=""> <label class="sr-only">Password</label> <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}"> Remember me--> <input type="checkbox" value="remember-me" th:text="#{login.remember}"> </label> </div> <!-- <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>--> <button class="btn btn-lg btn-primary btn-block" type="submit">[[#{login.btn}]]</button> <p class="mt-5 mb-3 text-muted">© 2017-2018</p> <a class="btn btn-sm">中文</a> <a class="btn btn-sm">English</a> </form> </body> </html>
此时的测试:首页:
如何通过按钮 进行中英文的转换?在index.html
页面中可以看到两个标签:
<a class="btn btn-sm">中文</a>
<a class="btn btn-sm">English</a>
在标签中加入跳转链接,点击后进行响应跳转,获取对应的信息资源参数:
<!--这里传入参数不需要使用?使用key=value-->
<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')}">English</a>
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')}">English</a>
自定义地区配置解析器: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 Bootstrap</title> <!-- Bootstrap core CSS --> <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet"> <!-- Custom styles for this template --> <link th:href="@{/css/signin.css}" rel="stylesheet"> </head> <body class="text-center"> <!--<form class="form-signin" action="dashboard.html">--> <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 in</h1> <label class="sr-only">Username</label> <input type="text" name="username" class="form-control" th:placeholder="#{login.username}" required="" autofocus=""> <label class="sr-only">Password</label> <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}"> Remember me--> <input type="checkbox" value="remember-me" th:text="#{login.remember}"> </label> </div> <!-- <button class="btn btn-lg btn-primary btn-block" type="submit" th:text="#{login.btn}">Sign in</button>--> <button class="btn btn-lg btn-primary btn-block" type="submit">[[#{login.btn}]]</button> <p class="mt-5 mb-3 text-muted">© 2017-2018</p> <!-- <a class="btn btn-sm">中文</a>--> <!-- <a class="btn btn-sm">English</a>--> <!--这里传入参数不需要使用?使用key=value--> <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')}">English</a> </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
对应该的代码为上述代码:
我们在templates
目录下新建一个包emp
,用来放所有关于员工信息的页面,我们将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"> <!--顶部导航栏,利用th:fragment提取出来,命名为topbar--> <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 name</a> <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 out</a> </li> </ul> </nav> <!--侧边栏,利用th:fragment提取出来,命名为sidebar--> <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 reports</span> <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> <!-- saved from url=(0052)http://getbootstrap.com/docs/4.0/examples/dashboard/ --> <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 Bootstrap</title> <!-- Bootstrap core CSS --> <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet"> <!-- Custom styles for this template --> <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">Dashboard</h1> <div class="btn-toolbar mb-2 mb-md-0"> <div class="btn-group mr-2"> <button class="btn btn-sm btn-outline-secondary">Share</button> <button class="btn btn-sm btn-outline-secondary">Export</button> </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> <!-- Bootstrap core JavaScript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <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> <!-- Icons --> <script type="text/javascript" src="asserts/js/feather.min.js" ></script> <script> feather.replace() </script> <!-- Graphs --> <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> <!-- saved from url=(0052)http://getbootstrap.com/docs/4.0/examples/dashboard/ --> <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 Bootstrap</title> <!-- Bootstrap core CSS --> <link th:href="@{/css/bootstrap.min.css}" rel="stylesheet"> <!-- Custom styles for this template --> <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 title</h2> <div class="table-responsive"> <table class="table table-striped table-sm"> <thead> <tr> <th>#</th> <th>Header</th> <th>Header</th> <th>Header</th> <th>Header</th> </tr> </thead> <tbody> <tr> <td>1,001</td> <td>Lorem</td> <td>ipsum</td> <td>dolor</td> <td>sit</td> </tr> <tr> <td>1,002</td> <td>amet</td> <td>consectetur</td> <td>adipiscing</td> <td>elit</td> </tr> <tr> <td>1,003</td> <td>Integer</td> <td>nec</td> <td>odio</td> <td>Praesent</td> </tr> <tr> <td>1,003</td> <td>libero</td> <td>Sed</td> <td>cursus</td> <td>ante</td> </tr> <tr> <td>1,004</td> <td>dapibus</td> <td>diam</td> <td>Sed</td> <td>nisi</td> </tr> <tr> <td>1,005</td> <td>Nulla</td> <td>quis</td> <td>sem</td> <td>at</td> </tr> <tr> <td>1,006</td> <td>nibh</td> <td>elementum</td> <td>imperdiet</td> <td>Duis</td> </tr> <tr> <td>1,007</td> <td>sagittis</td> <td>ipsum</td> <td>Praesent</td> <td>mauris</td> </tr> <tr> <td>1,008</td> <td>Fusce</td> <td>nec</td> <td>tellus</td> <td>sed</td> </tr> <tr> <td>1,009</td> <td>augue</td> <td>semper</td> <td>porta</td> <td>Mauris</td> </tr> <tr> <td>1,010</td> <td>massa</td> <td>Vestibulum</td> <td>lacinia</td> <td>arcu</td> </tr> <tr> <td>1,011</td> <td>eget</td> <td>nulla</td> <td>Class</td> <td>aptent</td> </tr> <tr> <td>1,012</td> <td>taciti</td> <td>sociosqu</td> <td>ad</td> <td>litora</td> </tr> <tr> <td>1,013</td> <td>torquent</td> <td>per</td> <td>conubia</td> <td>nostra</td> </tr> <tr> <td>1,014</td> <td>per</td> <td>inceptos</td> <td>himenaeos</td> <td>Curabitur</td> </tr> <tr> <td>1,015</td> <td>sodales</td> <td>ligula</td> <td>in</td> <td>libero</td> </tr> </tbody> </table> </div> </main> </div> </div> <!-- Bootstrap core JavaScript ================================================== --> <!-- Placed at the end of the document so the pages load faster --> <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> <!-- Icons --> <script type="text/javascript" src="asserts/js/feather.min.js"></script> <script> feather.replace() </script> <!-- Graphs --> <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 title</h2> <div class="table-responsive"> <table class="table table-striped table-sm"> <thead> <tr> <th>id</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.getId()}"></td>--> <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>
小结:
在登录过程中:
在展示员工列表的时候:
提取页面公共部分,代码复用。
<!--定义: 顶部导航栏,利用th:fragment提取出来,命名为topbar-->
<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>LastName</label> <input type="text" name="lastName" class="form-control" placeholder="lastname:zsr"> </div> <div class="form-group"> <label>Email</label> <input type="email" name="email" class="form-control" placeholder="email:xxxxx@qq.com"> </div> <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> <div class="form-group"> <label>department</label> <!--注意这里的name是department.id,因为传入的参数为id--> <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>Birth</label> <!--springboot默认的日期格式为yy/MM/dd--> <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>LastName</label> <input type="text" name="lastName" class="form-control" placeholder="lastname:zsr"> </div> <div class="form-group"> <label>Email</label> <input type="email" name="email" class="form-control" placeholder="email:xxxxx@qq.com"> </div> <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> <div class="form-group"> <label>department</label> <!--注意这里的name是department.id,因为传入的参数为id--> <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>Birth</label> <!--springboot默认的日期格式为yy/MM/dd--> <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>LastName</label> <input th:value="${emp.getLastName()}" type="text" name="lastName" class="form-control" placeholder="lastname:zsr"> </div> <div class="form-group"> <label>Email</label> <input th:value="${emp.getEmail()}" type="email" name="email" class="form-control" placeholder="email:xxxxx@qq.com"> </div> <div class="form-group"> <label>Gender</label><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>department</label> <!--注意这里的name是department.id,因为传入的参数为id--> <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>Birth</label> <!--springboot默认的日期格式为yy/MM/dd--> <!-- <input th:value="${emp.getBirth()}" type="text" name="birth" class="form-control" placeholder="birth:yyyy/MM/dd"> --> <!-- <input th:value="${#dates.format(emp.getBirth(),'yyyy-MM-dd HH:mm')}" type="text" name="birth" class="form-control" placeholder="birth:yyyy/MM/dd">--> <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
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
创建表之后再去修改:
方式二:创建表之后再创建外键关系 alter table `student` add constraint `FK_gradeid` foreign key(`gradeid`) references `grade` (`gradeid`); -- alter table `表名` -- add constraint `约束名` foreign key(作为外键的列) references 哪个表(哪个字段);
- 1
- 2
- 3
- 4
- 5
- 6
删除有外键关系的表的时候,必须要先删除引用别人的表(从表),再删除自己的表()。即先删除子表,再删除主表。
以上的操作都是物理外键,数据库级别的外键,,我们不建议使用,避免数据库过多造成困扰。
最佳实践
- 数据库就是单纯的表,只用来存数据,只有行和列
- 我们想使用多张表的数据,想使用外键,程序去实现
员工表: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','1356207897@qq.com',0,101,'2020-04-05'),(1002,'BBB','1356207897@qq.com',0,102,'2020-04-06'), (1003,'CCC','1356207897@qq.com',1,103,'2020-05-05'),(1004,'DDD','1356207897@qq.com',1,104,'2020-10-05'), (1005,'EEE','1356207897@qq.com',1,103,'2020-11-05'),(1006,'FFF','1356207897@qq.com',0,106,'2021-04-05')
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
数据库表成功创建后的结果:
新建一个SpringBoot项目:springboot-employee-management-system-mybatis。
选择的springBoot配置有 web、JDBC、MySQL的:
注意:我们需要导入 thymeleaf 模板引擎的maven依赖:
<!--thymeleaf. 我们都是基于 3.x 开发的-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
项目建立之后,将原来的静态资源文件,导入到现在的这个项目中。
对数据库配置文件进行连接的时候,导入 JDBC、mysql、Mybatis的maven依赖。
pom.xml :
<?xml version="1.0" encoding="UTF-8"?> <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.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.6.7</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>com.AL</groupId> <artifactId>springboot-employee-management-system-mybatis</artifactId> <version>0.0.1-SNAPSHOT</version> <name>springboot-employee-management-system-mybatis</name> <description>Employee Management System Mybatis for Spring Boot</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> </dependency> <!--thymeleaf. 我们都是基于 3.x 开发的--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <!--mybatis的maven依赖--> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>2.2.2</version> </dependency> <!-- Druid数据源--> <dependency> <groupId>com.alibaba</groupId> <artifactId>druid</artifactId> <version>1.1.21</version> </dependency> <!-- log4j日志--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> <!--静态资源导出问题--> <resources> <resource> <directory>src/main/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </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 接口的映射文件,实现类。 完成对数据库的操作。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace= 绑定一个对应的Dao/Mapper接口 <mapper namespace="org.mybatis.example.BlogMapper"> <select id="selectBlog" resultType="Blog"> select * from Blog where id = #{id} --> <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中的代码写错了,修改后:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace= 绑定一个对应的Dao/Mapper接口 <mapper namespace="org.mybatis.example.BlogMapper"> <select id="selectBlog" resultType="Blog"> select * from Blog where id = #{id} --> <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:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace= 绑定一个对应的Dao/Mapper接口 <mapper namespace="org.mybatis.example.BlogMapper"> <select id="selectBlog" resultType="Blog"> select * from Blog where id = #{id} --> <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":"1356207897@qq.com","gender":0,"department":101,"birth":"2020-04-05T00:00:00.000+00:00"},{"id":1002,"lastName":"BBB","email":"1356207897@qq.com","gender":0,"department":102,"birth":"2020-04-06T00:00:00.000+00:00"},{"id":1003,"lastName":"CCC","email":"1356207897@qq.com","gender":1,"department":103,"birth":"2020-05-05T00:00:00.000+00:00"},{"id":1004,"lastName":"DDD","email":"1356207897@qq.com","gender":1,"department":104,"birth":"2020-10-05T00:00:00.000+00:00"},{"id":1005,"lastName":"EEE","email":"1356207897@qq.com","gender":1,"department":103,"birth":"2020-11-05T00:00:00.000+00:00"},{"id":1006,"lastName":"FFF","email":"1356207897@qq.com","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),寻找对应的部门信息
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace= 绑定一个对应的Dao/Mapper接口 <mapper namespace="org.mybatis.example.BlogMapper"> <select id="selectBlog" resultType="Blog"> select * from Blog where id = #{id} --> <mapper namespace="com.AL.mapper.EmployeeMapper"> <!--查询员工信息: 单纯的 department id--> <!-- <select id="getEmployees" resultType="com.AL.pojo.Employee">--> <!-- select * from employee--> <!-- </select>--> <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 集合:collection--> <!--复杂的属性:我们需要单独处理 对象 关联:association 集合:collection association : 关联,多对一 column : 数据库对应的列名 property : 对应属性名 javaType : 多对一 字段对应的Java类型 select : 关联一个语句 --> <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":"1356207897@qq.com","gender":0,"birth":"2020-04-05T00:00:00.000+00:00","edepartment":{"id":101,"departmentName":"户部"}},{"id":1002,"lastName":"BBB","email":"1356207897@qq.com","gender":0,"birth":"2020-04-06T00:00:00.000+00:00","edepartment":{"id":102,"departmentName":"兵部"}},{"id":1003,"lastName":"CCC","email":"1356207897@qq.com","gender":1,"birth":"2020-05-05T00:00:00.000+00:00","edepartment":{"id":103,"departmentName":"吏部"}},{"id":1004,"lastName":"DDD","email":"1356207897@qq.com","gender":1,"birth":"2020-10-05T00:00:00.000+00:00","edepartment":{"id":104,"departmentName":"刑部"}},{"id":1005,"lastName":"EEE","email":"1356207897@qq.com","gender":1,"birth":"2020-11-05T00:00:00.000+00:00","edepartment":{"id":103,"departmentName":"吏部"}},{"id":1006,"lastName":"FFF","email":"1356207897@qq.com","gender":0,"birth":"2021-04-05T00:00:00.000+00:00","edepartment":{"id":106,"departmentName":"工部"}}]
分析:【自己的理解】
在这里的
<association property="eDepartment" javaType="Department">
<result property="id" column="did"/>
<result property="departmentName" column="dname"/>
</association>
里面的 eDepartment 是Employee实体类的一个属性,这个属性对应的其实是多个字段(一个类Department)。 那么在这种 resulMap的map映射里面,将这些查询到的结果 字段值 重新封装到一个对应的表里面,对应着 想要的对象属性,然后去进行展示。
对于剩下的CRUD操作代码的完善。
EmployeeMapper.xml:
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <!--namespace= 绑定一个对应的Dao/Mapper接口 <mapper namespace="org.mybatis.example.BlogMapper"> <select id="selectBlog" resultType="Blog"> select * from Blog where id = #{id} --> <mapper namespace="com.AL.mapper.EmployeeMapper"> <!--查询员工信息: 单纯的 department id--> <!-- <select id="getEmployees" resultType="com.AL.pojo.Employee">--> <!-- select * from employee--> <!-- </select>--> <!--按照查询嵌套处理--> <!--<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 集合:collection–>--> <!-- <!–复杂的属性:我们需要单独处理--> <!--对象 关联:association 集合:collection--> <!--association : 关联,多对一--> <!--column : 数据库对应的列名--> <!--property : 对应属性名--> <!--javaType : 多对一 字段对应的Java类型--> <!--select : 关联一个语句--> <!--–>--> <!-- <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>--> <!-- <!–结果封装,将查询出来的列封装到对象属性中–>--> <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 * from employee where id = #{id}--> <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("hhhhhh@qq.com"); 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/java</directory> <includes> <include>**/*.properties</include> <include>**/*.xml</include> </includes> <filtering>false</filtering> </resource> <resource> <directory>src/main/resources</directory> <includes> <include>**/*.properties</include> <include>**/*.yaml</include> <include>**/*.xml</include> <include>**/*.html</include> <include>**/*.js</include> <include>**/*.css</include> </includes> <filtering>false</filtering> </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 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="${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>LastName</label> <input type="text" name="lastName" class="form-control" placeholder="lastname:zsr"> </div> <div class="form-group"> <label>Email</label> <input type="email" name="email" class="form-control" placeholder="email:xxxxx@qq.com"> </div> <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> <div class="form-group"> <label>department</label> <!--注意这里的name是department.id,因为传入的参数为id--> <!-- <select class="form-control" name="department.id">--> <!--注意: 我将这里的属性 name 值设置成了department,这样才和 employee中的属性对应--> <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>Birth</label> <!--springboot默认的日期格式为yy/MM/dd--> <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>department</label>
<!--注意这里的name是department.id,因为传入的参数为id-->
<!-- <select class="form-control" name="department.id">-->
<select class="form-control" name="department">
<!-- <option th:selected="${emp.getDepartment().getId() == department.getId()}" th:each="department:${departments}" th:text="${department.getDepartmentName()}" th:value="${department.getId()}"></option>-->
<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,刷新列表
}
测试,删除员工。成功完成。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。