1. 动态SQL
概念: 动态SQL指的是在SQL块中使用动态标签来完成选择判断,循环等操作。
1.1 动态条件标签
概念: 条件标签配合OGNL表达式用于判断是否允许拼接部分指定的SQL语句:
<if>:用于单独条件判断:- 使用
test属性设置条件,OGNL表达式建议用单引号,以免冲突表达式中的字符串。
- 使用
<choose>:用于多选一条件判断:- 子标签
<when>使用test属性设置条件,OGNL表达式建议用单引号,以免冲突表达式中的字符串。 - 子标签
<otherwise>用于设置所有条件均不满足时的处理。
- 子标签
<where>:WHERE语句不为空时,生成WHERE关键字并删除WHERE语句中第一个AND。<trim prefix="WHERE" prefixOverrides="AND|OR">:WHERE语句不为空时,生成WHERE前缀关键字并删除WHERE语句中第一个AND或OR。
源码: /mybatis3/
- res:
classpath:mybatis-dynamic.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc/db.properties"/>
<typeAliases>
<package name="com.yap.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com/yap/mapper"/>
</mappers>
</configuration>
- src:
c.y.pojo.User
/**
* @author yap
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User implements Serializable {
private Integer id;
private String name;
private Integer gender;
private Integer age;
private String info;
}
- res:
classpath:com/yap/mapper/UserMapper.xml
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.joezhou.mapper.UserMapper">
<select id="findByIf" resultType="user">
SELECT `id`, `name`, `gender`, `age`, `info` FROM `user` WHERE
<if test='name != null'>
`name` like concat('%', #{name}, '%')
</if>
<if test='gender != null'>
`gender` = #{gender}
</if>
</select>
<select id="findByWhere" resultType="user">
SELECT `id`, `name`, `gender`, `age`, `info` FROM `user`
<where>
<if test='name != null'>
AND `name` like concat('%', #{name}, '%')
</if>
<if test='gender != null'>
AND `gender` = #{gender}
</if>
</where>
</select>
<select id="findByTrim" resultType="user">
SELECT `id`, `name`, `gender`, `age`, `info` FROM `user`
<trim prefix="WHERE" prefixOverrides="AND|OR">
<if test='name != null'>
AND `name` like concat('%', #{name}, '%')
</if>
<if test='gender != null'>
AND `gender` = #{gender}
</if>
</trim>
</select>
<select id="findByChoose" resultType="user">
SELECT `id`, `name`, `gender`, `age`, `info` FROM `user`
<where>
<choose>
<when test='name != null'>
AND `name` like concat('%', #{name}, '%')
</when>
<when test='gender != null'>
AND `gender` = #{gender}
</when>
<otherwise>
AND `id` = #{id}
</otherwise>
</choose>
</where>
</select>
</mapper>
- src:
c.y.mapper.UserMapper
/**
* 根据姓名模糊以及性别精准查询用户信息
* 若姓名为null,则按性别精准查询
* 若性别为null,则按姓名模糊查询
*
* @param user 用户实体
* @return 满足条件的用户
*/
List<User> findByIf(User user);
/**
* 根据姓名模糊以及性别精准查询用户信息
* 若姓名为null,则按性别精准查询
* 若性别为null,则按姓名模糊查询
* 若姓名和性别均不为null,则按姓名模糊且按性别精准查询
* 若姓名和性别均为null,全查
*
* @param user 用户实体
* @return 满足条件的用户
*/
List<User> findByWhere(User user);
/**
* 根据姓名模糊以及性别精准查询用户信息
* 若姓名为null,则按性别精准查询
* 若性别为null,则按姓名模糊查询
* 若姓名和性别均不为null,则按姓名模糊且按性别精准查询
* 若姓名和性别均为null,全查
*
* @param user 用户实体
* @return 满足条件的用户
*/
List<User> findByTrim(User user);
/**
* 根据姓名模糊以及性别精准查询用户信息
* id不为null时
* 若姓名不为null,按姓名模糊查询
* 若姓名为null,但性别不为null,则按性别精准查询
* 若姓名和性别都为null,则按照id精准查询
*
* @param user 用户实体
* @return 满足条件的用户
*/
List<User> findByChoose(User user);
- tst:
c.y.dynamic.UserTest.findByIf()
package com.yap.dynamic;
import com.yap.mapper.UserMapper;
import com.yap.pojo.User;
import com.yap.util.MyBatisUtil;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* @author yap
*/
public class UserTest {
private SqlSessionFactory factory = MyBatisUtil.getFactory("mybatis-dynamic.xml");
@Test
public void findByIf() {
try (SqlSession session = factory.openSession()) {
UserMapper userMapper = session.getMapper(UserMapper.class);
System.out.println(userMapper.findByIf(new User(null, null, 1, null, null)));
System.out.println(userMapper.findByIf(new User(null, "王", null, null, null)));
} catch (Exception e) {
e.printStackTrace();
}
}
}
- tst:
c.y.dynamic.UserTest.findByWhere()
@Test
public void findByWhere() {
try (SqlSession session = factory.openSession()) {
UserMapper userMapper = session.getMapper(UserMapper.class);
System.out.println(userMapper.findByWhere(new User(null, null, null, null, null)));
System.out.println(userMapper.findByWhere(new User(null, null, 1, null, null)));
System.out.println(userMapper.findByWhere(new User(null, "王", null, null, null)));
System.out.println(userMapper.findByWhere(new User(null, "王", 1, null, null)));
} catch (Exception e) {
e.printStackTrace();
}
}
- tst:
c.y.dynamic.UserTest.findByTrim()
@Test
public void findByTrim() {
try (SqlSession session = factory.openSession()) {
UserMapper userMapper = session.getMapper(UserMapper.class);
System.out.println(userMapper.findByTrim(new User(null, null, null, null, null)));
System.out.println(userMapper.findByTrim(new User(null, null, 1, null, null)));
System.out.println(userMapper.findByTrim(new User(null, "王", null, null, null)));
System.out.println(userMapper.findByTrim(new User(null, "王", 1, null, null)));
} catch (Exception e) {
e.printStackTrace();
}
}
- tst:
c.y.dynamic.UserTest.findByChoose()
@Test
public void findByChoose() {
try (SqlSession session = factory.openSession()) {
UserMapper userMapper = session.getMapper(UserMapper.class);
System.out.println(userMapper.findByChoose(new User(1, "王", 1, null, null)));
System.out.println(userMapper.findByChoose(new User(1, "王", null, null, null)));
System.out.println(userMapper.findByChoose(new User(1, null, 1, null, null)));
System.out.println(userMapper.findByChoose(new User(1, null, null, null, null)));
} catch (Exception e) {
e.printStackTrace();
}
}
1.2 动态循环标签
概念: 循环标签 <foreach> 用于循环拼接部分指定的SQL语句:
collection:值固定为array或list或一个Map结构(值为数组或列表)的KEY。items:设置循环中间变量。open/close:设置循环开始/结束时拼接的字符串SQL代码。separator:设置拼接符,自动忽略SQL语句中多余的拼接符。
源码: /mybatis3/
- res:
classpath:com/yap/mapper/UserMapper.xml
<select id="findWithArray" resultType="user">
SELECT `id`, `name`, `gender`, `age`, `info` FROM `user`
<where>
<foreach collection="array" item="e" open="id in (" close=")" separator=",">
${e}
</foreach>
</where>
</select>
<select id="findWithList" resultType="user">
SELECT `id`, `name`, `gender`, `age`, `info` FROM `user`
<where>
<foreach collection="list" item="e" open="id in (" close=")" separator=",">
${e}
</foreach>
</where>
</select>
<select id="findWithMap" resultType="user">
SELECT `id`, `name`, `gender`, `age`, `info` FROM `user`
<where>
<foreach collection="ids" item="e" open="id in (" close=")" separator=",">
${e}
</foreach>
</where>
</select>
- src:
c.y.mapper.UserMapper
/**
* 找到编号为3和4和6的用户
*
* @param ids 编号数组
* @return 满足条件的用户
*/
List<User> findWithArray(int[] ids);
/**
* 找到编号为3和4和6的用户
*
* @param ids 编号列表
* @return 满足条件的用户
*/
List<User> findWithList(List<Integer> ids);
/**
* 找到编号为3和4和6的用户
*
* @param userMap 用户信息集合
* @return 满足条件的用户
*/
List<User> findWithMap(Map<String, Object> userMap);
- tst:
c.y.dynamic.UserTest.findWithArray()
@Test
public void findWithArray() {
int[] ids = {3, 4, 6};
try (SqlSession session = factory.openSession()) {
UserMapper userMapper = session.getMapper(UserMapper.class);
System.out.println(userMapper.findWithArray(ids));
} catch (Exception e) {
e.printStackTrace();
}
}
- tst:
c.y.dynamic.UserTest.findWithList()
@Test
public void findWithList() {
List<Integer> ids = new ArrayList<>();
ids.add(3);
ids.add(4);
ids.add(6);
try (SqlSession session = factory.openSession()) {
UserMapper userMapper = session.getMapper(UserMapper.class);
System.out.println(userMapper.findWithList(ids));
} catch (Exception e) {
e.printStackTrace();
}
}
- tst:
c.y.dynamic.UserTest.findWithMap()
@Test
public void findWithMap() {
List<Integer> ids = new ArrayList<>();
ids.add(3);
ids.add(4);
ids.add(6);
Map<String, Object> map = new HashMap<>(1);
map.put("ids", ids);
try (SqlSession session = factory.openSession()) {
UserMapper userMapper = session.getMapper(UserMapper.class);
System.out.println(userMapper.findWithMap(map));
} catch (Exception e) {
e.printStackTrace();
}
}
1.3 动态修改标签
概念: 修改标签 <set> 用于控制修改操作时,忽略null字段:
<set>:生成SET关键字并删除SET语句中最后一个,。<trim prefix="SET" suffixOverrides=",">:生成SET前缀关键字并删除SET语句中最后一个,。
源码: /mybatis3/
- res:
classpath:com/yap/mapper/UserMapper.xml
<update id="updateBySet">
UPDATE `user`
<set>
<if test='name != null'>`name` = #{name},</if>
<if test='age != null'>`age` = #{age},</if>
<if test='gender != null'>`gender` = #{gender},</if>
</set>
<where>
id = #{id}
</where>
</update>
<update id="updateByTrim">
UPDATE `user`
<trim prefix="SET" suffixOverrides=",">
<if test='name != null'>`name` = #{name},</if>
<if test='age != null'>`age` = #{age},</if>
<if test='gender != null'>`gender` = #{gender},</if>
</trim>
<where>
id = #{id}
</where>
</update>
- src:
c.y.mapper.UserMapper
/**
* 根据主键修改用户信息
*
* @param user 用户实体
*/
void updateBySet(User user);
/**
* 根据主键修改用户信息
*
* @param user 用户实体
*/
void updateByTrim(User user);
- tst:
c.y.dynamic.UserTest.updateBySet()
@Test
public void updateBySet() {
SqlSession session = factory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
userMapper.updateBySet(new User(1, "赵四", null, null, null));
userMapper.updateBySet(new User(1, null, null, 100, null));
session.commit();
} catch (Exception e) {
session.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
- tst:
c.y.dynamic.UserTest.updateByTrim()
@Test
public void updateByTrim() {
SqlSession session = factory.openSession();
try {
UserMapper userMapper = session.getMapper(UserMapper.class);
userMapper.updateByTrim(new User(1, "刘能", null, null, null));
userMapper.updateByTrim(new User(1, null, null, 0, null));
session.commit();
} catch (Exception e) {
session.rollback();
e.printStackTrace();
} finally {
session.close();
}
}
1.4 多参数接口
概念:
@Param:用于对接口参数起别名,仅在客户通过接口方法调用语句块时生效:- OGNL表达式和占位符中仅支持别名或
param123..。
- OGNL表达式和占位符中仅支持别名或
_parameter:SQL语句块中的内置参数,可以在OGNL表达式中直接使用:- 接口单个简单参数时,
_parameter即为该参数,使用#{param1}获取。 - 接口多个简单参数时,
_parameter.get("param1")即为第一个参数,使用#{param1}获取。 - 接口单个实体类参数时,直接使用实体类中的属性进行判断和获取。
- 接口多个实体类参数时,
_parameter.param1.name即为第一个实体类的name属性,使用#{param1.name}获取。 - OGNL表达式和占位符中的
param12..均可使用@Param设置的别名替代,但不建议。
- 接口单个简单参数时,
源码: /mybatis3/
- res:
classpath:com/yap/mapper/UserMapper.xml
<select id="findByNameAndGender" resultType="user">
SELECT `id`, `name`, `gender`, `age`, `info` FROM `user`
<where>
<if test='param1 != null'>
AND `name` like concat('%', #{param1}, '%')
</if>
<if test='param2 != null'>
AND `gender` = #{param2}
</if>
</where>
</select>
<select id="findBySingleParam" resultType="user">
SELECT `id`, `name`, `gender`, `age`, `info` FROM `user`
<where>
<if test='_parameter != null'>
AND `name` like concat('%', #{param1}, '%')
</if>
</where>
</select>
<select id="findByMultipleParam" resultType="user">
SELECT `id`, `name`, `gender`, `age`, `info` FROM `user`
<where>
<if test='_parameter.get("param1") != null'>
AND `name` like concat('%', #{param1}, '%')
</if>
<if test='_parameter.get("param2") != null'>
AND `gender` = #{param2}
</if>
</where>
</select>
<select id="findByPojoParam" resultType="user">
SELECT `id`, `name`, `gender`, `age`, `info` FROM `user`
<where>
<if test='_parameter.param1 != null'>
<if test='_parameter.param1.name != null'>
AND `name` like concat('%', #{param1.name}, '%')
</if>
<if test='_parameter.param2.gender != null'>
AND `gender` = #{param2.gender}
</if>
</if>
</where>
</select>
- src:
c.y.mapper.UserMapper
/**
* 根据姓名模糊以及性别精准查询用户信息
*
* @param name 用户姓名
* @param gender 用户性别
* @return 满足条件的用户
*/
List<User> findByNameAndGender(@Param("aa") String name, @Param("bb") int gender);
/**
* 根据姓名模糊查询用户信息
*
* @param name 姓名
* @return 符合条件的用户信息
*/
List<User> findBySingleParam(@Param("aa") String name);
/**
* 根据姓名模糊和性别精准查询用户信息
*
* @param name 姓名
* @param gender 性别
* @return 符合条件的学生信息
*/
List<User> findByMultipleParam(@Param("aa") String name, @Param("bb") int gender);
/**
* 根据姓名模糊和性别精准查询用户信息
*
* @param userA 用户实体
* @param userB 用户实体
* @return 符合条件的学生信息
*/
List<User> findByPojoParam(@Param("aa") User userA, @Param("bb") User userB);
- tst:
c.y.dynamic.UserTest.findByNameAndGender()
@Test
public void findByNameAndGender() {
try (SqlSession session = factory.openSession()) {
UserMapper userMapper = session.getMapper(UserMapper.class);
System.out.println(userMapper.findByNameAndGender("王", 1));
} catch (Exception e) {
e.printStackTrace();
}
}
- tst:
c.y.dynamic.UserTest.findBySingleParam()
@Test
public void findBySingleParam() {
try (SqlSession session = factory.openSession()) {
UserMapper userMapper = session.getMapper(UserMapper.class);
System.out.println(userMapper.findBySingleParam("王"));
} catch (Exception e) {
e.printStackTrace();
}
}
- tst:
c.y.dynamic.UserTest.findByMultipleParam()
@Test
public void findByMultipleParam() {
try (SqlSession session = factory.openSession()) {
UserMapper userMapper = session.getMapper(UserMapper.class);
System.out.println(userMapper.findByMultipleParam("王", 0));
} catch (Exception e) {
e.printStackTrace();
}
}
- tst:
c.y.dynamic.UserTest.findByPojoParam()
@Test
public void findByPojoParam() {
User userA = new User(null, "王", null, null, null);
User userB = new User(null, null, 0, null, null);
try (SqlSession session = factory.openSession()) {
UserMapper userMapper = session.getMapper(UserMapper.class);
System.out.println(userMapper.findByMultiplePojoParam(userA, userB));
} catch (Exception e) {
e.printStackTrace();
}
}
2. 多表查询
概念: mybatis多表间支持一对一和一对多两种关系:
- 从员工看部门,是一对一关系,一个员工只能指定一个部门。
- 从部门看员工,是一对多关系,一个部门可以指定多个员工。
源码: /mybatis3/
- res:
练习sql
CREATE TABLE IF NOT EXISTS `dept`
(
`deptno` INT(2) NOT NULL PRIMARY KEY,
`dname` VARCHAR(14) NULL COMMENT '部门名',
`loc` VARCHAR(13) NULL COMMENT '部门地址'
)
COMMENT '部门表';
INSERT INTO mybatis.`dept`
VALUES (10, 'ACCOUNTING', 'NEW YORK'),
(20, 'RESEARCH', 'DALLAS'),
(30, 'SALES', 'CHICAGO'),
(40, 'OPERATIONS', 'BOSTON');
COMMIT;
CREATE TABLE IF NOT EXISTS `emp`
(
`empno` INT(4) NOT NULL PRIMARY KEY,
`ename` VARCHAR(10) NULL COMMENT '员工姓名',
`job` VARCHAR(9) NULL COMMENT '员工工作',
`mgr` INT(4) NULL COMMENT '上级领导编号',
`hiredate` DATE NULL COMMENT '入职日期',
`sal` INT(7) NULL COMMENT '员工月薪',
`comm` INT(7) NULL COMMENT '员工补助',
`deptno` INT(2) NULL COMMENT '部门表外键',
CONSTRAINT FK_EMP_DEPT
FOREIGN KEY (`deptno`) REFERENCES `dept` (`deptno`)
)
COMMENT '员工表';
INSERT INTO mybatis.`emp`
VALUES (7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 800, null, 20),
(7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 1600, 300, 30),
(7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250, 500, 30),
(7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 2975, null, 20),
(7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250, 1400, 30),
(7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 2850, null, 30),
(7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2450, null, 10),
(7788, 'SCOTT', 'ANALYST', 7566, '1987-04-19', 3000, null, 20),
(7839, 'KING', 'PRESIDENT', null, '1981-11-17', 5000, null, 10),
(7844, 'TURNER', 'SALESMAN', 7698, '1981-09-08', 1500, 0, 30),
(7876, 'ADAMS', 'CLERK', 7788, '1987-05-23', 1100, null, 20),
(7900, 'JAMES', 'CLERK', 7698, '1981-12-03', 950, null, 30),
(7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3000, null, 20),
(7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1300, null, 10);
COMMIT;
- res:
classpath:mybatis-join.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<properties resource="jdbc/db.properties"/>
<!--延迟加载 :分步查询时解决一次主查询伴随N次子查询的N+1问题: -->
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
<typeAliases>
<package name="com.yap.pojo"/>
</typeAliases>
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<package name="com/yap/mapper"/>
</mappers>
</configuration>
- src:
c.y.pojo.Dept
package com.yap.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.List;
/**
* @author yap
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept implements Serializable {
private Integer deptno;
private String dname;
private String loc;
private List<Emp> emps;
}
- src:
c.y.pojo.Emp
package com.yap.pojo;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.io.Serializable;
import java.util.Date;
/**
* @author yap
*/
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Emp implements Serializable {
private Integer empno;
private String ename;
private String job;
private Integer sal;
private Integer comm;
private Integer mgr;
private Date hiredate;
private Dept deptno;
}
2.1 一对一联表
概念: 在员工实体中埋一个部门实体属性:
- JOIN查询:SQL块中使用
JOIN,返回值使用<resultMap>映射:- 使用
<association>的javaType/property映射实体类外键属性类型/属性名。 - 使用
<association>的jdbcType/column映射数据库外键字段类型/字段名。 - 使用
<id>/<result>映射<resultMap>/<association>中对应实体的全部非外键属性。 - 配置对应接口方法并测试。
- 使用
- 分步查询:SQL块中使用全查(1步SQL块),返回值使用
<resultMap>映射:- 分步查询时可省略所有同名的非外键属性的映射。
- 在部门SQL配置文件中开发按主键查部门的2步SQL块,可直接用1步SQL块的外键。
- 额外使用
<association select="工作空间.id">调用2步SQL块。 - 配置对应接口方法并测试,其中2步SQL块无需配置对应接口方法。
- 使用注解:可简化分步查询的配置,省略所有SQL块,但SQL配置文件仍需保留:
- 使用
@Results/@Result/@Result(id=true)替代<resultMap>/<result>/<id>。 - 使用
@Result(one=@One(""))替代<association select="">。
- 使用
- 延迟加载:分步查询时可在
<settings>配置懒加载以解决一次主查询附带N次子查询的N+1问题:<setting name="lazyLoadingEnabled" value="true"/>:开启延迟加载。<setting name="aggressiveLazyLoading" value="false"/>: 关闭积极加载。
1对1 JOIN查询
源码: /mybatis3/
- src:
c.y.mapper.EmpMapper
package com.yap.mapper;
/**
* @author yap
*/
public interface EmpMapper {
/**
* 查询所有员工,同时查出员工所在部门信息
*
* @return 所有员工及其部门信息
*/
List<Emp> findWithDeptByJoin();
}
- res:
classpath:com/yap/mapper/EmpMapper.xml
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yap.mapper.EmpMapper">
<cache readOnly="true"/>
<resultMap id="emp-dept-join" type="emp">
<id column="empno" property="empno"/>
<result column="ename" property="ename"/>
<result column="job" property="job"/>
<result column="sal" property="sal"/>
<result column="comm" property="comm"/>
<result column="mgr" property="mgr"/>
<result column="hiredate" property="hiredate"/>
<!-- Emp 需要 private Dept deptno;-->
<association javaType="dept" property="deptno" column="deptno" jdbcType="INTEGER">
<id column="deptno" property="deptno"/>
<result column="dname" property="dname"/>
<result column="loc" property="loc"/>
</association>
</resultMap>
<select id="findWithDeptByJoin" resultMap="emp-dept-join">
SELECT e.`empno`,
e.`ename`,
e.`job`,
e.`mgr`,
e.`hiredate`,
e.`sal`,
e.`comm`,
d.`deptno`,
d.`dname`,
d.`loc`
FROM `emp` e
JOIN `dept` d ON e.`deptno` = d.`deptno`
</select>
</mapper>
- tst:
c.y.join.EmpTest.findWithDeptByJoin()
package com.yap.join;
/**
* @author yap
*/
public class EmpTest {
private SqlSessionFactory factory = MyBatisUtil.getFactory("mybatis-join.xml");
@Test
public void findWithDeptByJoin() {
try (SqlSession session = factory.openSession()) {
EmpMapper empMapper = session.getMapper(EmpMapper.class);
for (Emp emp : empMapper.findWithDeptByJoin()) {
System.out.printf("%s 在 %s 部门\n", emp.getEname(), emp.getDeptno().getDname());
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
1对1 分步查询
- src:
c.y.mapper.EmpMapper
/**
* 查询所有员工,同时查出员工所在部门信息
*
* @return 所有员工及其部门信息
*/
List<Emp> findWithDeptBySelect();
- res:
classpath:com/yap/mapper/EmpMapper.xml
<resultMap id="emp-dept-select" type="emp">
<association javaType="dept" property="deptno" column="deptno" jdbcType="INTEGER"
select="com.yap.mapper.DeptMapper.findByDeptno"/>
</resultMap>
<select id="findWithDeptBySelect" resultMap="emp-dept-select">
SELECT e.`empno`,
e.`ename`,
e.`job`,
e.`mgr`,
e.`hiredate`,
e.`sal`,
e.`comm`,
e.`deptno`
FROM `emp` e
</select>
- res:
classpath:com/yap/mapper/DeptMapper.xml
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.yap.mapper.DeptMapper">
<cache readOnly="true"/>
<select id="findByDeptno" resultType="dept">
SELECT `deptno`, `dname`, `loc` FROM `dept`
<where>
<if test='_parameter != null'>
`deptno` = #{param1}
</if>
</where>
</select>
</mapper>
- tst:
c.y.join.EmpTest.findWithDeptBySelect()
@Test
public void findWithDeptBySelect() {
try (SqlSession session = factory.openSession()) {
EmpMapper empMapper = session.getMapper(EmpMapper.class);
for (Emp emp : empMapper.findWithDeptBySelect()) {
System.out.printf("%s 在 %s 部门\n", emp.getEname(), emp.getDeptno().getDname());
}
} catch (Exception e) {
e.printStackTrace();
}
}
1对1 使用注解
- src:
c.y.mapper.EmpMapper
/**
* 查询所有员工,同时查出员工所在部门信息
*
* @return 所有员工及其部门信息
*/
@Select("SELECT `empno`, `ename`, `job`, `mgr`, `hiredate`, `sal`, `comm`, `deptno` FROM `emp`")
@Results(id = "emp-dept-one", value = {
// empno可以不用配置
@Result(column = "empno", property = "empno", jdbcType = JdbcType.INTEGER, id = true),
@Result(column = "deptno", property = "deptno", javaType = Dept.class,
one = @One(select = "com.yap.mapper.DeptMapper.findByDeptno")
)
})
List<Emp> findWithDeptByOne();
- res:
classpath:com/yap/mapper/DeptMapper.xml
<select id="findByDeptno" resultType="dept">
SELECT `deptno`, `dname`, `loc` FROM `dept`
<where>
<if test='_parameter != null'>
`deptno` = #{param1}
</if>
</where>
</select>
- tst:
c.y.join.EmpTest.findWithDeptByOne()
@Test
public void findWithDeptByOne() {
try (SqlSession session = factory.openSession()) {
EmpMapper empMapper = session.getMapper(EmpMapper.class);
for (Emp emp : empMapper.findWithDeptByOne()) {
System.out.printf("%s 在 %s 部门\n", emp.getEname(), emp.getDeptno().getDname());
}
} catch (Exception e) {
e.printStackTrace();
}
}
1对1 延迟加载
- res:
classpath:mybatis-join.xml
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
- tst:
c.y.join.EmpTest.findWithDeptByLazy()
@Test
public void findWithDeptByLazy() {
try (SqlSession session = factory.openSession()) {
EmpMapper empMapper = session.getMapper(EmpMapper.class);
// debug here
List<Emp> emps = empMapper.findWithDeptBySelect();
System.out.println(emps.get(0).getEname());
System.out.println(emps.get(0).getSal());
System.out.println(emps.get(0).getDeptno().getDname());
} catch (Exception e) {
e.printStackTrace();
}
}
2.2 一对多联表
概念: 在部门实体中埋一个员工实体列表属性:
- JOIN查询:SQL块中使用
JOIN,返回值使用<resultMap>映射:- 使用
<collection>的ofType/property映射实体类外键属性泛型类型/属性名。 - 使用
<collection>的jdbcType/column映射数据库外键字段类型/字段名。 - 使用
<id>/<result>映射<resultMap>/<collection>中对应实体的全部非外键属性。 - 配置对应接口方法并测试。
- 使用
- 分步查询:SQL块中使用全查(1步SQL块),返回值使用
<resultMap>映射:- 分步查询时可省略所有同名的非外键属性的映射。
- 在部门SQL配置文件中开发按主键查部门的2步SQL块,可直接用1步SQL块的外键。
- 额外使用
<collection select="工作空间.id">调用2步SQL块。 - 配置对应接口方法并测试,其中2步SQL块无需配置对应接口方法。
- 使用注解:可简化分步查询的配置,省略所有SQL块,但SQL配置文件仍需保留:
- 使用
@Results/@Result/@Result(id=true)替代<resultMap>/<result>/<id>。 - 使用
@Result(many=@Many(""))替代<collection select="">。
- 使用
- 延迟加载:分步查询时可配置
<settings>以解决一次主查询伴随N次子查询的N+1问题:<setting name="lazyLoadingEnabled" value="true"/>:开启延迟加载。<setting name="aggressiveLazyLoading" value="false"/>: 关闭积极加载。
1对多 JOIN查询
源码: /mybatis3/
- src:
c.y.mapper.DeptMapper
package com.yap.mapper;
/**
* @author yap
*/
public interface DeptMapper {
/**
* 查询所有部门,同时查出部门所有员工信息
*
* @return 所有部门及其员工信息
*/
List<Dept> findWithEmpsByJoin();
}
- res:
classpath:com/yap/mapper/DeptMapper.xml
<resultMap id="dept-emp-join" type="dept">
<id property="deptno" column="deptno"/>
<result property="dname" column="dname"/>
<result property="loc" column="loc"/>
<!-- private List<Emp> emps; 在pojo中添加-->
<collection ofType="emp" property="emps" column="deptno" jdbcType="INTEGER">
<id property="empno" column="empno"/>
<result property="ename" column="ename"/>
<result property="job" column="job"/>
<result property="sal" column="sal"/>
<result property="comm" column="comm"/>
<result property="mgr" column="mgr"/>
<result property="hiredate" column="hiredate"/>
</collection>
</resultMap>
<select id="findWithEmpsByJoin" resultMap="dept-emp-join">
SELECT e.`empno`,
e.`ename`,
e.`job`,
e.`mgr`,
e.`hiredate`,
e.`sal`,
e.`comm`,
d.`deptno`,
d.`dname`,
d.`loc`
FROM `dept` d
LEFT JOIN `emp` e ON e.`deptno` = d.`deptno`
</select>
- tst:
c.y.join.DeptTest.findWithEmpsByJoin()
package com.yap.join;
/**
* @author yap
*/
public class DeptTest {
private SqlSessionFactory factory = MyBatisUtil.getFactory("mybatis-join.xml");
@Test
public void findWithEmpsByJoin() {
try (SqlSession session = factory.openSession()) {
DeptMapper deptMapper = session.getMapper(DeptMapper.class);
for (Dept dept : deptMapper.findWithEmpsByJoin()) {
for (Emp emp : dept.getEmps()) {
System.out.print(emp.getEname() + " ");
}
System.out.println("在" + dept.getDname() + "部门");
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
1对多 分步查询
源码: /mybatis3/
- src:
c.y.mapper.DeptMapper
/**
* 查询所有部门,同时查出部门所有员工信息
*
* @return 所有部门及其员工信息
*/
List<Dept> findWithEmpsBySelect();
- res:
classpath:com/yap/mapper/DeptMapper.xml
<resultMap id="dept-emp-select" type="dept">
<collection property="emps" ofType="emp" column="deptno" jdbcType="INTEGER"
select="com.yap.mapper.EmpMapper.findByDeptno"/>
</resultMap>
<select id="findWithEmpsBySelect" resultMap="dept-emp-select">
SELECT d.`deptno`, d.`dname`, d.`loc`
FROM `dept` d
</select>
- res:
classpath:com/yap/mapper/EmpMapper.xml
<select id="findByDeptno" resultType="emp">
SELECT e.`empno`, e.`ename`, e.`job`, e.`mgr`, e.`hiredate`, e.`sal`, e.`comm`, e.`deptno`
FROM `emp` e
<where>
<if test='_parameter != null'>
`deptno` = #{param1}
</if>
</where>
</select>
- tst:
c.y.join.DeptTest.findWithEmpsBySelect()
@Test
public void findWithEmpsBySelect() {
try (SqlSession session = factory.openSession()) {
DeptMapper deptMapper = session.getMapper(DeptMapper.class);
for (Dept dept : deptMapper.findWithEmpsBySelect()) {
for (Emp emp : dept.getEmps()) {
System.out.print(emp.getEname() + " ");
}
System.out.println("在" + dept.getDname() + "部门");
}
} catch (Exception e) {
e.printStackTrace();
}
}
1对多 使用注解
源码: /mybatis3/
- src:
c.y.mapper.DeptMapper
/**
* 查询所有部门,同时查出部门所有员工信息
*
* @return 所有部门及其员工信息
*/
@Select("SELECT `deptno`, `dname`, `loc` FROM `dept`")
@Results(id = "dept-emp-many", value = {
@Result(column = "deptno", property = "emps",
many = @Many(select = "com.joezhou.mapper.EmpMapper.findByDeptno")
)
})
List<Dept> findWithEmpsByMany();
- res:
classpath:com/yap/mapper/EmpMapper.xml
<select id="findByDeptno" resultType="emp">
SELECT e.`empno`, e.`ename`, e.`job`, e.`mgr`, e.`hiredate`, e.`sal`, e.`comm`, e.`deptno`
FROM `emp` e
<where>
<if test='_parameter != null'>
`deptno` = #{param1}
</if>
</where>
</select>
- tst:
c.y.join.DeptTest.findWithEmpsByMany()
@Test
public void findWithDeptByMany() {
try (SqlSession session = factory.openSession()) {
DeptMapper deptMapper = session.getMapper(DeptMapper.class);
for (Dept dept : deptMapper.findWithEmpsByMany()) {
for (Emp emp : dept.getEmps()) {
System.out.print(emp.getEname() + " ");
}
System.out.println("在" + dept.getDname() + "部门");
}
} catch (Exception e) {
e.printStackTrace();
}
}
1对多 延迟加载
源码: /mybatis3/
- res:
classpath:mybatis-join.xml
<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>
- tst:
c.y.join.DeptTest.findWithEmpsByLazy()
@Test
public void findWithEmpsByLazy() {
try (SqlSession session = factory.openSession()) {
DeptMapper deptMapper = session.getMapper(DeptMapper.class);
// debug here
List<Dept> depts = deptMapper.findWithEmpsBySelect();
System.out.println(depts.get(0).getDname());
System.out.println(depts.get(0).getLoc());
System.out.println(depts.get(0).getEmps().get(0).getEname());
} catch (Exception e) {
e.printStackTrace();
}
}