MyBatis完成CRUD?詳細(xì)細(xì)節(jié)內(nèi)容剖析
1. MyBatis完成CRUD 詳細(xì)細(xì)節(jié)內(nèi)容
2. MyBatis工具類SqlSessionUtil的封裝
我們可以先將 SqlSession 對(duì)象的獲取,封裝成一個(gè)工具類來(lái)使用,方便一些。關(guān)于 SqlSession 對(duì)象的獲取的詳細(xì)內(nèi)容,大家可以移步至:?????? 初始MyBatis ,詳細(xì)步驟運(yùn)行第一個(gè)MyBatis程序,同時(shí)對(duì)應(yīng)步驟MyBatis底層剖析
一般我們的工具類,的構(gòu)造方法都是私有化的,防止 new 對(duì)象。

工具類中所以的方法都是靜態(tài)的,直接用 類名.方法名 的方式直接調(diào)用

獲取到SqlSessionFactory 對(duì)象
SQlsessionFactory對(duì)象,一個(gè)SqlSessionFactory對(duì)應(yīng)一個(gè) environment, 一個(gè)environment通常是一個(gè)數(shù)據(jù)庫(kù)。所以,我們定義一個(gè)靜態(tài)代碼塊,執(zhí)行加載一次即可。

package com.rainbowsea.mybatis.utils;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import java.io.IOException;
public class SqlSessionUtil {
// 工具類的構(gòu)造方法一般都是私有話化的
// 工具類中所有的方法都是靜態(tài)的,直接類名即可調(diào)用,不需要 new 對(duì)象
// 為了防止new對(duì)象,構(gòu)造方法私有化。
private SqlSessionUtil() {
}
private static SqlSessionFactory sessionFactory = null;
// 靜態(tài)代碼塊,類加載時(shí)執(zhí)行
// SqlSessionUtil 工具類在進(jìn)行第一次加載的時(shí)候,解析mybatis-config.xml 文件,創(chuàng)建SqlSessionFactory對(duì)象。
static {
// 獲取到 SqlSessionFactoryBuilder 對(duì)象
SqlSessionFactoryBuilder sqlSessionFactoryBuilder = new SqlSessionFactoryBuilder();
// 獲取到SqlSessionFactory 對(duì)象
// SQlsessionFactory對(duì)象,一個(gè)SqlSessionFactory對(duì)應(yīng)一個(gè) environment, 一個(gè)environment通常是一個(gè)數(shù)據(jù)庫(kù)
try {
sessionFactory = sqlSessionFactoryBuilder.build(Resources.getResourceAsStream("mybatis-config.xml"));
} catch (IOException e) {
throw new RuntimeException(e);
}
}
/**
* 獲取會(huì)話對(duì)象
* @return SqlSession
*/
public static SqlSession openSession() {
// 獲取到 SqlSession 對(duì)象
SqlSession sqlSession = sessionFactory.openSession();
return sqlSession;
}
}3. 準(zhǔn)備工作
首先我們準(zhǔn)備操作,實(shí)驗(yàn)的數(shù)據(jù)庫(kù),數(shù)據(jù)表。數(shù)據(jù)內(nèi)容


