欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

MyBatis查詢(xún)數(shù)據(jù)庫(kù)語(yǔ)句總結(jié)

 更新時(shí)間:2023年06月20日 09:20:58   作者:YoLo?  
MyBatis是一種持久化框架,可以與許多不同類(lèi)型的關(guān)系型數(shù)據(jù)庫(kù)連接,下面這篇文章主要給大家介紹了關(guān)于MyBatis查詢(xún)數(shù)據(jù)庫(kù)語(yǔ)句的相關(guān)資料,文中通過(guò)實(shí)例代碼介紹的非常詳細(xì),需要的朋友可以參考下

1.MyBatis是什么?

MyBatis是一款開(kāi)源的基于Java的持久層框架,它可以幫助Java開(kāi)發(fā)人員簡(jiǎn)化數(shù)據(jù)庫(kù)操作,通過(guò)XML配置文件或注解,把Java對(duì)象映射到對(duì)應(yīng)的數(shù)據(jù)庫(kù)表中,實(shí)現(xiàn)對(duì)象關(guān)系和數(shù)據(jù)關(guān)系的互相轉(zhuǎn)換,從而使得Java應(yīng)用程序能夠更簡(jiǎn)單的操作和讀取數(shù)據(jù)庫(kù)。

MyBatis相對(duì)于其他ORM框架最大的特點(diǎn)就是它提供了優(yōu)秀的SQL編寫(xiě)和調(diào)試機(jī)制。通過(guò)MyBatis,我們可以直接編寫(xiě)出原生SQL語(yǔ)句,并通過(guò)MyBatis的一些工具,如參數(shù)映射、結(jié)果集映射等機(jī)制,把查詢(xún)結(jié)果自動(dòng)映射成Java對(duì)象或者List/Map等數(shù)據(jù)格式。同時(shí),MyBatis還提供了懶加載、緩存和事務(wù)等高級(jí)特性,可以支持各種復(fù)雜的業(yè)務(wù)場(chǎng)景。

核心思想

將JavaBean中的數(shù)據(jù)和SQL語(yǔ)句中的數(shù)據(jù)進(jìn)行映射,因此,在使用MyBatis時(shí),我們需要定義一個(gè)Mapper接口,然后在XML中描述這個(gè)接口中每個(gè)方法對(duì)應(yīng)的SQL語(yǔ)句,并指定參數(shù)和返回值類(lèi)型,最后,讓MyBatis自動(dòng)生成這個(gè)Mapper接口的實(shí)現(xiàn)類(lèi),這樣就可以方便地對(duì)數(shù)據(jù)庫(kù)進(jìn)行操作。

持久層

在 Java 程序開(kāi)發(fā)中,數(shù)據(jù)通常被存儲(chǔ)在關(guān)系型數(shù)據(jù)庫(kù)或者非關(guān)系型數(shù)據(jù)庫(kù)中,而持久層就是用來(lái)處理和管理這些數(shù)據(jù)存取操作的技術(shù)層次。持久層的作用是將應(yīng)用程序中的對(duì)象轉(zhuǎn)換成可存儲(chǔ)的形式,然后將其存儲(chǔ)到數(shù)據(jù)存儲(chǔ)介質(zhì)中,同時(shí)還要負(fù)責(zé)從數(shù)據(jù)存儲(chǔ)介質(zhì)中獲取數(shù)據(jù)并將其轉(zhuǎn)換成應(yīng)用程序所需要的對(duì)象形式,這樣就把數(shù)據(jù)存儲(chǔ)和數(shù)據(jù)訪問(wèn)分開(kāi)了,提高了程序的可維護(hù)性和擴(kuò)展性。

持久層技術(shù)通常包括以下方面的內(nèi)容:

  • 對(duì)象-關(guān)系映射(ORM):ORM將Java對(duì)象與關(guān)系型數(shù)據(jù)庫(kù)之間進(jìn)行了映射,使得Java對(duì)象可以像操作內(nèi)存一樣操作數(shù)據(jù)庫(kù),簡(jiǎn)化了開(kāi)發(fā)。
  • 數(shù)據(jù)訪問(wèn)層(DAO):定義了對(duì)數(shù)據(jù)訪問(wèn)的接口,封裝了數(shù)據(jù)存取的細(xì)節(jié),使得上層業(yè)務(wù)邏輯不需要關(guān)注數(shù)據(jù)存取的具體實(shí)現(xiàn)方式。
  • 持久化框架:用于簡(jiǎn)化數(shù)據(jù)庫(kù)訪問(wèn)、提高性能、降低開(kāi)發(fā)難度、提供高級(jí)功能,如緩存、批量處理、分布式共享等等。

JDBC編程的缺點(diǎn)

  1. 繁瑣的代碼編寫(xiě):JDBC需要開(kāi)發(fā)人員自己手動(dòng)編寫(xiě)大量的結(jié)果集映射、異常處理等代碼,很容易出現(xiàn)重復(fù)代碼和錯(cuò)誤。
  2. 容易造成資源泄漏:使用JDBC需要顯式地打開(kāi)和關(guān)閉連接、Statement和ResultSet等對(duì)象,如果不仔細(xì)處理,可能會(huì)導(dǎo)致資源泄漏,從而影響應(yīng)用程序的性能和穩(wěn)定性。
  3. SQL語(yǔ)句的硬編碼:JDBC需要直接在Java代碼中編寫(xiě)SQL語(yǔ)句,這樣就容易出現(xiàn)硬編碼,對(duì)于SQL語(yǔ)句的修改和維護(hù)也不太方便。
  4. 難以應(yīng)對(duì)復(fù)雜業(yè)務(wù)場(chǎng)景:如果某個(gè)應(yīng)用有比較復(fù)雜的業(yè)務(wù)邏輯需求,例如多表關(guān)聯(lián)查詢(xún)、分頁(yè)查詢(xún)、事務(wù)控制等,使用JDBC來(lái)實(shí)現(xiàn)可能比較困難,需要編寫(xiě)大量的復(fù)雜代碼。

