文章目录
上篇文章 Mybatis系列(九)mappers的四种配置方式及源码解析,是对mybatis配置文件中的mappers节点的介绍,综合前几节的文章,都是对mybatis配置文件各个节点的介绍,今天我们开始看mybatis另外一个重要的配置文件– mapper映射文件。在mybatis开发中,映射器的开发量占了相当大的工作量,也是今后我们工作中的重中之重,这一节我们先来看映射器中的select元素——查询元素。
一、select元素的配置
在映射器中select元素代表SQL的select语句,用于查询,在SQL中,select语句也是用的最多的语句,关于select元素也有众多的配置。
元素 | 说明 | 备注 |
---|---|---|
id | 它和Mapper的命名空间组合起来是唯一的,供mybatis使用 | 如果命名空间和id结合起来不唯一,则抛出异常 |
parameterType | 可以给出类全限定名,也可以是别名,别名必须是内部定义或者自定义的 | 可以选择JavaBean、Map等简单的参数类型传递给SQL |
resultType | 定义类的全路径,在允许自动匹配的情况下,结果集将通过JavaBean的规范映射;或定义为int,double,float,map等参数;也可以使用别名,但要符合别名规范,且不能和resultMap同时使用 | 常用参数,返回值类型 |
resultMap | 它是映射集的引用,执行强大的映射功能,我们可以使用resultType和resultMap其中一个,resultMap能提供自定义的映射规则 | mybatis最复杂的元素,可以配置映射规则,级联,typeHandler等 |
flushCache | 调用SQL后,是否要求mybatis清空之前查询本地缓存和耳二级缓存 | 布尔类型,默认为false |
useCache | 启动二级缓存的开关 | 布尔类型,默认为true |
timeout | 设置超时时间参数,超时将抛出异常,单位为秒 | 默认值是数据库厂商提供的JDBC驱动设置的秒数 |
fetchSize | 获取记录的总条数 | 默认值是数据库厂商提供的JDBC驱动设置的条数 |
statementType | 告诉mybatis使用哪个JDBC的statement工作,可选 STATEMENT,PREPARED 或 CALLABLE | 默认PREPARED |
resultSetType | FORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动) | 默认值是数据库厂商提供的JDBC驱动所设置 |
databaseId | 如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略 | 提供多种数据库的支持 |
resultOrdered | 这个设置仅适用于嵌套结果select语句。如果是true,就是假设包含了嵌套结果或是分组了,当返回一个主结果时,就不能引用前面的结果集了,这就确保了在获取嵌套的结果集时不至于导致内存不够用 | 布尔类型,默认为false |
resultSets | 适合于多个结果的情况,它将列出执行SQL后每个结果集的名称,每个名称之间用逗号分隔 | 很少使用 |
在实际工作中用的最多的是id、parameterType,resultType、resultMap,如果要设置缓存,还会使用到flushCache、useCache,其他的都不是常用的功能。
先来个简单的demo试试水,看看这些个常用参数到底怎么常用的。
二、select查询小试牛刀
创建一个User对象
*/
@Data
public class User {
private Long id;
private String name;
private Integer age;
private String mobile;
private Date createTime;
}
数据库搞点数据
再来个mapper接口
public interface UserMapper {
User findUserByName(String name);
}
映射文件
<mapper namespace="com.sean.mapper.UserMapper">
<select id="findUserByName" parameterType="java.lang.String" resultType="com.sean.entity.User">
SELECT id,name,mobile,age,create_time FROM user WHERE name = #{name}
</select>
</mapper>
- id配合Mapper的全限定名,接口和xml的映射
- parameterType:传递的参数类型,参数为name指定String类型
- resultType:SQL执行后结果类型,这里返回User对象,全限定名,定义别名的话就不需要这么长了
一气呵成,执行一下:
咦?为什么createTime是空的,这里就要涉及到mybatis的自动映射和驼峰映射,javaBean对象属性要和表结构中的列名对应,createTime在表中是create_time,那就通过别名指定一下咯。
SELECT id,name,mobile,age,create_time as createTime FROM user WHERE name = #{name}
createTime打印出来了。
或者通过resultMap,自定义指定我们需要的参数,将数据库列名和java对象一一关联,这个后边再讲。
三、select小试牛刀之多参数传递
上边的列子是传递一个参数,如果我们传递多个参数怎么做呢?
- 可以使用map传递,这个可读性太差,平时也不用就跳过了。
- 使用mybatis提供的注解@Param
Mapper接口
User findUserByNameAndAge(@Param("name") String name,@Param("age")Integer age);
映射文件
<select id="findUserByNameAndAge" resultType="com.sean.entity.User">
SELECT id,name,mobile,create_time as createTime FROM user WHERE name = #{name} and age = #{age}
</select>
注意,此时不需要给出parameterType属性,mybatis自己去摸索就行了。
- 通过JavaBean传递多个参数
平时项目中,可能需要多个参数作为查询条件,那么一个方法写如此多参数,看着很不清爽,那我们就创建一个POJO类专门放我们需要查询的参数。
@Data
public class UserParam {
private String name;
private Integer age;
}
mapper接口
User findUserByNameAndAge(UserParam userParam);
测试一下:
UserParam param = new UserParam();
param.setAge(18);
param.setName("狗不理");
userMapper.findUserByNameAndAge(param);
mapper映射文件要不要改呢,其实改不改都可以,可以指定传入参数类型parameterType="com.sean.entity.UserParam"
,也可以不管,反正mybatis也挺聪明的,他懂我的意思。
四、resultMap元素属性
元素 | 说明 | 备注 |
---|---|---|
property | 映射到列结果的字段或者属性 | - |
column | 对应SQL的列 | - |
javaType | j配置ava的类型 | 可以指定特定的类全限定名或者mybatis上下文的别名 |
jdbcType | 配置数据库类型 | JDBC类型 |
typeHandler | 类型处理器 | mybatis默认处理器,也可自定义 |
基本的select操作掌握了,那我们下边就看看mybatis是如何实现一对一,一对多,多对多的。
五、select 深入小试
创建三个表:学生表,班级表,荣誉表
一个学生对应一个班级,一个班级对应多个学生,一个班级拥有多个荣誉,一个荣誉同时也可以被多个班级拥有。
Student 学生对象
@Data
public class Student {
private Long sid;
/**
* 学生证
*/
private String idCard;
/**
* 姓名
*/
private String name;
/**
* 班级
*/
private Grade grade;
}
Grade 班级对象
private Long gid;
/**
* 班级名字
*/
private String className;
/**
* 班级口号
*/
private String slogan;
/**
* 班级学生
*/
private List<Student> studentList;
/**
* 班级荣誉
*/
private List<Honor> honorList;
Honor 班级荣誉
@Data
public class Honor {
private Long hid;
private String honorName;
private List<Grade> gradeList;
}
表数据
SELECT * from t_student;
SELECT * from t_grade;
SELECT * from t_honor;
万事俱备,只欠一场景。
六、“帮我找到这个同学教育一番”(一对一)
这天校长在校园闲逛,一低头发现一张学生证,这个学生呢,比较调皮,他把学生证上的内容都给涂了涂,校长隐隐约约在上边看到了学生号"2020001",看来这是今天新来的新生啊,怪不得这么捣蛋,这可怎么找到他呢,校长找到教务处主任,说:“你去找到这个同学,把学生证给他,顺便把上边涂的东西给擦掉”。
好了教务处主任去找人了,只有"2020001"这么一串数字,一个学生号对应一个学生,那岂不很容易。脑子一闪想到了mybatis的一对一映射
studentMapper接口
public interface StudentMapper {
Student findStudentByIdCard(@Param("idCard") String idCard);
}
studentMapper.xml
<mapper namespace="com.sean.mapper.StudentMapper">
<resultMap id="studentMapper" type="student">
<id column="sid" jdbcType="BIGINT" property="sid" />
<result column="id_card" jdbcType="VARCHAR" property="idCard" />
<result column="name" jdbcType="VARCHAR" property="name" />
<association property="grade" javaType="com.sean.entity.Grade">
<id column="gid" jdbcType="BIGINT" property="gid"/>
<result column="class_name" jdbcType="VARCHAR" property="className" />
<result column="slogan" jdbcType="VARCHAR" property="slogan" />
</association>
</resultMap>
<select id="findStudentByIdCard" parameterType="java.lang.String" resultMap="studentMapper">
SELECT *
from t_student ts,t_grade tg
where ts.grade_id = tg.gid
and id_card = #{idCard}
</select>
</mapper>
然后一执行,真相就要大白了
@Test
public void findStudentByIdCard(){
SqlSession sqlSession = SessionUtil.getSession();
StudentMapper mapper = sqlSession.getMapper(StudentMapper.class);
Student student = mapper.findStudentByIdCard("2020001");
logger.debug(student);
}
哦,原来是一年级的"周杰伦"小朋友啊,教务处主任找到他,苦口婆心教导了一番,以后不要…
七:“给我找到口号最炸裂的那班同学”(一对多)
星期一早上,全校跑早操,这是无所事事的校长又在外边溜达,忽然听到一声相当有创意的口号"加油加油,我最棒",声音洪亮,激发斗志。校长立马召见教务处主任,你去看看这是谁喊的,把声音最尖的放学到我办公室。
教务处主任接到任务后,片刻不敢耽误,立刻行动了。
出早操,那么口号肯定是班级口号,我只要找到哪个班,然后挑出声音最"尖"的那个就行了,主任又想到了mybatis的collection
一对多关系
GradeMapper接口
public interface GradeMapper {
Grade findGradeBySlogan(String slogan);
}
grademapper.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.sean.mapper.GradeMapper">
<resultMap id="gradeMapper" type="grade">
<id column="gid" jdbcType="BIGINT" property="gid"/>
<result column="class_name" jdbcType="VARCHAR" property="className" />
<result column="slogan" jdbcType="VARCHAR" property="slogan" />
<collection property="studentList" javaType="list" ofType="com.sean.entity.Student">
<id column="sid" jdbcType="BIGINT" property="sid" />
<result column="id_card" jdbcType="VARCHAR" property="idCard" />
<result column="name" jdbcType="VARCHAR" property="name" />
</collection>
</resultMap>
<select id="findGradeBySlogan" parameterType="java.lang.String" resultMap="gradeMapper">
SELECT * from t_student ts,t_grade tg where ts.grade_id = tg.gid and slogan = #{slogan}
</select>
</mapper>
测试类
@Test
public void findGrade(){
SqlSession sqlSession = SessionUtil.getSession();
GradeMapper mapper = sqlSession.getMapper(GradeMapper.class);
Grade grade = mapper.findGradeBySlogan("加油加油,我最强");
for (Student stu : grade.getStudentList()){
logger.info(stu);
}
}
结果:
原来是这么励志的口号是五年级的啊,还好校长没说找姓周的,要不然…校长说什么声音最尖的,或许应该是个女孩子吧,那谁"周慧敏",放学去校长室。
校长见到她之后一顿夸奖,送给她一块糖,并颁发给她们班级,五年级一个“最佳卖力奖”。表彰他们班级口号内涵有营养,同学也积极(卖力)配合。
八、“获奖的同学到我办公室来一趟”(多对多)
学期结束了,校长想着给这学期表现最卖力的同学发个钱,教务处主任出来吧,你去统计一下。
主任想了一下,获得“最佳卖力奖”同学,不可能只有一个班级的同学获得,应该有多个班级的多个同学获得。那不就是一个奖项可能对应多个班级,一个班级也可能有多个奖项。多对多嘛,但是mybatis没有提供多对多的标签,这也难不倒我教务处主任吗,我用两个一对多,不就是多对多了吗,就好比你有一个苹果,我有一个苹果,我吃你一口,你吃我一口,不就吃了多个苹果吗,好像是这么一回事…
关联表
@Data
public class Grade_Honor {
private Grade grade;
private Honor honor;
}
关联数据,获奖的班级匹配
mapper接口
public interface HonorMapper {
Honor findHonorByType(@Param("honorName") String honorName);
}
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="com.sean.mapper.HonorMapper">
<resultMap id="honorMapper" type="honor">
<id column="id" jdbcType="BIGINT" property="id"/>
<result column="honor_name" jdbcType="VARCHAR" property="honorName" />
<collection property="gradeList" javaType="list" ofType="com.sean.entity.Grade">
<result column="class_name" jdbcType="VARCHAR" property="className" />
<collection property="studentList" javaType="list" ofType="com.sean.entity.Student">
<result column="name" jdbcType="VARCHAR" property="name" />
</collection>
</collection>
</resultMap>
<select id="findHonorByType" parameterType="string" resultMap="honorMapper">
SELECT th.honor_name, tg.class_name, ts.name
FROM t_honor th
LEFT JOIN t_grade_honor tgh ON tgh.honor_id = th.hid
LEFT JOIN t_grade tg ON tg.gid = tgh.grade_id
LEFT JOIN t_student ts ON ts.grade_id = tg.gid
WHERE th.honor_name = #{honorName}
</select>
</mapper>
测试代码
@Test
public void findHonor(){
SqlSession sqlSession = SessionUtil.getSession();
HonorMapper mapper = sqlSession.getMapper(HonorMapper.class);
Honor honor = mapper.findHonorByType("最佳卖力奖");
for(Grade grade : honor.getGradeList()){
logger.info("班级:"+grade.getClassName()+",学生信息:"+grade.getStudentList());
}
}
查询结果
五年级的三个“最佳卖力奖”我是知道的,口号喊得嘛,怎么多了个四年级的,是不是mybatis匹配错了,搞个特权查下库
select * from t_grade tg, t_honor th,t_grade_honor tgh where tg.gid = tgh.grade_id and th.hid=tgh.honor_id and th.honor_name='最佳卖力奖';
确实发给四年级了,多对多吗,可能校长大人看四年级的“周星星”,天天跟着“周星驰”混,多卖力啊。
关于select的讲解就结束了,因为在看映射的时候,有时候会搞混,于是采用这种场景是的配置和表达,希望大家能够理解并支持,并可提出宝贵意见。