上次说到了mybatis的全局配置文件,这次就来说说mybatis的映射文件。
一、首先看看MyBatis映射文件是什么?
1)MyBatis真正强大在乎它的映射文件。由于它的异常强大,映射器的XML文件就显得相对简单。如果拿它跟具有相同功能的JDBC代码进行对比,会发现省掉了近95%的代码。MyBatis就是针对SQL构建的,并且比普通的方法做的更好。
2)SQL映射文件有很少的几个顶级元素(按照它们应该被定义的顺序):
cache - 给定命名空间的缓存配置。
cache-ref - 其他命名空间缓存配置的引用。
resultMap - 是最复杂也是最强大的元素,用来描述如何从数据库结果集中来加载对象。
parameterMap - 已废弃
sql - 可被其他语句引用的可重用语句块。
insert - 映射插入语句
update - 映射更新语句
delete - 映射删除语句
select - 映射查询语句
二、MyBatis使用insert | update | delete | select 完成CRUD
1、select
1)Mapper接口方法
public Employee getEmployeeById(Integer id);
2)Mapper映射文件
<select id="getEmployeeBuId"
resultType="com.mybatis.beans.Employee"
databaseId="mysql">
select * from employee where id = ${_parameter}
</select>
2、insert
1)Mapper接口方法
public Integer insertEmployee(Employee employee);
2)Mapper映射文件
<insert id="insertEmployee"
parameterType="com.mybatis.beans.Employee"
databaseId="mysql">
insert into employee(name,email,gender)
values(#{name},${email},#{gender})
</insert>
3、update
1)Mapper接口方法
public Boolean updateEmployee(Employee employee);
2)Mapper映射文件
<update id="updateEmployee">
update employee
set name = #{name},email = #{email},gender = #{gender}
where id = #{id}
</update>
4、delete
1)Mapper接口方法
public void deleteEmployeeById(Integer id);
2)Mapper映射文件
<delete id="deleteEmployeeById">
delete from employee where id = #{id}
</delete>
三、主键生成方式、获取主键值
1、主键生成方式
1)支持主键自增,例如MySQL数据库
2)不支持主键自增,例如Oracle数据库
需求:插入一条新数据,立马查询这条数据。
2、获取主键值
1)若数据库支持自动生成主键的字段(比如MySQL和SQL Server),则可以设置useGeneratedKeys=“true”,然后再把keyProperty设置到目标属性上。
<insert id="insertEmployee"
parameterType="com.mybatis.beans.Employee"
databaseId="mysql"
useGeneratedKeys="true"
keyProperty="id">
insert into employee(name,email,gender)
values(#(name),#{email},${gender})
</insert>
2)而对于不支持自增型主键的数据库(例如Oracle),则可以使用selectKey子元素:selectKey元素将会会首先运行,id会被设置,然后插入语句会被调用。
<insert id="insertEmployee"
parameterType="com.mybatis.beans.Employee"
databaseId="oracle">
<selectKey oracle="BEFORE" keyProperty="id" resultType="integer">
select employee_seq.nextval from dual
</selectKey>
insert into employee(id,name,email,gender)
values(#{id},#{name},#{email},#{gender})
</insert>
或者
<insert id="insertEmployee"
parameterType="com.mybatis.beans.Employee"
databaseId="oracle">
<selectKey order="AFTER" keyProperty="id"
resultType="integer">
select employee_seq.currval from dual
</selectKey>
insert into employee(id,name,email,gender)
values(employee_seq.nextval,#{name},#{email},#{gender})
</insert>
四、参数传递
1、参数传递的方式
1)单个普通类型参数
可以接受基本类型,包装类型,字符串类型等。这种情况MyBatis可直接使用这个参数不需要经过经过任何处理。
2)多个参数
任意多个参数,都会被MyBatis重新包装成一个Map传入。Map的key是param1,param2,或者0,1…,值就是参数的值。
3)命名参数
为参数使用@Param起一个名字,MyBatis就会将这些参数封装进map中,key就是我们自己指定的名字。
4)POJO
当这些参数属于我们的业务POJO时,我们直接传递POJO。
5)Map
我们也可以封装多个参数为map,直接传递。
6)Collection/Array
会被MyBatis封装成一个map传入,Collection对应的key是collection,Array对应的key是array。如果确定是List集合,key还可以是list。
2、参数传递源码分析
1)以命名参数为例:
public Employee getEmployeeByIdAndName(@Param("id")Integer id,@Param("name")String name);
2)源码
public Object getNamedParams(Object[] args) {
final int paramCount = names.size();
if (args == null || paramCount == 0) {
return null;
} else if (!hasParamAnnotation && paramCount == 1) {
return args[names.firstKey()];
} else {
final Map<String, Object> param = new ParamMap<Object>();
int i = 0;
for (Map.Entry<Integer, String> entry : names.entrySet()) {
param.put(entry.getValue(), args[entry.getKey()]);
// add generic param names (param1, param2, ...)
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;
}
}
3、参数处理
1)参数位置支持的属性:
javaType、jdbcType、mode、numericScale、resultMap、typeHandler、jdbcTypeName、expression
2)实际上通常被设置的是:可能为空的列名jdbcType,例如:
insert into employee(id,name,email,gender)
values(employee_seq.nextval,#{lastName,jdbcType=NULL },#{email},#{gender})
4、参数的获取方式
1)#{key}:获取参数的值,预编译到SQL中。安全。
2)&{key}:获取参数的值,拼接到SQL中。有SQL注入问题。
五、select查询的几种情况
1)查询单行数据返回单个对象
public Employee getEmployeeById(Integer id);
2)查询多行数据返回对象的集合
public List<Employee> getAllEmps();
3)查询单行数据返回Map集合
public Map<String,Object> getEmployeeByIdReturnMap(Integer id );
4)查询多行数据返回Map集合
@MapKey("id") // 指定使用对象的哪个属性来充当map的key
public Map<Integer,Employee> getAllEmpsReturnMap();
六、resultType自动映射
1)autoMappingBehavior默认是PARTIAL,开启自动映射的功能。唯一的要求时结果集列名和javaBean属性名一致。
2)如果autoMappingBehavior设置为null,则会取消自动映射。
3)数据库字段命名规范,POJO属性符合驼峰命名法,如A_COLUMN_aColumn,我们可以开启自动驼峰命名规则映射功能,mapUnderscoreToCamelCase=true。
七、resultMap自定义映射
1)自定义resultMap,实现高级结果集映射。
2)id:用于完成主键值的映射。
3)association:一个复杂的类型关联,许多结果将包成这种类型。
4)collection:复杂类型的集。
1、id & result
属性 | 解释 |
---|---|
property | 映射到列结果的字段或属性。例如:“username” 或 “address.street.number” |
colunm | 数据表的列名。通常和resultSet.getString(columnName)的返回值一致。 |
javaType | 一个Java类的完全限定名,或一个类型别名。如果映射到一个JavaBean,MyBatis通常可以断定类型 |
jdbcType | JDBC类型是仅仅需要对插入,更新和删除操作可能为空的列进行处理。 |
typeHandler | 类型处理器。使用这个属性,可以覆盖默认的类型处理器。这个属性值是类的完全限定名或者是一个类型处理器的实现,或者是类型别名。 |
<select id="getEmployeeById" resultMap="myEmp">
select id,name,email,gender
from employee
where id = #{id}
</select>
<resultMap type="com.mybatis.beans.Employee" id="myEmp">
<id column="id" property="id">
<result column="name" property="name">
<result column="email" property="email">
<result column="gender" property="gender">
</resultMap>
2、association
1)POJO中的属性可能会是一个对象,我们可以使用联合查询,并以级联属性的方式封装对象。使用association标签定义对象的封装规则。
@Data
public class Depaartment {
private Integer id;
private String departmentName;
}
@Data
public class Employee {
private Integer id;
private Stirng lastName;
private Stirng email;
private Stirng gender;
private Department dept;
}
2)使用级联的方式
<select id="getEmployeeAndDept" resultMap="myEmpAndDept">
select e.id eid,e.name,e.email,e.gender,d.id did,d.dept_name
from employee e,dept d
where e.id=d.id and e.id=#{id}
</select>
<resultMap type="com.mybatis.beans.Employee" id="myEmpAndDept">
<id column="eid" property="id"/>
<result column="name" property="name"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!-- 级联的方式 -->
<result column="did" property="dept.id"/>
<result column="dept_name" property="dept.departmentName"/>
</resultMap>
3)Association
<resultMap type="com.mybatis.beans.Employee" id="myEmpAndDept">
<id column="eid" property="id"/>
<result column="name" property="name"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<association property="dept" javaType="com.mybatis.beans.Department">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
</association>
</resultMap>
3、association 分步查询
1)实际的开发中,对于每个实体类都应该有具体的增删改查方法,也就是dao层,因此对于查询员工信息并且将对应的部门信息也查询出来的需求,就可以通过分布的方式完成查询。
① 先通过员工的id查询员工信息
② 再通过查询出来的员工信息中的外键(部门id)查询对应的部门信息。
<select id="getEmployeeAndDeptStep" resultMap="myEmpAndDeptStep">
select id,name,email,gender,d_id
from employee where id=#{id}
</select>
<resultMap type="com.mybatis.beans.Employee" id="myEmpAndDeptStep">
<id column="id" property="id" />
<result column="name" property="name"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<association property="dept" select="com.mybatis.dao.DepartmentMapper.getDeptById"
column="d_id" fetchType="eager">
</association>
</resultMap>
4、association 分步查询使用延迟加载
1)在分步查询的基础上,可以使用延迟加载来提升查询的效率,只需要在全局的setting中进行如下的配置:
<!-- 开启延迟加载 -->
<setting name="lazyLoadingEnabled" value="true" />
<!-- 设置的加载的数据是按需还是全部 -->
<setting name="aggressiveLazyLoading" value="false" />
5、collection
1)POJO中的属性可能会是一个集合对象,我们可以使用联合查询,并以级联属性的方式封装对象,使用collection标签定义对象的封装规则。
@Data
public class Department {
private Integer id ;
private String departmentName ;
private List<Employee> emps ;
}
2)Collection
<select id="getDeptAndEmpsById" resultMap="myDeptAndEmps">
SELECT d.id did, d.dept_name ,e.id eid ,e.name ,e.email,e.gender
FROM dept d LEFT OUTER JOIN employee e ON d.id = e.d_id
WHERE d.id = #{id}
</select>
<resultMap type="com.mybatis.beans.Department" id="myDeptAndEmps">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
<!--
property: 关联的属性名
ofType: 集合中元素的类型
-->
<collection property="emps" ofType="com.mybatis.beans.Employee">
<id column="eid" property="id"/>
<result column="name" property="name"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</collection>
</resultMap>
6、collection分步查询
1)实际的开发汇总,对于每个实体类都应该有具体的增删改查方法,也就是dao层,因此,对于查询部门信息并且将对应的所有的员工信息也查询出来的需求,就可以通过分步的方式完成查询。
① 先通过部门的id查询部门信息。
② 再通过部门id作为员工的外键查询对应的部门信息。
<select id="getDeptAndEmpsByIdStep" resultMap="myDeptAndEmpsStep">
select id ,dept_name from tbl_dept where id = #{id}
</select>
<resultMap type="com.mybatis.beans.Department" id="myDeptAndEmpsStep">
<id column="id" property="id"/>
<result column="dept_name" property="departmentName"/>
<collection property="emps"
select="com.mybatis.dao.EmployeeMapper.getEmpsByDid"
column="id">
</collection>
</resultMap>
扩展:
1、分步查询多列值的传递
1)如果分步查询时,需要传递给调用的查询汇总多个参数,则需要将多个参数封装成Map来进行传递,语法如下:{k1=v1, k2=v2 …}
2)在所调用的查询方,取值时就要参考Map的取值方式,需要严格的按照封装map时所有的key来取值。
2、association或collection的fetchType属性
1)在<association>
和<collection>
标签汇总都可以设置fetchType,指定本次查询是否要使用延迟加载。默认为fetchType="lazy"
,如果本次的查询不想使用延迟加载,则可设置为fetchType=”eager”
。
2)fetchType可以灵活的设置查询是否需要使用延迟加载,而不需要因为某个查询不想使用延迟加载将全局的延迟加载设置关闭。