SQL片段的定义
将公用的SQL片段提取出来减少工作量
<sql id="studentColumns">
stu_id,stu_Age,stu_Sex,stu_Name
</sql>
<select id="queryStudentInfo" resultType="student" parameterType="map">
select <include refid="studentColumns"></include>
from student_info where stu_id = #{id}
</select>
也可以在sql片段中定义形参
<sql id="studentColumns">
${prefix}.stu_id,${prefix}.stu_Age,${prefix}.stu_Sex,${prefix}.stu_Name
</sql>
<select id="queryStudentInfo" resultMap="studentMap" parameterType="int">
select
<include refid="studentColumns">
<property name="prefix" value="m"/>
</include>
from student_info m where m.stu_id = #{id}
</select>
因为我们注入的参数并不是SQL语句的参数而是SQL语句本身,前面有说过#{}用来注入参数,${}注入sql语句本身
resultMap结果集定义详解
* resultMap里面的元素
<resultMap type="" id="">
<constructor></constructor>
<id/>
<result/>
<association property=""></association>
<collection property=""></collection>
<discriminator javaType=""></discriminator>
</resultMap>
type: 结果集的type,一般都是定义为javaBean,定义为map,不方便
constructor:在javabean没有创建无参的构造器时候,指定的调用的构造器方法
id和result:指定映射关系
<!--
column: 映射的列名
property: 映射的JavaBean的属性
jdbctype: 数据库里面的类型
javaType: java里面的类型
typeHandler: 类型转换中,指定的处理器,可以不指定
-->
<result column="stu_age" property="stuAge" jdbcType="varchar" javaType="string"
typeHandler="类型处理器"
/>
级联查询的处理
association: 表示一对一关系
collection: 表示一对多关系
discriminator: 根据条件选择结果集
一对一级联查询
* 学生和学生卡是一对一关系
需求是查询学生的时候将学生卡信息也一并查询出来
* student对象中定义了StudentCard对象
public class Student {
private Integer stuId;
private Integer stuAge;
private String stuSex;
private String stuName;
private StudentCard card;
//get/set .....
public class StudentCard {
private Integer stuId;
private Integer cardNum;
private Date registerTime;
//get/set....
定义studentMapper.xml
<resultMap type="student" id="studentAndCourse">
<id column="stu_id" property="stuId"/>
<result column="stu_age" property="stuAge" />
<result column="stu_sex" property="stuSex" />
<result column="stu_name" property="stuName"/>
<association property="card" column="stu_id"
select="cn.bing.mapper.StudentCardMapper.queryStudentCardInfo">
</association>
</resultMap>
<select id="queryStudentInfo" resultMap="studentAndCourse" parameterType="int">
select * from student_info m where m.stu_id = #{id}
</select>
在association元素中
card是关联对象的字段名称,select是调用的另一个CardMapper里的查询方法,必须写全路径+方法名称
column是方法形参获取的值对应在Student的列名(此时是student中Bean的主键列名)
* StudentCardMapper.xml
<mapper namespace="cn.bing.mapper.StudentCardMapper">
<resultMap type="studentCard" id="cardInfoMapper">
<id column="stu_id" property="stuId"/>
<result column="card_num" property="cardNum"/>
<result column="register_time" property="registerTime"
jdbcType="TIMESTAMP" javaType="java.util.Date"
typeHandler="org.apache.ibatis.type.DateTypeHandler"
/>
</resultMap>
<select id="queryStudentCardInfo" resultMap="cardInfoMapper" parameterType="int">
select * from student_card where stu_id = #{id}
</select>
</mapper>
测试:将mapper.xml 加到mybatis-config.xml配置中,直接调用queryStudentInfo方法,从日志中看出,发起了对学生卡的查询
DEBUG 2018-07-05 16:47:15,744 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:==> Preparing: select * from student_info m where m.stu_id = ?
DEBUG 2018-07-05 16:47:15,782 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:==> Parameters: 14(Integer)
DEBUG 2018-07-05 16:47:15,800 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:====> Preparing: select * from student_card where stu_id = ?
DEBUG 2018-07-05 16:47:15,800 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:====> Parameters: 14(Integer)
一对多级联查询
学生信息和学生课程是一对多关系
* pojo定义
在student类的一方,增加List<Course> list 属性
public class Student {
private Integer stuId;
private Integer stuAge;
private String stuSex;
private String stuName;
private StudentCard card;
private List<Course> list = new ArrayList<Course>();
//get/set .....
public class Course {
private Integer courseId;
private String courseName;
//get /set ...
* studentMapper.xml
<resultMap type="student" id="studentAndCourse">
<id column="stu_id" property="stuId"/>
<result column="stu_age" property="stuAge" />
<result column="stu_sex" property="stuSex" />
<result column="stu_name" property="stuName"/>
<association property="card" column="stu_id"
select="cn.bing.mapper.StudentCardMapper.queryStudentCardInfo">
</association>
<!-- 对课程表的一对多关系级联查询 -->
<collection property="list" column="stu_id"
select="cn.bing.mapper.CourseMapper.queryCourseInfos"
></collection>
</resultMap>
<select id="queryStudentInfo" resultMap="studentAndCourse" parameterType="int">
select * from student_info m where m.stu_id = #{id}
</select>
* courseMapper.xml
<mapper namespace="cn.bing.mapper.CourseMapper">
<select id="queryCourseInfos" resultType="course" parameterType="int" >
select course_id as courseId,course_name as courseName
from course_info where stu_id = #{id}
</select>
</mapper>
测试: 将xml加到mybatis-config.xml配置中 调用queryStudentInfo,打印日志
DEBUG 2018-07-09 09:36:14,278 org.apache.ibatis.transaction.jdbc.JdbcTransaction:Setting autocommit to false on JDBC Connection [com.mysql.jdbc.JDBC4Connection@176b067]
DEBUG 2018-07-09 09:36:14,281 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:==> Preparing: select * from student_info m where m.stu_id = ?
DEBUG 2018-07-09 09:36:14,316 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:==> Parameters: 5(Integer)
DEBUG 2018-07-09 09:36:14,342 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:====> Preparing: select stu_id,card_num,register_time from student_card where stu_id = ?
DEBUG 2018-07-09 09:36:14,343 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:====> Parameters: 5(Integer)
DEBUG 2018-07-09 09:36:14,352 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:<==== Total: 1
DEBUG 2018-07-09 09:36:14,353 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:====> Preparing: select course_id as courseId,course_name as courseName from course_info where stu_id = ?
DEBUG 2018-07-09 09:36:14,353 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:====> Parameters: 5(Integer)
DEBUG 2018-07-09 09:36:14,355 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:<==== Total: 2
DEBUG 2018-07-09 09:36:14,356 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:<== Total: 1
discriminator鉴别器级联
鉴别器级联是在特定的条件下去使用不同的结果映射,比如说需要根据学生的性别去区分学生的生理状况。
<!-- 鉴别器级联,根据性别,关联不同的健康结果集 -->
<discriminator javaType="string" jdbcType="TINYINT" column="stu_sex" >
<case value="1" resultMap="malehealthMapper"></case>
<case value="0" resultMap="femalhealthMapper">
</case>
</discriminator>
* 建立表结构
CREATE TABLE `female_health` (
`stu_id` INT(11) NOT NULL AUTO_INCREMENT,
`stu_heigth` DECIMAL(10,2) NULL DEFAULT '0.00',
`stu_weigth` DECIMAL(10,2) NULL DEFAULT '0.00',
`stu_strength` DECIMAL(10,2) NULL DEFAULT '0.00',
PRIMARY KEY (`stu_id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
ROW_FORMAT=COMPACT
AUTO_INCREMENT=7
;
CREATE TABLE `male_health` (
`stu_id` INT(11) NOT NULL AUTO_INCREMENT,
`stu_heigth` DECIMAL(10,2) NULL DEFAULT '0.00',
`stu_weigth` DECIMAL(10,2) NULL DEFAULT '0.00',
`stu_strength` DECIMAL(10,2) NULL DEFAULT '0.00',
PRIMARY KEY (`stu_id`)
)
COLLATE='utf8_general_ci'
ENGINE=InnoDB
AUTO_INCREMENT=6
;
* 创建男生的健康实体,映射类,映射文件
public class MaleHealth {
private Integer stuId;
private BigDecimal stuHeigth;
private BigDecimal stuWeigth;
private BigDecimal stuStrength;
//get/set ...
public interface MaleHealthMapper {
public List<MaleHealth> getMaleHealthInfos(@Param("id")Integer id);
}
<mapper namespace="cn.bing.mapper.MaleHealthMapper">
<select id="getMaleHealthInfos"
resultType="cn.bing.pojo.MaleHealth"
parameterType="int" >
select stu_id as stuId,
stu_heigth as stuHeigth,
stu_weigth as stuWeigth,
stu_strength as stuStrength
from male_health where stu_id = #{id}
</select>
</mapper>
* 创建女生的健康实体,映射类,映射文件
public class FemaleHealth {
private Integer stuId;
private BigDecimal stuHeigth;
private BigDecimal stuWeigth;
private BigDecimal stuStrength;
//set get 。。。
public interface FeMaleHealthMapper {
public List<MaleHealth> getFeMaleHealthInfos(@Param("id")Integer id);
}
<mapper namespace="cn.bing.mapper.FeMaleHealthMapper">
<select id="getFeMaleHealthInfos" resultType="femaleHealth" parameterType="int" >
select stu_id as stuId,
stu_heigth as stuHeigth,
stu_weigth as stuWeigth,
stu_strength as stuStrength
from female_health where stu_id = #{id}
</select>
</mapper>
* 创建男生健康封装类和女生健康封装类,接受映射结果
public class StudentMaleHealthBean extends Student{
private List<MaleHealth> maleList = new ArrayList<MaleHealth>();
//get set ...
public class StudentFeMaleHealthBean extends Student{
private List<FemaleHealth> femaleList = new ArrayList<FemaleHealth>();
//get set 。。。
* 修改studentMapper.xml
<resultMap type="student" id="studentAndCourse">
<id column="stu_id" property="stuId"/>
<result column="stu_age" property="stuAge" />
<result column="stu_sex" property="stuSex" />
<result column="stu_name" property="stuName"/>
<association property="card" column="stu_id"
select="cn.bing.mapper.StudentCardMapper.queryStudentCardInfo">
</association>
<!--对课程表的一对多关系级联查询 -->
<collection property="list" column="stu_id"
select="cn.bing.mapper.CourseMapper.queryCourseInfos"
></collection>
<!-- 鉴别器级联,根据性别,关联不同的健康结果集 -->
<discriminator javaType="string" jdbcType="TINYINT" column="stu_sex" >
<case value="1" resultMap="malehealthMapper"></case>
<case value="0" resultMap="femalhealthMapper">
</case>
</discriminator>
</resultMap>
<resultMap type="cn.bing.mapper.StudentMaleHealthBean" id="malehealthMapper" extends="studentAndCourse">
<collection property="maleList" column="stu_id"
select="cn.bing.mapper.MaleHealthMapper.getMaleHealthInfos"
>
</collection>
</resultMap>
<resultMap type="cn.bing.mapper.StudentFeMaleHealthBean" id="femalhealthMapper" extends="studentAndCourse">
<collection property="femaleList" column="stu_id"
select="cn.bing.mapper.FeMaleHealthMapper.getFeMaleHealthInfos"
></collection>
</resultMap>
<select id="queryStudentInfo" resultMap="studentAndCourse" parameterType="int">
select * from student_info m where m.stu_id = #{id}
</select>
注意: * 男生/女生健康的映射必须继承StudentAndCourse的resultMapper。
对于级联查询的性能分析
级联查询会查询出一些不必须的信息,而且增加上结果集映射的复杂度。每增加一个级联关系,会导致sql执行查询不必须的信息,导致不必要的资源浪费,这就是N+1问题。必须使用级联的情况,可以使用延迟加载。
延迟加载
MyBatis的配置中有两个全局的参数
lazyLoadingEnabled: 是否开启延迟加载功能,默认是false
aggressiveLazyLoading:对于任何延迟属性的加载是否使的带有延迟属性的对象完整加载,就是按调用加载。
<settings>
<!-- 这个比较好理解,就是开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 比如学生信息查询,只获取学生的学生卡信息,不会去将学生的课程信息、健康信息加载出来 -->
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
Student student = mapper.queryStudentInfo(5);
StudentCard card = student.getCard();
System.out.println(card.getCardNum());
DEBUG 2018-07-09 11:20:20,969 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:==> Preparing: select * from student_info m where m.stu_id = ?
DEBUG 2018-07-09 11:20:21,006 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:==> Parameters: 5(Integer)
DEBUG 2018-07-09 11:20:21,082 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:<== Total: 1
DEBUG 2018-07-09 11:20:21,083 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:==> Preparing: select stu_id,card_num,register_time from student_card where stu_id = ?
DEBUG 2018-07-09 11:20:21,084 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:==> Parameters: 5(Integer)
DEBUG 2018-07-09 11:20:21,090 org.apache.ibatis.logging.jdbc.BaseJdbcLogger:<== Total: 1
注意,如果只设置lazyLoadingEnabled参数为true,因为MyBatis是按照层级加载的,学生卡和课程信息在一个层级,查询
学生卡信息的时候会将课程信息查询出来,此时aggressiveLazyLoading参数设置为false,按照按需求加载解决。
设置局部加载
存在一些信息是需要级联查询的一并查询出来,一些信息是不需要的,此时可以在association,collection 元素加载
fetchType配置。
fetchType="eager" //立即加载
fetchType="lazy" //延迟加载
另一个查询方式
将查询的sql的字段,映射到指定的字段上
public List<Student> queryJoinInfo();
<select id="queryJoinInfo" resultMap="studentMap">
select
m1.*,
m2.card_num,
m3.course_name,
if(m1.stu_sex='1',m4.stu_heigth,m5.stu_heigth) stu_heigth,
if(m1.stu_sex='1',m4.stu_weigth,m5.stu_weigth) stu_weigth,
if(m1.stu_sex='1',m4.stu_strength,m5.stu_strength) stu_strength
from student_info m1
left join student_card m2 on m1.stu_id = m2.stu_id
left join course_info m3 on m1.stu_id = m3.stu_id
left join male_health m4 on m1.stu_id = m4.stu_id
left join female_health m5 on m1.stu_id = m5.stu_id
</select>
结果映射定义
<resultMap type="cn.bing.pojo.Student" id="studentMap">
<id column="stu_id" property="stuId"/>
<result column="stu_age" property="stuAge" />
<result column="stu_sex" property="stuSex" />
<result column="stu_name" property="stuName"/>
<association property="card"
javaType="cn.bing.pojo.StudentCard"
column="stu_id"
>
<id column="stu_id" property="stuId"/>
<result column="card_num" property="cardNum"/>
<result column="register_time" property="registerTime"
jdbcType="TIMESTAMP" javaType="java.util.Date"
typeHandler="org.apache.ibatis.type.DateTypeHandler"
/>
</association>
<collection property="list" ofType="cn.bing.pojo.Course" foreignColumn="stu_id">
<id column="course_id" property="courseId"/>
<result column="course_name" property="courseName"/>
</collection>
<!-- 鉴别器级联,根据性别,关联不同的健康结果集 -->
<discriminator javaType="string" jdbcType="TINYINT" column="stu_sex" >
<case value="1" resultMap="malehealthBeanMapper"></case>
<case value="0" resultMap="femalhealthBeanMapper"></case>
</discriminator>
</resultMap>
<resultMap type="cn.bing.mapper.StudentMaleHealthBean"
id="malehealthBeanMapper"
extends="studentMap"
>
<collection property="maleList" ofType="cn.bing.pojo.MaleHealth" >
<id column="stu_id" property="stuId"/>
<result column="stu_heigth" property="stuHeigth"/>
<result column="stu_weigth" property="stuWeigth"/>
<result column="stu_strength" property="stuStrength"/>
</collection>
</resultMap>
<resultMap type="cn.bing.mapper.StudentFeMaleHealthBean"
id="femalhealthBeanMapper" extends="studentMap">
<collection property="femaleList" ofType="cn.bing.pojo.FemaleHealth">
<id column="stu_id" property="stuId"/>
<result column="stu_heigth" property="stuHeigth"/>
<result column="stu_weigth" property="stuWeigth"/>
<result column="stu_strength" property="stuStrength"/>
</collection>
</resultMap>