mapper.xml映射文件中定义了操作数据库的SQL语句,每个SQL是一个statement,映射文件是MyBatis的核心。
1、传入参数到sql语句中的处理
parameterType输入参数映射:指定要传递什么类型的参数给SQL语句,之后从输入对象中 取值 设置 在SQL语句中。(输入参数的类型)
模块名:mybatis-006-param
表:t_student
表中现有数据:
pojo类:
Student.java
package com.powernode.mybatis.pojo;
import java.util.Date;
/**
* 学生类
*/
public class Student {
private Long id;
private String name;
private Integer age;
private Double height;
private Character sex;
private Date birth;
// constructor
// setter and getter
// toString
}
1.1 传入一个简单类型的参数
-
当Mapper接口中方法的参数只有一个,且传入一个简单类型的参数到SQL语句中时
-
结论:#{} 里面随便写内容就可以传值。对于 ${} 来说,注意加单引号。
-
简单类型有哪些:
-
byte short int long float double char(Java的几本数据类型)
-
Byte Short Integer Long Float Double Character(包装类)
-
String
-
java.util.Date
-
java.sql.Date
-
需求:根据name查、根据id查、根据birth查、根据sex查
Mapper接口:
StudentMapper.java
package org.example.mapper;
import org.example.pojo.Student;
import java.util.List;
public interface StudentMapper {
/**
* 当接口中的方法的参数只有一个,且参数的数据类型都是简单类型
* 根据id查询,根据name查询,根据birth查询,根据sex查询
*/
List<Student> selectById(Long id);
List<Student> selectByName(String name);
List<Student> selectByBirth(String birth);
List<Student> selectBySex(Character sex);
}
Mapper映射文件:
StudentMapper.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="org.example.mapper.StudentMapper">
<select id="selectById" resultType="Student">
select * from t_student where id = #{id}
</select>
<select id="selectByName" resultType="Student" parameterType="String">
select * from t_student where name = #{name}
</select>
<select id="selectByBirth" resultType="Student">
select * from t_student where birth = #{birth}
</select>
<select id="selectBySex" resultType="Student">
select * from t_student where sex = #{sex}
</select>
</mapper>
测试:
StudentMapperTest
package org.example.test;
import org.apache.ibatis.session.SqlSession;
import org.example.mapper.StudentMapper;
import org.example.pojo.Student;
import org.example.utils.SqlSessionUtil;
import org.junit.Test;
import java.util.List;
public class TestStudentMapper {
@Test
public void testSelectById(){
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.selectById(1L);
students.forEach(student -> System.out.println(student));
sqlSession.close();
}
@Test
public void testSelectByName(){
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.selectByName("张三");
students.forEach(student -> System.out.println(student));
sqlSession.close();
}
@Test
public void testSelectBySex(){
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.selectBySex('女');
students.forEach(student -> System.out.println(student));
sqlSession.close();
}
@Test
public void testSelectByBirth(){
try {
Date birth = new SimpleDateFormat("yyyy-MM-dd").parse("2022-08-16");
List<Student> students = mapper.selectByBirth(birth);
students.forEach(student -> System.out.println(student));
} catch (ParseException e) {
throw new RuntimeException(e);
}
}
}
-
其中sql语句中的#{}中的javaType,jdbcType,以及select标签中的parameterType属性,都是用来帮助mybatis确定传过来的参数是什么类型的。不过这些配置多数是可以省略的。因为mybatis它有强大的自动类型推断机制。
-
javaType:可以省略
-
jdbcType:可以省略
-
parameterType:可以省略
-
1.2 实体类或自定义类型
传入单个pojo对象或自定义类型对象给SQL映射文件 。
处理:在#{} 中填入java对象的属性名(成员变量名),取出其属性值设置给SQL语句。这个属性名其本质上是:set/get方法名去掉set/get之后的名字。
需求:插入一条Student数据
StudentMapper接口:
/**
* 保存学生数据
* @param student
* @return
*/
int insert(Student student);
StudentMapper.xml:
<insert id="insert">
insert into t_student values(null,#{name},#{age},#{height},#{birth},#{sex})
</insert>
StudentMapperTest.testInsert:
@Test
public void testInsert(){
Student student = new Student();
student.setName("李四");
student.setAge(30);
student.setHeight(1.70);
student.setSex('男');
student.setBirth(new Date());
int count = mapper.insert(student);
SqlSessionUtil.openSession().commit();
}
运行正常,数据库中成功添加一条数据。
-
其中sql语句中的#{}中的javaType,jdbcType,以及select标签中的parameterType属性,都是用来帮助mybatis进行传过来的参数的类型确定的。不过这些配置多数是可以省略的。因为mybatis它有强大的自动类型推断机制。
-
javaType:可以省略
-
jdbcType:可以省略
-
parameterType:可以省略
-
1.3 Map类型
如果一条数据是用Map集合封装的。
处理:在#{}中用map集合的key取出对应的value设置给SQL语句。
需求:根据name和age查询
StudentMapper接口
/**
* 根据name和age查询
* @param paramMap
* @return
*/
List<Student> selectByParamMap(Map<String,Object> paramMap);
StudentMapperTest.testSelectByParamMap
@Test
public void testSelectByParamMap(){
// 准备Map
Map<String,Object> paramMap = new HashMap<>();
paramMap.put("nameKey", "张三");
paramMap.put("ageKey", 20);
List<Student> students = mapper.selectByParamMap(paramMap);
students.forEach(student -> System.out.println(student));
}
StudentMapper.xml
<select id="selectByParamMap" resultType="student">
select * from t_student where name = #{nameKey} and age = #{ageKey}
</select>
测试运行正常。
-
其中sql语句中的#{}中的javaType,jdbcType,以及select标签中的parameterType属性,都是用来帮助mybatis进行传过来的参数的类型确定的。不过这些配置多数是可以省略的。因为mybatis它有强大的自动类型推断机制。
-
javaType:可以省略
-
jdbcType:可以省略
-
parameterType:可以省略
-
1.4 传入多个参数
MyBatis中允许有多个输入参数,可以让Mapper接口中的方法传多个输入参数过去给sql语句。
本质上是使用Map集合来封装数据,这个Map集合是MyBatis自动创建的。
1.4.1 不使用@Param注解
-
实现原理:mybatis底层会自动创建一个Map集合来存储这些参数,这个Map集合以arg0/param1作为key,以方法上的参数作为value存储在这个Map集合中
-
取值的时候用这个Map集合的key就可以取出对应的value
-
mybatis部分源码 Map<String,Object> map = new HashMap<>(); map.put("arg0", name); map.put("arg1", sex); map.put("param1", name); map.put("param2", sex); // 所以可以这样取值:#{arg0} #{arg1} #{param1} #{param2} // 其本质就是#{map集合的key}
需求:通过name和sex查询
StudentMapper接口
/**
* 根据name和sex查询
* @param name
* @param sex
* @return
*/
List<Student> selectByNameAndSex(String name, Character sex);
StudentMapperTest.testSelectByNameAndSex
@Test
public void testSelectByNameAndSex(){
List<Student> students = mapper.selectByNameAndSex("张三", '女');
students.forEach(student -> System.out.println(student));
}
StudentMapper.xml
<select id="selectByNameAndSex" resultType="student">
select * from t_student where name = #{name} and sex = #{sex}
</select>
执行结果:
异常信息描述了:name参数找不到,可用的参数包括[arg1, arg0, param1, param2]
修改StudentMapper.xml配置文件:尝试使用[arg1, arg0, param1, param2]取参数
StudentMapper.xml
<select id="selectByNameAndSex" resultType="student">
<!--select * from t_student where name = #{name} and sex = #{sex}-->
select * from t_student where name = #{arg0} and sex = #{arg1}
</select>
运行结果:
再次尝试修改StudentMapper.xml文件
StudentMapper.xml
<select id="selectByNameAndSex" resultType="student">
<!--select * from t_student where name = #{name} and sex = #{sex}-->
<!--select * from t_student where name = #{arg0} and sex = #{arg1}-->
<!--select * from t_student where name = #{param1} and sex = #{param2}-->
select * from t_student where name = #{arg0} and sex = #{param2}
</select>
通过测试可以看到:
-
arg0 是第一个参数
-
param1是第一个参数
-
arg1 是第二个参数
-
param2是第二个参数
注意:使用mybatis3.4.2之前的版本时:要用#{0}和#{1}这种形式。
1.4.2 使用@Param注解
使用@Param注解给要传入的多个参数命名。
-
实现原理:mybatis底层会自动创建一个Map集合来存储这些参数,这个Map集合以@Param注解的value属性值为Map的key,以方法上的参数作为value存储在这个Map集合中。
-
取值的时候用这个Map集合的key就可以取出对应的value
在映射文件中通过key获取对应的value,并且parameterType可以不指定类型。
-
注意:使用@Param()注解之后,arg0、arg1系列失效了,但param1和param2还可以用
需求:根据name和age查询
StudentMapper接口
/**
* 根据name和age查询
* @param name
* @param age
* @return
*/
List<Student> selectByNameAndAge(@Param(value="name") String name, @Param("age") int age);
List<Student> selectByNameAndSex(@Param("name") String name,@Param("sex") Character sex);
StudentMapper.xml
<select id="selectByNameAndAge" resultType="Student">
select * from t_student where name = #{name} and age = #{age}
</select>
StudentMapperTest.java
@Test
public void testSelectByNameAndSex(){
SqlSession sqlSession = SqlSessionUtil.openSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
List<Student> students = mapper.selectByNameAndSex("张三",'男');
students.forEach(student -> System.out.println(student));
sqlSession.close();
}
通过测试,一切正常。
2、resultType输出参数映射
resultType:指定将SQL语句的查询结果集的一条记录 映射成 指定类型的Java对象,并返回。(输出结果的类型)
模块名:mybatis-007-select
打包方式:jar
引入依赖:mysql驱动依赖、mybatis依赖、logback依赖、junit依赖。
引入配置文件:jdbc.properties、mybatis-config.xml、logback.xml
创建pojo类:Car
创建Mapper接口:CarMapper
创建Mapper接口对应的SQL映射文件:com/powernode/mybatis/mapper/CarMapper.xml
创建单元测试:CarMapperTest
拷贝工具类:SqlSessionUtil
2.1 返回简单类型
如果查询结果集只有一条数据并且是单个字段,那么resultType指定为简单类型,例如:根据id查询姓名,查询员工的总数量。
mapper接口:
public interface PersonMapper {
/**
* 根据id查询姓名
*/
public String selectById(Integer id);
/**
* 查询人员的总数量
*/
public Integer selectCount();
}
mapper文件:
<?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="org.example.mapper.PersonMapper">
<!--简单类型-->
<!--public String selectById(Integer id);-->
<select id="selectById" parameterType="int" resultType="string">
SELECT person_name FROM person WHERE id = #{id}
</select>
<!-- public Integer selectCount(); -->
<select id="selectCount" resultType="int">
SELECT count(1) FROM person
</select>
</mapper>
测试:
public class ResultTypeTest {
/**
* 测试resultType输出参数是简单类型的
*/
@Test
public void testSimpleParam(){
SqlSession sqlSession = MybatisUtil.getSession();
//获取Mapper接口的代理实现类对象
PersonMapper personMapper = sqlSession.getMapper(PersonMapper.class);
String name = personMapper.selectById(3);
System.out.println(name);
Integer count = personMapper.selectCount();
System.out.println("总数=" + count);
sqlSession.close();
}
}
2.2 返回一个pojo类对象
当查询的结果,有对应的实体类,并且查询结果只有一条时,resultType指定为:pojo对象的类型。
-
查询结果集的字段名和pojo类对象的属性名要一致,这样查询结果集中的结果才能赋值给pojo对象的属性(成员变量)
CarMapper.java
package com.powernode.mybatis.mapper;
import com.powernode.mybatis.pojo.Car;
public interface CarMapper {
/**
* 通过id主键进行查询:结果最多只有一条
* @param id
* @return
*/
Car selectById(Long id);
}
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">
<mapper namespace="com.powernode.mybatis.mapper.CarMapper">
<select id="selectById" resultType="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>
</select>
</mapper>
CarMapperTest.java
package com.powernode.mybatis.test;
import com.powernode.mybatis.mapper.CarMapper;
import com.powernode.mybatis.pojo.Car;
import com.powernode.mybatis.utils.SqlSessionUtil;
import org.junit.Test;
public class CarMapperTest {
@Test
public void testSelectById(){
CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
Car car = mapper.selectById(35L);
System.out.println(car);
sqlSession.close();
}
}
执行结果:
2.3 返回List<T>集合
-
如果查询记录是多条时,则使用实体类的集合来接收,将返回的多条记录用集合来存储,resultType指定为:集合中存储的元素的类型。
-
如果使用单个实体类接收会出现异常,TooManyResultsException异常:你期望的结果是返回一条记录,但实际的SQL语句在执行的时候,返回的记录条数是多条。
CarMapper.java
/**
* 查询所有的Car
* @return
*/
List<Car> selectAll();
CarMapper.xml
<select id="selectAll" resultType="Car">
select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car
</select>
CarMapperTest.testSelectAll
@Test
public void testSelectAll(){
CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
List<Car> cars = mapper.selectAll();
cars.forEach(car -> System.out.println(car));
}
2.4 返回一个Map集合
- 如果查询结果集和实体类的属性不一致时,可以使用Map集合来接收,resultType指定为:Map集合,比如:查询员工姓名,薪资,部门名称(两张表的查询),每条记录的字段名为Map集合的key,字段值为Map集合的value。
-
查询结果如果只有一条数据,则返回一个Map集合即可。
CarMapper.java
/**
* 通过id查询一条记录,将记录放到Map集合
* @param id
* @return
*/
Map<String, Object> selectByIdRetMap(Long id);
CarMapper.xml
<select id="selectByIdRetMap" resultType="map">
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>
CarMapperTest.java
@Test
public void testSelectByIdRetMap(){
CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
Map<String,Object> car = mapper.selectByIdRetMap(35L);
System.out.println(car);
}
执行结果:
2.5 返回List<Map>
-
如果返回的不是一条记录,是多条记录,只采用一个Map集合接收,这样会出现之前的异常:TooManyResultsException
-
查询结果集有多条记录时,则需要返回一个存储Map集合的List集合。List<Map>等同于List<Car>
-
resultType属性指定为:Map类型
CarMapper接口
/**
* 查询所有的Car,返回一个List集合。List集合中存储的是Map集合。
* @return
*/
List<Map<String,Object>> selectAllRetListMap();
CarMapper.xml
<select id="selectAllRetListMap" resultType="map">
select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car
</select>
CarMapperTest.java
@Test
public void testSelectAllRetListMap(){
CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
List<Map<String,Object>> cars = mapper.selectAllRetListMap();
System.out.println(cars);
}
执行结果:
2.5 返回Map<String,Map>
-
选择小Map集合的一个key作为大Map集合的key。用Car的id做key,以后取出对应的Map集合时更方便。
-
通过在Mapper接口的方法上加@MapKey("id")注解,指定大Map集合的key为小Map集合的哪个key
-
resultType属性指定为:Map类型。
CarMapper接口
/**
* 获取所有的Car,返回一个Map集合。
* Map集合的key是Car的id。
* Map集合的value是对应Car。
* @return
*/
@MapKey("id")
Map<Long,Map<String,Object>> selectAllRetMap();
CarMapper.xml
<select id="selectAllRetMap" resultType="map">
select id,car_num carNum,brand,guide_price guidePrice,produce_time produceTime,car_type carType from t_car
</select>
CarMapperTest.testSelectAllRetMap
@Test
public void testSelectAllRetMap(){
CarMapper mapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
Map<Long,Map<String,Object>> cars = mapper.selectAllRetMap();
System.out.println(cars);
}
执行结果:
{
64={carType=燃油车, carNum=133, guidePrice=50.30, produceTime=2020-01-10, id=64, brand=丰田霸道},
66={carType=燃油车, carNum=133, guidePrice=50.30, produceTime=2020-01-10, id=66, brand=丰田霸道},
67={carType=燃油车, carNum=133, guidePrice=50.30, produceTime=2020-01-10, id=67, brand=丰田霸道},
69={carType=燃油车, carNum=133, guidePrice=50.30, produceTime=2020-01-10, id=69, brand=丰田霸道},
......
}
2.6 resultMap手动结果映射
首先来看看Mybatis在实现resultType自动结果集映射的大概原理:
结果映射?:把查询结果集的列映射成(赋值给)java对象的属性。
-
resultType属性:可以将查询结果集映射为指定的实体类,
-
注意:但需要实体类的属性名和SQL查询结果的列名一样,才能映射到实体类中。
-
如果结果集的列名和实体类的字段名不一致,那么会赋值失败,成功进行结果映射的有三种方法:
-
第一种方式:在SQL语句中使用 as关键字 给列起别名(通过SQL解决)
-
实现结果集的列名和实体类的属性名一致
-
-
第二种方式:在SQL映射文件中使用resultMap标签进行结果映射,这个标签就在mapper根标签的下一级(手动映射)
-
第三种方式:在mybatis核心配置文件中开启驼峰命名自动映射(配置settings)
-
遵循命名规范,再开启驼峰命名自动映射,就可以实现映射
-
2.6.1 使用resultMap标签进行结果映射
在映射文件中,使用resultMap标签来定义一个结果集映射,手动进行结果映射。
应用:
- resultMap手动结果集映射可以实现将查询结果映射为复合型的实体类,比如在查询结果映射对象中包括实体类或List集合作为属性,实现一对一查询或一对多查询。 否则用resultType的话需要创建一个vo类,因为实体类属性和结果集不对应。
CarMapper接口
package org.example.mapper;
import org.example.pojo.Car;
import java.util.List;
public interface CarMapper {
/**
* 查询所有的Car信息,使用resultMap标签进行结果映射
* @return
*/
List<Car> selectAllByResultMap();
}
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">
<mapper namespace="org.example.mapper.CarMapper">
<!--
用resultMap标签:定义一个resultMap结果映射,在这个标签中:通过一系列的标签,指定数据库表的字段 如何映射成 java类的属性
id属性:resultMap的唯一标识,这个唯一标识将来在select标签中的resultMap属性中使用
type属性:查询结果集要映射成的java类(全限定类名),可以用类的别名
-->
<resultMap id="carResultMap" type="Car">
<!-- id单标签用来配置主键的映射,其余用result单标签来配置映射。官方解释是:为了提高mybatis的性能。建议写上。-->
<!-- property属性:pojo类的属性名;column属性:数据库表的字段名-->
<id property="id" column="id"/>
<result property="carNum" column="car_num"/>
<!-- 如果数据库表中字段名和java类的属性名是一样的话,则可以省略此配置-->
<!-- <result property="brand" column="brand"></result>-->
<result property="guidePrice" column="guide_price" />
<result property="produceTime" column="produce_time" />
<result property="carType" column="car_type" />
</resultMap>
<!-- select标签中通过resultMap属性来指定你要使用的结果映射是哪个(通过resultMap的id来指定)-->
<select id="selectAllByResultMap" resultMap="carResultMap">
select * from t_Car
</select>
</mapper>
CarMapperTest.java
package org.example;
import org.apache.ibatis.session.SqlSession;
import org.example.mapper.CarMapper;
import org.example.pojo.Car;
import org.example.utils.SqlSessionUtil;
import org.junit.Test;
import java.util.List;
public class TestCarMapper {
@Test
public void testselectAllByResultMap(){
SqlSession sqlSession = SqlSessionUtil.openSession();
CarMapper mapper = sqlSession.getMapper(CarMapper.class);
List<Car> cars = mapper.selectAllByResultMap();
cars.forEach(car -> System.out.println(car));
sqlSession.close();
}
}
2.7 返回总记录条数
CarMapper接口
/**
* 获取总记录条数
* @return
*/
Long selectTotal();
CarMapper.xml
<!--long是别名,可参考mybatis开发手册。-->
<select id="selectTotal" resultType="long">
select count(*) from t_car
</select>
CarMapperTest.java
@Test
public void testSelectTotal(){
CarMapper carMapper = SqlSessionUtil.openSession().getMapper(CarMapper.class);
Long total = carMapper.selectTotal();
System.out.println(total);
}