MyBatis-3(二)映射文件

1、MyBatis-映射文件

点击此处查看官方中文文档

环境准备

创建部门表,员工表添加外键,并往部门表和员工表插入测试数据:

CREATE TABLE t_dept(
	id INT PRIMARY KEY AUTO_INCREMENT,
	department_name VARCHAR(200)
);

INSERT INTO t_dept VALUES (1,'开发部');
INSERT INTO t_dept VALUES (2,'测试部');

#修改员工表,添加一个部门id字段
ALTER TABLE t_employee ADD COLUMN d_id INT ;

#添加外键约束
ALTER TABLE t_employee ADD CONSTRAINT fk_emp_dept FOREIGN KEY (d_id) REFERENCES t_dept(id);

#往员工表中添加几条记录
INSERT INTO t_employee VALUES ('2','jack',33,'长沙',1),('3','jack2',34,'北京',2),('4','jerry',36,'上海',1),('5','bob',31,'深圳',1);

在上一篇的工程中,添加部门相关的实体和接口以及映射文件,同时全局配置文件中,映射文件通过包扫描的方式进行引入,具体参考上一篇:工程的结构如下:

 

 

• 映射文件指导着MyBatis如何进行数据库增删改查,有着非常重要的意义;
       •cache –命名空间的二级缓存配置
       •cache-ref – 其他命名空间缓存配置的引用。
       •resultMap – 自定义结果集映射
       •parameterMap – 已废弃!老式风格的参数映射
       •sql –抽取可重用语句块。
       •insert – 映射插入语句
       •update – 映射更新语句
       •delete – 映射删除语句
       •select – 映射查询语句

1.1 insert, update 和 delete

数据变更语句 insert,update 和 delete 的实现非常接近;

                                                             Insert, Update, Delete 元素的属性

属性描述
id命名空间中的唯一标识符,可被用来代表这条语句。
parameterType将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器推断出具体传入语句的参数,默认值为未设置(unset)。
parameterMap这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:true(对于 insert、update 和 delete 语句)。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。
statementTypeSTATEMENT,PREPARED 或 CALLABLE 的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys(仅对 insert 和 update 有用)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系数据库管理系统的自动递增字段),默认值:false。
keyProperty(仅对 insert 和 update 有用)唯一标记一个属性,MyBatis 会通过 getGeneratedKeys 的返回值或者通过 insert 语句的 selectKey 子元素设置它的键值,默认值:未设置(unset)。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
keyColumn(仅对 insert 和 update 有用)通过生成的键值设置表中的列名,这个设置仅在某些数据库(像 PostgreSQL)是必须的,当主键列不是表中的第一列的时候需要设置。如果希望使用多个生成的列,也可以设置为逗号分隔的属性名称列表。
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。

下面就是 insert,update 和 delete 语句的示例:

<insert id="insertEmployee">
  insert into t_employee (id,last_name,age,addr,d_id)
  values (#{id},#{lastName},#{age},#{addr},#{dId})
</insert>

<update id="updateEmployee">
  update t_employee set
    last_name= #{lastName},
    age = #{age},
    addr = #{addr},
    d_id = #{dId}
  where id = #{id}
</update>

<delete id="deleteEmployee">
  delete from t_employee where id = #{id}
</delete>

1.2 获取主键值

1.2.1 获取自增主键的值

       首先,如果你的数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),那么你可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上就 OK 了。例如,如果上面的 t_employee 表已经对 id 使用了自动生成的列类型,那么语句可以修改为:

