一 、基础回顾
1)导入jar包
2)准备配置文件
3)domain+表
4)实体的映射文件需要导入核心配置文件
5)抽取util,方便管理sqlSession
6)写dao层crud,dao层impl类实现方法,通过session调用命名空间中的sql方法来实现
7)在实体映射中sql语句,注意和接口中的方法对应
8)测试
二、mybatis映射器
- 使用mybatis要想完成一个domain持久化操作,为它写一个dao实现,在里通过SqlSession调用我们映射sql来完成操作.
- 其实只要我们给出dao层方法就可以动态产生实现类.–其实mybati就已经提供了这个机制.-这个就是映射器.
- 只需要写一个接口xxMapper,然后在映射关系xml中的命名空间引用这个接口就可以完成方法
三、测试高级查询,使用映射器,动态SQL-where if sql include
- 准备员工表t_employee
新建mapper包,EmployeeMapper接口,定义一个高级查询的方法 - EmployeeMapper接口代码
public interface EmployeeMapper {
//根据查询条件查询满足条件的员工
List<Employee> query(EmployeeQuery employeeQuery);
}
- 创建查询类,用于封装查询条件
public class EmployeeQuery {
//模糊查询的like条件语句中的参数
private String keywords;
//最小年龄
private Integer minAge;
//最大年龄条件
private Integer maxAge;
。。getset...
}
- EmployeeMapper.xml文件,在核心mybatis文件中引入
<?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.lr.mybatis.mapper._02_querytest.mapper.EmployeeMapper">
<sql id="baseSql" >
<where>
<if test="keywords!=null and keywords!=''" >
and ( name LIKE concat('%',#{keywords},'%') OR password LIKE concat('%',#{keywords},'%'))
</if>
<if test="minAge!=null" >
and age>#{minAge}
</if>
<if test="maxAge!=null" >
<![CDATA[
and age < #{maxAge}
]]>
</if>
</where>
</sql>
<select id="query" parameterType="com.lr.mybatis.mapper._02_querytest.query.EmployeeQuery" resultType="Employee" >
SELECT * FROM t_employee
<include refid="baseSql" ></include>
</select>
</mapper>
分析:
- 需要执行的sql语句
select * from t_employee
where (name like '%条件1%' or password like '%条件2%') and age > minAge and age < maxAge
- 一些细节
<!--
List<Employee> query(EmployeeQuery employeeQuery);
1 where:里面所有的条件如果都在前面加上and,并且最后会把第一个and替换为where
2 if 判断条件是否满足,如果是 就用and拼接
3 模糊查询
方案1: 使用# sql语句中条件会被编译成?,查询失败
and ( name like '%#{keywords}%' or password like '%#{keywords}%' )
方案2:用$ 可以使用 但是问题是会造成:sql注入 问题
and ( name like '%${keywords}%' or password like '%${keywords}%' )
方案3:要使用#取值 用mysql中字符串拼接函数concat 将符号拼接起来
and ( name like concat('%',#{keywords},'%') or password like '%${keywords}%' )
4 如果有特殊符号比如大于符号
方案1:转义符号 很麻烦
方案2:cdata区,可以原样输出<![CDATA[and age < #{maxAge}]]>
5 如果语句被多个地方调用可以使用<sql></sql> 在sql语句方法中调用使用include,即可
-->
- 测试
使用方法直接使用session得到这个接口的代理实现类
SqlSession sqlSession = MybatisUtil.INSTANCE.openSqlSession();
EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
List<Employee> list = mapper.query(query);
System.out.println(list);
四、 关联映射(结果映射)
4.1 为什么需要结果映射
1)数据库表字段名和java对象属性名不一致
2)不管什么关系的关联对象的查询都需要使用resultMap
4.2 关联映射分类
一对一,多对一,一对多,多对多
一对一和多对一都是处理一方(association ),一对多和多对多都是处理多方(collection)集合
4.3 处理方式
1)嵌套结果(join)-只需要发送1条sql
2)嵌套查询-1+N条sql,先查询自己,再通过自己里面外键查询关联方
能用嵌套结果就用它,因为效率高
4.4 如果需要嵌套加上分页,只能使用嵌套查询,因为嵌套结果会将数据合并
五 、多对一的嵌套查询
- 准备dept,employee表
- 实体类
public class Dept {
private Long id;
private String name;
省略getset
}
//关系:多对一
//多个员工对应一个部门
public class Employee {
private Long id;
private String name;
private String password;
private Integer age;
//多对一关系 省略getset
private Dept dept;
}
为了方便测试,不写接口mapper,直接用session调用命名空间
<?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.lr.mybatis.mapper._03_many2one_result.mapper.EmployeeMapper">
<!-- 多对一嵌套结果:只发送1条sql,需要手动封装
1) 不能使用resultType,需要查询关联对象的信息,使用resultMap
2) 在sql中使用resultMap,并且取别名,resultMap中的type是真正查询的对象
<id property="id" column="id" ></id> 查询的对象的id
<result property="username" column="name"></result>
property 是查询的对象中的字段 column是查询的sql语句的字段名
一一对应
3) resultMap中的<association>
property="dept" javaType= 一方类型, property关联对象属性名,javaType关联对象类型
-->
<resultMap id="empMap" type="com.lr.mybatis.mapper._03_many2one_result.domain.Employee" >
<id property="id" column="id" ></id>
<result property="name" column="name" ></result>
<result property="password" column="password" ></result>
<result property="age" column="age" ></result>
<association property="dept" javaType="com.lr.mybatis.mapper._03_many2one_result.domain.Dept" >
<id property="id" column="did"></id>
<result property="name" column="dname"></result>
</association>
</resultMap>
<!-- 只要是要查询关联对象的情况-->
<select id="query" resultMap="empMap" >
SELECT e.*,d.id did,d.`name`
dname FROM t_employee e LEFT JOIN
t_dept d ON e.dept_id = d.id
</select>
</mapper>
- 测试
public class MapperTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtil.INSTANCE.openSqlSession();
//SELECT e.*,d.id did,d.`name` dname FROM t_employee e LEFT JOIN t_dept d ON e.dept_id = d.id
//只发送了一条sql,手动封装
List<Employee> list = sqlSession.selectList("com.lr.mybatis.mapper._03_many2one_result.mapper.EmployeeMapper.query");
System.out.println(list);
sqlSession.close();
}
}
六、多对一的嵌套查询
<?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.lr.mybatis.mapper._04_many2one_nselect.mapper.EmployeeMapper">
<!--
多对一嵌套查询:1+N条SQL,不需要手动封装
发一条sql查询主要对象
再对查询出来的每个对象,发送多条sql查询关联对象
注意:只要是要查询关联对象,就需要在主sql中使用resultMap结果映射 不能使用resultType
-->
<resultMap id="empMap" type="com.lr.mybatis.mapper._04_many2one_nselect.domain.Employee">
<id property="id" column="id"></id>
<result property="name" column="name"></result>
<result property="password" column="password" ></result>
<result property="age" column="age" ></result>
<!--关联属性查询-->
<!-- 嵌套查询,resultMap中的<association>
column="dept_id":把查询出来的那个字段的值作为参数值传递到查询关联对象的sql方法中
select="namespace+方法id":调用查询关联对象的sql,测试写在一起,
如果是在同一个文件中可以省略namespace
-->
<association property="dept" column="dept_id" javaType="com.lr.mybatis.mapper._04_many2one_nselect.domain.Dept"
select="com.lr.mybatis.mapper._04_many2one_nselect.mapper.EmployeeMapper.queryDept">
</association>
</resultMap>
<select id="query" resultMap="empMap" >
SELECT * FROM t_employee
</select>
<!--通过id,查询部门 正常的sql语句 参数类型。。返回值类型 在resultMap中将关联对象的id传入这条sql-->
<!--忘了加where条件,报错,注意是通过id来查的-->
<select id="queryDept" parameterType="long" resultType="com.lr.mybatis.mapper._04_many2one_nselect.domain.Dept" >
SELECT * FROM t_dept WHERE id = #{id}
</select>
</mapper>
在核心mybatis配置中注意更改映射文件配置
七、一对多嵌套结果
- 实体类中添加多方字段
public class Dept {
private Long id;
private String name;
//一对多
//一个部门对应多个员工
private List<Employee> employees = new ArrayList<>();
省略getset
}
- DeptMapper.xml中的sql映射
<?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.lr.mybatis.mapper._05_one2many_result.domain.DeptMapper">
<!-- 一对多嵌套结果 只发送一条sql 嵌套结果都需要手动封装
查询出来的记录和部门的数量一样,合并
如果需要分页就需要使用嵌套查询
-->
<resultMap id="deptMap" type="com.lr.mybatis.mapper._05_one2many_result.domain.Dept" >
<id property="id" column="did"></id>
<result property="name" column="dname" ></result>
<!-- 一对多的多方 -->
<!-- resultMap中的 <collection>
部门表中的字段employees
private List<Employee> employees = new ArrayList<>();
javaType表示:多方这个集合的属性类型,所以是arrayList
offType:集合属性里面的employee对象的类型
-->
<collection property="employees" javaType="arrayList" ofType="com.lr.mybatis.mapper._05_one2many_result.domain.Employee" >
<id property="id" column="id" ></id>
<result property="name" column="name" ></result>
<result property="password" column="password"></result>
<result property="age" column="age"></result>
</collection>
</resultMap>
<!--一对多的多方-->
<!--private List<Employee> employees = new ArrayList<>();
javaType:属性类型
offType:集合属性里面内容的类型
-->
<select id="query" resultMap="deptMap">
SELECT d.id did,d.`name` dname,e.* FROM t_dept d LEFT JOIN t_employee e on d.id = e.dept_id
</select>
</mapper>
- 测试
public class MapperTest {
public static void main(String[] args) {
SqlSession sqlSession = MybatisUtil.INSTANCE.openSqlSession();
//SELECT e.*,d.id did,d.`name` dname FROM t_employee e LEFT JOIN t_dept d ON e.dept_id = d.id
//只发送了一条sql,手动封装
List<Dept> list = sqlSession.selectList("com.lr.mybatis.mapper._05_one2many_result.domain.DeptMapper.query");
list.forEach(dept -> System.out.println(dept+"员工信息"+dept.getEmployees()));
sqlSession.close();
}
}
八、一对多嵌套查询
<?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.lr.mybatis.mapper._06_one2many_nselect.domain.DeptMapper">
<!--注意嵌套结果都需要给查询的字段取别名,嵌套查询不用,column中的字段容易出错-->
<resultMap id="deptMap" type="com.lr.mybatis.mapper._06_one2many_nselect.domain.Dept" >
<id property="id" column="id"></id>
<result property="name" column="name" ></result>
<!--column是当前部门对象的id
传入关联对象的查询语句中使用
-->
<collection property="employees" column="id" javaType="arrayList" ofType="com.lr.mybatis.mapper._06_one2many_nselect.domain.Employee"
select="queryEmployee"
></collection>
</resultMap>
<!--一对多嵌套查询 发送1加n条sql-->
<select id="query" resultMap="deptMap" >
SELECT * FROM t_dept
</select>
<select id="queryEmployee" parameterType="long" resultType="com.lr.mybatis.mapper._06_one2many_nselect.domain.Employee" >
SELECT * FROM t_employee e WHERE e.dept_id=#{dept_id}
</select>
</mapper>
commons工具类,sqlsession工具
小结:
- 多对一,一对一都是操作一方
- 一对多,多对多都是操作多方list
- 如果关联对象是字段,在resultMap中使用association
- 如果关联对象是字段是集合,在resultMap中使用collection
- 注意查询出来的字段如果是别名,在column中写别名
- 嵌套结果需要手动 封装
- 嵌套查询不需要封装,但是需要在collection,使用select查询关联对象自动封装