Mybatis的复杂查询,通过注解映射和动态SQL

这边的东西全部来自 https://itbaima.net/document

复杂查询

案例 假设有三张表 student teacher teach
结构如下

  1. Student 表
sidsname其他学生相关列
1学生A
2学生B
3学生C
  1. Teach 表
sidtid其他教学相关列
1101
2102
3103
  1. Teacher 表
tidtname其他教师相关列
101教师X
102教师Y
103教师Z

在这些表格中:

  • Student 表 包含学生的唯一标识符(sid)、学生的姓名(sname)以及其他可能的相关信息。
  • Teach 表 连接学生和教师,包含学生的唯一标识符(sid)、教师的唯一标识符(tid)以及其他可能的教学相关信息。
  • Teacher 表 包含教师的唯一标识符(tid)、教师的姓名(tname)以及其他可能的教师相关信息。
  1. Teacher Bean
@Data
public class Teacher {
    int tid;
    String name;
    List<Student> studentList;
}

一对多查询

一个老师对应多个学生

  • 需要使用resultMapcollection来自定义映射规则:


<resultMap id="asTeacher" type="Teacher">
    <id column="tid" property="tid"/>
    <result column="tname" property="name"/>
    <collection property="studentList" ofType="Student">
        <id property="sid" column="sid"/>
        <result column="name" property="name"/>
        <result column="sex" property="sex"/>
    </collection>
</resultMap>
<select id="getTeacherByTid" resultMap="asTeacher">
select *, teacher.name as tname from student inner join teach on student.sid = teach.sid
                                             inner join teacher on teach.tid = teacher.tid where teach.tid = #{tid}
</select>
  • 通过使用collection来表示将得到的所有结果合并为一个集合,比如上面的数据中每个学生都有单独的一条记录,因此tid相同的全部学生的记录就可以最后合并为一个List,得到最终的映射结果.
  • 为了区分和复用 建议给resultMap起个id

多对一查询

多个学生对应一个老师

  • 需使用resultMapassociation来自定义映射规则:
  • 其实和多个学生多个老师差不多,把多个老师当成一个即可
  1. Bean的结构
@Data
@Accessors(chain = true)
public class Student {
    private int sid;
    private String name;
    private String sex;
    private Teacher teacher;
}

@Data
public class Teacher {
    int tid;
    String name;
}
  1. resultMap的配置
<resultMap id="test2" type="Student">
    <id column="sid" property="sid"/>
    <result column="name" property="name"/>
    <result column="sex" property="sex"/>
    <association property="teacher" javaType="Teacher">
        <id column="tid" property="tid"/>
        <result column="tname" property="name"/>
    </association>
</resultMap>
<select id="selectStudent" resultMap="test2">
    select *, teacher.name as tname from student left join teach on student.sid = teach.sid
                                                 left join teacher on teach.tid = teacher.tid
</select>

通过使用association进行关联,形成多对一的关系,实际上和一对多是同理的,都是对查询结果的一种处理方式罢了。

使用注解开发

还是上面那三张表

注解和xml的对比

  1. 原先通过xml的方式
  • xml