<insert id="insertEmployee" useGeneratedKeys="true" keyProperty="id">
  insert into t_employee (id,last_name,age,addr,d_id)
  values (#{id},#{lastName},#{age},#{addr},#{dId})
</insert>

       如果你的数据库还支持多行插入, 你也可以传入一个 Employee 数组或集合,并返回自动生成的主键。

<insert id="insertEmployee" useGeneratedKeys="true" keyProperty="id">
  insert into Author (last_name, age, addr, d_id) values
  <foreach item="item" collection="list" separator=",">
    (#{item.lastName}, #{item.age}, #{item.addr}, #{item.dId})
  </foreach>
</insert>

1.2.2 获取非自增主键值

对于不支持自动生成类型的数据库或可能不支持自动生成主键的 JDBC 驱动,MyBatis 有另外一种方法来生成主键。

比如:Oracle,可以支持使用序列来作为主键:

<insert id="insertEmployee">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    select employees_seq.nextval from dual
  </selectKey>
  insert into t_employee
    (id, last_name, age, addr,d_id)
  values
    (#{id}, #{username}, #{password}, #{email}, #{bio}
</insert>

在上面的示例中,selectKey 元素中的语句将会首先运行,t_employee 的 id 会被设置,然后插入语句会被调用。这可以提供给你一个与数据库中自动生成主键类似的行为,同时保持了 Java 代码的简洁。

selectKey 元素的属性
属性描述
keyPropertyselectKey 语句结果应该被设置的目标属性。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
keyColumn匹配属性的返回结果集中的列名称。如果希望得到多个生成的列,也可以是逗号分隔的属性名称列表。
resultType结果的类型。MyBatis 通常可以推断出来,但是为了更加精确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果希望作用于多个生成的列,则可以使用一个包含期望属性的 Object 或一个 Map。
order这可以被设置为 BEFORE 或 AFTER。如果设置为 BEFORE,那么它会首先生成主键,设置 keyProperty 然后执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 中的语句 - 这和 Oracle 数据库的行为相似,在插入语句内部可能有嵌入索引调用。
statementType与前面相同,MyBatis 支持 STATEMENT,PREPARED 和 CALLABLE 语句的映射类型,分别代表 PreparedStatement 和 CallableStatement 类型。

 

1.3 参数的传递和取值

单个参数:mybatis不会做特殊处理,
	#{参数名/任意名}:取出参数值。
	
多个参数:mybatis会做特殊处理。
	多个参数会被封装成 一个map,
		key:param1...paramN,或者参数的索引也可以
		value:传入的参数值
	#{}就是从map中获取指定的key的值;
	
	异常:
	org.apache.ibatis.binding.BindingException: 
	Parameter 'id' not found. 
	Available parameters are [1, 0, param1, param2]
	操作:
		方法:public Employee getEmpByIdAndLastName(Integer id,String lastName);
		取值:#{id},#{lastName}

【命名参数】:明确指定封装参数时map的key;@Param("id")
	多个参数会被封装成 一个map,
		key:使用@Param注解指定的值
		value:参数值
	#{指定的key}取出对应的参数值


POJO:
如果多个参数正好是我们业务逻辑的数据模型,我们就可以直接传入pojo;
	#{属性名}:取出传入的pojo的属性值	

Map:
如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便,我们也可以传入map
	#{key}:取出map中对应的值

TO:
如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)数据传输对象

========================思考================================	
public Employee getEmp(@Param("id")Integer id,String lastName);
	取值:id==>#{id/param1}   lastName==>#{param2}

public Employee getEmp(Integer id,@Param("e")Employee emp);
	取值:id==>#{param1}    lastName===>#{param2.lastName/e.lastName}

##特别注意:如果是Collection(List、Set)类型或者是数组,
		 也会特殊处理。也是把传入的list或者数组封装在map中。
			key:Collection(collection),如果是List还可以使用这个key(list)
				数组(array)
public Employee getEmpById(List<Integer> ids);
	取值:取出第一个id的值:   #{list[0]}
	
========================结合源码,mybatis怎么处理参数==========================
总结:参数多时会封装map,为了不混乱,我们可以使用@Param来指定封装时使用的key;
#{key}就可以取出map中的值;

(@Param("id")Integer id,@Param("lastName")String lastName);
ParamNameResolver解析参数封装map的;
//1、names:{0=id, 1=lastName};构造器的时候就确定好了

	确定流程:
	1.获取每个标了param注解的参数的@Param的值:id,lastName;  赋值给name;
	2.每次解析一个参数给map中保存信息:(key:参数索引,value:name的值)
		name的值:
			标注了param注解:注解的值
			没有标注:
				1.全局配置:useActualParamName(jdk1.8):name=参数名
				2.name=map.size();相当于当前元素的索引
	{0=id, 1=lastName,2=2}
				

args【1,"Tom",'hello'】:

public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    //1、参数为null直接返回
    if (args == null || paramCount == 0) {
      return null;
     
    //2、如果只有一个元素,并且没有Param注解;args[0]:单个参数直接返回
    } else if (!hasParamAnnotation && paramCount == 1) {
      return args[names.firstKey()];
      
    //3、多个元素或者有Param标注
    } else {
      final Map<String, Object> param = new ParamMap<Object>();
      int i = 0;
      
      //4、遍历names集合;{0=id, 1=lastName,2=2}
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
      
      	//names集合的value作为key;  names集合的key又作为取值的参考args[0]:args【1,"Tom"】:
      	//eg:{id=args[0]:1,lastName=args[1]:Tom,2=args[2]}
        param.put(entry.getValue(), args[entry.getKey()]);
        
        
        // add generic param names (param1, param2, ...)param
        //额外的将每一个参数也保存到map中,使用新的key:param1...paramN
        //效果:有Param注解可以#{指定的key},或者#{param1}
        final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }
}

总结:参数多时会封装map,为了不混乱,我们可以使用@Param来指定封装时使用的key;

 

参数值的获取两种方式

#{}:可以获取map中的值或者pojo对象属性的值;
${}:可以获取map中的值或者pojo对象属性的值;

select * from tbl_employee where id=${id} and last_name=#{lastName}
Preparing: select * from tbl_employee where id=2 and last_name=?
	区别:
		#{}:是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入
		${}:取出的值直接拼装在sql语句中;会有安全问题;
		大多情况下,我们去参数的值都应该去使用#{};
		
		原生jdbc不支持占位符的地方我们就可以使用${}进行取值
		比如分表、排序。。。;按照年份分表拆分
			select * from ${year}_salary where xxx;
			select * from tbl_employee order by ${f_name} ${order}


#{}:更丰富的用法:
	规定参数的一些规则:
	javaType、 jdbcType、 mode(存储过程)、 numericScale、
	resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能);

	jdbcType通常需要在某种特定的条件下被设置:
		在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错);
		
		JdbcType OTHER:无效的类型111;因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,oracle不能正确处理,而mysql可以处理;
		
		由于全局配置中:jdbcTypeForNull=OTHER;oracle不支持;两种办法
		1、#{email,jdbcType=OTHER};
		2、jdbcTypeForNull=NULL,在全局配置文件mybatis-config.xml中设置
		<settings>
			<setting name="jdbcTypeForNull" value="NULL"/>
           		</settings>

 

