MyBatis实现CRUD的示例代码
作者:耀耀_很无聊
准备工作
创建module(Maven的普通Java模块):mybatis-002-crud
pom.xml
- 打包方式jar
- 依赖:
- mybatis依赖
- mysql驱动依赖
- junit依赖
- logback依赖
mybatis-config.xml
放在类的根路径下CarMapper.xml
放在类的根路径下logback.xml
放在类的根路径下- 提供
com.study.mybatis.utils.SqlSessionUtil
工具类 - 创建测试用例:
com.study.mybatis.CarMapperTest
1 insert(Create)
分析以下SQL映射文件中SQL语句存在的问题
<?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先随便写--> <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>
存在的问题是:SQL语句中的值不应该写死,值应该是用户提供的。之前的JDBC代码是这样写的:
// JDBC中使用 ? 作为占位符。那么MyBatis中会使用什么作为占位符呢? String sql = "insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(?,?,?,?,?)"; // ...... // 给 ? 传值。那么MyBatis中应该怎么传值呢? ps.setString(1,"103"); ps.setString(2,"奔驰E300L"); ps.setDouble(3,50.3); ps.setString(4,"2022-01-01"); ps.setString(5,"燃油车");
在MyBatis中可以这样做:
在Java程序中,将数据放到Map集合中在sql语句中使用 #{map集合的key} 来完成传值,#{} 等同于JDBC中的 ? ,#{}就是占位符Java程序这样写:
package com.study.mybatis; import com.study.mybatis.utils.SqlSessionUtil; import org.apache.ibatis.session.SqlSession; import org.junit.Test; import java.util.HashMap; import java.util.Map; /** * 测试MyBatis的CRUD * @author sqnugy * @version 1.0 * @since 1.0 */ public class CarMapperTest { @Test public void testInsertCar(){ // 准备数据 Map<String, Object> map = new HashMap<>(); map.put("k1", "103"); map.put("k2", "奔驰E300L"); map.put("k3", 50.3); map.put("k4", "2020-10-01"); map.put("k5", "燃油车"); // 获取SqlSession对象 SqlSession sqlSession = SqlSessionUtil.openSession(); // 执行SQL语句(使用map集合给sql语句传递数据) int count = sqlSession.insert("insertCar", map); System.out.println("插入了几条记录:" + count); } }
SQL语句这样写:
<?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先随便写--> <mapper namespace="car"> <insert id="insertCar"> insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{k1},#{k2},#{k3},#{k4},#{k5}) </insert> </mapper>
**#{} 的里面必须填写map集合的key,不能随便写。**运行测试程序,查看数据库:
如果#{}
里写的是map集合中不存在的key会有什么问题?
<?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="car"> <insert id="insertCar"> insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{kk},#{k2},#{k3},#{k4},#{k5}) </insert> </mapper>
运行程序:
通过测试,看到程序并没有报错。正常执行。不过 #{kk}
的写法导致无法获取到map集合中的数据,最终导致数据库表car_num插入了NULL。
在以上sql语句中,可以看到#{k1} #{k2} #{k3} #{k4} #{k5}
的可读性太差,为了增强可读性,我们可以将Java程序做如下修改:
Map<String, Object> map = new HashMap<>(); // 让key的可读性增强 map.put("carNum", "103"); map.put("brand", "奔驰E300L"); map.put("guidePrice", 50.3); map.put("produceTime", "2020-10-01"); map.put("carType", "燃油车");
SQL语句做如下修改,这样可以增强程序的可读性:
<?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="car"> <insert id="insertCar"> insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType}) </insert> </mapper>
运行程序,查看数据库表:
使用Map集合可以传参,那使用pojo(简单普通的java对象)可以完成传参吗?测试一下:
- 第一步:定义一个pojo类Car,提供相关属性。
package com.study.mybatis.pojo; /** * POJOs,简单普通的Java对象。封装数据用的。 * @author sqnugy * @version 1.0 * @since 1.0 */ public class Car { private Long id; private String carNum; private String brand; private Double guidePrice; private String produceTime; private String carType; @Override public String toString() { return "Car{" + "id=" + id + ", carNum='" + carNum + '\'' + ", brand='" + brand + '\'' + ", guidePrice=" + guidePrice + ", produceTime='" + produceTime + '\'' + ", carType='" + 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 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程序
@Test public void testInsertCarByPOJO(){ // 创建POJO,封装数据 Car car = new Car(); car.setCarNum("103"); car.setBrand("奔驰C200"); car.setGuidePrice(33.23); car.setProduceTime("2020-10-11"); car.setCarType("燃油车"); // 获取SqlSession对象 SqlSession sqlSession = SqlSessionUtil.openSession(); // 执行SQL,传数据 int count = sqlSession.insert("insertCarByPOJO", car); System.out.println("插入了几条记录" + count); }
- 第三步:SQL语句
<insert id="insertCarByPOJO"> <!--#{} 里写的是POJO的属性名--> insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType}) </insert>
- 运行程序,查看数据库表:
#{}
里写的是POJO的属性名,如果写成其他的会有问题吗?
<insert id="insertCarByPOJO"> insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{a},#{brand},#{guidePrice},#{produceTime},#{carType}) </insert>
运行程序,出现了以下异常:
错误信息中描述:在Car类中没有找到a属性的getter方法。
修改POJO类Car的代码,只将getCarNum()方法名修改为getA(),其他代码不变,如下:
再运行程序,查看数据库表中数据:
经过测试得出结论:
如果采用map集合传参,#{}
里写的是map集合的key,如果key不存在不会报错,数据库表中会插入NULL。
如果采用POJO传参,#{}
里写的是get方法的方法名去掉get之后将剩下的单词首字母变小写(例如:getAge对应的是#{age},getUserName对应的是#{userName}
),如果这样的get方法不存在会报错。
注意:其实传参数的时候有一个属性parameterType,这个属性用来指定传参的数据类型,不过这个属性是可以省略的
<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.study.mybatis.pojo.Car"> insert into t_car(car_num,brand,guide_price,produce_time,car_type) values(#{carNum},#{brand},#{guidePrice},#{produceTime},#{carType}) </insert>
2 delete(Delete)
需求:根据car_num进行删除。
SQL语句这样写:
<delete id="deleteByCarNum"> delete from t_car where car_num = #{SuiBianXie} </delete>
Java程序这样写:
@Test public void testDeleteByCarNum(){ // 获取SqlSession对象 SqlSession sqlSession = SqlSessionUtil.openSession(); // 执行SQL语句 int count = sqlSession.delete("deleteByCarNum", "102"); System.out.println("删除了几条记录:" + count); }
运行结果:
注意:当占位符只有一个的时候,${} 里面的内容可以随便写。
3 update(Update)
需求:修改id=34
的Car信息,car_num
为102
,brand
为比亚迪汉
,guide_price
为30.23
,produce_time
为2018-09-10
,car_type
为电车
修改前:
SQL语句如下:
<update id="updateCarByPOJO"> update t_car set car_num = #{carNum}, brand = #{brand}, guide_price = #{guidePrice}, produce_time = #{produceTime}, car_type = #{carType} where id = #{id} </update>
Java代码如下:
@Test public void testUpdateCarByPOJO(){ // 准备数据 Car car = new Car(); car.setId(34L); car.setCarNum("102"); car.setBrand("比亚迪汉"); car.setGuidePrice(30.23); car.setProduceTime("2018-09-10"); car.setCarType("电车"); // 获取SqlSession对象 SqlSession sqlSession = SqlSessionUtil.openSession(); // 执行SQL语句 int count = sqlSession.update("updateCarByPOJO", car); System.out.println("更新了几条记录:" + count); }
运行结果:
当然了,如果使用map传数据也是可以的。
4 select(Retrieve)
select语句和其它语句不同的是:查询会有一个结果集。来看mybatis是怎么处理结果集的!!!
查询一条数据
需求:查询id为1的Car信息
SQL语句如下:
<select id="selectCarById"> select * from t_car where id = #{id} </select>
Java程序如下:
@Test public void testSelectCarById(){ // 获取SqlSession对象 SqlSession sqlSession = SqlSessionUtil.openSession(); // 执行SQL语句 Object car = sqlSession.selectOne("selectCarById", 1); System.out.println(car); }
运行结果如下:
### Error querying database. Cause: org.apache.ibatis.executor.ExecutorException:
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查询之后返回一个Java对象的话,至少你要告诉mybatis返回一个什么类型的Java对象,可以在<select>标签中添加resultType属性,用来指定查询要转换类型:
<select id="selectCarById" resultType="com.study.mybatis.pojo.Car"> select * from t_car where id = #{id} </select>
运行结果:
运行后之前的异常不再出现了,这说明添加了resultType属性之后,解决了之前的异常,可以看出resultType是不能省略的。
仔细观察控制台的日志信息,不难看出,结果查询出了一条。并且每个字段都查询到值了:Row: 1, 100, 宝马520Li, 41.00, 2022-09-01, 燃油车
但是奇怪的是返回的Car对象,只有id和brand两个属性有值,其它属性的值都是null,这是为什么呢?我们来观察一下查询结果列名和Car类的属性
名是否能一一对应:
查询结果集的列名:id, car_num, brand, guide_price, produce_time, car_type
Car类的属性名:id, carNum, brand, guidePrice, produceTime, carType
通过观察发现:只有id和brand是一致的,其他字段名和属性名对应不上,这是不是导致null
的原因呢?我们尝试在sql语句中使用as关键字来给查
询结果列名起别名试试:
<select id="selectCarById" resultType="com.study.mybatis.pojo.Car"> select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car where id = #{id} </select>
运行结果如下:
通过测试得知,如果当查询结果的字段名和java类的属性名对应不上的话,可以采用as关键字起别名,当然还有其它解决方案,我们后面再看。
查询多条数据
需求:查询所有的Car信息。
SQL语句如下:
<!--虽然结果是List集合,但是resultType属性需要指定的是List集合中元素的类型。--> <select id="selectCarAll" resultType="com.study.mybatis.pojo.Car"> <!--记得使用as起别名,让查询结果的字段名和java类的属性名对应上。--> select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car </select>
Java代码如下:
@Test public void testSelectCarAll(){ // 获取SqlSession对象 SqlSession sqlSession = SqlSessionUtil.openSession(); // 执行SQL语句 List<Object> cars = sqlSession.selectList("selectCarAll"); // 输出结果 cars.forEach(car -> System.out.println(car)); }
运行结果如下:
5 关于SQL Mapper的namespace
在SQL Mapper配置文件中<mapper>
标签的namespace
属性可以翻译为命名空间,这个命名空间主要是为了防止sqlId
冲突的。
创建CarMapper2.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="car2"> <select id="selectCarAll" resultType="com.study.mybatis.pojo.Car"> select id, car_num as carNum, brand, guide_price as guidePrice, produce_time as produceTime, car_type as carType from t_car </select> </mapper>
不难看出,CarMapper.xml
和CarMapper2.xml
文件中都有 id="selectCarAll"
将CarMapper2.xml
配置到mybatis-config.xml
文件中。
<mappers> <mapper resource="CarMapper.xml"/> <mapper resource="CarMapper2.xml"/> </mappers>
编写Java代码如下:
@Test public void testNamespace(){ // 获取SqlSession对象 SqlSession sqlSession = SqlSessionUtil.openSession(); // 执行SQL语句 List<Object> cars = sqlSession.selectList("selectCarAll"); // 输出结果 cars.forEach(car -> System.out.println(car)); }
运行结果如下:
org.apache.ibatis.exceptions.PersistenceException:
### Error querying database. Cause: java.lang.IllegalArgumentException:
selectCarAll is ambiguous in Mapped Statements collection (try using the full name including the namespace, or rename one of the entries)
【翻译】selectCarAll在Mapped Statements集合中不明确(请尝试使用包含名称空间的全名,或重命名其中一个条目)
【大致意思是】selectCarAll重名了,你要么在selectCarAll前添加一个名称空间,要有你改个其它名字。
Java代码修改如下:
@Test public void testNamespace(){ // 获取SqlSession对象 SqlSession sqlSession = SqlSessionUtil.openSession(); // 执行SQL语句 //List<Object> cars = sqlSession.selectList("car.selectCarAll"); List<Object> cars = sqlSession.selectList("car2.selectCarAll"); // 输出结果 cars.forEach(car -> System.out.println(car)); }
运行结果如下:
到此这篇关于MyBatis实现CRUD的示例代码的文章就介绍到这了,更多相关MyBatis实现CRUD内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!