- 使用MyBatis完成CRUD:
- Create
- Retrieve
- Update
- Delete
三、使用MyBatis完成CRUD
- 准备工作
- 创建 module(Maven 的普通 Java 模块):
mybatis-002-crud pom.xml- 打包方式 jar
- 依赖:
- mybatis 依赖
- mysql 依赖
- JUnit 依赖
- logback 依赖
mybatis-config.xml放在类的根路径下(放在resources目录下)CarMapper.xml放在类的根路径下(放在resources目录下)logback.xml放在类的根路径下(放在resources目录下)- 提供
com.f.mybatis.utils.SqlSessionUtil工具类
- 创建 module(Maven 的普通 Java 模块):
3.1 ★insert (Create)
分析以下 SQL 映射文件中 SQL 语句存在的问题:
1
2
3
4
5
6
7
8
9
10<!-- CarMapper.xml文件 -->
<mapper namespace="car">
<!-- insert语句, id是这条sql语句的唯一标识,这个id就代表了这条sql语句-->
<insert id="insertCar">INSERT INTO t_car(id, car_num, brand, guide_price, produce_time, car_type)
VALUES (null, '1003', '问界', 35.00, '2023-10-10', '新能源')</insert>
</mapper>存在的问题是:SQL 语句中的值不应该写死,值应该是用户提供的。
类似于 JDBC,**在 Mybatis 中,用
#{}来充当占位符,作用类似于 JDBC 中的?**。- 在 MyBatis 中可以这样做:
- 在 Java 程序中,将数据放到 Map 集合中,然后在 sql 语句中使用
#{map集合的key}来完成传值。
- 在 Java 程序中,将数据放到 Map 集合中,然后在 sql 语句中使用
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32package com.f.mybatis.test;
import com.f.mybatis.utils.SqlSessionUtil;
import org.apache.ibatis.session.SqlSession;
import org.junit.Test;
import java.util.HashMap;
import java.util.Map;
/**
* @author fzy
* @date 2023/12/31 21:33
*/
public class CarMapperTest {
public void testInsertCar() {
SqlSession sqlSession = SqlSessionUtil.openSession();
Map<String, Object> map = new HashMap<>();
map.put("k1", "1003");
map.put("k2", "问界");
map.put("k3", "41.00");
map.put("k4", "2023-11-11");
map.put("k5", "新能源");
// insert方法的参数:
// 第一个参数:sqlId,从 CarMapper.xml 文件中复制
// 第二个参数:封装数据的对象
sqlSession.insert("insertCar", map);
sqlSession.commit();
sqlSession.close();
}
}1
2
3
4
5
6
7
8
9<!-- CarMapper.xml文件 -->
<mapper namespace="car">
<!-- insert语句, id是这条sql语句的唯一标识,这个id就代表了这条sql语句-->
<insert id="insertCar">INSERT INTO t_car(id, car_num, brand, guide_price, produce_time, car_type) VALUES (null, #{k1}, #{k2}, #{k3}, #{k4}, #{k5})</insert>
</mapper>如果
#{}里写的是 map 集合中不存在的 key 会有什么问题?例如1
2
3
4
5
6
7
8
9
10...
Map<String, Object> map = new HashMap<>();
map.put("k1", "1003");
map.put("k2", "问界");
map.put("k3", 41.00);
map.put("k4", "2023-11-11");
map.put("k5", "新能源");
sqlSession.insert("insertCar", map);
...1
<insert id="insertCar">INSERT INTO t_car(id, car_num, brand, guide_price, produce_time, car_type) VALUES (null, #{kkkkkk}, #{k2}, #{k3}, #{k4}, #{k5})</insert>
- 经过测试,看到程序并没有报错。正常执行。不过
#{kkkkkk}的写法导致无法获取到 map 集合中的数据,最终导致数据库表car_num插入了 NULL。
- 经过测试,看到程序并没有报错。正常执行。不过
- 在 MyBatis 中可以这样做:
思考
使用 Map 集合可以传参,那使用 pojo(简单普通的 java 对象)可以完成传参吗?–> 答案是可以的。
第一步:定义一个 pojo 类 Car,提供相关属性。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88package com.f.mybatis.bean;
/**
* @author fzy
* @date 2023/12/31 22:06
* 封装汽车相关信息的javabean类。普通的的java类。
*/
public class Car {
// javabean 的属性应该和数据表中的字段一一对应
private Long id;
private String carNum;
private String brand;
private Double guidePrice;
private String produceTime;
private String carType;
public Car() {
}
public Car(Long id, String carNum, String brand, Double guidePrice, String produceTime, String carType) {
this.id = id;
this.carNum = carNum;
this.brand = brand;
this.guidePrice = guidePrice;
this.produceTime = produceTime;
this.carType = carType;
}
public String toString() {
return "Car{" +
"id=" + id +
", carNum='" + carNum + '\'' +
", brand='" + brand + '\'' +
", guidePrice=" + guidePrice +
", produceTime='" + produceTime + '\'' +
", carType='" + carType + '\'' +
'}';
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public String getCarNum() {
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 getGuidePrice() {
return guidePrice;
}
public void setGuidePrice(Double guidePrice) {
this.guidePrice = guidePrice;
}
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 程序:
1
2
3
4
5
6
7
8
public void testInsertCarByPojo() {
SqlSession sqlSession = SqlSessionUtil.openSession();
Car car = new Car(null, "1004", "比亚迪", 33.23, "2020-10-01", "燃油车");
sqlSession.insert("insertCar", car);
sqlSession.commit();
sqlSession.close();
}第三步:xml 配置文件:
1
2
3
4
5
6
7
8
<mapper namespace="car">
<!-- #{}里写的是POJO的属性名 -->
<insert id="insertCar">INSERT INTO t_car(id, car_num, brand, guide_price, produce_time, car_type) VALUES (null, #{carNum}, #{brand}, #{guidePrice}, #{produceTime}, #{carType})</insert>
</mapper>第四步:执行 java 程序,成功插入数据。
#{}里写的是 POJO 的属性名,如果写成其他的会有问题吗?例如:1
<insert id="insertCar">INSERT INTO t_car(id, car_num, brand, guide_price, produce_time, car_type) VALUES (null, #{xyz}, #{brand}, #{guidePrice}, #{produceTime}, #{carType})</insert>
运行程序会出现异常,错误信息中描述:在 Car 类中没有找到
xyz属性的getter方法。There is no getter for property named 'xyz' in 'class com.f.mybatis.bean.Car'- 如果修改 POJO 类 Car 的代码,只将
getCarNum()方法名修改为getXyz(),其他代码不变,再运行 java 程序,发现也能成功插入数据。
- 如果修改 POJO 类 Car 的代码,只将
总结:
- 如果采用 map 集合传参,
#{}里写的是 map 集合的 key,如果 key 不存在,并不会报错,数据库表中会插入 NULL。 - 如果采用 POJO 传参,
#{}里写的是get方法的方法名去掉get之后将剩下的单词首字母变小写(例如:getAge对应的是#{age},getUserName对应的是#{userName}),如果这样的get方法不存在则会报错。
- 如果采用 map 集合传参,
注意:其实传参数的时候有一个属性
parameterType,这个属性用来指定传参的数据类型,不过这个属性是可以省略的。1
2
3
4
5
6
7<insert id="insertCar" parameterType="java.util.Map">
insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
</insert>
<insert id="insertCarByPOJO" parameterType="com.powernode.mybatis.pojo.Car">
insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType})
</insert>
3.2 delete (Delete)
需求:根据
car_num进行删除。sql 语句这样写:
1
2
3
4
5
6
7
<mapper namespace="car">
<delete id="deleteCarByNum">DELETE FROM t_car WHERE car_num = #{SuiBianXie}</delete>
</mapper>java 程序这样写:
1
2
3
4
5
6
7
public void testDeleteCarByNum() {
SqlSession sqlSession = SqlSessionUtil.openSession();
sqlSession.delete("deleteCarByNum", "1003");
sqlSession.commit();
sqlSession.close();
}
注意:当占位符只有一个的时候,
#{}里面的内容可以随便写,Mybatis 会自动识别并填入参数。但是最好见名知意。
3.3 update (Update)
需求:修改
car_num为 1001 的 Car 信息,将其guide_price改为 50.00 万元。sql 语句这样写:
1
2
3
4
5
6
7
<mapper namespace="car">
<update id="updateCar">UPDATE t_car SET guide_price = #{guidePrice} WHERE car_num = #{carNum}</update>
</mapper>java 程序这样写:
1
2
3
4
5
6
7
8
9
10
public void testUpdateCar() {
SqlSession sqlSession = SqlSessionUtil.openSession();
Car car = new Car();
car.setCarNum("1001");
car.setGuidePrice(50.00);
sqlSession.update("updateCar", car);
sqlSession.commit();
sqlSession.close();
}
当然,如果使用 map 传数据也是可以的。
3.4 ★select (Retrieve)
- select 语句和其它语句不同的是:查询会有一个结果集。来看 mybatis 是怎么处理结果集的。
3.4.1 查询一条数据 selectOne
resultType 属性
需求:查询 id 为 1 的 Car 信息。
sql 语句这样写:
1
2
3
4
5
6
7
<mapper namespace="car">
<select id="selectCarById">SELECT car_num, brand, guide_price, produce_time, car_type FROM t_car WHERE id = #{id}</select>
</mapper>java 程序这样写:
1
2
3
4
5
6
7
public void testSelectCarById() {
SqlSession sqlSession = SqlSessionUtil.openSession();
Car car = sqlSession.selectOne("selectCarById", 1);
System.out.println(car);
sqlSession.close();
}运行结果出现异常:
A query was run and no Result Maps were found for the Mapped Statement 'car.selectCarById'. It's likely that neither a Result Type nor a Result Map was specified.也就是说,对于一个查询语句,你需要指定它的“结果类型”或者“结果映射”,mybatis 才能知道要将查询的结果封装为什么类型的对象。
因此,如果你想让 mybatis 查询之后返回一个 Java 对象的话,至少你要告诉 mybatis 返回一个什么类型的 Java 对象,可以在
<select>标签中添加resultType属性,用来指定查询结果要转换的类型:1
<select id="selectCarById" resultType="com.f.mybatis.bean.Car">SELECT car_num, brand, guide_price, produce_time, car_type FROM t_car WHERE id = #{id}</select>
resultType通常写的是全限定类名。
运行后之前的异常不再出现了,这说明添加了
resultType属性之后,解决了之前的异常,可以看出resultType是不能省略的。但是奇怪的是返回的 Car 对象,只有 brand 属性有值,其它属性的值都是 null,这是为什么呢?
Car{id=null, carNum='null', brand='宝马', guidePrice=null, produceTime='null', carType='null'}我们来观察一下查询结果列名和 Car 类的属性名是否能一一对应:
- 查询结果集的列名:
car_num,brand,guide_price,produce_time,car_type - Car 类的属性名:
carNum,brand,guidePrice,produceTime,carType
观察发现:只有 brand 是一致的,其他字段名和属性名对应不上,这就是导致 null 的原因。
为了解决这个问题,可以在 sql 语句中采用
as关键字起别名:1
<select id="selectCarById" resultType="com.f.mybatis.bean.Car">SELECT car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType FROM t_car WHERE id = #{id}</select>
此时返回的 Car 对象就为:
Car{id=null, carNum='1001', brand='宝马', guidePrice=50.0, produceTime='2022-10-10', carType='燃油车'}。当然还有其它解决方案,我们后面再看。
- 查询结果集的列名:
3.4.2 查询多条数据 selectList
需求:查询所有 Car 的信息。
sql 语句这样写:
1
2
3
4
5
6
7
8
<mapper namespace="car">
<!-- 虽然结果是List集合,但是resultType属性需要指定的是List集合中元素的类型。 -->
<select id="selectCar" resultType="com.f.mybatis.bean.Car">SELECT car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType FROM t_car</select>
</mapper>java 程序这样写:
1
2
3
4
5
6
7
8
9
public void testSelectCar() {
SqlSession sqlSession = SqlSessionUtil.openSession();
List<Car> cars = sqlSession.selectList("selectCar");
for (Car car : cars) {
System.out.println(car);
}
sqlSession.close();
}