1.4 select详解

1.4.1 Select 元素的属性

                                                                               Select 元素的属性

属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数类的完全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler) 推断出具体传入语句的参数,默认值为未设置(unset)。
parameterMap这是引用外部 parameterMap 的已经被废弃的方法。请使用内联参数映射和 parameterType 属性。
resultType从这条语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。可以使用 resultType 或 resultMap,但不能同时使用。
resultMap外部 resultMap 的命名引用。结果集的映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂映射的情形都能迎刃而解。可以使用 resultMap 或 resultType,但不能同时使用。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
useCache将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖驱动)。
fetchSize这是一个给驱动的提示,尝试让驱动程序每次批量返回的结果行数和这个设置值相等。 默认值为未设置(unset)(依赖驱动)。
statementTypeSTATEMENT,PREPARED 或 CALLABLE 中的一个。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetTypeFORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖驱动)。
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有的不带 databaseId 或匹配当前 databaseId 的语句;如果带或者不带的语句都有,则不带的会被忽略。
resultOrdered这个设置仅针对嵌套结果 select 语句适用:如果为 true,就是假设包含了嵌套结果集或是分组,这样的话当返回一个主结果行的时候,就不会发生有对前面结果集的引用的情况。 这就使得在获取嵌套的结果集的时候不至于导致内存不够用。默认值:false。
resultSets这个设置仅对多结果集的情况适用。它将列出语句执行后返回的结果集并给每个结果集一个名称,名称是逗号分隔的。

