表示層 > 業務層 > 持久層 > 資料庫
使用分層結構進行解耦
表示層
controller 包
用來存放表示層的各種動作類。
命名規範:xxxController
如何讓一個類變為動作類:使用 @RestControl
註解
package com.hello.controller;
@RestController // 讓 Spring Boot 認識這個類是動作類
public class HelloController {
/* 不使用 MyBatis */
@Resource // 讓 Spring Boot 自動給變數賦值
private HelloService helloService; // Spring Boot 會自動將 HelloService 物件賦值給 helloService 變數
@RequestMapping("/hello") // 讓該方法變成一個動作方法
// 指定當前方法和一個 URL 的對應關係。當你在瀏覽器中訪問這個 URL 的時候,就會執行這個方法。URL 路徑名通常和方法名相同
public String hello() {
String data = helloService.saveHello(); // 不使用 MyBatis,使用了自定義的方法
return data; // 方法被呼叫時會將 data 的值作為 HTML 頁面 <body> 標籤的內容
}
/* 使用 MyBatis */
@Resource
private MybatisService mybatisService;
@RequestMapping("/mybatis")
public List<User> mybatis() {
List<User> data = mybatisService.list();
return data;
}
}
業務層
面向介面程式設計,業務層包括業務層介面和其實現類。
service 包
存放各種介面。命名規範:xxxService
package com.hello.service;
// 介面,一組規範,規定
/* 不使用 MyBatis */
public interface HelloService {
int saveHello();
}
/* 使用 MyBatis */
public interface MybatisService extends IService<User> {
// 需要用到的方法已經被 MyBatis 定義了,因此這裡為空
}
impl 包
存放介面的實現類。命名規範:xxxServiceImpl
package com.hello.service.impl;
/* 不使用 MyBatis */
@Service // 讓 Spring Boot 認識這個類是業務層類
public class HelloServiceImpl implements HelloService { // 介面實現類,必須重寫介面中的規範
@Resource // 讓 Spring Boot 將實際的物件賦值給變數
private HelloDao helloDao; // 宣告持久層變數
@Override // 讓編譯器檢查是否重寫了介面方法
public String saveHello() {
String data = helloDao.selectHello();
return data;
}
}
/* 使用 MyBatis */
@Service
public class MybatisServiceImpl implements MybatisService extends ServiceImpl<UserMapper, User> {
}
如果遺漏
@Service
註解,那麼在啟動程式時會出現報錯:A component required a bean of type 'HelloService' that could not be found.
持久層
持久層使用的框架是 MyBaits(不屬於 Spring 家族)。為了簡化操作,可以使用 MyBatis-Plus(MyBatis 的增強工具)。
涉及到持久層,一定需要資料庫驅動。(手動下載或者使用 Maven 下載)為了提高效率,MyBatis 需要資料來源連線池,常見:dpcp2、druid(阿里,國內常用),Spring Boot 內建有連線池。
如果使用了 MyBatis,那麼持久層就是介面。如果不使用,那麼就是寫自己的類。
持久層包名規範:dao(不使用 MyBatis)、mapper(使用 MyBatis)
讓表示層呼叫業務層,讓業務層呼叫持久層,讓持久層呼叫資料庫。
不使用 MyBatis
package com.hello.dao;
// 持久層類,能對錶進行 CURD
@Repository // 讓 Spring Boot 認識這個類是持久層類
public class HelloDao {
public String selectHello() {
String data = "模擬,在 hello 表中查詢到了資料,存放在 data 變數中";
return data;
}
使用 MyBatis
在 POM 中匯入持久層依賴:
<!-- mysql 驅動 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<!-- mybatis plus -->
<!-- 可以看到該依賴中包含了 mybatis 和 spring jdbc -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.2</version>
</dependency>
<!-- spring jdbc -->
<!-- 可以看到該依賴中包含了 HikariCP 連線池 -->
<!-- 由於 mybatis plus 已經包含了該依賴,因此該依賴可以省去 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
建立實體類包 entity
和實體類(用於封裝資料庫資料的類)
package com.hello.entity;
// 可以使用 lombok 外掛自動生成 get 和 set 方法,以及 toString 方法
@Getter
@Setter
@ToString
// 實體類,封裝和儲存表資料
public class User { // 封裝 User 表
// id, uname, pass, addr
//為了安全,欄位必須為 private
@TableId(type = IdType.AUTO) // 指定主鍵以及主鍵生成策略
private Integer id;
private String uname;
private String pass;
private String addr;
// 為每個欄位生成 get 和 set 方法
// 為每個欄位生成 toString 方法
}
建立持久層類
package com.hello.mapper;
public interface UserMapper extends BaseMapper<User> { // 使用 MyBatis-Plus 的方法很簡單,只需繼承 BaseMapper 類。操作哪個實體類,範型就是誰。
}
建立資料庫
CREATE TABLE users (
id INT AUTO_INCREMENT PRIMATY KEY,
uname VARCHAR(40) NOT NULL,
pass VARCHAR(40) NOT NULL,
addr VARCHAR(40)
)
INSERT INTO users VALUES (NULL, 'admin', 'admin', 'NewYork'), (NULL, 'zhangsan', '123456', NULL);
在主配置類上掃描 Mapper 介面
package com.hello;
@SpringBootApplication
@MapperScan("com.hello.mapper") // 掃描 Mapper 介面
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args);
}
}
業務層介面繼承
IService<T>
類業務層實現類繼承
ServiceImpl<M, T>
類持久層介面繼承
BaseMapper<T>
類
資料庫表名、列名:first_name
Java 類名、變數名: FirstName
MyBatis 會自動對應。
resources 資料夾
static 資料夾
web 伺服器的資料夾
application.properties
各種配置項
# 埠
server.port=8080
# 資料來源
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
# jdbc:protocol://ip:port/database
spring.datasource.url=jdbc:mysql://localhost:3306/xdsx
spring.datasource.username=root
spring.datasource.password=
# MyBatis-Plus 的設定
mybatis-plus.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl # 將日誌列印到控制檯
POM
dependencies
Spring Boot 的各種 starter 類:就是各種啟動器
比如,加了 test 啟動器,就可以透過增加 @Test
註解來使非 main 方法作為主方法執行。加了 web 啟動器,就可以獲得一個Tomcat Web 伺服器(目錄結構會出現伺服器的 static 資料夾)(開發 Web 應用程式必須使用 web 啟動器)。
spring-boot-starter-web
和 spring-boot-starter
的關係:前者包含後者
測試
GET 請求:引數在 Params
中
POST 請求:引數在 Body
中
{
"uname": "zhangsan",
"pass": "123456",
"addr": "NewYork"
}
CRUD
Controller 類:
@RequestMapping("/get_all_user")
public List<User> getAllUser() {
return mybatisService.list();
}
@RequestMapping("/save_user")
// /save_user?id=1&uname=zhangsan&pass==123456&addr=NewYork
public String saveUser(User user) {
return mybatisService.save(user) ? "新增成功" : "新增失敗";
}
@RequestMapping("/update_user")
public String updateUser(User user) {
return mybatisService.updateById(user) ? "修改成功" : "修改失敗";
}
@RequestMapping("/delete_user")
public String deleteUser(Integer id) {
return mybatisService.removeById(id) ? "刪除成功" : "刪除失敗";
}
@RequestMapping("/get_by_id")
public User getById(Integer id) { // 測試時在瀏覽器中訪問 /get_by_id?id=1
return mybatisService.getById(id);
}
// 使用 POST 方法
@PostMapping("/login")
public User login(@RequestBody User user) { // 要求從請求體中尋找引數
String uname = user.getUname();
String pass = user.getPass();
QueryWrapper<User> wrapper = new QueryWrapper<>(); // 獲取一個查詢包裝器
wrapper.eq("uname", uname); // 設定一個查詢條件:資料庫的 username 與引數 uname 的值相等
wrapper.eq("pass", pass);
return testService.getOne(wrapper); // 查詢並返回一條資料
}
測試可以使用 Apifox
分頁
MyBatis-Plus 中已經封裝了分頁功能,需要新增分頁外掛。
在主配置類中新增分頁攔截器:
@Bean
public MybatisPlusInterceptor mybatisPlusInterceptor() {
MybatisPlusInterceptor interceptor = new MybatisPlusInterceptor();
interceptor.addInnerInterceptor(new PaginationInnerInterceptor(DbType.MYSQL));
return interceptor;
}
在 Controller 類中使用分頁功能:
@RequestMapping("select_page")
public IPage<User> selectPage(Integer current, Integer size) {
Page<User> page = new Page<>(current, size);
return testService.page(page);
}
案例
登陸
// 校驗請求引數
if (uname === '' || pass === '') {
this.msg = '使用者名稱或密碼不能為空';
return;
}
axios.get("http://localhost:8081/login"), {
params: { // 請求引數
uname: this.uname,
pass: this.pass
}
.then(response => {
if (response.data) {
this.$router.push("/home"); // 跳轉到 home 頁面
} else {
alert("使用者名稱或密碼錯誤");
}
})
.catch(err => {
console.log(err);
})
}
// 動作類
@RequestMapping("/login")
public User login(String uname, String pass) { // /login?uname=xxx&pass=xxx
QueryWrapper<User> wrapper = new QueryWrapper<>(); // 獲取一個查詢包裝器
wrapper.eq("username", uname); // 設定一個查詢條件:資料庫的 username 與引數 uname 的值相等
wrapper.eq("password", pass);
return userService.getOne(wrapper); // 查詢並返回一條資料
}
上傳檔案
使用 MultipartFile
型別物件接收上傳的檔案。
@PostMapping("/upload")
public String upload(String uname, MultipartFile head, HttpRequest request) throws IOException {
String fileNmae = head.getOriginalFilename(); // 獲取檔名
request.getServletContext().getRealPath("/image"); // 獲取上傳路徑
// 確保上傳目錄存在
if (!new File(path).exists()) {
new File(path).mkdirs();
}
File file = new File(path, fileName);
head.transferTo(file); // 將上傳的位元組流傳入檔案
return "success";
}
application.properties
spring.servlet.multipart.max-file-size=1MB
spring.servlet.multipart.max-file-size=10MB
實用外掛
MyBatisX:自動生成實體層、業務層、持久層程式碼