在項(xiàng)目/模塊當(dāng)中導(dǎo)入相關(guān)的 jar 依賴,在pom.xml 配置相關(guān) jar依賴的。
我們需要的依賴有:
- mybatis依賴
- mysql驅(qū)動(dòng)依賴
- junit依賴
- logback依賴

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.rainbowsea</groupId>
<artifactId>mybatis-002-crud</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>
<properties>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
<dependencies>
<!-- mybatis依賴 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.10</version>
</dependency>
<!-- mysql驅(qū)動(dòng)器-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.30</version>
</dependency>
<!-- 引入 junit4 依賴-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- 引入 logback的依賴,這個(gè)日志框架實(shí)現(xiàn)了slf4j 規(guī)范-->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.11</version>
</dependency>
</dependencies>
</project>其次就是:
- mybatis-config.xml放在類的根路徑下
- CarMapper.xml放在類的根路徑下
- logback.xml放在類的根路徑下
- 提供com.powernode.mybatis.utils.SqlSessionUtil工具類
- 創(chuàng)建測(cè)試用例:com.powernode.mybatis.CarMapperTest
上述內(nèi)容,為什么放在類的根路徑下(resources) 就是為了提高項(xiàng)目的可移植性。詳細(xì)內(nèi)容,大家可以移步至:?????? 初始MyBatis ,詳細(xì)步驟運(yùn)行第一個(gè)MyBatis程序,同時(shí)對(duì)應(yīng)步驟MyBatis底層剖析
mybatis-config.xml放在類的根路徑下

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 開(kāi)啟mybatis 對(duì)標(biāo)準(zhǔn)日志的實(shí)現(xiàn)-->
<!-- 如果導(dǎo)入的第三方框架的日志,沒(méi)有下面這個(gè) settings 也是可以的-->
<!-- <settings>-->
<!-- <setting name="logImpl" value="STDOUT_LOGGING"/>-->
<!-- </settings>-->
<environments default="development">
<environment id="development">
<!-- MANAGED 沒(méi)有用第三框架管理的話,都是會(huì)被提交的,沒(méi)有事務(wù)上的管理了。-->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="MySQL123"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--sql映射文件創(chuàng)建好之后,需要將該文件路徑配置到這里-->
<!-- 執(zhí)行XxxMapper.xml 文件的路徑-->
<!-- resource 屬性自動(dòng)會(huì)從類的根路徑下開(kāi)始查找資源-->
<!-- <mapper resource="CarMapper.xml"/>-->
<!-- <mapper resource="com/CarMapper2.xml"/>-->
<!-- url屬性: 從絕對(duì)路徑當(dāng)中加載資源。-->
<!-- 語(yǔ)法格式:file:///絕對(duì)路徑-->
<!-- <mapper url="file:///e:/CarMapper.xml"></mapper>-->
<mapper resource="CarMapper.xml"></mapper>
</mappers>
</configuration>同時(shí)需要配置 MyBatis 的核心配置文件,告訴 MyBatis 要使用哪個(gè) XxxMapper .xml SQL 語(yǔ)句的映射文件。

CarMapper.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"> <!--namespace先隨意寫(xiě)一個(gè)--> <mapper namespace="rainbowsea"> </mapper>
logback.xml放在類的根路徑下

<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="false">
<!-- 控制臺(tái)輸出 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.classic.encoder.PatternLayoutEncoder">
<!--格式化輸出:%d表示日期,%thread表示線程名,%-5level:級(jí)別從左顯示5個(gè)字符寬度%msg:日志消息,%n是換行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n</pattern>
</encoder>
</appender>
<!--mybatis log configure-->
<logger name="com.apache.ibatis" level="TRACE"/>
<logger name="java.sql.Connection" level="DEBUG"/>
<logger name="java.sql.Statement" level="DEBUG"/>
<logger name="java.sql.PreparedStatement" level="DEBUG"/>
<!-- 日志輸出級(jí)別,logback日志級(jí)別包括五個(gè):TRACE < DEBUG < INFO < WARN < ERROR -->
<root level="DEBUG">
<appender-ref ref="STDOUT"/>
<appender-ref ref="FILE"/>
</root>
</configuration>分析以下SQL映射文件中SQL語(yǔ)句存在的問(wè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">
<!--namespace先隨便寫(xiě)-->
<mapper namespace="car">
<insert id="insertCar">
insert into t_car(car_num,brand,guide_price,produce_time,car_type) values('103', '奔馳E300L', 50.3, '2022-01-01', '燃油車')
</insert>
</mapper>
這樣寫(xiě)的問(wèn)題是:
值,顯然是寫(xiě)死到配置文件當(dāng)中了
這個(gè)實(shí)際開(kāi)發(fā)中是不存在的
一定是前端 form 表單提交過(guò)來(lái)數(shù)據(jù),然后將值傳給 sql 語(yǔ)句
SQL語(yǔ)句中的值不應(yīng)該寫(xiě)死,值應(yīng)該是用戶提供的。之前的JDBC代碼是這樣寫(xiě)的:
// JDBC中使用 ? 作為占位符。那么MyBatis中會(huì)使用什么作為占位符呢? String sql = "insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(?,?,?,?,?)"; // ...... // 給 ? 傳值。那么MyBatis中應(yīng)該怎么傳值呢? ps.setString(1,"103"); ps.setString(2,"奔馳E300L"); ps.setDouble(3,50.3); ps.setString(4,"2022-01-01"); ps.setString(5,"燃油車");
在JDBC當(dāng)中占位符采用的是?,在mybatis當(dāng)中是什么呢?
和?等效的寫(xiě)法是: #{}在mybatis當(dāng)中不能使用
?占位符,必須使用#{ }來(lái)代替JDBC當(dāng)中的?
#{ }和JDBC當(dāng)中的?是等效的。
在MyBatis 中,的Java程序中,將數(shù)據(jù)放到Map集合中
在sql語(yǔ)句中使用 #{map集合的key} 來(lái)完成傳值,#{} 等同于JDBC中的 ? ,#{}就是占位符
我們?cè)?XxxMapper.xml SQL 映射文件當(dāng)中,使用上 #{ } 重新編寫(xiě) 對(duì)于的 insert SQL 語(yǔ)句

