经典三层框架初识--一.Mybatis(下)使用注解和高级映射

一.使用注解实现

说到现在,我们发现,mybatis代码已经简化了很多了.和以前相比,我们不用去编写它的实现类.而是由mapper代理自动帮我们生成.但是我们还有一种更为经典的方式.mapper代理我们前面说的是通过xml配置文件的方式来实现的,那下面我们介绍一下使用注解来实现.annotations--注释.

我们通过注解实现的话,映射配置文件--我们前面的UserMapper就不需要了.删除即可.那我们思考一下,没有了映射文件,只有接口如何生成实现类呢?那我们下面了解一下如果通过注解代替原来的xml实现.

我们只需要在接口上的增删改查方法上面添加一些简单的注解标注即可.下面看下代码

package mapper;

import java.util.List;

import org.apache.ibatis.annotations.Delete;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Select;

import pojo.UserInfo;

public interface UserMapper {
	
	@Select("select * from userinfo") //把sql语句和方法对应起来
	List<UserInfo> queryAll();
	
	//和原来映射配置文件里面的写法一模一样
	//这里的#{}中和原来映射文件的规则一样:如果是简单类型,随意写;如果里面是对象,同样是对象的属性名称.结果封装到哪里根据返回值类型推断
	@Select("select * from userinfo where uid=#{uid}")
	UserInfo queryById(int uid);				
	
	//@Insert
	//@Delete
	
	
}

二.高级映射

我们先思考一下下面这个问题:我们想查询部门信息以及部门下所有员工的信息,该如何实现?查询设计的两张表我贴在下面.

员工表

经典三层框架初识--一.Mybatis(下)使用注解和高级映射

部门表

经典三层框架初识--一.Mybatis(下)使用注解和高级映射

我们先用sql语句写一下上面的要求:

SELECT * FROM dept LEFT JOIN emp ON  dept.deptno = emp.deptno;

我们发现这两个表,存在一对多的关系.首先实体类就不像上面一样是单一的了.那就需要部门和员工的两个实体类.正常情况下我们给实体类定义的成员属性如下:

经典三层框架初识--一.Mybatis(下)使用注解和高级映射

我们前面说过,我们只需要关注定制化的sql,输入参数以及封装输出结果类型.首先输入参数这里我们是不必考虑的.sql语句也很上面一样.那就剩下了输出结果的封装了.我们sql语句查询出来的结果集的字段数肯定是比每个实体类中的成员属性都要多的,这是毫无疑问的,而对于resultType(映射文件中的定制化sql语句的一个属性,还记得么?)最大的一个特点:结果集字段名称必须和resultType值里面的对象的属性名称一致,这样才能封装.所以在这里我们就不能直接封装结果.

其实这里的问题我们不难发现:我们查询出来的结果集是把两张表的字段都结合在一起了,但是我们上面声明的类的属性并没有声明出来可以产生结合到一个地方去.所以我们要对两个类进行改写.让这两个类产生上面说的一对多关系.那我们将部门类修改一下

经典三层框架初识--一.Mybatis(下)使用注解和高级映射

 这样就让部门这个类和员工类呈现出了一对多的关系.这样我们期望将结果集中的前三个字段(deptno,dname,loc)封装到这个部门类的前三个属性中,将结果集后面的属性封装到部门类的list集合中存储的emp对象的属性中.那我们试着编写一下这个类的mapper.

package mapper;

import java.util.List;

import pojo.Dept;

public interface DeptMapper {

	List<Dept> findDeptAndEmps();
	
}

那类的mapper编写完了,我们来编写一下这个mapper的映射配置文件.

 

<?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="mapper.DeptMapper">  <!--namespace要和类的全限定名称一致-->
	<select id="findDeptAndEmps" resultType="dept">  <!--select的id值和mapper方法名一致-->
		select * from dept left join emp on dept.deptno = emp.deptno
	</select>
	
</mapper>  

按照我们以前的写法,这样就写完了,但是select标签里的输出结果类型能用resultType呢?我们先来看下运行结果