示例:

<select
  id="selectPerson"
  parameterType="int"
  parameterMap="deprecated"
  resultType="hashmap"
  resultMap="personResultMap"
  flushCache="false"
  useCache="true"
  timeout="10"
  fetchSize="256"
  statementType="PREPARED"
  resultSetType="FORWARD_ONLY">

     SELECT * FROM PERSON WHERE ID = #{id}
</select>

• Select元素来定义查询操作。
       • Id:唯一标识符。
               – 用来引用这条语句,需要和接口的方法名一致
       • parameterType:参数类型。
               – 可以不传,MyBatis会根据TypeHandler自动推断
       • resultType:返回值类型。
               – 别名或者全类名,如果返回的是集合,定义集合中元素的类型。不能和resultMap同时使用

1.4.2 自动映射

1、全局setting设置

       – autoMappingBehavior默认是PARTIAL,开启自动映射的功能。唯一的要求是列名和javaBean属性名一致
       – 如果autoMappingBehavior设置为null则会取消自动映射
       – 数据库字段命名规范,POJO属性符合驼峰命名法,如A_COLUMN——》aColumn,我们可以开启自动驼峰命名规则映射功能,mapUnderscoreToCamelCase=true

2、resultType的使用

2.1 如果是查询到的结果是封装成java对象或对象的集合,那么resultType的值是java类的全类名或别名;

<select id="selectById" resultType="com.mybatis.pojo.Employee">
		select id,last_name,age,addr from t_employee where id = #{id}
	</select>

2.2 如果查询到的一条记录封装成Map,那么resultType的值就是map(内置别名),key就是列名,value就是对应的值;

<!--查询的结果封装成map-->
	<select id="selectByIdForMap" resultType="map">
		select id,last_name from t_employee where id = #{id}
	</select>
EmployeeMapper中添加方法:
//封装成map返回
    public Map<String,Object> selectByIdForMap(String id);

测试方法:

    @Test
    public void test3(){
        SqlSession sqlSession = null;
        try {
            sqlSession = sessionFactory.openSession();
            EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
            Map<String,Object> resultMap = employeeMapper.selectByIdForMap("1");
            System.out.println(resultMap.get("id")+"----名称:"+resultMap.get("last_name"));
        }finally {
            sqlSession.close();
        }
    }

2.3 如果查询的多条记录要封装成的Map<Integer,Java对象>,比如:

<!--查询的结果封装成map-->
	<select id="selectListByIdForMap" resultType="com.mybatis.pojo.Employee">
		select id,last_name,age,addr from t_employee where last_name like #{lastName}
	</select>

 EmployeeMapper中添加方法: 

//查询到的对象集合封装成map返回
    @MapKey("lastName")//@MapKey:告诉mybatis封装这个map的时候使用哪个属性作为map的key
    public Map<String,Employee> selectListByIdForMap(String lastName);

测试:

@Test
    public void test4(){
        SqlSession sqlSession = null;
        try {
            sqlSession = sessionFactory.openSession();
            EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
            Map<String,Employee> resultMap = employeeMapper.selectListByIdForMap("%o%");
            System.out.println(resultMap);
        }finally {
            sqlSession.close();
        }
    }