這里的 id 是作為主鍵的,自增的,可以省略不寫(xiě)。
<?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">
<!--namespace先隨意寫(xiě)一個(gè)-->
<mapper namespace="rainbowsea">
<!-- insert語(yǔ)句,id是這個(gè)條SQL語(yǔ)句的唯一標(biāo)識(shí),這個(gè)id就代表了這條SQL語(yǔ)句 -->
<insert id="insertCar">
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type)
values (null,#{k1},#{k2},#{k3},#{k4},#{k5})
<!-- map.get("fdsd") 找,結(jié)果找不到 = null-->
</insert>
</mapper>在MyBatis 中的Java程序中使用 map 可以給SQL語(yǔ)句的占位符傳值。
Map<String,Object> map = new HashMap<>();
map.put("k1","111");
map.put("k2","比亞迪漢");
map.put("k3",10.0);
map.put("k4","2020-11-11");
map.put("k5","電車");添加/插入,執(zhí)行 sqlSession.insert("Id", car); 方法

這里執(zhí)行 **insert( ) ** 插入操作,則是用:sqlSession.insert(String var1, Object var2); 兩個(gè)參數(shù)的方法,執(zhí)行 insert() 插入操作,返回影響的記錄條數(shù)。
// 執(zhí)行sql語(yǔ)句
// insert方法的參數(shù):
// 第一個(gè)參數(shù):sqlId;從CarMapper.xml 文件中復(fù)制,的對(duì)于是insert SQL 語(yǔ)句的 id 信息
// 第二個(gè)參數(shù): 封裝數(shù)據(jù)的對(duì)象,這里為 Map 集合
int count = sqlSession.insert("insertCar", map);
insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values (null,#{fdsd},#{k2},#{k3},#{k4},#{k5})
注意:#{這里寫(xiě)什么?寫(xiě)map集合的key,如果key不存在,獲取的是null}運(yùn)行測(cè)試:

在以上sql語(yǔ)句中,可以看到#{k1} #{k2} #{k3} #{k4} #{k5}的可讀性太差,為了增強(qiáng)可讀性,我們可以將Java程序做如下修改:

對(duì)應(yīng)的 CarMapper.xml SQL映射文件也是要一一對(duì)應(yīng)修改。

運(yùn)行測(cè)試:

insert into t_car(id,car_num,brand,guide_price,produce_time,car_type) values (null,#{fdsd},#{k2},#{k3},#{k4},#{k5})
注意:#{這里寫(xiě)什么?寫(xiě)map集合的key,如果key不存在,獲取的是null}。如下:我們測(cè)試

使用Map集合可以傳參,那使用pojo(簡(jiǎn)單普通的java對(duì)象)可以完成傳參。
java 程序中使用POJO類給SQL語(yǔ)句的占位符傳值。
這里我們定義一個(gè) 名為 Car.java 的類,該類要與t_car 數(shù)據(jù)表的屬性,像映射,對(duì)應(yīng)上的。不可以隨便定義的。

同時(shí)我們將屬性定義為包裝類,防止當(dāng)我們數(shù)據(jù)表當(dāng)中取出,獲取到的數(shù)據(jù)是為 null 的時(shí)候,包裝類可以賦值上,而簡(jiǎn)單類型 int 是無(wú)法賦值為 Null 的

同時(shí)一定要提供對(duì)應(yīng)的 set 和 get 方法,不然 ,MyBatis 無(wú)法通過(guò)反射機(jī)制獲取到相應(yīng)所需要的信息的。

package com.rainbowsea.mybatis.pojo;
public class Car {
//數(shù)據(jù)表當(dāng)中的字段應(yīng)該和pojo類的屬性一一對(duì)應(yīng)
// 建議使用包裝類,這樣可以防止 null 的問(wèn)題:int = null; 不行,Int = null 可以
private Long id;
private String carNum;
private String brand;
private Double guiderPrice;
private String produceTime;
private String carType;
public Car() {
}
public Car(Long id, String carNum, String brand, Double guiderPrice, String produceTime, String carType) {
this.id = id;
this.carNum = carNum;
this.brand = brand;
this.guiderPrice = guiderPrice;
this.produceTime = produceTime;
this.carType = carType;
}
@Override
public String toString() {
return "Car{" +
"id=" + id +
", carNum='" + carNum + '\'' +
", brand='" + brand + '\'' +
", guiderPrice=" + guiderPrice +
", produceTime='" + produceTime + '\'' +
", carType='" + carType + '\'' +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCarNum() {
return carNum;
}
/* public String getXyz() {
return carNum;
}*/
public void setCarNum(String carNum) {
this.carNum = carNum;
}
public String getBrand() {
return brand;
}
public void setBrand(String brand) {
this.brand = brand;
}
public Double getGuiderPrice() {
return guiderPrice;
}
public void setGuiderPrice(Double guiderPrice) {
this.guiderPrice = guiderPrice;
}
public String getProduceTime() {
return produceTime;
}
public void setProduceTime(String produceTime) {
this.produceTime = produceTime;
}
public String getCarType() {
return carType;
}
public void setCarType(String carType) {
this.carType = carType;
}
}java 程序中使用POJO類給SQL語(yǔ)句的占位符傳值:
// 封裝數(shù)據(jù)
Car car = new Car(null, "333", "比亞迪泰", 30.0, "2020-11-11", "新能源");
注意:占位符#{ }, 大括號(hào)里面寫(xiě):pojo類的屬性名
insert into t_car(id,car_num,bread,guider_prive,produce_time,car_type)
values(null,#{xyz},#{brand},#{guiderPrice},#{produceTime},#{carType})

運(yùn)行測(cè)試:

public void testInsertCarByPOJO() {
SqlSession sqlSession = SqlSessionUtil.openSession();
// 封裝數(shù)據(jù)
Car car = new Car(null, "333", "比亞迪泰", 30.0, "2020-11-11", "新能源");
// 執(zhí)行SQL
int count = sqlSession.insert("insertCar", car); // ORM // 對(duì)應(yīng)XxxMapper.xml 上的id
System.out.println(count);
sqlSession.commit();
sqlSession.close();
}
如果我們?cè)?XxxMapper.xml(這里是CarMapper.xml) 的SQL映射文件中 的 <insert> 標(biāo)簽 中的 #{ } 占位符,寫(xiě)的不是對(duì)應(yīng)pojo(這里是 Car) 類上的屬性值時(shí),會(huì)出現(xiàn)什么問(wèn)題,是報(bào)錯(cuò),還是賦值為 Null呢?

運(yùn)行測(cè)試:

報(bào)錯(cuò)信息:
There is no getter for property named 'xyz' in 'class com.rainbowsea.mybatis.pojo.Car'
mybatis 去找,Car類中的getXyz()方法去了,沒(méi)找到,報(bào)錯(cuò)了。
怎么解決的?
我們?cè)趐ojo(這里是 Car) 類當(dāng)中加入一個(gè) getXyz( ) 方法,方法的返回值和 原來(lái)的getCarNum( )的返回值,一樣就是,方法名不同而已:就是方法名不同,返回的值還是:carNum

再運(yùn)行測(cè)試;

通過(guò)這個(gè)測(cè)試,得出一個(gè)結(jié)論:
嚴(yán)格意義上來(lái)說(shuō),如果使用POJO對(duì)象傳遞值的話,#{}這個(gè)大括號(hào)中i給你到底寫(xiě)什么?
寫(xiě)的是對(duì)應(yīng)的屬性的 get方法的方法名去掉 get,然后將剩下的單詞字母小寫(xiě),然后放進(jìn)去。
例如:getUsername() ---> #{username}
例如: getEmail() ---> #{email}
也就是說(shuō)MyBatis在底層,傳值的時(shí)候,先要獲取值,怎么獲取的?
調(diào)用了pojo對(duì)象的get方法,例如:car.getCarNum(); car.getCarType(), car.getBreand() 方法
經(jīng)過(guò)測(cè)試得出結(jié)論:
如果采用map集合傳參,#{} 里寫(xiě)的是map集合的key,如果key不存在不會(huì)報(bào)錯(cuò),數(shù)據(jù)庫(kù)表中會(huì)插入NULL。
如果采用POJO傳參,#{} 里寫(xiě)的是get方法的方法名去掉get之后將剩下的單詞首字母變小寫(xiě)(例如:getAge對(duì)應(yīng)的是#{age},getUserName對(duì)應(yīng)的是#{userName}),如果這樣的get方法不存在會(huì)報(bào)錯(cuò)。
注意:其實(shí)傳參數(shù)的時(shí)候有一個(gè)屬性 parameterType,這個(gè)屬性用來(lái)指定傳參的數(shù)據(jù)類型,不過(guò)這個(gè)屬性是可以省略的

3.2 delete 刪除記錄
需求:根據(jù) id 刪除數(shù)據(jù),將 id = 44 的數(shù)據(jù)刪除。
編寫(xiě)XxxMapper.xml SQL 映射的文件, 刪除用 <delete> 標(biāo)簽

<!--注意: 如果占位符只有一個(gè),那么#{}的大括號(hào)里可以隨意,但是最好見(jiàn)名知意--><delete id="deleteById">delete from t_car where id = #{id}</delete>
注意:當(dāng)占位符只有一個(gè)的時(shí)候,#{} 里面的內(nèi)容可以隨便寫(xiě)。
只有一個(gè)占位符的時(shí)候,傳一個(gè)值,MyBatis 可以自動(dòng)載入,但是 #{} 不可以空著,要寫(xiě)上值
運(yùn)行測(cè)試:
Java程序執(zhí)行,刪除操作,用sqlSession.delete("Id", 值) 方法,刪除記錄


@Test
public void testDeleteById() {
SqlSession sqlSession = SqlSessionUtil.openSession();
// 執(zhí)行SQL語(yǔ)句
sqlSession.delete("deleteById",44); // 如果只要一個(gè)值的時(shí)候,就不需要對(duì)應(yīng)上的的 Object 類型了
sqlSession.commit(); // 提交
sqlSession.close(); // 關(guān)閉
}
3.3 update 修改記錄
需求: 根據(jù) id 修改某條記錄
需求:修改 id=46 的Car信息,car_num為999,brand為凱美瑞,guide_price為30.00,produce_time為2020-011-11,car_type為燃油車。
編寫(xiě)XxxMapper.xml SQL 映射的文件,修改用 <update> 標(biāo)簽

<update id="updateById">
update t_car set car_num = #{carNum}, brand=#{brand}, guide_price=#{guiderPrice},produce_time=#{produceTime},
car_type=#{carType} where id =#{id}
</update>運(yùn)行測(cè)試:
Java程序執(zhí)行,修改/更新數(shù)據(jù)操作,用sqlSession.update("Id", 值) 方法,修改記錄




public void testUpdateById() {
SqlSession sqlSession = SqlSessionUtil.openSession();
Car car = new Car(46L, "999", "凱美瑞", 30.3, "1999-11-10", "燃油車");
// 執(zhí)行SQL語(yǔ)句
int count = sqlSession.update("updateById", car);
System.out.println(count);
sqlSession.commit();
sqlSession.close();
}當(dāng)然了,如果使用 map傳數(shù)據(jù)也是可以的。
3.4 select 查詢記錄
3.4.5 select 查詢一條記錄
select語(yǔ)句和其它語(yǔ)句不同的是:查詢會(huì)有一個(gè)結(jié)果集。
需求:根據(jù)id 查詢對(duì)應(yīng)的一條記錄,這里我們查詢 id = 1 的記錄信息。
在XxxMapper.xml 文件當(dāng)中編寫(xiě),對(duì)應(yīng)的查詢語(yǔ)句,查詢用 <select> 標(biāo)簽。

運(yùn)行測(cè)試:
因?yàn)椴樵儯瑳](méi)有修改的操作,是不需要事務(wù)操作的,所以我們不同提交數(shù)據(jù),給數(shù)據(jù)庫(kù)。
這里我們查詢的是一條記錄,用 sqlSession.selectOne("id") 方法,返回一個(gè) 映射對(duì)象。



報(bào)錯(cuò)信息:Error querying database. Cause: org.apache.ibatis.executor.ExecutorException: A query was run and no Result Maps were found for the Mapped Statement 'rainbowsea.selectById'. It's likely that neither a Result Type nor a Result Map was specified.
以上的異常大致的意思是:對(duì)于一個(gè)查詢語(yǔ)句來(lái)說(shuō),你需要指定它的“結(jié)果類型”或者“結(jié)果映射”。
所以說(shuō),你想讓mybatis查詢之后返回一個(gè)Java對(duì)象的話,至少你要告訴mybatis返回一個(gè)什么類型的Java對(duì)象,可以在<select>標(biāo)簽中添加 resultType 屬性,所賦值的對(duì)象是:全限定類名 ,用來(lái)指定查詢要轉(zhuǎn)換的類型:
需要特別注意的是:
select 標(biāo)簽中給的resultType屬性,這個(gè)屬性用來(lái)告訴mybatis,查詢結(jié)果封裝什么類型的Java對(duì)象
resultType通常寫(xiě)的是:全限定類名

<select id="selectById" resultType="com.rainbowsea.mybatis.pojo.Car">
select id,car_num as carNum, brand, guide_price as guiderPrice,
produce_time as produceTime,
car_type as carType
from t_car
where id= #{id}
</select>我們?cè)俅芜\(yùn)行:

