SpringBoot 學習筆記

Undefined443發表於2024-06-10

表示層 > 業務層 > 持久層 > 資料庫

使用分層結構進行解耦

表示層

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-webspring-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:自動生成實體層、業務層、持久層程式碼

相關文章