结果:{tom=Employee{id='1', lastName='tom', age=22, addr='changshashi'}, bob=Employee{id='5', lastName='bob', age=31, addr='深圳'}}
 

2 自定义映射规则(resultMap)

如果我们的sql查询返回的结果,能和我们的Java类的属性一一对应的话,那么resultType就可以解决问题。但是当进行多表关联查询的时候,一般是没有现成的Java对象可以进行结果映射的。因此,就需要使用强大的resultMap,来自定义映射规则。

resultMap的属性如下:

  • constructor - 用于在实例化类时,注入结果到构造方法中
    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
    • arg - 将被注入到构造方法的一个普通结果
  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能
  • result – 注入到字段或 JavaBean 属性的普通结果
  • association – 一个复杂类型的关联;许多结果将包装成这种类型
    • 嵌套结果映射 – 关联本身可以是一个 resultMap 元素,或者从别处引用一个
  • collection – 一个复杂类型的集合
    • 嵌套结果映射 – 集合本身可以是一个 resultMap 元素,或者从别处引用一个
  • discriminator – 使用结果值来决定使用哪个 resultMap
    • case – 基于某些值的结果映射
      • 嵌套结果映射 – case 本身可以是一个 resultMap 元素,因此可以具有相同的结构和元素,或者从别处引用一个
ResultMap 的属性列表
属性描述
id当前命名空间中的一个唯一标识,用于标识一个结果映射。
type类的完全限定名, 或者一个类型别名(关于内置的类型别名,可以参考上面的表格)。
autoMapping如果设置这个属性,MyBatis将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)。

2.1 一对一映射

修改Employee,添加一个属性dept(部门信息),一个员工属于一个部门;

public class Employee {
    private String id;
    private String lastName;
    private int age;
    private String addr;
    private Department dept;
    //get/set方法以及toString方法
}

2.1.1 级联绑定

需求:根据员工id查询员工信息,同时也要查询出对应部门信息

mapper.xml中的sql

<resultMap id="empDept1" type="com.mybatis.pojo.Employee">
		<id property="id" column="id"/>
		<result property="lastName" column="last_name"/>
		<result property="age" column="age"/>
		<result property="addr" column="addr"/>
		<!--部门信息的封装,使用级联封装,dept是Employee类中的属性名称-->
		<result property="dept.id" column="did"/>
		<result property="dept.departmentName" column="department_name"/>
	</resultMap>
	<select id="selectEmpAndDept" resultMap="empDept1">
		SELECT e.id,e.last_name,e.age,e.addr,d.id did,d.department_name FROM t_employee e, t_dept d WHERE e.d_id=d.id and e.id = #{id}
	</select>

mapper接口新增一个方法

//级联封装
    public Employee selectEmpAndDept(String id);

测试

@Test
    public void test5(){
        SqlSession sqlSession = null;
        try {
            sqlSession = sessionFactory.openSession();
            EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
            Employee employee = employeeMapper.selectEmpAndDept("2");
            System.out.println("员工信息:"+employee);
            System.out.println("部门信息:"+employee.getDept());
        }finally {
            sqlSession.close();
        }
    }

结果:

员工信息:Employee{id='2', lastName='jack', age=33, addr='长沙'}
部门信息:Department{id=1, departmentName='开发部'}

可以看到,resultMap中,通过属性名来进行级联,也可以把查询结果封装成我们想要的结果。

2.1.2 association绑定单个对象

使用association来封装,跟级联的唯一不同只是resultMap的不同,修改resultMap定义如下:

<resultMap id="empDept1" type="com.mybatis.pojo.Employee">
		<id property="id" column="id"/>
		<result property="lastName" column="last_name"/>
		<result property="age" column="age"/>
		<result property="addr" column="addr"/>
		<!--使用association来进行单一对应的封装
			dept:是Employee中的部门属性名称
			javaType:指定对应的类型
		-->
		<association property="dept" javaType="com.mybatis.pojo.Department">
			<id property="id" column="did"/>
			<result property="departmentName" column="department_name"/>
		</association>
	</resultMap>