MyBatis可以解決這些問(wèn)題.ORM框架可以將Java對(duì)象和數(shù)據(jù)庫(kù)表自動(dòng)映射,并提供了更加優(yōu)雅和高效的API,讓開(kāi)發(fā)人員專(zhuān)注于業(yè)務(wù)邏輯,而不是繁瑣的數(shù)據(jù)訪問(wèn)代碼。同時(shí),ORM框架還可以提供緩存、懶加載和批處理等高級(jí)特性,從而進(jìn)一步提高程序的性能和可維護(hù)性。

MyBatis與JDBC

MyBatis是基于JDBC實(shí)現(xiàn)的。MyBatis雖然與JDBC有很大的相似之處,但是它封裝了JDBC的細(xì)節(jié),使得開(kāi)發(fā)人員可以更加便捷和高效地進(jìn)行數(shù)據(jù)庫(kù)操作。

在使用MyBatis進(jìn)行數(shù)據(jù)訪問(wèn)時(shí),程序仍然需要使用JDBC的API來(lái)獲取連接對(duì)象、創(chuàng)建Statement對(duì)象、執(zhí)行SQL語(yǔ)句以及處理查詢(xún)結(jié)果等操作。不過(guò),MyBatis對(duì)JDBC進(jìn)行了簡(jiǎn)化和封裝,提供了面向?qū)ο蟮姆绞皆L問(wèn)數(shù)據(jù)庫(kù),通過(guò) XML 或注解方式配置 SQL 語(yǔ)句,并且提供了一些高級(jí)特性,如緩存、動(dòng)態(tài) SQL 和對(duì)象關(guān)系映射(ORM)等。

MyBatis相對(duì)于JDBC的優(yōu)勢(shì)在于,MyBatis更加簡(jiǎn)單易用,提升了開(kāi)發(fā)效率;同時(shí),MyBatis還提供了更多的功能,如自動(dòng)映射、動(dòng)態(tài) SQL 和二級(jí)緩存等,可以提升應(yīng)用程序的性能和可維護(hù)性。

因此,MyBatis是一種基于JDBC實(shí)現(xiàn)的高級(jí)數(shù)據(jù)訪問(wèn)框架,它封裝了JDBC的底層細(xì)節(jié),提供了一種高級(jí)的、面向?qū)ο蟮姆绞絹?lái)訪問(wèn)數(shù)據(jù)庫(kù),并且提供了多種高級(jí)特性,以方便開(kāi)發(fā)人員進(jìn)行數(shù)據(jù)訪問(wèn)和業(yè)務(wù)邏輯的實(shí)現(xiàn)。

ORM框架

ORM是對(duì)象關(guān)系映射(Object-Relational Mapping)的縮寫(xiě),是一種將對(duì)象模型和關(guān)系數(shù)據(jù)庫(kù)模型進(jìn)行轉(zhuǎn)換、映射的技術(shù)。通過(guò)ORM框架,可以將Java對(duì)象映射到關(guān)系數(shù)據(jù)庫(kù)表中的行,從而通過(guò)面向?qū)ο蟮姆绞絹?lái)操作數(shù)據(jù)庫(kù),而無(wú)需編寫(xiě)大量的SQL語(yǔ)句。

Java的類(lèi),對(duì)象,對(duì)象屬性映射到ORM框架中是什么

類(lèi)映射:在MyBatis中,Java的類(lèi)映射到數(shù)據(jù)庫(kù)中的某個(gè)表

對(duì)象映射:在MyBatis中,Java的對(duì)象映射到數(shù)據(jù)庫(kù)中的數(shù)據(jù)行

屬性映射:Java對(duì)象的屬性和數(shù)據(jù)庫(kù)表的列是一一對(duì)應(yīng)的

2.配置MyBatis環(huán)境

1.添加MyBatis框架支持

運(yùn)行后會(huì)報(bào)錯(cuò)

2.設(shè)置MyBatis配置信息

1.設(shè)置數(shù)據(jù)庫(kù)連接相關(guān)信息

2.配置xml保存路徑,xml命名規(guī)則 

classpath

        表示類(lèi)路徑,即應(yīng)用程序的CLASSPATH環(huán)境變量所指的路徑,用于指示 MyBatis 框架在類(lèi)路徑下查找配置文件。

/mybatis/

        是一個(gè)目錄路徑,表示 MyBatis 框架將在類(lèi)路徑下的 mybatis 目錄中查找 *Mapper.xml 配置文件。

*Mapper.xml

        *是通配符,表示所有以 Mapper.xml 結(jié)尾的文件都應(yīng)該被 MyBatis 框架解析和加載。