運(yùn)行后之前的異常不再出現(xiàn)了,這說(shuō)明添加了resultType屬性之后,解決了之前的異常,可以看出resultType是不能省略的。
仔細(xì)觀察控制臺(tái)的日志信息,不難看出,結(jié)果查詢出了一條。并且每個(gè)字段都查詢的到值了:

但是奇怪的是返回的Car對(duì)象,只有 id 和 brand 兩個(gè)屬性有值,其它屬性的值都是 null,

這是為什么呢?我們來(lái)觀察一下查詢結(jié)果列名和Car類的屬性名是否能一一對(duì)應(yīng):

通過(guò)觀察發(fā)現(xiàn):只有 id 和 brand 是一致的,其他字段名和屬性名對(duì)應(yīng)不上,這就是導(dǎo)致null的原因了?我們可以在sql語(yǔ)句中使用 as 關(guān)鍵字來(lái)給查詢結(jié)果列名起別名的方式,讓它們兩者保持一致的關(guān)系。


<select id="selectById" resultType="com.rainbowsea.mybatis.pojo.Car">
select id,car_num as carNum, brand, guide_price as guiderPrice,
produce_time as produceTime,
car_type as carType
from t_car
where id= #{id}
</select>
3.4.6 select 查詢多條記錄
需求:查詢所有的Car信息。
編寫(xiě)對(duì)應(yīng)的SQL語(yǔ)句,在 XxxMapper.xml SQl語(yǔ)句映射文件當(dāng)中。
同樣我們需要使用上 as 關(guān)鍵字,定義別名,使其Java程序和數(shù)據(jù)庫(kù)的字段名兩者之間的字段保持一致。