执行程序,结果分上面的级联是一样的。

2.1.3 association 分步查询

上面的2.1.2相当于是使用的嵌套结果集,一次性查询出了员工以及部门信息。其实我们可以先查询出员工信息,就能知道他的部门id,再根据部门id去查询部门信息就可以了。

1、编写DepartmentMapper.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.mybatis.EmployeeMapper">-->
<mapper namespace="com.mybatis.mapper.DepartmentMapper">

    <select id="selectById" resultType="com.mybatis.pojo.Department">
        SELECT id,department_name FROM t_dept WHERE id = #{id}
    </select>
</mapper>

2、DepartmentMapper.java添加方法

package com.mybatis.mapper;

import com.mybatis.pojo.Department;

public interface DepartmentMapper {
    //根据id查询
    public Department selectById(int id);
}

3、修改EmpoyeeMapper.xml的sql

<resultMap id="empDept1" type="com.mybatis.pojo.Employee">
		<id property="id" column="id"/>
		<result property="lastName" column="last_name"/>
		<result property="age" column="age"/>
		<result property="addr" column="addr"/>
		<!--使用association来进行单一对应的封装
			dept:是Employee中的部门属性名称
			select:表明当前属性dept是调用select指定的方法查出的结果
			column:指定将哪一列的结果传给select当作参数
		-->
		<association property="dept" select="com.mybatis.mapper.DepartmentMapper.selectById" column="d_id">

		</association>
	</resultMap>
	<select id="selectEmpAndDept" resultMap="empDept1">
		select * from t_employee where id = #{id}
	</select>

4、继续执行之前的测试方法

@Test
    public void test5(){
        SqlSession sqlSession = null;
        try {
            sqlSession = sessionFactory.openSession();
            EmployeeMapper employeeMapper = sqlSession.getMapper(EmployeeMapper.class);
            Employee employee = employeeMapper.selectEmpAndDept("2");
            System.out.println("员工信息:"+employee);
            System.out.println("部门信息:"+employee.getDept());
        }finally {
            sqlSession.close();
        }
    }

结果:

员工信息:Employee{id='2', lastName='jack', age=33, addr='长沙'}
部门信息:Department{id=1, departmentName='开发部'}

2.1.4 association 分步查询实现延迟加载

延迟加载:顾名思义就是真正使用的时候才去做查询。比如我们查询员工信息,但是当我们没有使用部门相关的信息时,不会发起查询部门的sql,只有在使用部门信息的时候,才会发sql进行查询操作。

实现延迟加载:在上面的2.1.3的分步查询基础上,只要在全局配置文件mybatis-config.xml中配置两个参数就可以了。

    <settings>
         <!--开启驼峰命名-->
		<setting name="mapUnderscoreToCamelCase" value="true"/>
		<!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。
		     特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。默认false-->
		<setting name="lazyLoadingEnabled" value="true"/>
		<!--当开启时,任何方法的调用都会加载该对象的所有属性。
		    否则,每个属性会按需加载(参考 lazyLoadTriggerMethods)。默认false-->
		<setting name="aggressiveLazyLoading" value="false"/>
	</settings>

效果:

没有延迟加载的时候,sql是一起发送的:

2.2 一对多映射

association是用来关联单个对象的,如果是一个对象关联一个集合,那就要使用collection了。

修改Department实体,添加一个List<Employee> emps 属性;

public class Department {
    private Integer id;
    private String departmentName;
    private List<Employee> emps;
    //get/set方法以及toString方法
}

需求:查询部门的时候,把部门下面所有的员工也查询出来。

2.2.1 collection关联集合对象

1、修改DepartmentMapper.xml

