Mybatis系列(十)mapper映射文件之select元素(一对一,一对多,多对多)


上篇文章 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
resultSetTypeFORWARD_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对象,全限定名,定义别名的话就不需要这么长了

一气呵成,执行一下:

>  [UserMapperTest]-User(id=2, name=小笨蛋, age=20, mobile=120, createTime=null)
咦?为什么createTime是空的,这里就要涉及到mybatis的自动映射和驼峰映射,javaBean对象属性要和表结构中的列名对应,createTime在表中是create_time,那就通过别名指定一下咯。

SELECT id,name,mobile,age,create_time as createTime  FROM user WHERE name = #{name}

createTime打印出来了。
在这里插入图片描述
或者通过resultMap,自定义指定我们需要的参数,将数据库列名和java对象一一关联,这个后边再讲。

三、select小试牛刀之多参数传递

上边的列子是传递一个参数,如果我们传递多个参数怎么做呢?

  1. 可以使用map传递,这个可读性太差,平时也不用就跳过了。
  2. 使用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自己去摸索就行了。

  1. 通过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的列-
javaTypej配置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的讲解就结束了,因为在看映射的时候,有时候会搞混,于是采用这种场景是的配置和表达,希望大家能够理解并支持,并可提出宝贵意见。

  • 7
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 4
    评论
MyBatis一对多映射可以通过使用嵌套查询或嵌套结果映射来实现。下面是两种方法的详细说明: 1. 嵌套查询 在一对多关系中,通常是一方有多个子项,我们可以使用嵌套查询来实现这种映射。例如,我们有两个表:`order`和`order_item`,`order`表有一个`id`字段,`order_item`表有一个`order_id`字段,用于将订单与订单项关联起来。 我们可以使用以下Mapper XML来查询一个订单及其所有订单项: ```xml <select id="getOrderWithItems" resultMap="orderMap"> SELECT * FROM order WHERE id = #{id} </select> <resultMap id="orderMap" type="Order"> <id column="id" property="id"/> <result column="order_no" property="orderNo"/> <result column="create_time" property="createTime"/> <collection property="orderItems" ofType="OrderItem"> <select id="getOrderItemsByOrderId" parameterType="int" resultMap="itemMap"> SELECT * FROM order_item WHERE order_id = #{id} </select> </collection> </resultMap> <resultMap id="itemMap" type="OrderItem"> <id column="id" property="id"/> <result column="product_id" property="productId"/> <result column="product_name" property="productName"/> <result column="price" property="price"/> <result column="quantity" property="quantity"/> </resultMap> ``` 在上面的代码中,`getOrderWithItems`方法将返回一个包含订单及其所有订单项的完整对象图。在`resultMap`元素中,我们定义了一个`collection`元素,并将其`property`属性设置为`orderItems`,这是`Order`类中的一个`List<OrderItem>`属性。`ofType`属性指定了集合中存储的对象类型,这里是`OrderItem`。`select`元素中的SQL语句将查询该订单的所有订单项,并将它们映射到`OrderItem`对象中。这个查询将在MyBatis执行`getOrderWithItems`方法时自动执行。 2. 嵌套结果映射 除了嵌套查询之外,我们还可以使用嵌套结果映射来实现一对多映射。 ```xml <select id="getOrderWithItems" resultMap="orderMap"> SELECT * FROM order WHERE id = #{id} </select> <resultMap id="orderMap" type="Order"> <id column="id" property="id"/> <result column="order_no" property="orderNo"/> <result column="create_time" property="createTime"/> <collection property="orderItems" ofType="OrderItem" resultMap="itemMap"/> </resultMap> <resultMap id="itemMap" type="OrderItem"> <id column="id" property="id"/> <result column="product_id" property="productId"/> <result column="product_name" property="productName"/> <result column="price" property="price"/> <result column="quantity" property="quantity"/> <result column="order_id" property="orderId"/> </resultMap> ``` 在上面的代码中,我们在`resultMap`元素中定义了一个`collection`元素,它将订单的所有订单项映射到`Order`对象的`orderItems`属性中。与嵌套查询不同的是,我们使用了一个名为`itemMap`的嵌套结果映射映射每个订单项。这个映射定义了`OrderItem`类中的所有属性,并将`order_id`列映射到`orderId`属性上。 在使用嵌套结果映射时,MyBatis会将每个嵌套结果映射作为单独的查询执行。这意味着如果我们查询10个订单,就会执行11个查询(一个查询用于获取订单,另外10个查询用于获取每个订单的订单项)。因此,在使用嵌套结果映射时需要注意性能问题。
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值