MyBatis 初步

来源:https://www.cnblogs.com/enrace/archive/2022/07/21/16503123.html
-Advertisement-
Play Games

MyBatis 初步 目前我對 MyBatis 的瞭解不是很深,停留在企業比較常用的"資料庫框架"上,系統性的學習要看官方文檔。 這篇隨筆主要圍繞 SpringBoot 中 gradle 環境的搭建來講,是我從《深入淺出SpringBoot2》中討的一些知識。 可以跟著文章做一個基礎環境的項目。 引 ...


MyBatis 初步

目前我對 MyBatis 的瞭解不是很深,停留在企業比較常用的"資料庫框架"上,系統性的學習要看官方文檔

這篇隨筆主要圍繞 SpringBoot 中 gradle 環境的搭建來講,是我從《深入淺出SpringBoot2》中討的一些知識。

可以跟著文章做一個基礎環境的項目。

引入插件

倉庫地址:mvnrepository阿裡雲

因為用的 gradle ,在 build.gradle 中的 dependencies 中加入依賴包:

implementation 'org.mybatis.spring.boot:mybatis-spring-boot-starter:2.2.2'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'mysql:mysql-connector-java:8.0.29'

創建數據表和插入數據

終端進入 MySQL 中 xxx 資料庫,輸入 SQL 語句:

CREATE TABLE t_user(
	id INT(12) NOT NULL AUTO_INCREMENT,
    user_name VARCHAR(60) NOT NULL,
    sex INT(3) NOT NULL DEFAULT 1 CHECK (sex in (1,2)),
    note VARCHAR(256) NULL,
    PRIMARY KEY(id)
);
 INSERT INTO t_user(id,user_name,note)  VALUES(1,"user_name_1","zhangsan");

創建實體類並設置別名

因為類的全限定名很長,所以使用 @Alias(value = "xxx") 的方式,一般用來和數據表屬性相對的類上(實體類)。

數據表與之對應的實體類如下(自行加入 getter 和 setter ):

package mybatis.pojo;

import mybatis.enumeration.SexEnum;
import org.apache.ibatis.type.Alias;

/**
 * @author enrace
 * @Alias MyBatis give the class other name.
 */
@Alias(value = "user")
public class User {

    private Long id = null;

    private String userName = null;

    private String note = null;

    /**
     * The sex is numeration here need use typeHandler to switch.
     */
    private SexEnum sex = null;

    public User() {}

    /** setter 和 getter 方法自行加入即可 **/
}

創建enum 枚舉類型和 typeHandler

typeHandler 是 MyBatis 的重要配置之一,用於不同類型的數據進行自定義轉換。

我學習了將 Java 中的 enum (枚舉類)的實例和資料庫的 int 進行轉換。

先看看 enum 的使用,enum 的代碼如下(自行加入 getter 和 setter ):

package mybatis.enumeration;

/**
 * @author enrace
 */
public enum SexEnum {
    
    MALE(1, "男"),
    FEMALE(2,"女");
    
    private int id;
    
    private String name;
    
    SexEnum(int id, String name) {
        this.id = id;
        this.name = name;
    }
    
    public static SexEnum getEnumById(int id) {
        for (SexEnum sex : SexEnum.values() ) {
            if (sex.getId() == id) {
                return sex;
            }
        }
        return null;
    }

    /** setter 和 getter 方法自行加入即可 **/
}

enum 中有兩個類型 MALE 和 FEMALE 分別對應 MySQL 中 int 的 1 和 2。

其中的 getEnumById() 的方法是通過接收 int 返回 enum 的實例。

接著說說 typeHandler,這是通過繼承的方式自定以了一個 typeHandler:

package mybatis.typehandler;

import mybatis.enumeration.SexEnum;
import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

/**
 * @MappendJdbcTypes Declare JdbcType to be an integer.
 * @author enrace
 */