<resultMap id="MyDeptEpml" type="com.mybatis.pojo.Department">
        <id property="id" column="did"/>
        <result property="departmentName" column="department_name"/>
        <!--collection 可以封装集合
            emps:部门对象中的员工集合的属性名称
            ofType:要把查询的数据封装成哪个对象
        -->
        <collection property="emps" ofType="com.mybatis.pojo.Employee">
            <id property="id" column="id"/>
            <result property="lastName" column="last_name"/>
            <result property="age" column="age"/>
            <result property="addr" column="addr"/>
        </collection>
    </resultMap>

    <select id="selectDeptAndEmployees" resultMap="MyDeptEpml">
        SELECT d.id did,d.department_name,e.id,e.last_name,e.age,e.addr
        FROM t_dept d LEFT JOIN t_employee e ON d.id=e.d_id
        WHERE d.id=#{id}
    </select>

2、DempartmentMapper接口中加上方法

//根据部门id查询,同时查询出员工信息
    public Department selectDeptAndEmployees(int id);

3、测试

@Test
    public void test6(){
        SqlSession sqlSession = null;
        try {
            sqlSession = sessionFactory.openSession();
            DepartmentMapper departmentMapper = sqlSession.getMapper(DepartmentMapper.class);
            Department department = departmentMapper.selectDeptAndEmployees(1);
            System.out.println("员工信息:"+department);//如果直接打印employee,就不会有延迟加载效果
            System.out.println("部门信息:"+department.getEmps());
        }finally {
            sqlSession.close();
        }
    }

结果:

DEBUG 02-19 21:22:03,756 ==>  Preparing: SELECT d.id did,d.department_name,e.id,e.last_name,e.age,e.addr FROM t_dept d LEFT JOIN t_employee e ON d.id=e.d_id WHERE d.id=?   (BaseJdbcLogger.java:145) 
DEBUG 02-19 21:22:03,819 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:145) 
DEBUG 02-19 21:22:03,857 <==      Total: 3  (BaseJdbcLogger.java:145) 
员工信息:Department{id=1, departmentName='开发部'}
部门信息:[Employee{id='2', lastName='jack', age=33, addr='长沙'}, Employee{id='4', lastName='jerry', age=36, addr='上海'}, Employee{id='5', lastName='bob', age=31, addr='深圳'}]

2.2.2 collection分步查询

collection的分步查询原理和association分步查询是一样的。下面来实现:

1、在EmployeeMapper.xml中添加根据部门id查询员工信息的方法

<!--根据部门id查询员工信息-->
	<select id="selectByDeptId" resultType="com.mybatis.pojo.Employee">
		SELECT * FROM t_employee WHERE d_id=#{deptId}
	</select>

2、在EmployeeMapper接口中添加根据部门id查询员工信息的方法

//根据部门id查询员工的方法
    public List<Employee> selectByDeptId(String id);

3、在DepartmentMapper.xml中添加根据id查询部门的方法

<select id="selectDeptAndEmployees" resultMap="MyDeptEpml">
        SELECT d.id did,d.department_name,e.id,e.last_name,e.age,e.addr
        FROM t_dept d LEFT JOIN t_employee e ON d.id=e.d_id
        WHERE d.id=#{id}
    </select>

    <resultMap id="MyDeptEpml2" type="com.mybatis.pojo.Department">
        <id property="id" column="id"/>
        <result property="departmentName" column="department_name"/>
        <!--collection 可以封装集合
            emps:部门对象中的员工集合的属性名称
            ofType:要把查询的数据封装成哪个对象
        -->
        <collection property="emps" select="com.mybatis.mapper.EmployeeMapper.selectByDeptId" column="id">

        </collection>
    </resultMap>
    <!--根据部门id查询部门信息,封装成map-->
    <select id="selectByDeptId" resultMap="MyDeptEpml2">
        SELECT id,department_name departmentName FROM t_dept WHERE id = #{id}
    </select>