<select id="selectAll" resultType="com.rainbowsea.mybatis.pojo.Car"> select id,car_num as carNum, brand, guide_price as guiderPrice, produce_time as produceTime, car_type as carType from t_car </select>
Java代碼如下:
這里,因?yàn)槲覀儾樵兊氖嵌鄺l記錄,用 sqlSession.selectList("id") 方法,返回一個(gè) List 集合,存儲(chǔ)著我們的查詢結(jié)果集。

如果是返回的是鍵值對(duì) 的話,我們還可以用 sqlSession.selectMap("id") 方法 返回的是一個(gè) Map 集合

運(yùn)行結(jié)果:

<select id="selectAll" resultType="com.rainbowsea.mybatis.pojo.Car">
select
id,car_num as carNum, brand, guide_price as guiderPrice,
produce_time as produceTime,
car_type as carType
from
t_car
</select>
List<Car> cars = sqlSession.selectList("selectAll");
注意: resultType 還是指定封裝的結(jié)果集的類型,不是指定List類型,是指定List集合中元素的類型
selectList 方法,mybatis通過(guò)這個(gè)方法就可以得知你需要一個(gè)List集合,它會(huì)自動(dòng)給你返回一個(gè)List集合4. 關(guān)于SQL Mapper 的 namespace 的使用方式
在SQL Mapper配置文件中 標(biāo)簽的 namespace 屬性可以翻譯為命名空間,這個(gè)命名空間主要是為了防止sqlId沖突的。

