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 = "https://www.cnblogs.com/enrace/archive/2022/07/21/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 = "https://www.cnblogs.com/enrace/archive/2022/07/21/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 = https://www.cnblogs.com/enrace/archive/2022/07/21/SexEnum.class)
public class SexTypeHandler extends BaseTypeHandler {
/**
* 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,
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/499973.html
標籤:其他
上一篇:python面向物件編程