<insert id="addStudent">
    insert into student(name, sex) values(#{name}, #{sex})
</insert>
  • mapper
int addStudent(Student student);
  1. 通过注解的改进版
  • mapper
@Insert("insert into student(name, sex) values(#{name}, #{sex})")
int addStudent(Student student);

Mapper 扫描路径

  • mybatis-config.xml
    需要修改扫描路径,其实混合着也行
<mappers>
    <mapper class="com.test.mapper.MyMapper"/>
    //这种是扫描注解的一个类,不配置mapper.xml那种
    <package name="com.test.mapper"/> 
    //直接扫描目标包下所有mapper, 包下面加不加@Mapper注解随意
</mappers>

注解使用@Results(以前xml的resultMap)

  1. 注解版
@Results({ //这里可以加一个id参数 用于后面的@ResultMap
        @Result(id = true, column = "sid", property = "sid"),
        @Result(column = "sex", property = "name"),
        @Result(column = "name", property = "sex")
})
@Select("select * from student")
List<Student> getAllStudent();

直接通过@Results注解,就可以直接进行配置了,此注解的value是一个@Result注解数组,每个@Result注解都都一个单独的字段配置,其实就是我们之前在XML映射器中写的:
2. xml版

<resultMap id="test" type="Student">
    <id property="sid" column="sid"/>
    <result column="name" property="sex"/>    
  	<result column="sex" property="name"/>
</resultMap>
<select resultMap="test">
    select * from student
</select>

注解@Result使用复杂查询+拆分SQL

一对多查询 @Many+@Result

@Results({
        @Result(id = true, column = "tid", property = "tid"),
        @Result(column = "name", property = "name"),
        @Result(column = "tid", property = "studentList", many =
            @Many(select = "getStudentByTid")
        )
})
@Select("select * from teacher where tid = #{tid}")
Teacher getTeacherBySid(int tid);  //1
//@Many 关联的方法
@Select("select * from student inner join teach on student.sid = teach.sid where tid = #{tid}")
List<Student> getStudentByTid(int tid); //2
  • 通过@Many子注解来实现一对多的关系表示,类似于之前的collection标签:
  • @Many后面写着要执行的子方法名
  • 将SQL 拆分成两个sql 先执行2 再将结果给 1 解耦
<resultMap id="asTeacher" type="Teacher">
    <id column="tid" property="tid"/>
    <result column="tname" property="name"/>
    <collection property="studentList" ofType="Student">
        <id property="sid" column="sid"/>
        <result column="name" property="name"/>
        <result column="sex" property="sex"/>
    </collection>
</resultMap>

多对一查询 @One+@Result

同理,@Result也提供了@One子注解来实现一对一的关系表示,类似于之前的assocation标签:

@Results({
        @Result(id = true, column = "sid", property = "sid"),
        @Result(column = "sex", property = "name"),
        @Result(column = "name", property = "sex"),
        @Result(column = "sid", property = "teacher", one =
            @One(select = "getTeacherBySid")
        )
})
@Select("select * from student")
List<Student> getAllStudent();
// @One 关联的方法
@Select("SELECT * FROM teacher WHERE sid = #{sid}")
Teacher getTeacherBySid(int sid);

@results的复用 @ResultMap

  • 如果希望复用之前定义好的Results(比如bean field和column不匹配,我们需要继续用这个映射关系)
  • 通过Results的id属性+@ResultMap注解来实现:
@Results(id = "studentMap", value = {
        @Result(id = true, column = "sid", property = "sid"),
        @Result(column = "sex", property = "sex"),
        @Result(column = "name", property = "name")
})
@Select("select * from student")
List<Student> getAllStudents();
// 定义id 即可在其他地方复用
@Select("select * from student where sid = #{sid}")
@ResultMap("studentMap")
Student getStudentById(int sid);

提供了@ResultMap注解,直接指定ID即可,这样我们就可以使用XML中编写的映射规则了,这里就不再演示了。

多个构造器的冲突 @ConstructorArgs

@Data
@Accessors(chain = true)
public class Student {

    public Student(int sid){
        System.out.println("我是一号构造方法"+sid);
    }

    public Student(int sid, String name){
        System.out.println("我是二号构造方法"+sid+name);
    }

    private int sid;
    private String name;
    private String sex;
}

我们可以通过@ConstructorArgs注解来指定构造方法:

@ConstructorArgs({
        @Arg(column = "sid", javaType = int.class),
        @Arg(column = "name", javaType = String.class)
})
@Select("select * from student where sid = #{sid} and sex = #{sex}")
Student getStudentBySidAndSex(@Param("sid") int sid, @Param("sex") String sex);

得到的结果和使用constructor标签效果一致,这里就不多做讲解了。

@Param 处理两个参数以上的查询

如果全是基本数据类型

参数两个以上的时候用@Param 指定参数,没有@Param报错

@Select("select * from student where sid = #{sid} and sex = #{sex}")
Student getStudentBySidAndSex(@Param("sid") int sid, @Param("sex") String sex);

如果一个基本类型一个对象类型

通过参数名称.属性的方式去让Mybatis知道我们要用的是哪个属性:

@Insert("insert into student(sid, name, sex) values(#{sid}, #{student.name}, #{student.sex})")
int addStudent(@Param("sid")int sid, @Param("student")Student student);

动态SQL(了解)

使用 MyBatis 动态 SQL 的代码示例:

1. 使用 <if> 条件

<select id="selectUser" parameterType="map" resultType="User">
  SELECT * FROM user
  <where>
    <if test="name != null">
      AND name = #{name}
    </if>
    <if test="age != null">
      AND age = #{age}
    </if>
  </where>
</select>

在这个例子中,SQL 查询的 WHERE 部分根据参数 nameage 的存在与否动态变化。

2. 使用 <choose>, <when>, 和 <otherwise>

<select id="findActiveUser" parameterType="User" resultType="User">
  SELECT * FROM user
  <where>
    <choose>
      <when test="name != null">
        name = #{name}
      </when>
      <when test="email != null">
        email = #{email}
      </when>
      <otherwise>
        active = true
      </otherwise>
    </choose>
  </where>
</select>

这个例子展示了如何根据不同的条件选择不同的SQL片段。

3. 使用 <foreach> 遍历集合

<select id="selectUsersById" parameterType="map" resultType="User">
  SELECT * FROM user
  WHERE id IN
  <foreach item="id" collection="ids" open="(" separator="," close=")">
    #{id}
  </foreach>
</select>

在这个例子中,<foreach> 用于构造一个 IN 子句,它将迭代一个 ID 集合并为每个 ID 创建一个查询参数。

4. 定义和使用 SQL 片段

<sql id="userColumns">
  id, name, age, email
</sql>

<select id="selectUser" resultType="User">
  SELECT
    <include refid="userColumns"/>
  FROM user
  WHERE id = #{id}
</select>

这里,<sql> 元素定义了一个可重用的列列表,然后在 SELECT 语句中使用 <include> 来引用它。

通过这些示例,你可以看到 MyBatis 动态 SQL 的多样性和灵活性,这极大地简化了 SQL 语句的构建和维护。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值