DEBUG [main] - Created connection 1525037790.
DEBUG [main] - ==>  Preparing: select * from dept left join emp on dept.deptno = emp.deptno 
DEBUG [main] - ==> Parameters: 
DEBUG [main] - <==      Total: 16
[Dept [deptno=10, dname=ACCOUNTING, loc=NEW YORK, emps=null], Dept [deptno=10, dname=ACCOUNTING, loc=NEW YORK, emps=null], Dept [deptno=10, dname=ACCOUNTING, loc=NEW YORK, emps=null], Dept [deptno=20, dname=RESEARCH, loc=DALLAS, emps=null], Dept [deptno=20, dname=RESEARCH, loc=DALLAS, emps=null], Dept [deptno=20, dname=RESEARCH, loc=DALLAS, emps=null], Dept [deptno=20, dname=RESEARCH, loc=DALLAS, emps=null], Dept [deptno=20, dname=RESEARCH, loc=DALLAS, emps=null], Dept [deptno=30, dname=SALES, loc=CHICAGO, emps=null], Dept [deptno=30, dname=SALES, loc=CHICAGO, emps=null], Dept [deptno=30, dname=SALES, loc=CHICAGO, emps=null], Dept [deptno=30, dname=SALES, loc=CHICAGO, emps=null], Dept [deptno=30, dname=SALES, loc=CHICAGO, emps=null], Dept [deptno=30, dname=SALES, loc=CHICAGO, emps=null], Dept [deptno=40, dname=OPERATIONS, loc=BOSTON, emps=null], Dept [deptno=50, dname=DEVELOPMENT, loc=JAP, emps=null]]
DEBUG [main] - Closing JDBC Connection [[email protected]]
DEBUG [main] - Returned connection 1525037790 to pool.

首先通过打印出来的日志我们发现,并没有报错.但是我们注意一下,emps的值是null.这是为什么呢?这其实是和resultType有关.

这里简单说一下resultType这个属性的特点:

    1.结果集中字段名称和类属性名称完全一致,此时映射成功.

    2.结果集中字段名称和类属性名称部分一致,部分映射成功。

    3.结果集中的字段名称和类属性中的名称都不一致,此时不会创建对象。

我们这里出现的情况是就是第二条特点导致的.因为Dept这个类的前三个属性和结果集中的字段名字完全一致,可以对应起来,而第四个属性emps与结果集中后面一大堆数据没有办法对应.这个时候就出现了部分映射成功.这里一个resultType并不能实现一个对应结果集多个字段.所以这里我们用另外一个杀器resultMap.那如何使用这个杀器呢?我直接先将deptMapper的映射配置文件贴在下面.

<?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="mapper.DeptMapper">
	<!--id:唯一标识    type:最终封装类型,这里我们最后要得到一个dept的对象类型,所以这里就是dept,不区分大小写-->
	<resultMap type="dept" id="deptMap">
		<!--  id:主键字段的映射  property:属性名称   column:结果集中字段名称-->
		<id property="deptno" column="deptno"/>
		<result  property="dname" column="dname"/>
		<result  property="loc" column="loc"/>
		<!-- 将表中数据封装到集合中   ofType:集合中对象的类型  column:是将两个表关联在一起的字段,外键-->
		<collection property="emps" ofType="emp" column="deptno">
			<id property="empno" column="empno"/>
			<result column="ename" property="ename"/>
			<result column="job" property="job"/>
			<result column="mgr" property="mgr"/>
			<result column="hiredate" property="hiredate"/>
			<result column="sal" property="sal"/>
			<result column="comm" property="comm"/>
			<result column="deptno" property="deptno"/>
		</collection>
	</resultMap>
	<select id="findDeptAndEmps" resultMap="deptMap">
		select * from dept left join emp
			on dept.deptno = emp.deptno
	</select>
	
</mapper>  

这里注意:resultMap是一个标签了,不在作为属性.其他的相关解释上面配置文件的注解都有.下面我们再来测试一下,顺便将测试的代码主要部分分享一下:

public class Test {