@MappedJdbcTypes(JdbcType.INTEGER)
@MappedTypes(value = SexEnum.class)
public class SexTypeHandler extends BaseTypeHandler<SexEnum> {
    /**
     * Read gender by column name.
     */
    @Override
    public SexEnum getNullableResult(ResultSet rs, String col) throws SQLException {
        int sex = rs.getInt(col);
        if(sex != 1 && sex != 2) {
            return null;
        }
        return SexEnum.getEnumById(sex);
    }

    /**
     * Read gender by subscript.
     * @param rs
     * @param idx
     * @return SexEnum
     * @throws SQLException
     */
    @Override
    public SexEnum getNullableResult(ResultSet rs, int idx) throws SQLException{
        int sex = rs.getInt(idx);
        if (sex != 1 && sex != 2) {
            return null;
        }
        return SexEnum.getEnumById(sex);
    }

    /**
     * Read gender from a stored procedure.
     * @param cs
     * @param idx
     * @return SexEnum
     * @throws SQLException
     */
    @Override
    public SexEnum getNullableResult(CallableStatement cs, int idx) throws SQLException {
        int sex = cs.getInt(idx);
        if (sex != 1 && sex != 2) {
            return null;
        }
        return SexEnum.getEnumById(sex);
    }

    /**
     * Set not null gender parameter.
     * @param ps
     * @param idx
     * @param sex
     * @param jdbcType
     * @throws SQLException
     */
    @Override
    public void setNonNullParameter(PreparedStatement ps, int idx, SexEnum sex, JdbcType jdbcType)
        throws SQLException {
        ps.setInt(idx, sex.getId());
    }

}

這個類的幾種方法,主要是通過 enum.getEnumById() 返回一個 enum 類型的結果。

我將 SexTypeHandler 分成簡述和細述:

簡敘:

網上瞭解的是: 一個 setxxx 方法,表示向 PreparedStatement 裡面設置值。三個 getxxx 方法,一個是根據列名獲取值,一個是根據列索引位置獲取值,最後一個是存儲過程。

細述:

ResultSet 類型我去查了一下,表示資料庫結果集的數據表,其中的 getXXX 表示在結果集中檢索 XXX 類型。

ResultSet.getInt(col) 方法通過 SQL 子句中指定的列的標簽獲取 sex 的 int。

ResultSet.getInt(idx) 方法通過第幾列的方式從數據表中獲取 sex 的 int。

CallableStatement 類型可以返回一個對象或多個對象ResultSet。

網上瞭解到 CallableStatement 類型用於從Java程式調用存儲過程,存儲過程是我們在資料庫中為某些任務編譯的一組語句。 當我們處理具有複雜場景的多個表時,存儲過程是有益的,而不是向資料庫發送多個查詢,我們可以將所需的數據發送到存儲過程,併在資料庫伺服器本身中執行邏輯。

自己不是很明白存儲過程,不過 CallableStatement 可以獲取到結果,自然能獲取到我們需要的 int 類型。

cs.getInt(idx) 方法通過傳入的 int 檢索指定JDBC INT類型的值。

ps.setInt(idx, sex.getId()) 設置給定的 java int 指定參數值。驅動將一個 SQL INTEGER 值發送到資料庫。

定義 MyBatis 操作介面

操作介面(Mapper 介面)使用來幫助資料庫和 POJO 映射的。

註意: 僅僅為一個介面,不需要任何實現類。

package mybatis.dao;

import mybatis.pojo.User;
import org.springframework.stereotype.Repository;

/**
 * @author enrace
 */
@Repository
public interface MyBatisUserDao {
    /**
     * Get User.
     * @param id
     * @return User
     */
    public User getUser(Long id);
}

@Repository 用於標註數據訪問組件,即 DAO 組件。

除了 操作介面還需要創建映射文件,映射文件的 namespace 是與操作介面對應的。

創建映射文件(小坑)和添加配置

映射文件讓 POJO (類) 能夠與資料庫的數據對應,是 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">
<mapper namespace="mybatis.dao.MyBatisUserDao">
    <select id="getUser" parameterType="long" resultType="user">
        select id, user_name as userName, sex, note from t_user where id = #{id}
    </select>
</mapper>

主要的幾個屬性:

namespace 指定一個介面,就是需要方法需要執行 sql 的介面。