我們?cè)趧?chuàng)建一個(gè) UserMapper.xml 的SQL 語(yǔ)句的映射文件,同樣將其 namespace = "rainbowsa" 這個(gè)值,同時(shí)兩個(gè)配置文件當(dāng)中都有同一個(gè):select 查詢語(yǔ)句,同時(shí) id 都為 selectAll 。運(yùn)行看看,存在什么問(wè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"> <!--namespace先隨意寫(xiě)一個(gè)--> <mapper namespace="rainbowsea"> <select id="selectAll" resultType="com.rainbowsea.mybatis.pojo.Car"> select id,car_num as carNum, brand, guide_price as guiderPrice, produce_time as produceTime, car_type as carType from t_car </select> </mapper>
將它們都配置到:將CarMapper.xml 和 UserMapper.xml 都配置到mybatis-config.xml文件中。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 開(kāi)啟mybatis 對(duì)標(biāo)準(zhǔn)日志的實(shí)現(xiàn)-->
<!-- 如果導(dǎo)入的第三方框架的日志,沒(méi)有下面這個(gè) settings 也是可以的-->
<!-- <settings>-->
<!-- <setting name="logImpl" value="STDOUT_LOGGING"/>-->
<!-- </settings>-->
<environments default="development">
<environment id="development">
<!-- MANAGED 沒(méi)有用第三框架管理的話,都是會(huì)被提交的,沒(méi)有事務(wù)上的管理了。-->
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="username" value="root"/>
<property name="password" value="MySQL123"/>
</dataSource>
</environment>
</environments>
<mappers>
<!--sql映射文件創(chuàng)建好之后,需要將該文件路徑配置到這里-->
<!-- 執(zhí)行XxxMapper.xml 文件的路徑-->
<!-- resource 屬性自動(dòng)會(huì)從類的根路徑下開(kāi)始查找資源-->
<!-- <mapper resource="CarMapper.xml"/>-->
<!-- <mapper resource="com/CarMapper2.xml"/>-->
<!-- url屬性: 從絕對(duì)路徑當(dāng)中加載資源。-->
<!-- 語(yǔ)法格式:file:///絕對(duì)路徑-->
<!-- <mapper url="file:///e:/CarMapper.xml"></mapper>-->
<mapper resource="CarMapper.xml"></mapper>
<mapper resource="UserMapper.xml"></mapper>
</mappers>
</configuration>編寫(xiě)Java代碼如下:

public void testNamespace() {
SqlSession sqlSession = SqlSessionUtil.openSession();
// 執(zhí)行SQL語(yǔ)句
// 正確的完整寫(xiě)法:namespace.id
List<Car> cars = sqlSession.selectList("selectAll");
// 遍歷
cars.forEach(car -> {
System.out.println(car);
});
//sqlSession.commit(); 查詢不用提交,沒(méi)有事務(wù)問(wèn)題
sqlSession.close();
}運(yùn)行報(bào)錯(cuò):

Cause: org.apache.ibatis.builder.BuilderException: Error parsing SQL Mapper Configuration. Cause: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. The XML location is 'UserMapper.xml'. Cause: java.lang.IllegalArgumentException: Mapped Statements collection already contains value for rainbowsea.selectAll. please check CarMapper.xml and UserMapper.xml
【翻譯】selectCarAll在Mapped Statements集合中不明確(請(qǐng)嘗試使用包含名稱空間的全名,或重命名其中一個(gè)條目)
【大致意思是】selectCarAll重名了,你要么在selectCarAll前添加一個(gè)名稱空間,要有你改個(gè)其它名字。
這里我們修改一下 UserMapper.xml 文件當(dāng)中的 namespace = "rainbowsea2" 命名空間的值。

同時(shí)編寫(xiě)Java程序的時(shí)候,使用上 namespace="rainbowsea2"的命名空間。


查詢成功:
@Test
public void testNamespace() {
SqlSession sqlSession = SqlSessionUtil.openSession();
// 執(zhí)行SQL語(yǔ)句
// 正確的完整寫(xiě)法:namespace.id
List<Car> cars = sqlSession.selectList("rainbowsea2.selectAll");
// 遍歷
cars.forEach(car -> {
System.out.println(car);
});
//sqlSession.commit(); 查詢不用提交,沒(méi)有事務(wù)問(wèn)題
sqlSession.close();
}實(shí)際上,本質(zhì)上,mybatis中的
sqlId的完整寫(xiě)法:namespace.id,注意,之后都這么寫(xiě)了,這是完整正確的寫(xiě)法。
5. 總結(jié):
在sql語(yǔ)句中使用 #{map集合的key} 來(lái)完成傳值,#{} 等同于JDBC中的 ? ,#{}就是占位符。
在mybatis當(dāng)中不能使用
?占位符,必須使用#{ }來(lái)代替JDBC當(dāng)中的?{ } 不可以空著,就算是只有一個(gè)傳值,也不可以空著,隨便寫(xiě),都不可以空著。
#{ }和JDBC當(dāng)中的?是等效的。pojo 對(duì)象的數(shù)據(jù)表 ORM 映射的 類對(duì)象,一定要提供對(duì)應(yīng)的 set 和 get 方法,不然 ,MyBatis 無(wú)法通過(guò)反射機(jī)制獲取到相應(yīng)所需要的信息的。 注意:占位符#{ }, 大括號(hào)里面寫(xiě):pojo類的屬性名
如果采用POJO傳參,#{} 里寫(xiě)的是get方法的方法名去掉get之后將剩下的單詞首字母變小寫(xiě)(例如:getAge對(duì)應(yīng)的是#{age},getUserName對(duì)應(yīng)的是#{userName}),如果這樣的get方法不存在會(huì)報(bào)錯(cuò)。
注意:其實(shí)傳參數(shù)的時(shí)候有一個(gè)屬性 parameterType,這個(gè)屬性用來(lái)指定傳參的數(shù)據(jù)類型,不過(guò)這個(gè)屬性是可以省略的
添加/插入,執(zhí)行 sqlSession.insert("Id", car); 方法
Java程序執(zhí)行,刪除操作,用sqlSession.delete("Id", 值) 方法,刪除記錄,只有一個(gè)占位符的時(shí)候,傳一個(gè)值,MyBatis 可以自動(dòng)載入,但是 #{} 不可以空著,要寫(xiě)上值
Java程序執(zhí)行,修改/更新數(shù)據(jù)操作,用sqlSession.update("Id", 值) 方法,修改記錄
因?yàn)椴樵儯瑳](méi)有修改的操作,是不需要事務(wù)操作的,所以我們不同提交數(shù)據(jù),給數(shù)據(jù)庫(kù)。
這里我們查詢的是一條記錄,用 sqlSession.selectOne("id") 方法,返回一個(gè) 映射對(duì)象。
你想讓mybatis查詢之后返回一個(gè)Java對(duì)象的話,至少你要告訴mybatis返回一個(gè)什么類型的Java對(duì)象,可以在<select>標(biāo)簽中添加 resultType 屬性,所賦值的對(duì)象是:全限定類名 ,用來(lái)指定查詢要轉(zhuǎn)換的類型:
select 查詢時(shí),需要 pojo的類當(dāng)中的映射的類對(duì)象中的屬性名要與 對(duì)應(yīng)數(shù)據(jù)表中的字段名一致,才能賦值成功,不然為 Null。可以使用 as 關(guān)鍵字,定義別名的方式,解決。后面有更好的解決方式。我們查詢的是多條記錄,用 sqlSession.selectList("id") 方法,返回一個(gè) List 集合,存儲(chǔ)著我們的查詢結(jié)果集。
如果是返回的是鍵值對(duì) 的話,我們還可以用 sqlSession.selectMap("id") 方法 返回的是一個(gè) Map 集合
實(shí)際上,本質(zhì)上,mybatis中的
sqlId的完整寫(xiě)法:namespace.id,注意,之后都這么寫(xiě)了,這是完整正確的寫(xiě)法。
在Java程序當(dāng)中的寫(xiě)法: // 執(zhí)行SQL語(yǔ)句 List<Car> cars = sqlSession.selectList("rainbowsea2.selectAll");實(shí)際上,本質(zhì)上,mybatis中的sqlId的完整寫(xiě)法: namespace.id ,注意,之后都這么寫(xiě)了,這是完整正確的寫(xiě)法。

