這篇文章主要用於學習SpringBoot以及整合MyBatisPlus和前端框架LayUI,使用的都是最新的版本:
SpringBoot2.1.0 , MyBatisPlus3.0.6 , LayUI2.4.5 ,MySQL5.5.15, 以及SpringBoot默認支持的模板引擎Thymeleaf3.0.4,使用以上技術,瞭解SpringBoot怎麼集成以上框架技術,怎麼完成XML配置的替換等…
本來是做一個RBAC權限管理系統的,但是這樣做的話,這個博客會很臃腫,後面把它改了,重點還是介紹SpringBoot的相關知識。以及各個技術的集成,先簡單的入門,後面再統一完成項目說明,就不在做技術知識點講解了
文章目錄
SpringBoot簡介
Spring由於其繁瑣的配置,一度被人認爲“配置地獄”,各種XML、Annotation配置,讓人眼花繚亂,而且如果出錯了也很難找出原因。Spring Boot更多的是採用Java Config的方式,對Spring進行配置。
Spring Boot可以輕鬆創建獨立的,生產級的基於Spring的應用程序,您可以直接“運行”啓動服務,而不用再去配置Tomcat啓動。包括靜態資源處理,視圖解析器,註解掃描等…
- SpringBoot特徵
- 創建獨立的Spring應用程序
- 直接嵌入Tomcat,Jetty或Undertow(無需部署WAR文件)
- 提供簡潔的“入門”依賴項以簡化構建配置,簡化Maven配置
- 儘可能自動配置Spring和第三方庫,大部分數據源,Mapper掃描,組件注入都不用再處理,去處理繁瑣的Xml配置
- 提供生產就緒功能,例如指標,運行狀況檢查和外部化配置
- 絕對沒有代碼生成,也不需要XML配置
- SpringBoot2.1.0官方學習地址
- 依賴管理
每個版本的Spring Boot都提供了它支持的依賴項的精選列表。實際上,您不需要爲構建配置中的任何這些依賴項提供版本,因爲Spring Boot會爲您管理這些依賴項。當您升級Spring Boot時,這些依賴項也會以一致的方式升級。就不用再單獨使用properties來提取對應的jar包版本,SpringBoot的parent
幫你搞定.在配置對應jar的時候,不需要再寫版本號了。SpringBoot沒有提供的除外。
<! - 繼承默認值爲Spring Boot - >
<parent>
<groupId> org.springframework.boot </ groupId>
<artifactId> spring-boot-starter-parent </ artifactId>
<version> 2.1.0.RELEASE < / version>
</ parent>
如果需要,您仍然可以指定版本並覆蓋Spring Boot的建議。當然,每個版本的Spring Boot都與Spring Framework的基本版本相關聯。我們強烈建議您不要指定其版本。SpringBoot沒有提供的除外.
- SpringBoot提供的應用程序啓動器,也就是封裝好的Maven配置常用的如下,具體查看幫助文檔。
名稱 | 描述 |
---|---|
spring-boot-starter | 核心啓動器,包括自動配置支持,日誌記錄和YAML |
spring-boot-starter-aop | 使用Spring AOP和AspectJ進行面向方面編程的入門者 |
spring-boot-starter-freemarker | 使用FreeMarker視圖構建MVC Web應用程序的入門者 |
spring-boot-starter-thymeleaf | 使用Thymeleaf視圖構建MVC Web應用程序的入門者 |
spring-boot-starter-jdbc | 將JDBC與HikariCP連接池一起使用的入門者 |
spring-boot-starter-json | 配置jackson,完成對象轉換JSON的操作,需要注意的是要轉換的JSON對象需要符合JavaBean規範 |
spring-boot-starter-mail | 使用Java Mail和Spring Framework的電子郵件發送支持的初學者 |
spring-boot-starter-test | 使用JUnit,Hamcrest和Mockito等庫來測試Spring Boot應用程序的初學者 |
spring-boot-starter-validation | 使用Java Bean Validation和Hibernate Validator的初學者 |
spring-boot-starter-web | 使用Spring MVC構建Web(包括RESTful)應用程序的入門者。使用Tomcat作爲默認嵌入式容器 |
spring-boot-starter-webflux | 使用Spring Framework的Reactive Web支持構建WebFlux應用程序的初學者 |
spring-boot-starter-websocket | 使用Spring Framework的WebSocket支持構建WebSocket應用程序的初學者 |
spring-boot-starter-logging | 使用Logback進行日誌記錄的入門。默認日誌啓動器 |
spring-boot-starter-log4j2 | 使用Log4j2進行日誌記錄的入門。替代spring-boot-starter-logging |
spring-boot-starter-tomcat | 使用Tomcat作爲嵌入式servlet容器的入門者。使用的默認servlet容器啓動器spring-boot-starter-web |
spring-boot-starter-jetty | 使用Jetty作爲嵌入式servlet容器的入門。替代spring-boot-starter-tomcat |
- SpringBoot和SpringMVC常用註解
註解名稱 | 描述 |
---|---|
@SpringBootApplication | 包含了@ComponentScan、@Configuration和@EnableAutoConfiguration註解。其中@ComponentScan讓spring Boot掃描到Configuration類並把它加入到程序上下文 |
@ImportResource | 用來加載xml配置文件 |
@Bean | 用@Bean標註方法等價於XML中配置的bean |
@Value | 注入SpringBoot 核心配置文件(application.yml > application.properties )中屬性對應的值 |
@Configuration | 等同於spring的XML配置文件;使用Java代碼可以檢查類型安全。替代xml配置,通過@Bean完成注入,可以使用@ImportResource註解加載xml配置文件 |
@ConfigurationProperties | 方法註解,用於獲取核心配置文件中對應以什麼開頭的多個屬性,並自動注入到對象中 |
@Import | 用來導入其他配置類 |
@Primary | 方法註解,用於標註,如果IOC容器中存在多個相同類型的Bean,優先使用被標註的Bean對象 |
@EnableAutoConfiguration | 自動配置 |
@ExceptionHandler | 用在方法上面表示遇到這個異常就執行以下方法 |
@ControllerAdvice、@RestControllerAdivce | 包含@Component。可以被掃描到。統一處理異常,類註解,需要和@ExceptionHandler配合使用 |
@ResponseBody | 標註返回的信息使用Json轉換 |
@Controller | 用於定義控制器類,通常方法需要配合註解@RequestMapping |
@RestController | 是@Controller和@ResponseBody的合集,表示這是個控制器bean,並且是將函數的返回值直 接填入HTTP響應體中,是REST風格的控制器 |
@PathVariable | 獲取參數 |
@Autowired / @Resource | 自動注入,獲取SpringIOC容器中的bean對象 |
@ComponentScan | 組件掃描,可自動發現和裝配一些Bean |
@Component、@Service、@Repository | 把當前標註的類注入到IOC容器中,需要@ComponentScan配置註解標註類路徑,各個註解實現結果一致,只是應用場景不同,屬於語義話註解 |
- 其它常用註解
- lombok註解
註解名稱 | 描述 |
---|---|
@Data | 註解在類上;提供類所有屬性的 getting 和 setting 方法,此外還提供了equals、canEqual、hashCode、toString 方法 |
@Setter / @Gette r | 註解在屬性上;爲屬性提供 setting 方法/getting方法 |
@Log4j2 / @Slf4j | 註解在類上;爲類提供一個 屬性名爲log 的 log4j 日誌對象,和@Log4j註解類似 |
@NoArgsConstructor / @AllArgsConstructor | 註解在類上;爲類提供一個無參/有參的構造方法 |
@EqualsAndHashCode | 默認情況下,會使用所有非瞬態(non-transient)和非靜態(non-static)字段來生成equals和hascode方法,也可以指定具體使用哪些屬性。 |
@toString | 生成toString方法,默認情況下,會輸出類名、所有屬性,屬性會按照順序輸出,以逗號分割 |
@Builder | 被註解的類加個構造者模式 |
@NonNull | 如果給參數加個這個註解 參數爲null會拋出空指針異常 |
@Value | 註解和@Data類似,區別在於它會把所有成員變量默認定義爲private final修飾,並且不會生成set方法 |
@Synchronized | 加個同步鎖 |
- jackson註解
註解名稱 | 描述 |
---|---|
@JsonNaming | 字段命名映射策略: KebabCaseStrategy: 肉串策略 - 單詞小寫,使用連字符’-‘連接,SnakeCaseStrategy: 蛇形策略 - 單詞小寫,使用下劃線’_'連接;UpperCamelCaseStrategy: 駝峯策略 - 單詞首字母大寫其它小寫,不添加連接符 |
@JsonIgnoreProperties | 類註解,指定序列化時忽略這些屬性,可以用於覆蓋超類中默認輸出的屬性 |
@JsonInclude | 僅在屬性不爲空時序列化此字段,對於字符串,即null或空字符串 |
@JsonIgnore | 字段註解,序列化時忽略此字段 |
@JsonProperty | 指定序列化時的字段名,默認使用屬性名 |
@JsonFormat | 指定Date類字段序列化時的格式,java8 LocalDateTime序列號需要加入jsr310解析包 |
@JsonUnwrapped | 把成員對象中的屬性提升到其容器類,並添加給定的前綴,比如上例中: User類中有name和age兩個屬性,不使用此註解則序列化爲:… “user”: { “name”: “xxx”, “age”: 22 } …使用此註解則爲:… “user_name”: “xxx”, “user_age”: 22, |
@JsonIgnoreType | 類註解,序列化時忽略此類 |
使用IDEA工具快速構建SpringBoot入門案例
主要目的就是了解Spring的使用原理,以及它的Java替代XML配置的寫法,比如攔截器,異常處理,數據源注入,MyBatis配置信息等處理。這裏使用的IDEA使用2017.3.5這個版本。
pom.xml文件和之前的springMVC中的略有差異,使用了很多SpringBoot提供的繼承配置,直接在parent包中查找,大部分是不用提供版本號的。具體如下:
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cdh.springboot</groupId>
<artifactId>rbac-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>rbac-demo</name>
<description></description>
<!--SpringBoot核心依賴,是屬於maven的繼承父類,提供了很多集成配置,還有很多jar包的版本全局屬性等。-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.0.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--基本上包含了jdbc操作所需的所有jar:spring-jdbc,logback,log4j,sl4j,annotation... -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<!--模板引擎:thymeleaf,java8time-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<!--spring Web模塊:jackson,tomcat,validator,spring-web,webmvc-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--實體幫助類-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!--spring-boot-test,junit4.12,spring-test,json-path..-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<!--**********以上是使用IDEA工具勾選後默認生成的,以下是我自定義添加的其他依賴包*****************-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<!--mybatis-plus,mybatis-spring,mybatis,mybatis-plus-generator ...-->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.0.6</version>
</dependency>
<!--mybatis-plus完成項目構建所需模板,真實項目不需要使用-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
</dependency>
<!--數據源+session監控-->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.12</version>
</dependency>
<!-- 驗證碼 -->
<dependency>
<groupId>com.github.penggle</groupId>
<artifactId>kaptcha</artifactId>
<version>2.3.2</version>
</dependency>
<!-- 漢字轉換爲拼音 -->
<dependency>
<groupId>com.belerweb</groupId>
<artifactId>pinyin4j</artifactId>
<version>2.5.1</version>
</dependency>
</dependencies>
<build>
<plugins>
<!--springBoot打包插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在數據庫中創建兩張表,用於測試
創建表可以直接使用SQL語句完成,也可以直接在數據庫UI工具中完成,當然比較建議大家使用建模工具,這樣不僅可以提高建錶速度,也可以提高對工具的熟練度,不要覺得麻煩,這是必須要掌握的,常用的建模工具有Power Designer和EZDML,我經常用的是後者,如果要學習它,可以查看我博客中有一篇是介紹這個工具的使用表設計工具EZDML使用詳細教程
表結構和字段說明如下:
SQL腳本:
使用MybatisPlus 的代碼生成器獲取所需文件
AutoGenerator 是 MyBatis-Plus 的代碼生成器,通過 AutoGenerator 可以快速生成 Entity、Mapper、Mapper XML、Service、Controller 等各個模塊的代碼,極大的提升了開發效率。而要完成這一功能,只需要提供一個Java 可運行方法即可。文件結構如下:
-- sys_dept
/*
警告: 字段名可能非法 - status
*/
create table `sys_dept`(
`dept_id` INT auto_increment primary key not null comment '編號',
`dept_name` VARCHAR(25) comment '名稱',
`loc` VARCHAR(300) comment '地址',
`parent_id` INT comment '上級部門',
`level` VARCHAR(64) comment '層級',
`leader` VARCHAR(16) comment '部門負責人',
`phone` VARCHAR(16) comment '聯繫電話',
`order_num` INT(4) comment '排序號',
`status` INT(1) comment '狀態',
`del_flg` INT(1) comment '刪除標誌',
`create_by` VARCHAR(64) comment '創建者',
`create_time` TIMESTAMP DEFAULT 0 comment '創建時間',
`update_by` VARCHAR(64) comment '更新着',
`update_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP comment '更新時間'
);
alter table `sys_dept` comment= '部門表';
-- sys_user
/*
警告: 字段名可能非法 - status
*/
create table `sys_user`(
`user_id` INT auto_increment primary key not null comment '編號',
`user_name` VARCHAR(32) comment '真實名稱',
`log_name` VARCHAR(32) comment '登錄名',
`user_type` VARCHAR(8) comment '用戶類型 系統用戶,臨時用戶',
`dept_id` INT comment '部門編號',
`user_pwd` VARCHAR(64) comment '登錄密碼',
`email` VARCHAR(32) comment '郵箱',
`phone` VARCHAR(14) comment '聯繫方式',
`sex` INT(1) comment '性別',
`order_num` INT(4) comment '排序號',
`status` INT(1) comment '狀態',
`del_flg` INT(1) comment '刪除標誌',
`create_by` VARCHAR(64) comment '創建者',
`create_time` TIMESTAMP DEFAULT 0 comment '創建時間',
`update_by` VARCHAR(64) comment '更新者',
`update_time` TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP comment '更新時間'
);
alter table `sys_user` comment= '用戶表';
alter table `sys_user` add constraint `FK_sys_user_dept_id` foreign key (`dept_id`) references `sys_dept`(`dept_id`);
需要注意的是在MySQL5.7之前的版本在同一張表中寫入兩個日期字段必須有一個default 0否則無法執行.
MyBatisPlusGenerator.java文件信息
package com.cdh.springboot.generator;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.generator.AutoGenerator;
import com.baomidou.mybatisplus.generator.InjectionConfig;
import com.baomidou.mybatisplus.generator.config.*;
import com.baomidou.mybatisplus.generator.config.po.TableInfo;
import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy;
import com.baomidou.mybatisplus.generator.engine.FreemarkerTemplateEngine;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
public class MyBatisPlusGenerator {
@Test
public void codeGenerator() {
// 代碼生成器
AutoGenerator mpg = new AutoGenerator();
// 全局配置
GlobalConfig gc = new GlobalConfig();
String projectPath = System.getProperty("user.dir");
gc.setOutputDir(projectPath + "/src/main/java");
gc.setAuthor("CDHong");//Mapper,Service類註解中顯示創建人信息
//gc.setBaseColumnList(true); //在Mapper.xml文件中是否生成公用SQL代碼段
//gc.setBaseResultMap(true); //在Mapper.xml文件中是否生成公用返回集合ResultMap
gc.setOpen(false); //文件生成完畢後,是否需要打開所在路徑
mpg.setGlobalConfig(gc);
// 數據源配置
DataSourceConfig dsc = new DataSourceConfig();
dsc.setUrl("jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&useSSL=false&characterEncoding=utf8");
dsc.setDriverName("com.mysql.cj.jdbc.Driver");
dsc.setUsername("root");
dsc.setPassword("root");
mpg.setDataSource(dsc);
// 包配置
PackageConfig pc = new PackageConfig();
pc.setParent("com.cdh.springboot"); //父級公用包名,就是自動生成的文件放在項目路徑下的那個包中
mpg.setPackageInfo(pc);
// 自定義配置
InjectionConfig cfg = new InjectionConfig() {
@Override
public void initMap() {
// to do nothing
}
};
List<FileOutConfig> focList = new ArrayList<>();
focList.add(new FileOutConfig("/templates/mapper.xml.ftl") {
@Override
public String outputFile(TableInfo tableInfo) {
// 自定義Mapper.xml文件存放的路徑
return projectPath + "/src/main/resources/mapper/"
+ tableInfo.getEntityName() + "Mapper" + StringPool.DOT_XML;
}
});
cfg.setFileOutConfigList(focList);
mpg.setCfg(cfg);
mpg.setTemplate(new TemplateConfig().setXml(null));
// 策略配置
StrategyConfig strategy = new StrategyConfig();
strategy.setNaming(NamingStrategy.underline_to_camel); //Entity文件名稱命名規範
strategy.setColumnNaming(NamingStrategy.underline_to_camel); //Entity字段名稱
strategy.setEntityLombokModel(true); //是否使用lombok完成Entity實體標註Getting Setting ToString 方法
//strategy.setRestControllerStyle(true); //Controller註解使用是否RestController標註,否則是否開啓使用Controller標註
strategy.entityTableFieldAnnotationEnable(true); //是否在Entity屬性上通過註解完成對數據庫字段的映射
strategy.setControllerMappingHyphenStyle(true); //Controller註解名稱,不使用駝峯,使用連字符
strategy.setTablePrefix("sys_"); //表前綴,添加該表示,則生成的實體,不會有表前綴,比如sys_dept 生成就是Dept
//strategy.setFieldPrefix("sys_"); //字段前綴
mpg.setStrategy(strategy);
mpg.setTemplateEngine(new FreemarkerTemplateEngine());
mpg.execute();
}
}
- 生成結果如圖所示:
SpringBoot啓動類
SpringBoot啓動類就是用於項目的啓動,而啓動方式是使用java的applications方式啓動,即在包根目錄下添加啓動類,必須包含main方法,再添加Spring Boot啓動方法:
- SpringApplication.run(SampleController.class, args);
- new SpringApplicationBuilder().run(args);
不再使用之間的Tomcat部署,war包啓動。簡化了我們項目部署的步驟。文件內容如下:
package com.cdh.springboot;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//這是一個組合註解,包含了@Configuration,@EnableAutoConfiguration,@ComponentScan。
//@Configuration : 標識這爲一個配置文件類型,可以通過@Bean註解來標註配置文件中的Bean對象
//@EnableAutoConfiguration : 能夠自動配置spring的上下文,試圖猜測和配置你想要的bean類,通常會自動根據你的類路徑和你的bean定義自動配置。
//@ComponentScan : 會自動掃描指定包下的全部標有@Component的類,並註冊成bean,當然包括@Component下的子註解@Service,@Repository,@Controller。
// 前提是標註的類是當前類的子孫包中。
@SpringBootApplication
@MapperScan(basePackages = "com.cdh.springboot.mapper") //掃描自定義的Mapper接口,並注入對應的SqlSession實例
public class RbacDemoApplication {
//SpringBoot的啓動方法
public static void main(String[] args) {
SpringApplication.run(RbacDemoApplication.class, args);
}
}
集成配置MyBatisPlus以及數據源
在SpringMVC中數據源配置和MyBatis相關配置都是在xml中進行的,而SpringBoot中不推薦這麼寫,且大部分功能都通過自動配置完成了,極少部分需要手動配置,那麼這些配置都是通過Java+Annotation的方式來完成的。
新建一個包(config)用於後續的其他配置(攔截器,異常…)在這個包中創建一個MyBatisPlusConfig類用於Druid數據源和MP的分頁注入:
package com.cdh.springboot.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.ResourceServlet;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import com.baomidou.mybatisplus.extension.plugins.PaginationInterceptor;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import javax.sql.DataSource;
@Configuration //標註它是一個配置類,類似於新建一個spring的xml配置文件
public class MyBatisPlusConfig {
/**
* Druid數據源配置
* @return
*/
@Bean("druidDataSource") //標註這是一個Bean對象,並取一個特有的名字,避免衝突
@Primary //如果在IOC容器中找到相同類型的Bean對象,則優先使用這個
@ConfigurationProperties(prefix = "spring.datasource.druid") //讀取核心配置文件application.properties中前綴是spring.datasource.druid的數據注入到當前Bean中
//注意配置文件中的key值需要和DataSource中對應的set方法名稱一致,注意不是屬性值,是set方法
public DataSource druidDataSource(){
return new DruidDataSource();
}
/**
* 註冊一個Servlet ,把Druid提供的監控Servlet註冊進去,並提供一個訪問路徑,用戶名和密碼
* 當前自定義Servlet的註冊方式一致,你也可以在web.xml中配置,只是SpringBoot項目中不建議這麼做
* @return
*/
@Bean
public ServletRegistrationBean druidStatViewServlet(){
//監控界面Servlet的訪問設置,訪問路勁爲根目錄下的/druid/**,Druid數據源提供了一套顯示頁面,StatViewServlet,只需要注入即可,
ServletRegistrationBean servletRegistration = new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
//添加Servlet的初始值,訪問這個監控界面的用戶名和密碼,如果不配置,則默認不需要密碼,不顯示登錄界面
servletRegistration.addInitParameter(ResourceServlet.PARAM_NAME_USERNAME,"admin");
servletRegistration.addInitParameter(ResourceServlet.PARAM_NAME_PASSWORD,"admin");
return servletRegistration;
}
/**
* 過濾器註冊,需要配置Druid監控器需要監控的請求和操作
* 配置一下過濾規則,讓靜態資源和它自己的視圖界面不攔截
* @return
*/
@Bean
public FilterRegistrationBean druidStatFilter(){
//那些信息要監控,需要定義該過濾器來進行攔截,Druid是數據源,當然只攔截請求操作了,靜態資源需要放行
FilterRegistrationBean filterRegistration = new FilterRegistrationBean(new WebStatFilter());
//過濾器攔截路徑
filterRegistration.addUrlPatterns("/*");
//不攔截的請求
filterRegistration.addInitParameter(WebStatFilter.PARAM_NAME_EXCLUSIONS,"*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
return filterRegistration;
}
/**
* MyBatisPlus分頁插件啓用,比較簡單,只需要實例化即可
* @return
*/
@Bean
public PaginationInterceptor paginationInterceptor(){
return new PaginationInterceptor();
}
}
SpringBoot核心配置文件
添加一些Druid數據源冬天配置新和MyBatisPlus的相關配置信息,這些動態信息可以直接在SpringBoot的核心配置文件中application.properites中配置,在java文件中通過@Value(key)註解注入單個值,或 @ConfigurationProperties(prefix = “spring.datasource.druid”)注入多個值。
#durid 數據源配置 特別注意 常規的4個字符串連接的名字,必須符合DruidDataSource的命名規則,注意是set方法,不是字段名稱,比如url
spring.datasource.type=com.alibaba.druid.pool.DruidDataSource
#####自定義的配置信息,即SpringBoot中沒有提供的配置,是我們自己額外提供的動態配置信息###########
spring.datasource.druid.username=root
spring.datasource.druid.password=root
spring.datasource.druid.driverClassName=com.mysql.cj.jdbc.Driver
spring.datasource.druid.url=jdbc:mysql://localhost:3306/test?serverTimezone=UTC&useUnicode=true&useSSL=false&characterEncoding=utf8
# 初始化大小,最小,最大
spring.datasource.druid.initialSize=5
spring.datasource.druid.minIdle=2
spring.datasource.druid.maxActive=20
# 配置獲取連接等待超時的時間
spring.datasource.druid.maxWait=60000
# 配置間隔多久才進行一次檢測,檢測需要關閉的空閒連接,單位是毫秒
spring.datasource.druid.timeBetweenEvictionRunsMillis=60000
spring.datasource.druid.dbType=mysql
# 配置監控統計攔截的filters,去掉後監控界面sql無法統計,'wall'用於防火牆
spring.datasource.druid.filters=stat,wall
# 通過connectProperties屬性來打開mergeSql功能(參數不同的sql合併統計)、慢SQL記錄(執行時間長的sql)
spring.datasource.druid.connectionProperties=druid.stat.mergeSql=true;druid.stat.slowSqlMillis=5000
#############################SpringBoot內置的配置信息,它會自動讀取注入
#mybatisplus配置
mybatis-plus.mapper-locations=mapper/*.xml
mybatis-plus.configuration.use-column-label=true
mybatis-plus.configuration.auto-mapping-behavior=full
mybatis-plus.configuration.map-underscore-to-camel-case=true
#配置SpringBoot默認的日誌環境,開啓打印SQL語句的Debug模式,語法:logging.level.<mapper所在包名>=debug
logging.level.com.cdh.springboot.mapper=debug
#Tomcat端口號 默認是8080端口
server.port=80
整合測試
到此整合基本完畢,我們通過啓動項目訪問數據源的監控頁面,確認我們配置的監控沒有問題,還可以通過數據源配置信息的顯示來查看我們注入的druid是否成功。即核心配置文件中的信息生效沒有。
- 運行SpringBoot啓動類查看打印信息
- 訪問localhost/druid/ 來打開監控界面,並查看數據源配置信息
在瀏覽器上輸入localhost/durid/進入數據源監控界面,因爲我們配置了登錄名和密碼,所以會被攔截器,需要登錄才能正常方法,如果沒有配置則可以直接進入,不用登錄。
輸入正確的用戶名和密碼,我們就可以進入到監控界面如下,可以查看到我們在覈心配置文件中配置的信息生效了。到此整合就完畢了。
MyBatisPlus 封裝的CRUD接口
MyBatisPlus爲我們封裝了一套CRUD接口,提供了我們常用的方法及實現,不僅僅是Mapper接口,還有Service接口和實現以及封裝了一個條件構造器,爲我們的簡單條件提供了便利,具體情況可以通過官網去了解,在這裏我就簡單羅列一下:
Mapper CRUD 接口
說明:
通用 CRUD 封裝BaseMapper接口,爲 Mybatis-Plus 啓動時自動解析實體表關係映射轉換爲 Mybatis 內部對象注>入容器
泛型 T 爲任意實體對象
參數 Serializable 爲任意類型主鍵 Mybatis-Plus 不推薦使用複合主鍵約定每一張表都有自己的唯一 id 主鍵
對象 Wrapper 爲 條件構造器
Mapper接口 | Mapper接口 | Mapper接口 | Mapper接口 | Mapper接口 | Mapper接口 | Mapper接口 | Mapper接口 | Mapper接口 |
---|---|---|---|---|---|---|---|---|
insert | deleteById | deleteByMap | delete | deleteBatchIds | updateById | update | selectById | selectBatchIds |
selectByMap | selectOne | selectCount | selectList | selectMaps | selectObjs | selectPage | selectMapsPage |
Service CRUD 接口
說明:
通用 Service CRUD 封裝IService接口,進一步封裝 CRUD 採用 get 查詢單行 remove 刪除 list
查詢集合 page 分頁 前綴命名方式區分 Mapper 層避免混淆, 泛型 T 爲任意實體對象 建議如果存在自定義通用 Service
方法的可能,請創建自己的 IBaseService 繼承 Mybatis-Plus 提供的基類 對象 Wrapper 爲 條件構造器
Service接口 | Service接口 | Service接口 | Service接口 | Service接口 | Service接口 | Service接口 | Service接口 | Service接口 |
---|---|---|---|---|---|---|---|---|
save | saveBatch | saveOrUpdateBatch | removeById | removeByMap | remove | removeByIds | updateById | update |
updateBatchById | saveOrUpdate | getById | listByIds | listByMap | getOne | getMap | getObj | count |
list | page | listMaps | listObjs | pageMaps |
條件構造器
QueryWrapper(LambdaQueryWrapper) 和 UpdateWrapper(LambdaUpdateWrapper) 的父類
用於生成 sql 的 where 條件, entity 屬性也用於生成 sql 的 where 條件,特別注意的是,因爲是直接應用於SQL,所以它們其中使用的參數key都是數據庫中表的字段名稱,和實體屬性沒有關係。
條件方法 | 條件方法 | 條件方法 | 條件方法 | 條件方法 | 條件方法 | 條件方法 | 條件方法 | 條件方法 |
---|---|---|---|---|---|---|---|---|
allEq | eq | ne | gt | ge | lt | le | between | notBetween |
like | notLike | likeLeft | likeRight | isNull | isNotNull | in | notIn | inSql |
notInSql | groupBy | orderByAsc | orderByDesc | orderBy | having | or | and | last |
- QueryWrapper對象特有的方法:select
- UpdateWrapper對象特有的方法:set , setSql
使用SpringBootTest熟悉一下MyBatisPlus提供的CRUD接口
SpringBootTest的使用方式和之前的spring-test+junit的模式差不多,只需要在測試類上配置如下幾個點:
- 在類上添加第一個註解: @RunWith(SpringRunner.class)
- 在類上添加第二個註解: @SpringBootTest
- 在需要測試的方法上添加@Test即可,需要注意的是,測試的方法必須是
public void
開頭的 - 如果需要注入對象在類中直接使用@Autowired 或 @Resource 放在對應的字段上即可
package com.cdh.springboot;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;
@RunWith(SpringRunner.class)
@SpringBootTest
public class RbacDemoApplicationTests {
@Resource
private IDeptService deptService; //這裏可以使用接口類型接收(多態),也可以使用實現類接收。
@Test
public void contextLoads() {
}
}
接下來我們測試幾個常見的方法
- 添加一個 ,代碼如下:
@Test
public void saveDeptTest() {
//給Dept實體類添加lombok的@Builder註解,就可以使用如下的方式構建對象
Dept dept = Dept.builder().deptName("科技部").loc("廣州").parentId(0).level("0").phone("12345678901")
.orderNum(1).status(0).delFlg(0).createTime(LocalDateTime.now()).build();
boolean flg = deptService.save(dept);
System.out.println(flg);
}
執行結果,以及SQL打印信息:
- 添加多個,代碼如下
@Test
public void saveBatchDeptTest(){
Dept dept1 = Dept.builder().deptName("科技部1").loc("廣州1").parentId(1).level("0").phone("12345678901").orderNum(1).status(0).delFlg(0).createTime(LocalDateTime.now()).build();
Dept dept2 = Dept.builder().deptName("科技部2").loc("廣州2").parentId(1).level("0.1").phone("12345677701").orderNum(2).status(0).delFlg(0).createTime(LocalDateTime.now()).build();
Dept dept3 = Dept.builder().deptName("科技部3").loc("廣州3").parentId(1).level("0.2").phone("12345668901").orderNum(3).status(0).delFlg(0).createTime(LocalDateTime.now()).build();
List<Dept> list = Arrays.asList(dept1,dept2,dept3);
boolean flg = deptService.saveBatch(list);
//這裏使用日誌打印,在要打印的類上添加lombok註解@Slf4j,它會提供一個log對象,我們就可以使用它打印統一樣式的日誌信息了
log.info("添加多個部門,結果爲:{}",flg);
}
執行結果爲:
- 根據id刪除,代碼如下:
@Test
public void delByIdTest(){
boolean flg = deptService.removeById(1);
log.info("刪除1號部門,執行結果爲:{}",flg);
}
執行結果爲:
- 根據ids刪除,代碼如下:
@Test
public void delByIdsTest(){
List<Integer> ids = Arrays.asList(4, 5);
boolean flg = deptService.removeByIds(ids);
log.info("刪除多個部門,執行結果爲:{}",flg);
}
執行結果爲:
- 根據id修改,代碼如下:
@Test
public void updateById(){
Dept dept = Dept.builder().deptId(1).deptName("市場部").build();
boolean flg = deptService.updateById(dept);
log.info("修改部門,執行結果爲:{}",flg);
}
- 查詢所有,代碼如下:
@Test
public void findAll(){
//查詢所有,注意需要添加有參構造和無參構造 @AllArgsConstructor , @NoArgsConstructor
List<Dept> list = deptService.list();
list.forEach(System.out::println);
}
- 根據id查詢,代碼如下:
@Test
public void findById(){
Dept dept = deptService.getById(7);
log.info(dept.toString());
}
- 根據自定義條件查詢,代碼如下:
@Test
public void findByInfo(){
//自定義查詢條件,這個時候就需要使用MyBatisPlus提供的條件構造器QueryWrapper<T>了
//條件如下loc like '廣州%' and status = 0 and create_time between '2018-11-28 02:02:20' and '2018-11-28 02:05:15' order by dept_id
Map<String,Object> where = new HashMap<>();
where.put("status",0);
QueryWrapper<Dept> queryWrapper = new QueryWrapper<>();
queryWrapper.likeRight("loc","廣州")
.eq("status",0)
.between("create_time","2018-11-28 02:02:20","2018-11-28 02:05:15")
.orderByAsc("dept_id");
List<Dept> list = deptService.list(queryWrapper);
list.forEach(dept -> log.info(dept.toString()));
}
執行結果,以及打印的SQL語句如下:
- 分頁查詢,代碼如下:
@Test
public void page(){
Page<Dept> page = new Page<>(1,3); //current:頁碼 , size:每頁顯示的條數
QueryWrapper<Dept> queryWrapper = new QueryWrapper<>();
queryWrapper.like("loc","廣州").orderByAsc("dept_id");
IPage<Dept> pageInfo = deptService.page(page, queryWrapper);
log.info("總條數:{}",pageInfo.getTotal());
log.info("顯示數據:{}:",pageInfo.getRecords());
log.info("頁碼:{}",page.getCurrent());
log.info("每頁顯示的條數:{}",page.getSize());
}
執行結果,以及打印的SQL語句如下:
- 多表帶條件分頁查詢 ,多表查詢需要自定義SQL,也就是需要在Mapper映射文件中添加自己的需求,這個時候需要自定映射實體,也就是經常所見的VO。我們先添加一個員工,外鍵關聯部門表,然後查詢該員工對應的部門信息。
添加多個員工:
@Autowired
private IUserService userService;
@Test
public void saveBatch(){
//給User實體添加三個註解@NoArgsConstructor @AllArgsConstructor @Builder
User user1 = User.builder().userName("admin").userPwd("admin").createTime(LocalDateTime.now()).deptId(7).build();
User user2 = User.builder().userName("zhangsan").userPwd("zhangsan").createTime(LocalDateTime.now()).deptId(8).build();
User user3 = User.builder().userName("lis").userPwd("lisi").createTime(LocalDateTime.now()).deptId(9).build();
List<User> users = Arrays.asList(user1,user2,user3);
boolean flg = userService.saveBatch(users);
log.info("添加多個員工,執行結果爲:{}",flg);
}
在entity包中添加一個vo包,在該包下添加一個UserDeptVO類,定義要獲取的信息字段。如下:
import lombok.Data;
import java.time.LocalDateTime;
@Data
public class UserDeptVO {
private Integer userId;
private String userName;
private Integer deptId;
private String deptName;
private String loc;
private String level;
private LocalDateTime createTime;
}
在UserMapper中添加兩個接口,一個查詢員工詳細信息,一個帶條件的分頁查詢。
package com.cdh.springboot.mapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cdh.springboot.entity.User;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.cdh.springboot.entity.vo.UserDeptVO;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* <p>
* 用戶表 Mapper 接口
* </p>
*
* @author CDHong
* @since 2018-11-27
*/
public interface UserMapper extends BaseMapper<User> {
UserDeptVO findByUserId(Integer userId);
/**
* 分頁查詢一定要添加IPage作爲參數,傳入頁碼和每頁顯示的條數
* @param page
* @param vo 查詢的條件
* @return
*/
List<UserDeptVO> userPage(IPage<UserDeptVO> page,@Param("vo") UserDeptVO vo);
}
Mapper.xml中SQL映射信息如下;
<?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">
<mapper namespace="com.cdh.springboot.mapper.UserMapper">
<select id="findByUserId" resultType="com.cdh.springboot.entity.vo.UserDeptVO">
SELECT user_id,user_name,d.dept_id,dept_name,loc,level,u.create_time user_create_time
FROM sys_user u
JOIN sys_dept d ON u.dept_id = d.dept_id WHERE user_id = #{userId}
</select>
<select id="userPage" resultType="com.cdh.springboot.entity.vo.UserDeptVO">
SELECT user_id,user_name,d.dept_id,dept_name,loc,level,u.create_time user_create_time
FROM sys_user u
JOIN sys_dept d ON u.dept_id = d.dept_id
<where>
<if test="vo!=null">
<if test="vo.userId!=null"> AND user_id = #{vo.userId}</if>
<if test="vo.userName!=null"> AND user_name like '%${vo.userName}%' </if>
<if test="vo.deptId!=null"> AND d.dept_id = #{vo.deptId} </if>
<if test="vo.deptName!=null"> AND dept_name like '${vo.deptName}' </if>
<if test="vo.loc!=null"> AND d.loc like '${vo.loc}' </if>
</if>
</where>
</select>
</mapper>
IUserService接口中添加一個查詢方法,基本上和UserMapper接口中的方法一致,你可以直接粘貼複製過去,這裏就不在提供代碼了,接着在UserServiceImp實現類中完成接口的實現,這裏需要注入UserMapper的實例,注意這裏想要獲取到UserMapper實例,需要在SpringBoot的啓動類中添加註解掃描Mapper接口(
@MapperScan(basePackages = "com.cdh.springboot.mapper")
),否則會報找不到對應的方法`
package com.cdh.springboot.service.impl;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.cdh.springboot.entity.User;
import com.cdh.springboot.entity.vo.UserDeptVO;
import com.cdh.springboot.mapper.UserMapper;
import com.cdh.springboot.service.IUserService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;
import javax.annotation.Resource;
import java.util.List;
/**
* <p>
* 用戶表 服務實現類
* </p>
*
* @author CDHong
* @since 2018-11-27
*/
@Service
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {
@Resource
private UserMapper userMapper;
@Override
public UserDeptVO findById(Integer userId) {
return userMapper.findByUserId(userId);
}
@Override
public List<UserDeptVO> userPage(IPage<UserDeptVO> page, UserDeptVO vo) {
return userMapper.userPage(page,vo);
}
}
Junit測試,根據ID獲取對應的多表數據
@Test
public void findUserAndDeptByUserId(){
UserDeptVO userDeptVO = userService.findById(2);
log.info(userDeptVO.toString());
}
@Test
public void userPage(){
UserDeptVO vo = new UserDeptVO();
vo.setDeptName("科技部");
vo.setUserName("m");
Page<UserDeptVO> page = new Page<>(1,3);
List<UserDeptVO> list = userService.userPage(page, vo);
log.info("總條數:{}",page.getTotal());
list.forEach(userDeptVO -> log.info(userDeptVO.toString()));
}
測試結果以及打印SQL語句如下:
接下來是把我們寫好的方法通過Thymeleaf模板進行數據展示。
Thymeleaf模板引擎簡單入門
SpringBoot建議使用模板引擎Thymeleaf替代JSP的操作,Thymeleaf是面向Web和獨立環境的現代服務器端Java模板引擎,能夠處理HTML,XML,JavaScript,CSS甚至純文本。
Thymeleaf的主要目標是提供一個優雅和高度可維護的創建模板的方式。爲了實現這一點,它建立在自然模板的概念上,將其邏輯注入到模板文件中,不會影響模板被用作設計原型。這改善了設計的溝通,彌合了設計和開發團隊之間的差距。也就是可以動態更改路徑和數據。
注意在SpringBoot中Thymeleaf是開箱即用的,不需要做什麼配置,當然如果你有其他需求,也可以更改默認配置,直接在SpringBoot的核心配置文件中寫入對應的鍵值對即可。
- SpringBoot中 Thymeleaf常見配置項:
# Thymeleaf模板引擎配置
#開啓模板緩存(默認值:true)
spring.thymeleaf.cache=true
#在呈現模板之前檢查模板是否存在
spring.thymeleaf.check-template=true
#檢查模板位置是否正確(默認值:true)
spring.thymeleaf.check-template-location=true
#Content-Type的值(默認值:text/html)
spring.thymeleaf.servlet.content-type=text/html
#開啓MVC Thymeleaf視圖解析(默認值:true)
spring.thymeleaf.enabled=true
#模板編碼
spring.thymeleaf.encoding=UTF-8
#要被排除在解析之外的視圖名稱列表,用逗號分隔
spring.thymeleaf.excluded-view-names=
#在構建URL時添加到視圖名稱前的前綴(默認值:classpath:/templates/)
spring.thymeleaf.prefix=classpath:/templates/
#在構建URL時添加到視圖名稱後的後綴(默認值:.html)
spring.thymeleaf.suffix=.html
#可解析的視圖名稱列表,用逗號分隔
spring.thymeleaf.view-names=
- Thymeleaf使用詳情:
Thymeleaf模板引擎使用其實功能和JSP差不多,都是在靜態頁面對後臺作用域中的數據進行相應的處理,替換默認值,邏輯判斷,循環遍歷,簡單表達式計算,路徑映射…1. 語法
- 引入靜態資源文件: 在html中,資源文件路徑是一個頭痛的問題,而模板引擎中,只需要使用
@{/url}
包裹路徑,則代表該路徑從項目根目錄開始。之前的路徑不用刪除,它會自動替換。th:href="@{/css/public.css}"
- 訪問後端作用域數據: 訪問model中的數據,和EL表達式一致,
th:attr="class= ${btn.code}"
, 訪問session中的數據需要加上作用域,th:text="${session.currUser.relName}"
- 條件判斷:在html標籤中,加入th:if = 表達式,可以根據條件顯示html元素
- 循環迭代:在要循環的標籤上添加 th:each 或者單獨構建一個塊標籤:th:block:
- 引入靜態資源文件: 在html中,資源文件路徑是一個頭痛的問題,而模板引擎中,只需要使用
<th:block th:each="job : ${jobs}">
<option th:text="${job.name}" th:value="${job.id}"></option>
</th:block>
2. 表達式
-
簡單表達式 : ${…} 變量表達式 , *{…} 選擇變量表達式 , @{…} 鏈接url表達式
-
字面量表達式: 文本 ->‘one text’ , 數值->5 ,布爾->true ,空值->null ,
-
操作符: 算術運算符,布爾運算符(and,or,!,not),關係運算符(gt , lt , ge , le , eq , ne ,>= , <= , == ),三目運算
-
表達式工具對象:
1. #dates 與java.util.Date對象的方法對應,格式化、日期組件抽取等等
2. #calendars 類似#dates,與java.util.Calendar對象對應
3. #numbers 格式化數字對象的工具方法
4. #strings 與java.lang.String對應的工具方法:contains、startsWith、prepending/appending等等
5. #objects 用於對象的工具方法
6. #bools 用於布爾運算的工具方法
7. #arrays 用於數組的工具方法
8. #lists 用於列表的工具方法
9. #sets 用於set的工具方法
10. #maps 用於map的工具方法
11. #aggregates 用於創建數組或集合的聚合的工具方法
12. #messages 用於在變量表達式內部獲取外化消息的工具方法,與#{…}語法獲取的方式相同
13. #ids 用於處理可能重複出現(例如,作爲遍歷的結果)的id屬性的工具方法 -
頁面操作:
內嵌標記 內嵌標記 內嵌標記 內嵌標記 內嵌標記 內嵌標記 內嵌標記 內嵌標記 th:action th:align th:alt-title th:autocomplete th:cellpadding th:cellspacing th:class th:attr :async th:autofocus th:autoplay th:checked th:disabled th:hidden th:readonly th:required :selected th:each th:classappend th:styleappend th:attrappend 以上寫法可能比較怪異,可以使用HTML5友好的屬性及元素名來完成操作,
<tr data-th-each="user : ${users}"> 或 <td data-th-text="${user.login}">...</td>
-
條件運算: th.switch 、 th:if 不只運算布爾條件,它對以下情況也運算爲true:
- 值不爲null , 爲boolean且爲true ,值爲數字且非0 , 值爲字符且非0 , 值是字符串且不是:“false”,“off”,“no” , 值不是boolean、數字、字符、字符串 , 如果值爲null,則th:if運算結果爲false
- th:if的反面是th:unless
<div th:switch="${user.role}">
<p th:case="'admin'">User is an administrator</p>
<p th:case="#{roles.manager}">User is a manager</p>
<p th:case="*">User is some other thing</p>
</div>
- 循環遍歷: th:each 除了獲取對象外,還可以得到獲取一些遍歷狀態,通過指定狀態變量iterStat獲取:擁有的屬性(index ,count,size,current,even,odd,first,last),
若不指定狀態變量,Thymeleaf會默認生成一個名爲“變量名Stat”的狀態變量
:
<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
<tr th:each="prod : ${prods}" th:class="${prodStat.odd}? 'odd'">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>