目录
1.前言
2.准备工作
2.1 类型别名
2.2 列名和属性名
2.3 mybatis映射文件处理特殊字符
2.4 sql片段
2.5 mybatis完成模糊查询
3.结果映射(resultMap)
4. 联表查询
4.1 一对一关联association
4.2 一对多关联collection
5.鉴别器
1.前言
resultMap元素是 MyBatis 中最重要最强大的元素。MyBatis 创建时的一个思想是:数据库不可能永远是你所想或所需的那个样子。 我们希望每个数据库都具备良好的第三范式或 BCNF 范式,可惜它们并不都是那样。 如果能有一种数据库映射模式,完美适配所有的应用程序,那就太好了,但可惜也没有。 而 ResultMap 就是 MyBatis 对这个问题的答案。
2.准备工作
2.1 类型别名
代码中会出现大量重复,为了节省时间及空间,类型别名是好帮手。使用它们,就可以不用输入类的全限定名了。在mybatis的全局变量文件config.xml中填写。
<typeAliases>
<!--单独为某个实体类起别名, alias别名,下边使用这个别名-->
<typeAlias type="com.qy151wd.entity.User" alias="u"/>
<!--为指定包下的实体类起别名该别名就是实体类名-->
<package name="com.qy151wd.entity"/>
</typeAliases>
2.2 列名和属性名
可以有两种解决办法:
第一种: 为查询的列起别名,而别名和属性名一致。
<!--根据id查询学生信息-->
<select id="findOne" resultType="com.qy151wd.entity.Student">
select stu_id id,stu_name name,stu_age age from tb_stu where stu_id=#{id}
</select>
第二种: 使用resultMap完成列和属性之间的映射关系。
<resultMap id="StuMapper" type="com.qy151wd.entity.Student">
<!--主键的映射关系 column:列名(数据库列名) property:属性名(与实体类类名一致)-->
<id column="stu_id" property="id"/>
<!--普通列的映射关系-->
<result column="stu_name" property="name"/>
<result column="stu_age" property="age"/>
</resultMap>
<!--resultType和ResultMap二者只能用一个-->
<select id="findOne" resultMap="StuMapper">
select * from tb_stu where stu_id=#{id}
</select>
2.3 mybatis映射文件处理特殊字符
共有两种方法,建议使用第二种:
第一种:转义标签 <
第二种: <![CDATA[sql]]>
<select id="findByMaxMin" resultType="com.qy151wd.entity.Account">
<![CDATA[select * from account where id >#{min} and id <#{max}]]>
</select>
2.4 sql片段
在执行查询语句时不建议使用select *,建议把查询的列写出。
2.5 mybatis完成模糊查询
select * from 表名 where 列名 like '%a%'
(1)使用字符串函数 完成拼接 concat
<select id="findByLike" resultType="com.qy151wd.entity.Account">
select * from account where name like concat('%',#{name},'%')
</select>
(2) 使用${}
<select id="findByLike" resultType="com.qy151wd.entity.Account">
select * from account where name like '%${name}%'
</select>
区别:发现使用${}实际上是字符串拼接,它不能防止sql注入, 而#{}它是预编译,它可以防止sql注入问题,#{}实际使用的PreparedStatement.
3.结果映射(resultMap)
4. 联表查询
4.1 一对一关联association
property | 映射到列结果的字段或属性。如果用来匹配的 JavaBean 存在给定名字的属性,那么它将会被使用。否则 MyBatis 将会寻找给定名称的字段。 无论是哪一种情形,你都可以使用通常的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。 |
javaType | 一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。 |
jdbcType | JDBC 类型,所支持的 JDBC 类型参见这个表格之前的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可能存在空值的列指定这个类型。 |
实体类演示
//班级对象
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Clazz {
private int cId;
private String cName;
private int teacherId;
//把教师对象当作属性
private Teacher teacher;
}
//教师对象
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Teacher {
private int tId;
private String tName;
}
xml演示
<resultMap id="ClassResultMap" type="com.qy151wd.entity.Clazz">
<!-- 主键 property为实体类属性 column为数据库字段 jdbcType为实体类对应的jdbc类型此处没有使用-->
<id property="cId" column="c_id"/>
<!-- 普通属性 property为实体类属性 column为数据库字段-->
<result property="cName" column="c_name"/>
<result property="teacherId" column="teacher_id"/>
<!--property:teacher Clazz里面Teacher teacher column:数据库中teacher表的对应的列名-->
<!--一对一映射association property 为实体类Clazz中的属性名字 javaType为实体类属性的类型 -->
<association property="teacher" column="teacher_id" javaType="com.qy151wd.entity.Teacher">
<id property="tId" column="t_id"/>
<result property="tName" column="t_name"/>
</association>
</resultMap>
若sql片段中起了别名,column就变成了这个别名,因为是临时表 。
4.2 一对多关联collection
一个班级(Clazz)只有一个教师(Teacher)。但一个班级有很多学生(Student)。像下面这样,映射嵌套结果集合到一个 List 中,可以使用集合元素。 和关联元素一样,我们可以使用嵌套 Select 查询,或基于连接的嵌套结果映射集合。 在班级类中,这可以用下面的写法来表示:
//学生类
public class Student {
private int id;
private String name;
}
//班级类
public class Clazz {
private int id;
private String name;
private Teacher teacher;
//学生类的集合的引用
private List<Student> students;
}
如何使用嵌套 Select 查询(两种方式)来为班级加载学生
<!--
方式一: 嵌套结果: 使用嵌套结果映射来处理重复的联合结果的子集
SELECT * FROM class c, teacher t,student s WHERE c.teacher_id=t.t_id AND c.C_id=s.class_id
AND c.c_id=1
-->
<!-- parameterType:输入类型 resultMap:输出结果类型-->
<select id="getClass" parameterType="int" resultMap="ClassResult">
select * from class c, teacher t,student s where c.teacher_id=t.t_id and c.C_id=s.class_id and
c.c_id=#{id}
</select>
<resultMap type="com.qy151wd.entity.Clazz" id="ClassResult">
<id property="id" column="c_id"/>
<result property="name" column="c_name"/>
<!--教师类查询-->
<association property="teacher" column="teacher_id" javaType="com.qy151wd.entity.Teacher">
<id property="id" column="t_id"/>
<result property="name" column="t_name"/>
</association>
<!--教学生类集合查询-->
<!-- ofType 指定 students 集合中的对象类型 -->
<collection property="students" ofType="com.qy151wd.entity.Student">
<id property="id" column="s_id"/>
<result property="name" column="s_name"/>
</collection>
</resultMap>
<!--
方式二:嵌套查询:通过执行另外一个 SQL 映射语句来返回预期的复杂类型
SELECT * FROM class WHERE c_id=1;
SELECT * FROM teacher WHERE t_id=1 //1 是上一个查询得到的 teacher_id 的值
SELECT * FROM student WHERE class_id=1 //1 是第一个查询得到的 c_id 字段的值
-->
<resultMap id="ClassResultMap" type="com.qy151wd.entity.Clazz">
<id property="cId" column="c_id"/>
<result property="cName" column="c_name"/>
<result property="teacherId" column="teacher_id"/>
<!--property:teacher Clazz里面Teacher teacher column:数据库中teacher表的对应的列名-->
<association property="teacher" column="teacher_id" javaType="com.qy151wd.entity.Teacher">
<id property="tId" column="t_id"/>
<result property="tName" column="t_name"/>
</association>
<!--column="c_id":根据班级的外键Id去寻找班级的学生 property="students":与Clazz类中的Student集合对应-->
<collection property="students" ofType="com.qy151wd.entity.Student" column="c_id"
select="getStudent">
</collection>
</resultMap>
<!--此处随意命名 下边引用 省略*的内容-->
<sql id="refiledId">
c_id,c_name,teacher_id,t_id,t_name
</sql>
<!--此处resultMap有是因为下面的select是它的子查询-->
<select id="getClazz" resultMap="ClassResultMap" parameterType="int">
select <include refid="refiledId"/> from class c join teacher t on c.teacher_id=t.t_id where c.teacher_id=#{id}
</select>
<!--此处不用resultMap原因是因为select调用,因此会有输出结果类型-->
<select id="getStudent" parameterType="int" resultType="com.qy151wd.entity.Student">
SELECT s_id sId,s_name sName,class_id classId FROM student3 WHERE class_id=#{id}
</select>
测试类
//ClassMapper接口内
public interface ClassMapper {
public Clazz getClazz(int id);
}
//测试类
public class UserTest {
SqlSession sqlSession;
UserMapper mapper;
ClassMapper mapper1;
{
try{
Reader reader= Resources.getResourceAsReader("conf.xml");
SqlSessionFactory sqlSessionFactory=new SqlSessionFactoryBuilder().build(reader);
sqlSession=sqlSessionFactory.openSession();
mapper = sqlSession.getMapper(UserMapper.class);
mapper1 = sqlSession.getMapper(ClassMapper.class);
}catch(Exception e){
e.printStackTrace();
}
}
@Test
public void testLianBiao(){
Clazz clazz = mapper1.getClazz(1);
System.out.println(clazz);
}
}
5.鉴别器
有时候,一个数据库查询可能会返回多个不同的结果集(但总体上还是有一定的联系的)。 鉴别器(discriminator)元素就是被设计来应对这种情况的,另外也能处理其它情况,例如类的继承层次结构。 鉴别器的概念很好理解——它很像 Java 语言中的 switch 语句。一个鉴别器的定义需要指定 column 和 javaType 属性。column 指定了 MyBatis 查询被比较值的地方。 而 javaType 用来确保使用正确的相等测试(虽然很多情况下字符串的相等测试都可以工作)。
<discriminator javaType="int" column="draft"> <case value="1" resultType="DraftPost"/> </discriminator>
例子
<resultMap id="vehicleResult" type="Vehicle">
<id property="id" column="id" />
<result property="vin" column="vin"/>
<result property="year" column="year"/>
<result property="make" column="make"/>
<result property="model" column="model"/>
<result property="color" column="color"/>
<!--javaType:这一属性在java段中代表的属性为int类型-->
<discriminator javaType="int" column="vehicle_type">
<case value="1" resultType="carResult">
<result property="doorCount" column="door_count" />
</case>
<case value="2" resultType="truckResult">
<result property="boxSize" column="box_size" />
<result property="extendedCab" column="extended_cab" />
</case>
<case value="3" resultType="vanResult">
<result property="powerSlidingDoor" column="power_sliding_door" />
</case>
<case value="4" resultType="suvResult">
<result property="allWheelDrive" column="all_wheel_drive" />
</case>
</discriminator>
</resultMap>
更多推荐
mybatis的resultMap用法
发布评论