<select> 標簽代表一個查詢語句。

id 指代這個 SQL,它與介面是同名的(個人認為是映射)。

parameterType 是說明屬性配置為 Long (個人理解為傳入參數類型)。

resultType 這裡指定返回的類型(記得 @Alias 設置的別名就是 user ,那麼到時會返回一個 User 實例)。

然後再去 application.properties 添加如下信息:

# 資料庫 url
spring.datasource.url = jdbc:mysql://localhost:3306/xxx
# 資料庫用戶名
spring.datasource.username = zhangsan
# 資料庫密碼
spring.datasource.password = passwd123
# 最大等待連接中的數量你,設置 0 沒有限制
spring.datasource.tomcat.max-idle = 10
# 最大連接活動數
spring.datasource.tomcat.max-active = 50
# 最大等待毫秒數,單位 ms ,超過時間會出錯誤信息
spring.datasource.tomcat.max-wait = 10000
# 資料庫連接池初始化連接數
spring.datasource.tomcat.initial-size = 5

# 映射文件配置
mybatis.mapper-locations=classpath:mybatis/mapper/*.xml
# 掃描別名包,和註解 @Alias 聯用
mybatis.type-aliases-package=mybatis.pojo
# 配置 typeHandler 的掃描包
mybatis.type-handlers-package=mybatis.typehandler
#logging.level.root = DEBUG
#logging.level.org.springframework = DEBUG
#logging.level.org.org.mybatis = DEBUG

小坑

映射文件有了,我將它放在了項目 mybatis.mapper 包下,但是後面執行報了以下異常:

org.apache.ibatis.binding.BindingException: Invalid bound statement (not found): mybatis.dao.MyBatisUserDao.getUser

分析過程:

classpath 指的文件目錄是什麼?

classpath 指的是 build/resources (gradle 構建包) 或是 target/classes (maven 構建包)。

結果:

發現沒有 mapper 目錄和我的映射文件,手動添加可正常執行。

解決方法一

在 resources 資源目錄創建 mybatis 目錄和 mapper 子目錄,將映射文件放入其中,原因是 resources 中的文件 gradle build 的時候會保留下來。這種是資源文件分離的方式。

解決方法二

build.gradle 中添加下麵的代碼:

sourceSets {
    main {
        resources {
            srcDirs 'src/main/java'
        }
    }
}

這是資源路徑設置,添加代碼後 gradle build 的時候不會刪除 java 目錄下的 非 .java 尾碼文件。

使用 MapperFactoryBean 裝配 MyBatis 介面

上面的 MyBatisUserDao 是一個 Mapper 介面,不可以使用 new 為其 生成對象實例。需要用到兩個類,它 們 是 MapperFactoryBean 和 MapperScannerConfigurer 。其中 MapperFactoryBean 針對介面配置,MapperScannerConfigurer 則是掃描裝配。書中提到 @MapperScan 可以後面去使用一下,它更為簡便也是用來將對應介面掃描裝配到 Spring IoC 容器中的。

接下來我們創建一個 Bean 來配置 MyBatisUserDao 介面,在 @SpringBootApplication 註解文件下增加代碼:

  @Autowired
    SqlSessionFactory sqlSessionFactory = null;

    /**
     * Define a Mapper interface of MyBatis.
     * @return MapperFactoryBean\<MyBatisUserDao\>
     */
    @Bean
    public MapperFactoryBean<MyBatisUserDao> initMyBatisUserDao() {
        MapperFactoryBean<MyBatisUserDao> bean = new MapperFactoryBean<>();
        bean.setMapperInterface(MyBatisUserDao.class);
        bean.setSqlSessionFactory(sqlSessionFactory);
        return bean;
    }

SqlSessionFactory 是 Spring Boot 自動為我們生成的。

開發服務層

由於不是很難理解,直接上代碼。

服務介面類代碼,如下:

package mybatis.service;

import mybatis.pojo.User;

/**
 * @author enrace
 */
public interface MyBatisUserService {
    /**
     * Get user object.
     * @param id
     * @return User
     */
    public User getUser(Long id);

}

實現類代碼,如下:

package mybatis.service.impl;

import mybatis.dao.MyBatisUserDao;
import mybatis.pojo.User;
import mybatis.service.MyBatisUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author enrace
 */
@Service
public class MyBatisUserServiceImpl implements MyBatisUserService {

    @Autowired
    private MyBatisUserDao myBatisUserDao = null;

    @Override
    public User getUser(Long id) {
        return myBatisUserDao.getUser(id);
    }
}

實現類中,通過 @Autowired 自動裝配 MyBatisUserDao 的 Bean ,我們就實現了 getUser() 方法,從而可以獲得 User 對象。

創建控制器

有了控制器就可以通過 url 測試結果。

控制器代碼,如下:

package mybatis.controller;

import mybatis.pojo.User;
import mybatis.service.MyBatisUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

/**
 * @author enrace
 */
@RestController
public class MyBatisController {

    @Autowired
    private MyBatisUserService myBatisUserService = null;

    @RequestMapping("/getUser")
    public User getUser(Long id) {
        return myBatisUserService.getUser(id);
    }

}

@RequestMapping("/getUser") 設置請求的映射,通過傳入 id 得到用戶( JSON )格式。

完成

啟動 Spring Boot 項目,訪問 localhost:8080/getUser?id = 1

至此,您已經瞭解到了 MyBatis 基本的執行過程,祝:事事無憂,天天無 BUG。


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • ES6 class中的一些問題 記錄下class中的原型,實例,super之間的關係 //父類 class Dad { constructor(x, y) { this.x = 5; this.y = 1; this.state = 789 } static x = 521 state1 = 666 ...
  • 在早期的隨筆就介紹過,把常規頁面的內容拆分為幾個不同的組件,如普通的頁面,包括列表查詢、詳細資料查看、新增資料、編輯資料、導入資料等頁面場景,這些內容相對比較獨立,而有一定的代碼量,本篇隨筆介紹基於Vue3+Typescript+Setup語法方式,來拆分頁面模塊內容為組件,實現分而治之的處理。 ...
  • @(文章目錄) 提示:本文僅供學習交流,請勿用於非法活動! 前言 本文大概內容: 在官網展現圖表及報表(含導出) 一、使用highcharts前後端交互展示圖表,及使用報表導出 如下圖,我們在首頁如何將折線圖、柱狀圖結合報表,並實現根據不同的倉庫實時刷新不同的數據,最後可以選擇導出圖表及報表。 二、 ...
  • JavaScript進階內容——DOM詳解 當我們已經熟練掌握JavaScript的語法之後,我們就該進入更深層次的學習了 首先我們思考一下:JavaScript是用來做什麼的? JavaScript誕生就是為了能夠讓它在瀏覽器中運行 那麼DOM就是我們學習中不可或缺的一個環節,下麵讓我們深入瞭解D ...
  • 概述 beanstalkd 是一個簡單快速的分散式工作隊列系統,協議基於 ASCII 編碼運行在 TCP 上。其最初設計的目的是通過後臺非同步執行耗時任務的方式降低高容量 Web 應用的頁面延時。其具有簡單、輕量、易用等特點,也支持對任務優先順序、延時/超時重發等控制,同時還有眾多語言版本的客戶端支持, ...
  • 看《C++ Primer Plus》時整理的學習筆記,部分內容完全摘抄自《C++ Primer Plus》(第6版)中文版,Stephen Prata 著,張海龍 袁國忠譯,人民郵電出版社。只做學習記錄用途。 ...
  • 一個簡單的仿華為商城後端Java項目,技術棧使用SpringBoot+MyBatis+MySQL 包含用戶登錄註冊,個人資料維護,收貨地址,購物車,及簡單的訂單業務功能 始於2022年6月21日 ...
  • Dokcer運行Nacos容器自動退出問題 參考博文 學生黨,租的雲伺服器,2核2G。使用Docker運行Nacos容器的時候發現總是自動退出。Nacos日誌裡面沒有明顯的報錯信息。查了一下是記憶體溢出錯誤,指令如下 journalctl -k | grep -i -e memory -e oom 發 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...