4、在DepartmentMapper.java接口中添加方法

public Department selectByDeptId(int id);

5、测试

@Test
    public void test6(){
        SqlSession sqlSession = null;
        try {
            sqlSession = sessionFactory.openSession();
            DepartmentMapper departmentMapper = sqlSession.getMapper(DepartmentMapper.class);
            Department department = departmentMapper.selectByDeptId(1);
            System.out.println("部门信息:"+department);//如果直接打印department,就不会有延迟加载效果
            System.out.println("员工信息:"+department.getEmps());
        }finally {
            sqlSession.close();
        }
    }

结果:

DEBUG 02-19 21:35:15,789 ==>  Preparing: SELECT id,department_name departmentName FROM t_dept WHERE id = ?   (BaseJdbcLogger.java:145) 
DEBUG 02-19 21:35:15,877 ==> Parameters: 1(Integer)  (BaseJdbcLogger.java:145) 
DEBUG 02-19 21:35:15,903 ====>  Preparing: SELECT * FROM t_employee WHERE d_id=?   (BaseJdbcLogger.java:145) 
DEBUG 02-19 21:35:15,904 ====> Parameters: 1(Integer)  (BaseJdbcLogger.java:145) 
DEBUG 02-19 21:35:15,911 <====      Total: 3  (BaseJdbcLogger.java:145) 
DEBUG 02-19 21:35:15,911 <==      Total: 1  (BaseJdbcLogger.java:145) 
部门信息:Department{id=1, departmentName='开发部'}
员工信息:[Employee{id='2', lastName='jack', age=33, addr='长沙'}, Employee{id='4', lastName='jerry', age=36, addr='上海'}, Employee{id='5', lastName='bob', age=31, addr='深圳'}]

2.2.3 collection分步查询实现延迟加载

原理和association的延迟加载是一样的,只要设置了那两个值就可以了。

 

2.2.4 collection分步查询传递多列值

上面的分步查询,我们都是只传了一个值作为查询条件,那么怎么传递多个值呢?

其实很简单,只要将多个值封装成map传递就好了:{key1=column1,key2=column2....}

在2.2.3的示例中,在EmployeeMapper.xml中,根据部门id查询员工信息的sql是:

SELECT * FROM t_employee WHERE d_id=#{deptId}

那么说明,map传值的时候是:{deptId=id}

所以把DepartmentMapper.xml的sql修改如下:

<collection property="emps" select="com.mybatis.mapper.EmployeeMapper.selectByDeptId" 
column="{deptId=id}">

</collection>

column="{deptId=id}":主要修改的是这一块,其他都是不变的。

 

3、discriminator鉴别器

鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为

需求:封装Employee:
                如果查出的是女生(gender=0):就把部门信息查询出来,否则不查询;
                如果是男生(gender=0),把last_name这一列的值赋值给email;

resultMap的编写

<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpDis">
	 	<id column="id" property="id"/>
	 	<result column="last_name" property="lastName"/>
	 	<result column="email" property="email"/>
	 	<result column="gender" property="gender"/>
	 	<!--
	 		column:指定判定的列名
	 		javaType:列值对应的java类型  -->
	 	<discriminator javaType="string" column="gender">
	 		<!--女生  resultType:指定封装的结果类型;不能缺少。/resultMap-->
	 		<case value="0" resultType="com.atguigu.mybatis.bean.Employee">
	 			<association property="dept" 
			 		select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
			 		column="d_id">
		 		</association>
	 		</case>
	 		<!--男生 ;如果是男生,把last_name这一列的值赋值给email; -->
	 		<case value="1" resultType="com.atguigu.mybatis.bean.Employee">
		 		<id column="id" property="id"/>
			 	<result column="last_name" property="lastName"/>
			 	<result column="last_name" property="email"/>
			 	<result column="gender" property="gender"/>
	 		</case>
	 	</discriminator>
	 </resultMap>

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值