	public static void main(String[] args) throws IOException {
		Reader reader = Resources.getResourceAsReader("mybatis.xml");
		SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(reader);
		SqlSession session = ssf.openSession(true);
		DeptMapper mapper = session.getMapper(DeptMapper.class);
		System.out.println(mapper.findDeptAndEmps());
	
		session.close();
	}
}

结果如下:

DEBUG [main] - Opening JDBC Connection
DEBUG [main] - Created connection 523691575.
DEBUG [main] - ==>  Preparing: select * from dept left join emp on dept.deptno = emp.deptno 
DEBUG [main] - ==> Parameters: 
DEBUG [main] - <==      Total: 16
[Dept [deptno=10, dname=ACCOUNTING, loc=NEW YORK, emps=[Emp [empno=7782, ename=CLARK, job=MANAGER, mgr=7839, hiredate=1981-06-09, sal=2450.00, comm=null, deptno=10], Emp [empno=7839, ename=KING, job=PRESIDENT, mgr=0, hiredate=1981-11-17, sal=5000.00, comm=null, deptno=10], Emp [empno=7934, ename=MILLER, job=CLERK, mgr=7782, hiredate=1982-01-23, sal=1300.00, comm=null, deptno=10]]], Dept [deptno=20, dname=RESEARCH, loc=DALLAS, emps=[Emp [empno=7369, ename=SMITH, job=CLERK, mgr=7902, hiredate=1980-12-17, sal=800.00, comm=null, deptno=20], Emp [empno=7566, ename=JONES, job=MANAGER, mgr=7839, hiredate=1981-04-02, sal=2975.00, comm=null, deptno=20], Emp [empno=7788, ename=SCOTT, job=ANALYST, mgr=7566, hiredate=1987-04-19, sal=3000.00, comm=null, deptno=20], Emp [empno=7876, ename=ADAMS, job=CLERK, mgr=7788, hiredate=1987-05-23, sal=1100.00, comm=null, deptno=20], Emp [empno=7902, ename=FORD, job=ANALYST, mgr=7566, hiredate=1981-12-03, sal=3000.00, comm=null, deptno=20]]], Dept [deptno=30, dname=SALES, loc=CHICAGO, emps=[Emp [empno=7499, ename=ALLEN, job=SALESMAN, mgr=7698, hiredate=1981-02-20, sal=1600.00, comm=300.00, deptno=30], Emp [empno=7521, ename=WARD, job=SALESMAN, mgr=7698, hiredate=1981-02-22, sal=1250.00, comm=500.00, deptno=30], Emp [empno=7654, ename=MARTIN, job=SALESMAN, mgr=7698, hiredate=1981-09-28, sal=1250.00, comm=1400.00, deptno=30], Emp [empno=7698, ename=BLAKE, job=MANAGER, mgr=7839, hiredate=1981-05-01, sal=2850.00, comm=null, deptno=30], Emp [empno=7844, ename=TURNER, job=SALESMAN, mgr=7698, hiredate=1981-09-08, sal=1500.00, comm=0.00, deptno=30], Emp [empno=7900, ename=JAMES, job=CLERK, mgr=7698, hiredate=1981-12-03, sal=950.00, comm=null, deptno=30]]], Dept [deptno=40, dname=OPERATIONS, loc=BOSTON, emps=[Emp [empno=0, ename=null, job=null, mgr=0, hiredate=null, sal=null, comm=null, deptno=40]]], Dept [deptno=50, dname=DEVELOPMENT, loc=JAP, emps=[Emp [empno=0, ename=null, job=null, mgr=0, hiredate=null, sal=null, comm=null, deptno=50]]]]
DEBUG [main] - Closing JDBC Connection [[email protected]]
DEBUG [main] - Returned connection 523691575 to pool.

这样我们就得到了这个题的正解了.

上面其实就是我们要说的高级映射中的一种情况:一对多的情况.还有一对一的情况.其实当我们吃透上面的一对多的案例后,一对一的高级映射也是很相似的.

那给大家留一个关于一对一的思考题:同样是上面的两张表,我想查询出每个员工的个人信息和其部门信息.那如何做呢?下一篇我再将答案贴出来..