6. 最后:
到此這篇關(guān)于MyBatis完成CRUD 詳細(xì)細(xì)節(jié)內(nèi)容的文章就介紹到這了,更多相關(guān)MyBatis完成CRUD內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Cloud Alibaba Nacos 入門(mén)詳解
這篇文章主要介紹了Spring Cloud Alibaba Nacos入門(mén)詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-03-03
一文簡(jiǎn)介Java中BlockingQueue阻塞隊(duì)列
本文主要介紹了一文簡(jiǎn)介Java中BlockingQueue阻塞隊(duì)列,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
Java并發(fā)工具類CountDownLatch CyclicBarrier使用詳解
這篇文章主要為大家介紹了Java并發(fā)工具類CountDownLatch CyclicBarrier使用詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-06-06
關(guān)于Spring多數(shù)據(jù)源TransactionManager沖突的解決方案
這篇文章主要介紹了關(guān)于Spring多數(shù)據(jù)源TransactionManager沖突的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-07-07
JAVA中實(shí)現(xiàn)原生的 socket 通信機(jī)制原理
本篇文章主要介紹了JAVA中實(shí)現(xiàn)原生的 socket 通信機(jī)制原理,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08
Spring SmartLifecycle:如何精準(zhǔn)控制Bean的生命周期
這篇文章主要介紹了Spring SmartLifecycle:如何精準(zhǔn)控制Bean的生命周期問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2025-03-03
Java后端實(shí)現(xiàn)生成驗(yàn)證碼圖片的示例代碼
驗(yàn)證碼是一種用于驗(yàn)證用戶身份或確保用戶操作安全的技術(shù)手段,通常以圖形、聲音或文字的形式出現(xiàn),本文主要介紹了如何通過(guò)java實(shí)現(xiàn)生成驗(yàn)證碼圖片,需要的可以參考下2023-12-12

