这边的东西全部来自 https://itbaima.net/document
复杂查询
案例 假设有三张表 student teacher teach
结构如下
- Student 表
sid | sname | 其他学生相关列 |
---|---|---|
1 | 学生A | … |
2 | 学生B | … |
3 | 学生C | … |
… | … | … |
- Teach 表
sid | tid | 其他教学相关列 |
---|---|---|
1 | 101 | … |
2 | 102 | … |
3 | 103 | … |
… | … | … |
- Teacher 表
tid | tname | 其他教师相关列 |
---|---|---|
101 | 教师X | … |
102 | 教师Y | … |
103 | 教师Z | … |
… | … | … |
在这些表格中:
- Student 表 包含学生的唯一标识符(
sid
)、学生的姓名(sname
)以及其他可能的相关信息。 - Teach 表 连接学生和教师,包含学生的唯一标识符(
sid
)、教师的唯一标识符(tid
)以及其他可能的教学相关信息。 - Teacher 表 包含教师的唯一标识符(
tid
)、教师的姓名(tname
)以及其他可能的教师相关信息。
- Teacher Bean
@Data
public class Teacher {
int tid;
String name;
List<Student> studentList;
}
一对多查询
一个老师对应多个学生
- 需要使用
resultMap
和collection
来自定义映射规则:
<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
多对一查询
多个学生对应一个老师
- 需使用
resultMap
和association
来自定义映射规则: - 其实和多个学生多个老师差不多,把多个老师当成一个即可
- 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;
}
- 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的对比
- 原先通过xml的方式
- xml
<insert id="addStudent">
insert into student(name, sex) values(#{name}, #{sex})
</insert>
- mapper
int addStudent(Student student);
- 通过注解的改进版
- 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)
- 注解版
@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
部分根据参数 name
和 age
的存在与否动态变化。
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 语句的构建和维护。