1.MyBatis是什么?
MyBatis 是?款優(yōu)秀的持久層框架,它?持?定義 SQL、存儲(chǔ)過程以及?級(jí)映射。MyBatis 去除了?乎所有的 JDBC 代碼以及設(shè)置參數(shù)和獲取結(jié)果集的?作。MyBatis 可以通過簡(jiǎn)單的 XML 或注解來配置和映射原始類型、接?和 Java POJO(Plain Old Java Objects,普通?式 Java 對(duì)象)為數(shù)據(jù)庫中的記錄。
簡(jiǎn)單來說 MyBatis 是更簡(jiǎn)單完成程序和數(shù)據(jù)庫交互的?具,也就是更簡(jiǎn)單的操作和讀取數(shù)據(jù)庫?具。
2.為什么要學(xué)習(xí)MyBatis?
對(duì)于后端開發(fā)來說,程序是由以下兩個(gè)重要的部分組成的:
- 后端程序
- 數(shù)據(jù)庫
對(duì)于 JDBC 來說,整個(gè)操作?常的繁瑣,我們不但要拼接每?個(gè)參數(shù),?且還要按照模板代碼的?式,?步步的操作數(shù)據(jù)庫,并且在每次操作完,還要?動(dòng)關(guān)閉連接等,?所有的這些操作步驟都需要在每個(gè)?法中重復(fù)書寫。于是我們就想,那有沒有?種?法,可以更簡(jiǎn)單、更?便的操作數(shù)據(jù)庫呢?
答案是肯定的,這就是我們要學(xué)習(xí) MyBatis 的真正原因,它可以幫助我們更?便、更快速的操作數(shù)據(jù)庫。
3.學(xué)習(xí)MyBatis
MyBatis 學(xué)習(xí)分為兩部分:
- 配置 MyBatis 開發(fā)環(huán)境;
- 使? MyBatis 模式和語法操作數(shù)據(jù)庫。
4.第一個(gè)MyBatis查詢
開始搭建 MyBatis 之前,我們先來看?下 MyBatis 在整個(gè)框架中的定位,框架交互流程圖:在MyBatis中,程序員需要做如下幾件事;
- SQL語句 (可以寫在java代碼中,也可以寫在MyBatis配置文件中)
- 傳參(非必須)
- 結(jié)果映射(可以寫在java代碼中,也可以寫在MyBatis配置文件中)
4.1 創(chuàng)建數(shù)據(jù)庫和表
-- 創(chuàng)建數(shù)據(jù)庫
drop database if exists mycnblog;
create database mycnblog DEFAULT CHARACTER SET utf8mb4;
-- 使?數(shù)據(jù)數(shù)據(jù)
use mycnblog;
-- 創(chuàng)建表[?戶表]
drop table if exists userinfo;
create table userinfo(
id int primary key auto_increment,
username varchar(100) not null,
password varchar(32) not null,
photo varchar(500) default '',
createtime datetime default now(),
updatetime datetime default now(),
`state` int default 1
) default charset 'utf8mb4';
-- 添加?個(gè)?戶信息
INSERT INTO `mycnblog`.`userinfo` (`id`, `username`, `password`, `photo`,`createtime`, `updatetime`, `state`) VALUES(1, 'admin', 'admin', '', '2021-12-06 17:10:48', '2021-12-06 17:10:48', 1);
4.2 添加MyBatis框架?持
新項(xiàng)目添加MyBatis
在創(chuàng)建項(xiàng)目是添加MyBatis依賴:
老項(xiàng)目添加MyBatis
1.下載插件
2.右擊generate
3.搜索”MyBatis“添加
4.3 配置連接字符串和MyBatis
配置連接字符串
在配置文件中添加如下內(nèi)容,這里在application.yml文件中添加:
# 數(shù)據(jù)庫連接配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/mycnblog?characterEncoding=utf8&useSSL=false
username: root
password: 111111
driver-class-name: com.mysql.cj.jdbc.Driver
配置 MyBatis 中的 XML 路徑
# 配置 mybatis xml 的?件路徑,在 resources/mapper 創(chuàng)建所有表的 xml ?件
mybatis:
mapper-locations: classpath:mapper/**Mapper.xml
在配置文件中建一個(gè)mapper文件夾:
4.4 添加業(yè)務(wù)代碼
按照如下流程來實(shí)現(xiàn)MyBatis查詢所有用戶的功能:
添加實(shí)體類
添加用戶的實(shí)體類(屬性需要和字段名一致):
@Data
public class User {
private Integer id;
private String username;
private String password;
private String photo;
private Date createtime;
private Date updatetime;
}
添加mapper接口
@Mapper
public interface userMapper {
//查詢
public List<User> queryAll();
}
添加sql語句,實(shí)現(xiàn)接口
在配置文件的mapper文件夾中,定義UserMapper.xml 查詢所有?戶的具體實(shí)現(xiàn) SQL:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybati
s.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.mybatisdemo.mapper.UserMapper">
<select id="queryAll" resultType="com.example.mybatisdemo.model.User">
select * from userinfo
</select>
</mapper>
以下是對(duì)以上標(biāo)簽的說明:
- mapper標(biāo)簽:需要指定 namespace 屬性,表示命名空間,值為 mapper 接?的全限定 名,包括全包名.類名。
- select查詢標(biāo)簽:是?來執(zhí)?數(shù)據(jù)庫的查詢操作的:
- id:是和 Interface(接?)中定義的?法名稱?樣的,表示對(duì)接?的具體實(shí)現(xiàn)?法。
- resultType:是返回的數(shù)據(jù)類型,也就是開頭我們定義的實(shí)體類
測(cè)試一下: 在接口頁面右鍵選擇generate,點(diǎn)擊test:
選擇要測(cè)試的方法:
會(huì)自動(dòng)生成測(cè)試方法。
測(cè)試一下
UserMapperTest.java代碼:
@Slf4j
@SpringBootTest
class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
void queryAll() {
List<User> users = userMapper.queryAll();
log.info(users.toString());
}
}
如何傳參
添加sql語句:根據(jù)id查詢:通過 “#” 傳參
<select id="queryById" resultType="com.example.mybatisdemo.model.User">
select * from userinfo where id=#{uid}
</select>
添加mapper接口:
//根據(jù)id查詢
User queryById(@Param("uid") Integer id);
@Param是重命名
補(bǔ)充: 當(dāng)只有一個(gè)參數(shù)時(shí),可以不加注解,并且參數(shù)名稱可以不一樣,如下圖所示:
5.增刪改操作
- insert標(biāo)簽:插?語句
- update標(biāo)簽:修改語句
- delete標(biāo)簽:刪除語句
5.1 增加數(shù)據(jù)
方法聲明:
//插入
Integer insert(User user);
添加sql語句:
<insert id="insert">
insert into userinfo(username,password,photo) values(#{username},#{password},#{photo})
</insert>
通過"#"傳參:
如果設(shè)置了param注解,就必須要使用param注解的命名:
方法聲明:
Integer insert1(@Param("userinfo") User user);
SQL語句:
<insert id="insert1">
insert into userinfo(username,password,photo) values(#{userinfo.username},#{userinfo.password},#{userinfo.photo})
</insert>
特殊的添加:返回自增的id
默認(rèn)情況下返回的是受影響的?號(hào),如果想要返回?增 id,具體實(shí)現(xiàn)如下:
方法聲明:
Integer insert1(@Param("userinfo") User user);
UserMapper.xml:
<insert id="insert1" useGeneratedKeys="true" keyProperty="id" >
insert into userinfo(username,password,photo) values(#{userinfo.username},#{userinfo.password},#{userinfo.photo})
</insert>
- useGeneratedKeys:這會(huì)令 MyBatis 使? JDBC 的 getGeneratedKeys ?法來取出由數(shù)據(jù)庫內(nèi)部?成的主鍵(?如:像 MySQL 和 SQL Server 這樣的關(guān)系型數(shù)據(jù)庫管理系統(tǒng)的?動(dòng)遞增字段),默認(rèn)值:false。
- keyColumn:設(shè)置?成鍵值在表中的列名,在某些數(shù)據(jù)庫(像 PostgreSQL)中,當(dāng)主鍵列不是表中的第?列的時(shí)候,是必須設(shè)置的。如果?成列不??個(gè),可以?逗號(hào)分隔多個(gè)屬性名稱。
- keyProperty:指定能夠唯?識(shí)別對(duì)象的屬性,MyBatis 會(huì)使? getGeneratedKeys 的返回值或 insert 語句的selectKey ?元素設(shè)置它的值,默認(rèn)值:未設(shè)置(unset)。如果?成列不??個(gè),可以?逗號(hào)分隔多個(gè)屬性名稱。
5.2 修改數(shù)據(jù)
方法聲明:
//修改
void update(User user);
UserMapper.xml:
<update id="update">
update userinfo set username=#{username},password=#{password} where id=#{id}
</update>
5.3 刪除數(shù)據(jù)
方法聲明:
//刪除
void delete(Integer id);
UserMapper.xml:
<delete id="delete">
delete from userinfo where id=#{id}
</delete>
6.表單查詢
6.1 參數(shù)占位符 #{} 和 ${}
- #{}:預(yù)編譯處理。
- ${}:字符直接替換。
預(yù)編譯處理是指:MyBatis 在處理#{}時(shí),會(huì)將 SQL 中的 #{} 替換為?號(hào),使? PreparedStatement的 set ?法來賦值。
直接替換:是MyBatis 在處理 ${} 時(shí),就是把 ${} 替換成變量的值。
舉個(gè)例子來看看這兩者的區(qū)別:
- ?般航空公司乘機(jī)都是頭等艙和經(jīng)濟(jì)艙分離的,頭等艙的?先登機(jī),登機(jī)完之后,封閉經(jīng)濟(jì)艙,然后再讓經(jīng)濟(jì)艙的乘客登機(jī),這樣的好處是可以避免渾?摸?,經(jīng)濟(jì)艙的?混到頭等艙的情況,這就相當(dāng)于預(yù)處理,可以解決程序中不安全(越權(quán)處理)的問題
- ?直接替換的情況相當(dāng)于,頭等艙和經(jīng)濟(jì)艙不分離的情況,這樣經(jīng)濟(jì)艙的乘客在通過安檢之后可能越權(quán)摸到頭等艙,這就相當(dāng)于參數(shù)直接替換,它的問題是可能會(huì)帶來越權(quán)查詢和操作數(shù)據(jù)等問題。比如6.3的sql注入問題。
6.2 使用 ${} 在排序查詢中的優(yōu)勢(shì)
使用 #{} 進(jìn)行排序查詢
方法聲明:
//排序
List<User>queryByOrder(String order);
使用 #{} 進(jìn)行排序查詢:
<select id="queryByOrder" resultType="com.example.mybatisdemo.model.User">
select * from userinfo order by #{order}
</select>
查詢測(cè)試:
void queryByOrder() {
List<User>users=userMapper.queryByOrder("desc");
log.info(users.toString());
}
運(yùn)行結(jié)果如下所示:
這是因?yàn)楫?dāng)使? #{sort} 查詢時(shí),如果傳遞的值為 String 則會(huì)加單引號(hào),就會(huì)導(dǎo)致 sql 錯(cuò)誤。
使用 ${} 進(jìn)行排序查詢
排序時(shí),只能使用 $ 。
使用 ${} 進(jìn)行排序查詢則查詢成功:
<select id="queryByOrder" resultType="com.example.mybatisdemo.model.User">
select * from userinfo order by id ${order}
</select>
但是它有可能會(huì)引起sql注入問題。即用戶輸入的參數(shù)中,有可能帶有惡意sql。
解決方法:可以在前端只能讓用戶進(jìn)行點(diǎn)擊,選擇排序方式,參數(shù)由后端控制輸入,后端在查詢之前,對(duì)參數(shù)進(jìn)行校驗(yàn),只能傳 desc 和 asc 兩個(gè)值。
6.3 sql注入
SQL注入(SQL lnjection)是發(fā)生在Web程序中數(shù)據(jù)庫層的安全漏洞,是比較常用的網(wǎng)絡(luò)攻擊方式之一,他不是利用操作系統(tǒng)的BUG來實(shí)現(xiàn)攻擊,而是針對(duì)程序員編寫時(shí)的疏忽,通過SQL語句,實(shí)現(xiàn)無賬號(hào)登錄,甚至修改數(shù)據(jù)庫。也就是說,SQL注入就是在用戶輸入的字符串中添加SQL語句,如果在設(shè)計(jì)不良的程序中忽略了檢查,那么這些注入進(jìn)去的SQL語句就會(huì)被數(shù)據(jù)庫服務(wù)器誤認(rèn)為是正常的SQL語句而運(yùn)行,攻擊者就可以執(zhí)行計(jì)劃外的命令或者訪問未授權(quán)的數(shù)據(jù)。
6.4 like 查詢
like 查詢是一個(gè)模糊查詢。使? #{} 會(huì)報(bào)錯(cuò),而這個(gè)又不能直接使用 ${},會(huì)有sql注入問題,可以考慮使? mysql 的內(nèi)置函數(shù) concat() 來處理,實(shí)現(xiàn)代碼如下:
concat ()方法用于連接字符串
方法聲明:
List<User>queryByLike(String name);
UserMapper.xml:
<select id="queryByLike" resultType="com.example.mybatisdemo.model.User">
select * from userinfo where username like concat('%',#{name},'%')
</select>
6.4 如何打印sql日志
在配置文件中添加如下文件即可:
# 配置打印 MyBatis 執(zhí)行的 SQL
mybatis:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
7.多表查詢
增、刪、改返回影響的?數(shù),那么在 mapper.xml 中是可以不設(shè)置返回的類型的,然?即使是最簡(jiǎn)單查詢?戶的名稱也要設(shè)置返回的類型。
對(duì)于 select 查詢標(biāo)簽來說?少需要兩個(gè)屬性:
- id 屬性:?于標(biāo)識(shí)實(shí)現(xiàn)接?中的那個(gè)?法;
- 結(jié)果映射屬性:結(jié)果映射有兩種實(shí)現(xiàn)標(biāo)簽:resultType和resultMap
7.1 返回類型:resultType
絕?數(shù)查詢場(chǎng)景可以使? resultType 進(jìn)?返回,如下代碼所示:
<select id="queryAll" resultType="com.example.mybatisdemo.model.User">
select * from userinfo
</select>
它的優(yōu)點(diǎn)是使??便,直接定義到某個(gè)實(shí)體類即可。
7.2 返回字典映射:resultMap
resultMap 使?場(chǎng)景:
- 字段名稱和程序中的屬性名不同的情況,可使? resultMap 配置映射;
- ?對(duì)?和?對(duì)多關(guān)系可以使? resultMap 映射并查詢數(shù)據(jù)。
字段名稱和程序中的屬性名不同的情況
使用下面的代碼查詢就會(huì)報(bào)錯(cuò):
<select id="queryAll" resultType="com.example.mybatisdemo.model.User">
select * from userinfo
</select>
這個(gè)時(shí)候就可以使? resultMap 了,resultMap 的使?如下:
<resultMap id="baseMap" type="com.example.mybatisdemo.model.User">
<id column="id" property="id"></id>
<result column="username" property="name"></result>
<result column="password" property="pwd"></result>
</resultMap>
<select id="queryAll" resultType="com.example.mybatisdemo.model.User">
select * from userinfo
</select>
查詢成功:
7.3 多表查詢
在多表查詢時(shí),如果使? resultType 標(biāo)簽,在?個(gè)類中包含了另?個(gè)對(duì)象是查詢不出來被包含的對(duì)象
的,所以這里使用resultMap標(biāo)簽。
前面創(chuàng)建了一個(gè)user表,這里再創(chuàng)建一個(gè)文章表articleinfo。
創(chuàng)建數(shù)據(jù)庫和表
-- 創(chuàng)建文章表
drop table if exists articleinfo;
create table articleinfo(
id int primary key auto_increment,
title varchar(100) not null,
content text not null,
createtime datetime default now(),
updatetime datetime default now(),
uid int not null,
rcount int not null default 1,
`state` int default 1
)default charset 'utf8mb4';
-- 文章添加測(cè)試數(shù)據(jù)
insert into articleinfo(title,content,uid)
values('Java','Java正?',1);
添加實(shí)體類
@Data
public class ArticleInfo {
private Integer id;
private String title;
private String content;
private Date createtime;
private Date updatetime;
private Integer rcount;
//作者相關(guān)信息
private User user;
private Integer userId;
private String username;
}
方法聲明
List<ArticleInfo> queryArticle2();
添加mapper接口
<resultMap id="BaseMap2" type="com.example.mybatisdemo.model.ArticleInfo">
<id property="id" column="id"></id>
<result property="title" column="title"></result>
<result property="content" column="content"></result>
<result property="createtime" column="createtime"></result>
<result property="updatetime" column="updatetime"></result>
<result property="userId" column="uid"></result>
<result property="username" column="username"></result>
</resultMap>
<select id="queryArticle2" resultMap="BaseMap2">
select ta.*,tb.id as userId,tb.username as username from articleinfo ta left join userinfo tb on ta.uid = tb.id
</select>
8.復(fù)雜情況:動(dòng)態(tài)SQL的使用
動(dòng)態(tài)SQL:根據(jù)輸入的參數(shù)不同,動(dòng)態(tài)的拼接SQL。
8.1 if 標(biāo)簽
在注冊(cè)?戶的時(shí)候,可能會(huì)有這樣?個(gè)問題,如下圖所示:
注冊(cè)分為兩種字段:必填字段和?必填字段,那如果在添加?戶的時(shí)候有不確定的字段傳?,程序應(yīng)該如何實(shí)現(xiàn)呢?
這個(gè)時(shí)候就需要使?動(dòng)態(tài)標(biāo)簽 來判斷了。
在7.3創(chuàng)建的articleinfo表中,state屬性為非必填字段,以它為例具體實(shí)現(xiàn)代碼如下:
方法聲明
void insertByCondition(ArticleInfo articleInfo);
添加mapper接口
<insert id="insertByCondition">
insert into articleinfo(title,content,uid
<if test="state!=null">
,state
</if>
)
values
(#{title},#{content},#{userId}
<if test="state!=null">
,#{state}
</if>
)
</insert>
補(bǔ)充:test后面的內(nèi)容是Java對(duì)象的屬性字段,不是MySQL字段?。。?/strong>
測(cè)試:不填寫state
@Test
void insertByCondition() {
ArticleInfo articleInfo=new ArticleInfo();
articleInfo.setTitle("測(cè)試文章");
articleInfo.setContent("測(cè)試內(nèi)容");
articleInfo.setUserId(2);
articleMapper.insertByCondition(articleInfo);
}
結(jié)果如下,state自動(dòng)填寫了默認(rèn)值。
測(cè)試:填寫state
@Test
void insertByCondition() {
ArticleInfo articleInfo=new ArticleInfo();
articleInfo.setTitle("測(cè)試文章1");
articleInfo.setContent("測(cè)試內(nèi)容1");
articleInfo.setUserId(3);
articleInfo.setState(2);
articleMapper.insertByCondition(articleInfo);
}
這次填寫了state,結(jié)果如下:
8.2 trim標(biāo)簽
之前的插??戶功能,只是有?個(gè) sex 字段可能是選填項(xiàng),如果所有字段都是?必填項(xiàng),就考慮使?trim標(biāo)簽結(jié)合if標(biāo)簽,對(duì)多個(gè)字段都采取動(dòng)態(tài)?成的?式。
trim標(biāo)簽中有如下屬性:
- prefix:表示整個(gè)語句塊,以prefix的值作為前綴
- suffix:表示整個(gè)語句塊,以suffix的值作為后綴
- prefixOverrides:表示整個(gè)語句塊要去除掉的前綴
- suffixOverrides:表示整個(gè)語句塊要去除掉的后綴
示例:在articleinfo表中,設(shè)置title,content,uid,state都為非必填字段:
添加mapper接口
<insert id="insertByCondition">
insert into articleinfo
<trim prefix="(" suffix=")" prefixOverrides="," suffixOverrides="," >
<if test="title!=null">
title
</if>
<if test="content!=null">
,content
</if>
<if test="userId!=null">
,uid
</if>
<if test="state!=null">
,state
</if>
</trim>
values
<trim prefix="(" suffix=")" prefixOverrides="," suffixOverrides=",">
<if test="title!=null">
#{title}
</if>
<if test="content!=null">
,#{content}
</if>
<if test="userId!=null">
,#{userId}
</if>
<if test="state!=null">
,#{state}
</if>
</trim>
</insert>
8.3 where標(biāo)簽
where標(biāo)簽的作用:
- 生成where關(guān)鍵字
- 去掉多余的字符(比如下面示例中uid前面的and)
示例:
select * from articleinfo where uid=? and state=?;
方法聲明
//多個(gè)參數(shù)需要重命名
List<ArticleInfo> queryByCondition(@Param("uid") Integer uid, @Param("state") Integer state);
添加mapper接口
<select id="queryByCondition" resultType="com.example.mybatisdemo.model.ArticleInfo">
select * from articleinfo
<where>
<if test="uid!=null">
and uid=#{state}
</if>
<if test="state!=null">
and state=#{state}
</if>
</where>
</select>
8.4 set標(biāo)簽
set標(biāo)簽作用:
- 生成set關(guān)鍵字
- 去除最后一個(gè)逗號(hào)(如下面示例,state為非必填字段,當(dāng)不填寫state時(shí),uid后面就會(huì)多一個(gè)逗號(hào),導(dǎo)致語句出錯(cuò),set則可以去除這個(gè)逗號(hào),也可以使用trim標(biāo)簽)
示例:
update articleinfo set uid=?,state=?;
方法聲明
void updateByCondition(@Param("uid") Integer uid, @Param("state") Integer state);
添加mapper接口
<update id="updateByCondition">
update articleinfo
<set>
<if test="uid!=null">
uid=#{uid},
</if>
<if test="state!=null">
state=#{state}
</if>
</set>
</update>
8.5 foreach標(biāo)簽
對(duì)集合進(jìn)?遍歷時(shí)可以使?該標(biāo)簽。foreach標(biāo)簽有如下屬性:
- collection:綁定?法參數(shù)中的集合,如 List,Set,Map或數(shù)組對(duì)象
- item:遍歷時(shí)的每?個(gè)對(duì)象
- open:語句塊開頭的字符串
- close:語句塊結(jié)束的字符串
- separator:每次遍歷之間間隔的字符串
示例:
delete from articleinfo where id in ( ? , ? )
方法聲明
void batchDelete(List<Integer> ids);
添加mapper接口
<delete id="batchDelete">
delete from articleinfo where id in
<foreach collection="list" open="(" close=")" separator="," item="id">
#{id}
</foreach>
</delete>
補(bǔ)充:使用注解實(shí)現(xiàn)mybatis查詢
myBatis有兩種實(shí)現(xiàn)方式:
- 使用xml (本文用的就是這種方式)
- 使用注解
舉例
查詢?nèi)浚?/p>
@Select("select * from userinfo")
List<User> queryAll();
根據(jù)id查詢:文章來源:http://www.zghlxwxcb.cn/news/detail-603072.html
@Select("select * from userinfo where id=#{id}")
User queryById(Integer id);
一個(gè)項(xiàng)目中,注解和xml的方式,可以并存,對(duì)于簡(jiǎn)單的SQL,注解寫起來非常簡(jiǎn)單。但是,對(duì)于復(fù)雜的SQL,注解寫起來就會(huì)非常的復(fù)雜??梢愿鶕?jù)自己項(xiàng)目的需求進(jìn)行選擇。文章來源地址http://www.zghlxwxcb.cn/news/detail-603072.html
到了這里,關(guān)于MyBatis操作數(shù)據(jù)庫的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!