綜合起來(lái),classpath:/mybatis/*Mapper.xml 用于指示 MyBatis 框架在類(lèi)路徑下的 mybatis 目錄中查找所有以 Mapper.xml 結(jié)尾的配置文件,并將其解析為 MyBatis 的 Mapper 映射文件。

舉個(gè)例子,假設(shè)我們的應(yīng)用程序類(lèi)路徑中有一個(gè)名為 mybatis 的目錄,該目錄下包含了兩個(gè) MyBatis 的映射文件:UserMapper.xmlOrderMapper.xml。那么,使用 classpath:/mybatis/*Mapper.xml 這個(gè)路徑表達(dá)式,MyBatis 框架將會(huì)自動(dòng)解析這兩個(gè)映射文件,并將其配置信息綁定到對(duì)應(yīng)的 Java 接口或類(lèi)上,從而實(shí)現(xiàn)與數(shù)據(jù)庫(kù)的數(shù)據(jù)訪問(wèn)操作。

至此,配置環(huán)境就完成了

3. MyBatis模式開(kāi)發(fā)

先向數(shù)據(jù)庫(kù)中插入一些數(shù)據(jù)

首先,創(chuàng)建一個(gè)實(shí)體類(lèi)UserEntity,包含上述幾個(gè)屬性,必須對(duì)應(yīng)。

接著,在Mapper接口中聲明對(duì)UserEntity表進(jìn)行操作的方法:

Mapper是MyBatis框架中的一個(gè)概念,用于描述一組將Java對(duì)象映射到SQL語(yǔ)句的規(guī)則。通常情況下,Mapper會(huì)包含一組接口和配置文件,用于定義Java對(duì)象和數(shù)據(jù)庫(kù)表之間的映射關(guān)系。

然后,在Mapper.xml文件中,實(shí)現(xiàn)這些接口方法

<?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.example.demo.mapper.UserMapper">
    <select id="getAll" resultType="com.example.demo.entity.UserEntity">
        select * from userinfo
    </select>
</mapper>

id對(duì)應(yīng)接口中的方法名

namesapce:實(shí)現(xiàn)接口(包名加類(lèi)名)

按照標(biāo)準(zhǔn)分層,然后,創(chuàng)建Service層訪問(wèn)Mapper 

Service層處理業(yè)務(wù)邏輯,并對(duì)外提供服務(wù)接口

創(chuàng)建Controller層

Controller層:負(fù)責(zé)接收用戶(hù)請(qǐng)求,調(diào)用 Service 層處理請(qǐng)求,并通過(guò)視圖呈現(xiàn)處理結(jié)果

啟動(dòng)項(xiàng)目,成功訪問(wèn) 

向表中插入一條數(shù)據(jù)

刷新后,更新數(shù)據(jù)庫(kù)中插入的新數(shù)據(jù)

上述是最簡(jiǎn)單的查詢(xún)操作

4.單元測(cè)試

 此處介紹一下單元測(cè)試的概念

SpringBoot已經(jīng)內(nèi)置了單元測(cè)試的依賴(lài),不用添加支持

spring-boot-starter-test 是 Spring Boot 提供的一種測(cè)試 Starter,旨在提供各種常見(jiàn)的測(cè)試工具和框架,使開(kāi)發(fā)人員能夠更加輕松地編寫(xiě)、運(yùn)行、管理 Spring Boot 應(yīng)用程序的單元測(cè)試和集成測(cè)試

包含了以下幾個(gè)主要組件:

  1. JUnit:一個(gè)廣泛使用的 Java 單元測(cè)試框架,能夠幫助開(kāi)發(fā)人員快速構(gòu)建單元測(cè)試用例,并自動(dòng)執(zhí)行這些測(cè)試用例;()我們此處使用的JUit5)
  2. Spring Test:一個(gè)專(zhuān)門(mén)為 Spring 應(yīng)用程序設(shè)計(jì)的測(cè)試框架,能夠幫助開(kāi)發(fā)人員快速搭建 Spring 容器環(huán)境,在容器環(huán)境中執(zhí)行測(cè)試用例,并驗(yàn)證應(yīng)用程序的正確性;
  3. AssertJ:一個(gè)優(yōu)秀的斷言庫(kù),能夠幫助開(kāi)發(fā)人員編寫(xiě)簡(jiǎn)潔、易于理解、易于擴(kuò)展的斷言語(yǔ)句;
  4. Hamcrest:另一個(gè)流行的斷言庫(kù),也提供了類(lèi)似于 AssertJ 的各種斷言方法;
  5. Mockito:一個(gè)強(qiáng)大的 Java 模擬框架,能夠幫助開(kāi)發(fā)人員快速創(chuàng)建模擬對(duì)象,并進(jìn)行各種測(cè)試;
  6. JsonPath:一個(gè)用于處理 JSON 路徑的庫(kù),能夠幫助開(kāi)發(fā)人員快速過(guò)濾、提取 JSON 數(shù)據(jù)。

優(yōu)點(diǎn)主要有以下幾個(gè):

  1. 提升測(cè)試效率:借助 spring-boot-starter-test Starter 提供的各種測(cè)試工具和框架,開(kāi)發(fā)人員能夠更加輕松地編寫(xiě)、運(yùn)行、管理單元測(cè)試和集成測(cè)試,從而大大提升測(cè)試效率;
  2. 方便集成 Spring Boot:spring-boot-starter-test Starter 與 Spring Boot 完美集成,可直接使用 Spring Boot 的自動(dòng)配置機(jī)制和依賴(lài)注入機(jī)制,使得單元測(cè)試與實(shí)際應(yīng)用程序開(kāi)發(fā)更加無(wú)縫銜接;
  3. 簡(jiǎn)化代碼編寫(xiě):借助 spring-boot-starter-test Starter 提供的各種測(cè)試工具和框架,開(kāi)發(fā)人員不需要編寫(xiě)大量的測(cè)試框架代碼,只需要關(guān)注業(yè)務(wù)邏輯和測(cè)試用例的編寫(xiě)即可;
  4. 多樣化的測(cè)試支持:spring-boot-starter-test Starter 支持多種類(lèi)型的測(cè)試:?jiǎn)卧獪y(cè)試、集成測(cè)試、端到端測(cè)試等等;
  5. 良好的社區(qū)支持與更新:spring-boot-starter-test Starter 是 Spring Boot 社區(qū)的核心組件之一,擁有龐大的用戶(hù)群和活躍的維護(hù)者團(tuán)隊(duì),能夠及時(shí)更新和修復(fù)各種測(cè)試工具和框架的問(wèn)題。

單元測(cè)試實(shí)現(xiàn)步驟

1.在要測(cè)試的類(lèi)上右鍵Generate

會(huì)生成測(cè)試類(lèi)

寫(xiě)測(cè)試信息.不要忘記添加注解!!

啟動(dòng)測(cè)試類(lèi)

這是測(cè)試類(lèi)的創(chuàng)建,向測(cè)試類(lèi)中添加測(cè)試時(shí),會(huì)報(bào)一個(gè)錯(cuò)誤

意思是已經(jīng)存在了這個(gè)測(cè)試類(lèi),是否向其中追加測(cè)試,直接點(diǎn)擊Ok

單元測(cè)試就簡(jiǎn)單了解到這里

5.增刪查改

下來(lái)看傳參問(wèn)題

當(dāng)這兩處,參數(shù)名稱(chēng)不一致,xml中應(yīng)該寫(xiě)成什么

啟動(dòng)測(cè)試類(lèi)會(huì)報(bào)錯(cuò),參數(shù)id沒(méi)找到

 修改xml文件

此時(shí)單元測(cè)試運(yùn)行成功了 .

 所以xml文件的參數(shù)命名要和@Param("uid")一致..如果沒(méi)有加這個(gè)注解,就要和原參數(shù)名相同

 加了注解,生效的就是注解中的參數(shù)命名

Mybatis獲取動(dòng)態(tài)參數(shù)的方式有兩種:

1.${key}

${key} 是一種占位符語(yǔ)法,被稱(chēng)為字符串替換,用于將 SQL 語(yǔ)句中的參數(shù)占位符替換為實(shí)際的值。使用 ${} 語(yǔ)法會(huì)直接將參數(shù)替換為 SQL 字符串,而不是通過(guò)預(yù)編譯語(yǔ)句來(lái)傳遞參數(shù)。在使用 ${key} 時(shí),如果不正確地處理輸入的參數(shù),可能會(huì)導(dǎo)致 SQL 注入攻擊。

可以打印出執(zhí)行的生成的sql語(yǔ)句,就能看到效果了

需要先配置一下

spring.datasource.url=jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8&useSSL=false
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
#配置mybatis xml 的文件路徑,在resources/mapper 創(chuàng)建的所有表的 xml 文件
mybatis.mapper-locations=classpath:/mybatis/*Mapper.xml
#日志打印sql
mybatis.configuration.log-impl=org.apache.ibatis.logging.stdout.StdOutImpl
#日志打印級(jí)別(默認(rèn)是info,需要設(shè)置位debug,才能顯示)
logging.level.com.example.demo=debug

然后執(zhí)行測(cè)試

可以看到這是直接將參數(shù)替換為 SQL 字符串 

2.#{key}

在 MyBatis 中,#{key} 是一種占位符語(yǔ)法,被稱(chēng)為參數(shù)占位符,用于將 SQL 語(yǔ)句中的參數(shù)占位符替換為實(shí)際的值。它可以幫助我們?cè)?SQL 語(yǔ)句中使用參數(shù),使得 SQL 語(yǔ)句更加靈活和通用

具體來(lái)說(shuō),當(dāng)應(yīng)用程序調(diào)用 DAO 層的方法時(shí),會(huì)傳遞一些參數(shù)給對(duì)應(yīng)的 SQL 語(yǔ)句,這些參數(shù)可以通過(guò) #{} 占位符來(lái)引用。MyBatis 會(huì)自動(dòng)將 #{} 中的參數(shù)值進(jìn)行預(yù)編譯處理,保證輸入數(shù)據(jù)的安全性,并將它們以安全的方式插入到 SQL 語(yǔ)句中,然后發(fā)送給數(shù)據(jù)庫(kù)執(zhí)行,從而防止了 SQL 注入攻擊等安全問(wèn)題

啟動(dòng)單元測(cè)試

這里的 #{uid} 是一個(gè)占位符,代表該位置需要?jiǎng)討B(tài)替換為具體的參數(shù)值。在執(zhí)行 SQL 時(shí),MyBatis 會(huì)自動(dòng)將 #{uid} 替換為實(shí)際傳入的 uid 參數(shù)的值,并將其進(jìn)行預(yù)編譯處理,從而確保 SQL 查詢(xún)語(yǔ)句的安全性。

日志打印看到的也不是sql語(yǔ)句,而是通過(guò)占位符模式進(jìn)行內(nèi)容的填充

這里數(shù)據(jù)是int類(lèi)型的,我們換用String類(lèi)型數(shù)據(jù)嘗試

結(jié)果:

當(dāng)將#{}替換為${} 再次進(jìn)行執(zhí)行.報(bào)錯(cuò)了 

原因:${}是直接替換字符串的,你輸入的是什么就完全替換

'${username}',才不會(huì)報(bào)錯(cuò),不加單引號(hào)sql語(yǔ)句就會(huì)出現(xiàn)問(wèn)題

這樣只能保障不報(bào)錯(cuò),但是會(huì)存在安全性問(wèn)題

例如登陸場(chǎng)景下:sql注入問(wèn)題

 為了只返回一條數(shù)據(jù),先將表中數(shù)據(jù)刪除

mysql> select*from userinfo;
+----+----------+----------+-------+---------------------+---------------------+-------+
| id | username | password | photo | createtime          | updatetime          | state |
+----+----------+----------+-------+---------------------+---------------------+-------+
|  2 | lisi     | lisi     |       | 2022-12-06 17:10:48 | 2022-12-06 18:10:48 |     1 |
+----+----------+----------+-------+---------------------+---------------------+-------+
1 row in set (0.00 sec)

正常行為: 

攻擊者發(fā)送如下輸入:

mysql> select 1 = '1';
+---------+
| 1 = '1' |
+---------+
|       1 |
+---------+
1 row in set (0.00 sec)

那么該 SQL 查詢(xún)語(yǔ)句將會(huì)被替換為:

SELECT * FROM users WHERE username = '' or '1'='1' AND password = '' or '1'='1'

使用不存在的username,不存在的password也能查到用戶(hù)信息

這就是使用${key}時(shí)的sql注入攻擊.

將${key}替換為#{key}

占位符的方式,不會(huì)出現(xiàn)安全性問(wèn)題 

${}在特定場(chǎng)景下,也有優(yōu)點(diǎn)

此時(shí)使用#{key}會(huì)被錯(cuò)誤地解析為字符串: 

SELECT * FROM userinfo ORDER BY id "desc"

即使設(shè)置了 ord 參數(shù)的值為 ASC 或 DESC,也無(wú)法正確地替換該字符串

需要將該 SQL 語(yǔ)句更改為以下形式進(jìn)行直接替換:

SELECT * FROM userinfo ORDER BY id ${ord}

總結(jié):

1.${key}   主要用于字符串替換,可以方便地將參數(shù)替換為 SQL 語(yǔ)句

        使用 ${key} 時(shí)需要注意安全性問(wèn)題,應(yīng)該避免潛在的 SQL 注入攻擊

2.#{key}   主要用于預(yù)處理 SQL 語(yǔ)句,可以有效地防止 SQL 注入攻擊

#{key}   底層原理:

在 MyBatis 中,#{} 在底層的實(shí)現(xiàn)中使用了 PreparedStatement 進(jìn)行參數(shù)的預(yù)處理和類(lèi)型轉(zhuǎn)換。

當(dāng) MyBatis 執(zhí)行 SQL 語(yǔ)句時(shí),它會(huì)將 SQL 語(yǔ)句中的 #{} 占位符替換為 JDBC 的 Placeholder(如 ?),然后生成一個(gè)預(yù)編譯的 SQL 語(yǔ)句。這個(gè)預(yù)編譯的 SQL 語(yǔ)句會(huì)被傳遞給 DriverManager,并由 DriverManager 創(chuàng)建一個(gè) PreparedStatement 對(duì)象。

然后,MyBatis 會(huì)將查詢(xún)參數(shù)的值按照 JDBC 規(guī)范設(shè)置到 PreparedStatement 的參數(shù)位置上,然后執(zhí)行 PreparedStatement 對(duì)象的查詢(xún)操作,最后將查詢(xún)結(jié)果映射成 Java 對(duì)象并返回給調(diào)用方。

使用預(yù)處理語(yǔ)句可以提高 SQL 的執(zhí)行效率,避免 SQL 注入等安全問(wèn)題,并且可以自動(dòng)進(jìn)行類(lèi)型轉(zhuǎn)換,讓開(kāi)發(fā)人員更加專(zhuān)注于業(yè)務(wù)邏輯的實(shí)現(xiàn)。

需要注意的是,由于預(yù)處理語(yǔ)句必須在編譯和執(zhí)行之間進(jìn)行變量綁定,因此不能在運(yùn)行時(shí)動(dòng)態(tài)地構(gòu)建 SQL 語(yǔ)句。如果需要在運(yùn)行時(shí)動(dòng)態(tài)地構(gòu)建 SQL 語(yǔ)句,需要使用 ${} 實(shí)現(xiàn)字符串替換,并且需要注意防止 SQL 注入攻擊的問(wèn)題。

PreparedStatement 是 JDBC 提供的一種接口,用于提高執(zhí)行 SQL 的性能和安全性 PreparedStatement 的特點(diǎn):

  1. 預(yù)編譯:PreparedStatement 對(duì)象在創(chuàng)建時(shí)會(huì)將 SQL 語(yǔ)句編譯成一個(gè)可執(zhí)行的語(yǔ)句,這樣可以提高 SQL 的執(zhí)行效率。
  2. 參數(shù)綁定:PreparedStatement 可以在執(zhí)行前設(shè)置參數(shù),可以使用占位符 (?) 或命名參數(shù)(:param)來(lái)代替實(shí)際的參數(shù)值,從而避免了 SQL 注入攻擊。
  3. 批處理:PreparedStatement 支持批量處理,可以將多個(gè) SQL 語(yǔ)句一次性提交到數(shù)據(jù)庫(kù)執(zhí)行,從而提高數(shù)據(jù)庫(kù)處理的效率。
  4. 預(yù)處理語(yǔ)句緩存:預(yù)編譯語(yǔ)句可以緩存,再次執(zhí)行同樣的 SQL 語(yǔ)句時(shí),可以直接從緩存中獲取已編譯的語(yǔ)句,避免了每次都重新編譯 SQL,從而提高 SQL 的執(zhí)行效率。

修改密碼 

運(yùn)行單元測(cè)試

成功修改 

mysql> select*from userinfo ;
+----+----------+----------+-------+---------------------+---------------------+-------+
| id | username | password | photo | createtime          | updatetime          | state |
+----+----------+----------+-------+---------------------+---------------------+-------+
|  2 | lisi     | 123456   |       | 2022-12-06 17:10:48 | 2022-12-06 18:10:48 |     1 |
+----+----------+----------+-------+---------------------+---------------------+-------+
1 row in set (0.00 sec)

 在單元測(cè)試情況下,我們是不愿意改動(dòng)數(shù)據(jù)庫(kù)的,也就是不污染數(shù)據(jù)庫(kù)

默認(rèn)情況下,是會(huì)持久化改動(dòng)數(shù)據(jù)庫(kù)的

這里就用到@Transactional注解了

正常執(zhí)行,但不污染數(shù)據(jù)庫(kù),執(zhí)行時(shí)開(kāi)啟事務(wù),執(zhí)行結(jié)束后進(jìn)行回滾操作,就不會(huì)污染數(shù)據(jù)庫(kù)

看看數(shù)據(jù)庫(kù)中,沒(méi)有改變,說(shuō)明進(jìn)行了回滾...并且成功驗(yàn)證了程序,返回修改:1

mysql> select*from userinfo ;
+----+----------+----------+-------+---------------------+---------------------+-------+
| id | username | password | photo | createtime          | updatetime          | state |
+----+----------+----------+-------+---------------------+---------------------+-------+
|  2 | lisi     | 123456   |       | 2022-12-06 17:10:48 | 2022-12-06 18:10:48 |     1 |
+----+----------+----------+-------+---------------------+---------------------+-------+
1 row in set (0.00 sec)

 下來(lái)學(xué)習(xí)刪除操作

//刪除用戶(hù),返回受影響行數(shù)
    int deleteById(@Param("id")Integer id);
 
<delete id="deleteById">
<!--        默認(rèn)返回受影響的行數(shù)-->
        delete from userinfo where id = #{id}
    </delete>
 
@Transactional
    @Test
    void deleteById() {
        int result = userMapper.deleteById(2);
        System.out.println("刪除: "+result);
    }

執(zhí)行結(jié)果

因?yàn)樵O(shè)置事務(wù)了,所以測(cè)試完成后進(jìn)行了回滾,不會(huì)污染數(shù)據(jù)庫(kù)

普通得增加模塊,這里我們不進(jìn)行回滾了,讓數(shù)據(jù)插入到數(shù)據(jù)庫(kù)中

//添加用戶(hù)
    int addUser(UserEntity user);
 
<insert id="addUser">
        <!--        默認(rèn)返回受影響的行數(shù)-->
        insert into userinfo(username,password) values(#{username},#{password})
    </insert>
 
@Test
    void addUser() {
        UserEntity user = new UserEntity();
        user.setUsername("zhangsan");
        user.setPassword("123456");
        int result = userMapper.addUser(user);
        System.out.println(result);
    }

執(zhí)行結(jié)果,返回影響行數(shù):1.數(shù)據(jù)庫(kù)中新增了一條數(shù)據(jù)

==>  Preparing: insert into userinfo(username,password) values(?,?)
==> Parameters: zhangsan(String), 123456(String)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3e4e4c1]
1

mysql> select*from userinfo ;
+----+----------+----------+-------+---------------------+---------------------+-------+
| id | username | password | photo | createtime          | updatetime          | state |
+----+----------+----------+-------+---------------------+---------------------+-------+
|  2 | lisi     | 123456   |       | 2022-12-06 17:10:48 | 2022-12-06 18:10:48 |     1 |
|  3 | zhangsan | 123456   |       | 2023-05-18 17:21:49 | 2023-05-18 17:21:49 |     1 |
+----+----------+----------+-------+---------------------+---------------------+-------+
2 rows in set (0.00 sec)

添加用戶(hù)得到id

//添加用戶(hù)得到ID
    int addUserGetId(UserEntity user);
 
<insert id="addUserGetId" useGeneratedKeys="true" keyProperty="id">
        <!--返回受影響的行數(shù)  和  id-->
        <!--useGeneratedKeys="true"keyProperty="id"是否自動(dòng)生成id,拿到id放到id字段中-->
 
        insert into userinfo(username,password) values(#{username},#{password})
    </insert>
 
@Test
    void addUserGetId() {
        UserEntity user = new UserEntity();
        user.setUsername("wangwu");
        user.setPassword("123456");
        int result = userMapper.addUserGetId(user);
        System.out.println("ID: "+user.getId());
    }

執(zhí)行結(jié)果

JDBC Connection [HikariProxyConnection@1775046789 wrapping com.mysql.cj.jdbc.ConnectionImpl@60bb7995] will not be managed by Spring
==>  Preparing: insert into userinfo(username,password) values(?,?)
==> Parameters: wangwu(String), 123456(String)
<==    Updates: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@ef60710]
ID: 4

mysql> select*from userinfo ;
+----+----------+----------+-------+---------------------+---------------------+-------+
| id | username | password | photo | createtime          | updatetime          | state |
+----+----------+----------+-------+---------------------+---------------------+-------+
|  2 | lisi     | 123456   |       | 2022-12-06 17:10:48 | 2022-12-06 18:10:48 |     1 |
|  3 | zhangsan | 123456   |       | 2023-05-18 17:21:49 | 2023-05-18 17:21:49 |     1 |
|  4 | wangwu   | 123456   |       | 2023-05-18 17:36:28 | 2023-05-18 17:36:28 |     1 |
+----+----------+----------+-------+---------------------+---------------------+-------+
3 rows in set (0.00 sec)

模糊查詢(xún)-concat

 使用#{key}方式的查詢(xún)結(jié)果會(huì)報(bào)錯(cuò)

因?yàn)閭鬟f參數(shù)為string類(lèi)型,會(huì)被#{}解析為字符串,因此會(huì)自動(dòng)加上'',那么占位符替換后變成

'%'li'%'

mysql> select*from userinfo where username like '%'li'%';
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near 'li'%'' at line 1

需要借助concat來(lái)進(jìn)行拼接

mysql>  select concat ('%','li','%');
+-----------------------+
| concat ('%','li','%') |
+-----------------------+
| %li%                  |
+-----------------------+
1 row in set (0.00 sec)

執(zhí)行結(jié)果 

JDBC Connection [HikariProxyConnection@1622899093 wrapping com.mysql.cj.jdbc.ConnectionImpl@40fa8766] will not be managed by Spring
==>  Preparing: select * from userinfo where username like concat('%',?,'%')
==> Parameters: li(String)
<==    Columns: id, username, password, photo, createtime, updatetime, state
<==        Row: 2, lisi, 123456, , 2022-12-06 17:10:48, 2022-12-06 18:10:48, 1
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@56399b9e]
UserEntity(id=2, username=lisi, password=123456, photo=, createtime=2022-12-06T17:10:48, updatetime=2022-12-06T18:10:48, state=1)

 錯(cuò)誤的模糊匹配查詢(xún)會(huì)導(dǎo)致索引失效

1.like 'zhang%'

2.like '%zhang'

3.like '%zhang%'

只有第一種查詢(xún)會(huì)走索引,后面兩個(gè)都不會(huì)走索引,索引失效了

由于DBA和后端開(kāi)發(fā)人員代碼風(fēng)格不同,可能會(huì)出現(xiàn)不兼容的問(wèn)題,比如

然后我們執(zhí)行該單元測(cè)試

我們發(fā)現(xiàn)JDBC成功執(zhí)行,但是由于password映射不上pwd,pwd的值為空

此時(shí)resultType="com.example.demo.entity.UserEntity"就失效了,因?yàn)楸碇凶侄魏皖?lèi)的屬性不一樣了,無(wú)法一對(duì)一直接映射了

解決方法--<resultMap>

<resultMap> 是 MyBatis 中用于映射查詢(xún)結(jié)果集的標(biāo)簽。它能夠解決以下兩個(gè)問(wèn)題:

  1. 解決列名與屬性名不一致的問(wèn)題:在數(shù)據(jù)庫(kù)中,有些列名可能并不符合 Java 對(duì)象中屬性的命名規(guī)范,這時(shí)可以通過(guò) <resultMap> 映射來(lái)將查詢(xún)結(jié)果集中的列名與 Java 對(duì)象中屬性名進(jìn)行映射,使得結(jié)果集與對(duì)象之間能夠正確匹配。
  2. 解決多表關(guān)聯(lián)查詢(xún)的問(wèn)題:在進(jìn)行多表關(guān)聯(lián)查詢(xún)時(shí),查詢(xún)結(jié)果集通常會(huì)包含多個(gè)表中的列,這時(shí)可以使用 <resultMap> 將不同表中的列映射到不同的 Java 對(duì)象屬性中,從而完成對(duì)結(jié)果集的映射轉(zhuǎn)換。

第一步,設(shè)置映射關(guān)系

    <resultMap id="BaseMap" type="com.example.demo.entity.UserEntity">
    <!--設(shè)置主鍵   property:屬性,類(lèi)中的  column:字段,數(shù)據(jù)庫(kù)表中的     -->
        <id property="id" column="id"></id>
    <!--設(shè)置普通參數(shù)-->
        <result property="pwd" column="password"></result>
        <result property="username" column="username"></result>
        <result property="createtime" column="createtime"></result>
        <result property="photo" column="photo"></result>
        <result property="updatetime" column="updatetime"></result>
        <result property="state" column="state"></result>
    </resultMap>

第二步,修改返回類(lèi)型

    <select id="getUserById" resultMap="BaseMap">
        select * from userinfo where id = #{uid}
    </select>

執(zhí)行結(jié)果

還有比較簡(jiǎn)單的方式,在sql語(yǔ)句中設(shè)置重命名也能成功映射

<select id="getUserByName" resultType="com.example.demo.entity.UserEntity">
        select id,username,password as pwd from userinfo where username = '${username}'
    </select>

多表聯(lián)查

mysql> update userinfo set id = 1 where username = 'lisi';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
文章信息表
mysql> select*from articleinfo;
+----+-------+----------+---------------------+---------------------+-----+--------+-------+
| id | title | content  | createtime          | updatetime          | uid | rcount | state |
+----+-------+----------+---------------------+---------------------+-----+--------+-------+
|  1 | Java  | Java正文 | 2023-05-15 09:12:59 | 2023-05-15 09:12:59 |   1 |      1 |     1 |
+----+-------+----------+---------------------+---------------------+-----+--------+-------+
1 row in set (0.01 sec)

為了對(duì)應(yīng),將lisi 的id修改為1

mysql> update userinfo set id = 1 where username = 'lisi';
Query OK, 1 row affected (0.00 sec)
Rows matched: 1  Changed: 1  Warnings: 0
mysql> select*from userinfo;
+----+----------+----------+-------+---------------------+---------------------+-------+
| id | username | password | photo | createtime          | updatetime          | state |
+----+----------+----------+-------+---------------------+---------------------+-------+
|  1 | lisi     | 123456   |       | 2022-12-06 17:10:48 | 2022-12-06 18:10:48 |     1 |
|  3 | zhangsan | 123456   |       | 2023-05-18 17:21:49 | 2023-05-18 17:21:49 |     1 |
|  4 | wangwu   | 123456   |       | 2023-05-18 17:36:28 | 2023-05-18 17:36:28 |     1 |
+----+----------+----------+-------+---------------------+---------------------+-------+
3 rows in set (0.00 sec)

創(chuàng)建文章表實(shí)體類(lèi)

@Data
public class ArticleInfo {
    private int id;
    private String title;
    private String content;
    private LocalDateTime createtime;
    private LocalDateTime updatrtime;
    private int uid;
    private int rcount;
    private int state;
}

擴(kuò)展類(lèi)

package com.example.demo.entity.vo;
import com.example.demo.entity.ArticleInfo;
import lombok.Data;
@Data
public class ArticleInfoVO extends ArticleInfo {
    private String username;
}

在Mapper中創(chuàng)建接口

package com.example.demo.mapper;
 
import com.example.demo.entity.vo.ArticleInfoVO;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
 
@Mapper
public interface ArticleMapper {
    //查詢(xún)文章詳情
    ArticleInfoVO getDetail(@Param("id")Integer id);
}

在mybatis包下創(chuàng)建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="com.example.demo.mapper.ArticleMapper">
    <select id="getDetail" resultType="com.example.demo.entity.vo.ArticleInfoVO">
        select a.*,u.username from articleinfo a
        left join userinfo u on u.id = a.uid
        where a.id = #{id}
    </select>
</mapper>

創(chuàng)建測(cè)試類(lèi)

@SpringBootTest
class ArticleMapperTest {
    @Autowired
    private ArticleMapper articleMapper;
    @Test
    void getDetail() {
        ArticleInfoVO articleInfoVO = articleMapper.getDetail(1);
        System.out.println(articleInfoVO);
    }
}

執(zhí)行單元測(cè)試

JDBC執(zhí)行是正確的,但是返回只有username,并沒(méi)有查詢(xún)到父類(lèi)的屬性

其實(shí)是lombok出現(xiàn)了問(wèn)題,打印時(shí)沒(méi)包含父類(lèi)的屬性

解決辦法:手動(dòng)設(shè)置toString

public class ArticleInfoVO extends ArticleInfo {
    private String username;
    @Override
    public String toString() {
        return "ArticleInfoVO{" +
                "username='" + username + '\'' +
                "} " + super.toString();
    }
}

查看字節(jié)碼

 再次執(zhí)行 

JDBC Connection [HikariProxyConnection@166022233 wrapping com.mysql.cj.jdbc.ConnectionImpl@5dbb50f3] will not be managed by Spring
==>  Preparing: select a.*,u.username from articleinfo a left join userinfo u on u.id = a.uid where a.id = ?
==> Parameters: 1(Integer)
<==    Columns: id, title, content, createtime, updatetime, uid, rcount, state, username
<==        Row: 1, Java, <<BLOB>>, 2023-05-15 09:12:59, 2023-05-15 09:12:59, 1, 1, 1, lisi
<==      Total: 1
Closing non transactional SqlSession [org.apache.ibatis.session.defaults.DefaultSqlSession@3662bdff]
ArticleInfoVO{username='lisi'} ArticleInfo(id=1, title=Java, content=Java正文, createtime=2023-05-15T09:12:59, updatrtime=null, uid=1, rcount=1, state=1)

查詢(xún)一個(gè)用戶(hù)的所有文章

先將插入一條文章記錄

mysql> select*from articleinfo;
+----+-------+-----------+---------------------+---------------------+-----+--------+-------+
| id | title | content   | createtime          | updatetime          | uid | rcount | state |
+----+-------+-----------+---------------------+---------------------+-----+--------+-------+
|  1 | Java  | Java正文  | 2023-05-15 09:12:59 | 2023-05-15 09:12:59 |   1 |      1 |     1 |
|  2 | mysql | mysql正文 | 2023-05-19 11:14:49 | 2023-05-19 11:14:49 |   1 |      1 |     1 |
+----+-------+-----------+---------------------+---------------------+-----+--------+-------+
2 rows in set (0.00 sec)

其他代碼

//查詢(xún)一個(gè)用戶(hù)所有文章
    List<ArticleInfoVO> getListByUid(@Param("uid")Integer uid);
<select id="getListByUid" resultType="com.example.demo.entity.vo.ArticleInfoVO">
        select a.*,u.username from articleinfo a
        left join userinfo u on u.id = a.uid
        where a.uid = #{uid}
    </select>
@Test
    void getListByUid() {
        Integer uid =1;
        List<ArticleInfoVO> list = articleMapper.getListByUid(uid);
        //使用多線程的方式,順序可能會(huì)不同
        list.stream().parallel().forEach(System.out::println);
    }

執(zhí)行

總結(jié)

到此這篇關(guān)于MyBatis查詢(xún)數(shù)據(jù)庫(kù)的文章就介紹到這了,更多相關(guān)MyBatis查詢(xún)數(shù)據(jù)庫(kù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論