MyBatis查詢數(shù)據(jù)庫
經(jīng)過前面的學(xué)習(xí)Spring系列的操作已經(jīng)差不多了,接下來我們繼續(xù)學(xué)習(xí)更加重要的知識,將前端傳遞的參數(shù)存儲(chǔ)起來,或者查詢數(shù)據(jù)庫里面的數(shù)據(jù)
1. MyBatis 是什么?
MyBatis是款優(yōu)秀的持久層框架,它支持自定義SQL、存儲(chǔ)過程以及高級映射。MyBatis幾乎祛除了所有的JDBC代碼以及設(shè)置參數(shù)和獲取結(jié)果集的操作。MyBatis可以通過簡單的XML或者注解來配置和映射原始類型、接口和Java POJO為數(shù)據(jù)庫中的記錄
簡單來說MyBatis是簡單完成程序和數(shù)據(jù)庫交互的工具,也就是更簡單的操作和讀取數(shù)據(jù)庫的工具
2. 為什么要學(xué)習(xí)MyBatis?
對于后端程序員來說,程序是由以下兩個(gè)程序組成的:
- 后端程序
- 數(shù)據(jù)庫
而這兩個(gè)重要的組成部分要通訊,就要依靠數(shù)據(jù)庫連接工具,那么數(shù)據(jù)庫連接工具有哪些?比如JDBC還有今天我們要介紹的MyBatis。既然已經(jīng)有了JDBC,為什么還要學(xué)習(xí)MyBatis?這是因?yàn)镴DBC操作太繁瑣了,我們來回顧一下JDBC的操作流程:
- 創(chuàng)建數(shù)據(jù)庫連接池
DataSource
- 通過
DataSource
獲取數(shù)據(jù)庫連接Connection
- 編寫要帶?的占位符的SQL語句
- 通過
Connection
及SQL創(chuàng)建操作命令對象Statement
- 替換占位符:指定要替換的數(shù)據(jù)庫字段類型,占位符索引及要替換的值
- 使用
Statement
執(zhí)行SQL語句 - 查詢操作:返回結(jié)果集
ResultSet
,更新操作:返回更新的數(shù)量 - 處理結(jié)果集
- 釋放資源
3. 怎么學(xué)MyBatis?
MyBatis的學(xué)習(xí)只分為兩部分:
- 配置MyBatis開發(fā)環(huán)境
- 使用MyBatis模式和語法操作數(shù)據(jù)庫
4. 第一個(gè)MyBatis查詢
開始搭建MyBatis之前,我們需要先來看看它在整個(gè)框架之中的地位
MyBatis也是一個(gè)ORM框架,ORM即對象關(guān)系映射。在面向?qū)ο缶幊陶Z言當(dāng)中,講關(guān)系型數(shù)據(jù)庫中的數(shù)據(jù)與對象建立起映射關(guān)系,進(jìn)行自動(dòng)完成數(shù)據(jù)與對象的相互轉(zhuǎn)換
- 將輸入數(shù)據(jù)(即傳入對象)+ SQL映射原生SQL
- 將結(jié)果集映射為返回對象,即輸出對象
ORM把數(shù)據(jù)庫映射為對象:
- 數(shù)據(jù)表—》 類
- 記錄—》對象
- 字段—》對象的屬性
一般的ORM框架,會(huì)將數(shù)據(jù)庫模型的每一張表都映射為一個(gè)Java類
也就是說使用MyBatis可以像操作對象一樣操作數(shù)據(jù)庫中的表,可以實(shí)現(xiàn)對象和數(shù)據(jù)表之間的轉(zhuǎn)換,我們接下來看看MyBatis的使用
4.1 創(chuàng)建數(shù)據(jù)庫和表
使用MyBatis的方式來讀取用戶表當(dāng)中的所有用戶,我們使用個(gè)人博客的數(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';
-- 創(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';
-- 創(chuàng)建視頻表
drop table if exists videoinfo;
create table videoinfo(
vid int primary key,
`title` varchar(250),
`url` varchar(1000),
createtime datetime default now(),
updatetime datetime default now(),
uid int
)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)
;
-- ?章添加測試數(shù)據(jù)
insert into articleinfo(title,content,uid)
values('Java','Java正?',1);
-- 添加視頻
insert into videoinfo(vid,title,url,uid) values(1,'java title','http://ww
w.baidu.com',1);
4.2 添加MyBatis框架支持
添加MyBatis框架支持分為兩種情況:一種情況是對之前的Spring項(xiàng)目進(jìn)行升級,另一種情況是創(chuàng)建一個(gè)全新的MyBatis和Spring Boot項(xiàng)目
擴(kuò)展:在老項(xiàng)目當(dāng)中快速的添加框架,更簡單的操作是使用EditStarters插件
4.3 配置連接字符串和MyBatis
此步驟需要進(jìn)行兩項(xiàng)設(shè)置,數(shù)據(jù)庫連接字符串設(shè)置和MyBatis的XML文件配置
4.3.1 配置連接數(shù)據(jù)庫
spring:
datasource:
url: jdbc:mysql://127.0.0.1:3306/mycnblog?characterEncoding=utf8&&useSSL=false
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
注意事項(xiàng):
如果使用mysql-connector-java是5.x之前使用的
con.mysql.jdbc.Driver
如果大于5.x使用的是com.mysql.cj.jdbc.Driver
配置MyBatis中的XML路徑
MyBatis的XML中保存的是查詢數(shù)據(jù)庫的具體操作的SQL,配置如下:
mybatis:
mapper-locations: classpath:mybatis/**Mapper.xml
4.4 添加業(yè)務(wù)代碼
下面按照后端開發(fā)的工程思路,也就是下面的流程來實(shí)現(xiàn)MyBatis查詢所有用戶的功能:
4.4.1 添加實(shí)體類
先添加用戶的實(shí)體類:
import lombok.Data;
import java.util.Date;
@Data
public class User {
private Integer id;
private String username;
private String password;
private String photo;
private Date createTime;
private Date updateTime;
}
4.4.2 添加Mapper接口
數(shù)據(jù)持久層的接口定義:
@Mapper
public interface UserMapper {
public UserInfo getUserById(@Param("id") Integer id);
public void add(@RequestBody UserInfo userInfo);
}
4.4.3 添加UserMapper.xml
數(shù)據(jù)持久層的實(shí)現(xiàn),MyBatis的固定格式:
<?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="getUserById" resultType="com.example.demo.model.UserInfo">
select * from userinfo where id = #{id}
</select>
</mapper>
- 標(biāo)簽:需要指定namespace屬性,表示命名空間,值為mapper接口的全限定名,包括包名.類名
-
<select>
標(biāo)簽:是用來執(zhí)行數(shù)據(jù)庫的查詢操作的- id:是和interface中定義的方法名稱是一樣的,表示對接口的具體實(shí)現(xiàn)方法
- resultType:是返回的數(shù)據(jù)類型,也就是開頭定義的實(shí)體類
4.4.4 添加Service
服務(wù)層代碼如下:
@Service
public class UserService {
@Resource
private UserMapper userMapper;
public UserInfo getUserById(Integer id) {
return userMapper.getUserById(id);
}
}
4.4.5 添加Controller
控制層的代碼實(shí)現(xiàn)如下:
@Controller
@ResponseBody
public class UserController {
@Resource
private UserService userService;
@RequestMapping("/getuserbyid")
public UserInfo getUserById(Integer id) {
return userService.getUserById(id);
}
}
5. 增、刪、改操作
5.1 增加用戶的操作
<insert id="add">
insert into userinfo(username, password, photo) values (#{username}, #{password}, #{photo})
</insert>
<insert id="getId" useGeneratedKeys="true" keyProperty="id" keyColumn="id">
insert into userinfo(username, password, photo) values (#{username}, #{password}, #{photo})
</insert>
- useGeneratedKeys: 這會(huì)令MyBatis使用JDBC的getGeneratedKeys 方法取出由數(shù)據(jù)庫內(nèi)部生成的主鍵。(像MySQL和SQL service這樣的關(guān)系型數(shù)據(jù)庫關(guān)系系統(tǒng)自增主鍵)默認(rèn)值是:false
- keyColumn: 設(shè)置生成建在表中的列名,在某些數(shù)據(jù)庫當(dāng)中,當(dāng)主鍵的列不是第一列的時(shí)候,是必須設(shè)置的。如果生成列不止一個(gè),可以用逗號分割各個(gè)屬性名稱
- keyProperty:指定能夠唯一
5.2 刪除用戶的操作
<delete id="del">
delete from userinfo where id = #{id}
</delete>
5.3 修改用戶操作
<update id="update">
update userinfo set username = #{name} where id = #{id}
</update>
6. 查詢操作
6.1 單表查詢
controller代碼如下:
@RequestMapping("/getuserbyid")
public UserInfo getUserById(Integer id) {
return userService.getUserById(id);
}
Mapper.xml實(shí)現(xiàn)代碼如下:
<select id="getUserById" resultType="com.example.demo.model.UserInfo">
select * from userinfo where id = #{id}
</select>
6.1.1 參數(shù)占位符 #{} 和 ${}
- #{} :預(yù)編譯處理
- ${}:字符直接替換
預(yù)編譯處理是指:MyBatis在處理#{}時(shí),會(huì)將SQL中的#{}替換為?,使用statement的set方法來賦值。直接替換:是MyBatis在處理 時(shí),就是把 {}時(shí),就是把 時(shí),就是把{}替換成變量的值
6.1.2 $優(yōu)點(diǎn)
使用${}可以實(shí)現(xiàn)排序查詢,而是用#{}就不能實(shí)現(xiàn)排序查詢,因?yàn)楫?dāng)使用#{}查詢時(shí),如果傳遞的值為String則會(huì)加引號,就會(huì)導(dǎo)致SQL錯(cuò)誤。
6.1.3 SQL 注入問題
<select id="isLogin" resultType="com.example.demo.model.User">
select * from userinfo where username='${name}' and password='${pwd}'
</select>
結(jié)論:用于查詢的字段,盡量使用#{}預(yù)查詢的方式
6.1.4 like查詢
like使用#{}
報(bào)錯(cuò),這個(gè)時(shí)候不能使用${}
,可以考慮使用MySQL內(nèi)置的函數(shù)concat()來處理,實(shí)現(xiàn)代碼如下:
<select id="findUserByName3" resultType="com.example.demo.model.User">
select * from userinfo where username like concat('%',#{username},'%');
</select>
concat可以連接字符。
6.2 多表查詢
如果增、刪、改返回影響的行數(shù),那么在mapper.xml中可以不設(shè)置返回的類型的。但是即使是最簡單的查詢,也要設(shè)置返回類型否則就會(huì)出現(xiàn)錯(cuò)誤。
也就是說**對于<select>
查詢標(biāo)簽,至少存在兩個(gè)屬性:
- id 屬性:用于標(biāo)識實(shí)現(xiàn)接口的那個(gè)方法
- 結(jié)果映射屬性:結(jié)果映射有兩種實(shí)現(xiàn)標(biāo)簽:
<resultType
和<resultMap
6.2.1 返回類型
絕大多數(shù)查詢場景可以使用resultType進(jìn)行返回,如下代碼所示:
<select id="getNameById" resultType="java.lang.String">
select username from userinfo where id=#{id}
</select>
它的優(yōu)點(diǎn)就是使用方便、直接定義到某個(gè)實(shí)體類就可以
6.2.2 返回字典映射:resultMap
resultMap使用場景:
- 字段名稱和程序中的屬性名稱不相同的情況,可以使用resultMap配置映射
- 一對一和多對一關(guān)系可以使用resultMap映射并查詢數(shù)據(jù)
字段名和屬性名不同的情況
<resultMap id="BaseMap" type="com.example.demo.model.User">
<id column="id" property="id"></id>
<result column="username" property="username"></result>
<result column="password" property="pwd"></result>
</resultMap>
<select id="getUserById" resultMap="com.example.demo.mapper.UserMapper.BaseMap">
select * from userinfo where id=#{id}
</select>
6.2.3 多表查詢
在多表查詢的時(shí)候,如果使用resultMap標(biāo)簽,在一個(gè)類中包含了另一個(gè)對象是查詢不出來包含的對象的。如下圖所示:
此時(shí)我們就需要使用特殊手段來實(shí)現(xiàn)聯(lián)表查詢了。
6.2.3.1 一對一的映射
一對一映射要使用<association>
標(biāo)簽,具體實(shí)現(xiàn)如下所示:一篇文章只能對應(yīng)一個(gè)作者
<resultMap id="BaseMap" type="com.example.demo.model.ArticleInfo">
<id column="id" property="id"></id>
<result column="title" property="title"></result>
<result column="content" property="content"></result>
<result column="createtime" property="createtime"></result>
<result column="updatetime" property="updatetime"></result>
<result column="uid" property="uid"></result>
<result column="rcount" property="rcount"></result>
<result column="state" property="state"></result>
<association property="userInfo" resultMap="com.example.demo.mapper.UserMapper.BaseMap" columnPrefix="u_"> </association>
</resultMap>
<select id="getArticleById" resultMap="BaseMap">
select a.*, u.id u_id, u.username u_username, u.password u_password from articleinfo a left join userinfo u on a.uid = u.id where a.id = #{id}
</select>
以上使用<assciation>
標(biāo)簽,表示一對一的結(jié)果映射:
- property屬性:指定Article中對應(yīng)的屬性,即用戶
- resultMap屬性:指定關(guān)聯(lián)的結(jié)果集映射,將基于該映射配置來組織用戶數(shù)據(jù)
- columnPrefix屬性:綁定一對一對象時(shí),是通過columnPrefix+assoction.result.colmn來映射結(jié)果集字段
注意:columnPrefix屬性不能省略,如果省略當(dāng)聯(lián)表中有相同的字段,那么會(huì)導(dǎo)致查詢出錯(cuò)
6.2.3.2 一對多的映射
一對多需要使用<collection>
標(biāo)簽,用法和<association>
相同,如下所示:
<resultMap id="BaseMap" type="com.example.demo.model.ArticleInfo">
<id column="id" property="id"></id>
<result column="title" property="title"></result>
<result column="content" property="content"></result>
<result column="createtime" property="createtime"></result>
<result column="updatetime" property="updatetime"></result>
<result column="uid" property="uid"></result>
<result column="rcount" property="rcount"></result>
<result column="state" property="state"></result>
<association property="userInfo" resultMap="com.example.demo.mapper.UserMapper.BaseMap" columnPrefix="u_"> </association>
</resultMap>
<select id="getArticleById" resultMap="BaseMap">
select a.*, u.id u_id, u.username u_username, u.password u_password from articleinfo a left join userinfo u on a.uid = u.id where a.id = #{id}
</select>
7. 復(fù)雜情況
動(dòng)態(tài)SQL是MyBatis的強(qiáng)大特性之一,能夠完成不同條件下不同的sql拼接
7.1 <if>
標(biāo)簽
在注冊的時(shí)候,我們可能遇到這樣一個(gè)問題。注冊分為兩個(gè)字段:必填字段和非必傳字段,那如果在添加用戶的時(shí)候又不確定的字段傳入,程序應(yīng)該如何實(shí)現(xiàn)?
這個(gè)時(shí)候我們就需要使用動(dòng)態(tài)標(biāo)簽了。
<insert id="add2">
insert into userinfo(username, password
<if test="photo != null">
,photo
</if>) values (#{username}, #{password}
<if test="photo != null">
,#{photo}
</if>
);
</insert>
注意test中的,是傳入對象的屬性,不是數(shù)據(jù)庫字段
7.2 <trim>
標(biāo)簽
之前的插入功能,只是有一個(gè)字段為可選項(xiàng),如果所有字段都是非必選項(xiàng),就需要考慮用標(biāo)簽結(jié)合標(biāo)簽,對多個(gè)字段都采用動(dòng)態(tài)生成的方式。
標(biāo)簽當(dāng)中有如下屬性:
- prefix: 表示整個(gè)語句塊,以prefix值為前綴
- suffix:表示整個(gè)語句塊,以suffix的值為后綴
- prefixOverrides:表示整個(gè)語句塊要去除的前綴
- suffixOverrides:表示整個(gè)語句塊要去除的后綴
使用方法如下:
<insert id="add3">
insert into userinfo
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username != null">
username,
</if>
<if test="password != null">
password,
</if>
<if test="photo != null">
photo
</if>
values
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="username != null">
#{username},
</if>
<if test="password != null">
#{password},
</if>
<if test="photo != null">
#{photo}
</if>
</trim>
</trim>
</insert>
以上SQL動(dòng)態(tài)解析時(shí),會(huì)將第一個(gè)trim部分如下處理:
- 基于
prefix
配置,開始加上( - 基于
suffix
配置,結(jié)束部分加上) - 多個(gè)if組織的語句都以, 結(jié)尾,在最后拼接好的字符串還會(huì)以,結(jié)尾會(huì)基于
suffixOverrides
配置去掉最后一個(gè), - if 中的test是傳入對象的屬性
7.3 <where>
標(biāo)簽
傳入對象時(shí),根據(jù)屬性做where條件查詢,用戶對象中屬性不為null,都為查詢條件。
<select id="getUserById" resultMap="BaseMap">
select * from userinfo
<where>
<if test="id != null">
id = #{id}
</if>
</where>
</select>
7.4 <set>
標(biāo)簽
根據(jù)傳入的用戶對象屬性來更新用戶數(shù)據(jù),可以使用標(biāo)簽來指定動(dòng)態(tài)內(nèi)容文章來源:http://www.zghlxwxcb.cn/news/detail-799808.html
<update id="updateById" parameterType="org.example.model.User">
update user
<set>
<if test="username != null">
username=#{username},
</if>
<if test="password != null">
password=#{password},
</if>
<if test="nickname != null">
nickname=#{nickname},
</if>
<if test="sex != null">
sex=#{sex},
</if>
<if test="birthday != null">
birthday=#{birthday},
</if>
<if test="head != null">
head=#{head},
</if>
<if test="createTime != null">
create_time=#{createTime},
</if>
</set>
where id=#{id}
</update>
7.5 <foreach>
標(biāo)簽
對集合進(jìn)行遍歷時(shí), 可以使用該標(biāo)簽。<foreach>
標(biāo)簽有以下屬性。文章來源地址http://www.zghlxwxcb.cn/news/detail-799808.html
- collection:綁定方法參數(shù)中的集合,如List、Set、Map或數(shù)組對象
- item:遍歷時(shí)的每一個(gè)對象
- open:語句塊開頭的字符串
- close:語句塊結(jié)束時(shí)的字符串
到了這里,關(guān)于【Java】Mybatis查詢數(shù)據(jù)庫的文章就介紹完了。如果您還想了解更多內(nèi)容,請?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!