一、映射文件中的元素及属性
1.元素种类
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):
cache
– 该命名空间的缓存配置。cache-ref
– 引用其它命名空间的缓存配置。resultMap
– 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。parameterMap
– 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!sql
– 可被其它语句引用的可重用语句块。insert
– 映射插入语句。update
– 映射更新语句。delete
– 映射删除语句。select
– 映射查询语句。
2.主要元素介绍
①mapper
在该元素中用来书写一个映射所需的元素,其中的namespace属性用来标识一个命名空间
②select
该元素用来映射一个查询语句,简单示例如下:
<select id="selectOneById" resultType="com.lazy.bean.Employee">
select id,user_name username,email from employee where id = #{id}
</select>
其中常用属性有:
id属性:用来在命名空间中唯一标识一条sql查询语句;
resultType属性:用来查询结果的放回值类型;
resultMap属性:自定义结果映射;
其中resultType和resultMap只能存在一个
#{id}<!-- 在mapper映射文件中为占位符 -->
③insert、update、delete
他们分别用来映射不同的sql语句,用法同select相同,但是没有返回值;
④sql
该元素用来提取可重复使用的sql映射语句,通过include元素调用,同时可在其中设置参数的值,示例如下:
<sql id="userColumns"> ${alias}.id,${alias}.username,${alias}.password </sql>
<select id="selectUsers" resultType="map">
select
<include refid="userColumns"><property name="alias" value="t1"/></include>,
<include refid="userColumns"><property name="alias" value="t2"/></include>
from some_table t1
cross join some_table t2
</select>
3.主键生成方法
若数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),则可以设置useGeneratedKeys=”true”,然后再把
keyProperty 设置到目标属性上,示例如下:
<insert id="addOne" useGeneratedKeys="true" keyProperty="id" databaseId="mysql">
insert into employee (user_name,email) values (#{username},#{email})
</insert>
public void test01(){
SqlSession sqlSession = null;
try {
sqlSession = getSqlSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = new Employee(null, "jerry", "jerry@qq.com");
mapper.addOne(employee);
System.out.println(employee);
sqlSession.commit();
} catch (IOException e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
4.参数传递
①单个参数
如果参数只有一个且不为我们的数据模型可以直接获取,不论是基本数据类型还是对象类型集合类型且不涉及到属性的引用时,mybatis不做特殊处理,示例如下:
<select id="selectOneById" resultType="com.lazy.bean.Employee">
select id,user_name username,email from employee where id = #{id}
</select>
②多个参数
mybatis会做特殊处理,多个参数会被封装成一个map,
其中key:param1,param2,…,paramN,或者参数的索引也可以即1,2,…,N;
value:按照你所写顺序的参数值;
命名参数:
在有多个参数时,我们可以通过一个注解**@Param**来为参数指定key,在调用参数时就可以使用该指定的key获取;
示例如下:
sql映射文件:
<!-- Employee selectOneByIdAndUsername(Integer id, String username);-->
<select id="selectOneByIdAndUsername" resultType="com.lazy.bean.Employee">
select id,user_name username,email from employee where id = #{param1} and user_name = #{username}
</select>
EmployeeMapper接口:
Employee selectOneByIdAndUsername(Integer id, @Param("username") String username);
测试:
@Test
public void test02(){
SqlSession sqlSession = null;
try {
sqlSession = getSqlSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.selectOneByIdAndUsername(1, "tom");
System.out.println(employee);
} catch (IOException e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
③POJO参数
如果参数是一个我们业务逻辑的数据模型,我们可以直接传入pojo,
单个参数:通过#{属性名}:取出传入的pojo属性值;
多个参数:通过#{对象的key.属性名}:取出传入的值
示例如下:
单个参数:
<!-- int addOne(Employee employee);-->
<insert id="addOne" useGeneratedKeys="true" keyProperty="id" databaseId="mysql">
insert into employee (user_name,email) values (#{username},#{email})
</insert>
多个参数:
<!-- void updateOneById(Integer id,@Param("emp") Employee employee);-->
<update id="updateOneById">
update employee set user_name = #{emp.username},email = #{emp.email} where id = #{param1}
</update>
④map作为参数
如果多个参数不是我们业务逻辑中的数据模型,没有对应的pojo,不经常使用,为了方便,我们也可以传入map:
#{map中的key}:取出传入的参数值
示例如下:
<!-- void updateOneByUsername(Map<String,Object> map);-->
<update id="updateOneByUsername">
update employee set email = #{email} where user_name = #{username}
</update>
⑤参数为TO
如果有多个参数不是我们业务逻辑中的数据模型,没有对应的pojo,经常使用,我们可以来编写一个TO数据传输对象;
⑥参数为Collection(Set,List)类型或者是数组
会特殊处理。也是把传入的list或者数组封装在map中。
key:Collection(collection),如果是List还可以使用这个key(list),数组(array)
⑦参数位置支持的属性
javaType、jdbcType、mode、numericScale、resultMap、typeHandler、jdbcTypeName;
前两种属性比较常用:javaType可以指定传入的Java数据类型,jdbcType指定对应的sql中的数据类型;
当传入Java数据类型是null时,由于mybatis默认的null对应的sql数据类型时OTHER,而oracle数据库无法识别,需要进行处理,有两种处理方案:
第一种:
在配置参数时指定jdbcType:#{参数,jdbcType=OTHER};
第二种:
在全局配置文件中设置:
<setting name="jdbcTypeForNull" value="NULL"/>
⑧参数传递的两种方式#{}和${}:
#{}:可以获取map中的值或者pojo对象属性的值;
${}:可以获取map中的值或者pojo对象属性的值;
区别:
#{}:是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入
${}:取出的值直接拼装在sql语句中;会有安全问题;
大多情况下,我们去参数的值都应该去使用#{};
5.select返回值处理
①返回值是POJO
当返回值为POJO类型时,我们可以在select元素中将resultType设置为该POJO的全类名,并且查询的列的别名与POJO属性名一致,此时mybatis就会将查询的结果封装到POJO中;
示例如下:
<select id="selectOneById" resultType="com.lazy.bean.Employee">
select id,user_name username,email from employee where id = #{id}
</select>
②返回值为Collection类型其中的泛型为POJO
当返回值为Collection类型,我们只需在select元素中将resultType设置为集合中元素的类型即可,要注意别名和属性名一致,示例如下:
sql映射文件:
<!-- List<Employee> selectAll(); mapper接口中的方法-->
<select id="selectAll" resultType="com.lazy.bean.Employee">
select id,user_name username,email from employee
</select>
测试代码:
@Test
public void test05(){
SqlSession sqlSession = null;
try {
sqlSession = getSqlSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
List<Employee> employees = mapper.selectAll();
for (Employee employee : employees) {
System.out.println(employee);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
测试结果:
③返回值为一个map
返回值为一个map时,mybatis会将查询的结果中的列名作为key,值作为value封装在map中,resultType直接填写map(为Map的别名),示例如下:
sql映射文件:
<!-- Map<String,Object> selectOneByIdReturnMap(Integer id);-->
<select id="selectOneByIdReturnMap" resultType="map">
select id,user_name username,email from employee where id = #{id}
</select>
测试结果:
④自定义结果映射
自定义结果映射即设置select元素中的resultMap属性,通过编写resultMap元素,并将编写的resultMap的id传入到select元素resultMap属性中即可;
接下来将详细介绍resultMap的使用:
第一类,基本查询:
<!-- 使用resultMap来处理返回值 -->
<resultMap id="oneEmp" type="com.lazy.bean.Employee">
<id column="id" property="id"/>
<result column="user_name" property="username"/>
<result column="email" property="email"/>
</resultMap>
<select id="selectOneById" resultMap="oneEmp">
select id,user_name ,email from employee where id = #{id}
</select>
resultMap元素中有两属性:
id:为该resultMap的唯一标识;
type:返回的查询结果对应的POJO;
resultMap中的id元素用来标识主键:
column属性:用来标识查询结果对应的列的别名;
property属性:用来标识POJO的属性名;
result和id效果一致,即表示查询结果要对应的属性,不过主键使用id时能够提高效率;
注意:其他不指定的列会自动封装:我们只要写resultMap就把全部的映射规则都写上;
第二类,级联查询:
不使用association:
<resultMap id="unionQuery1" type="com.lazy.bean.Employee">
<id column="id" property="id"/>
<result column="user_name" property="username"/>
<result column="email" property="email"/>
<result column="dept_id" property="dep.deptId"/>
<result column="dept_name" property="dep.deptName"/>
</resultMap>
使用association:
<resultMap id="unionQuery2" type="com.lazy.bean.Employee">
<id column="id" property="id"/>
<result column="user_name" property="username"/>
<result column="email" property="email"/>
<association property="dep" javaType="com.lazy.bean.Department">
<result column="dept_id" property="deptId"/>
<result column="dept_name" property="deptName"/>
</association>
</resultMap>
**注意:**association中的property属性表示要对应的POJO中的属性名,javaType必填与属性值的对象类型对应
association来进行分步查询:
<resultMap id="stepQuery" type="com.lazy.bean.Employee">
<id column="id" property="id"/>
<result column="user_name" property="username"/>
<result column="email" property="email"/>
<association property="dep" select="com.lazy.mapper.DepartmentMapper.getOneById" column="department_id"></association>
</resultMap>
<select id="selectEmpWithDeptById" resultMap="stepQuery">
select id,user_name,email,department_id from employee where id = #{id}
</select>
**注意:**association中的select属性表示相应接口方法对应的mapper的sql语句,column表示将所查询的列作为参数传递到sql映射中去,即以department_id作为com.lazy.mapper.DepartmentMapper.getOneById方法的参数传入;
使用分步查询之后可以通过设置启动延迟加载,延迟加载即当我们要使用到对象的某个属性时才会加载sql语句进行查询,不使用时不会加载该sql语句,只要进行如下配置:
在mybatis全局配置文件中:
<!-- 开启懒加载或延时加载 -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 关闭侵入式加载 -->
<setting name="aggressiveLazyLoading" value="false"/>
测试代码:
@Test
public void test10(){
SqlSession sqlSession = null;
try {
sqlSession = getSqlSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.selectEmpWithDeptById(1);
System.out.println(employee.getUsername());
// System.out.println(employee.getDep());
} catch (IOException e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
测试结果:
测试代码2:
@Test
public void test10(){
SqlSession sqlSession = null;
try {
sqlSession = getSqlSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
Employee employee = mapper.selectEmpWithDeptById(1);
System.out.println(employee.getUsername());
System.out.println(employee.getDep());
} catch (IOException e) {
e.printStackTrace();
} finally {
sqlSession.close();
}
}
测试结果2: