MyBatis复杂sql查询
1、MyBatis获取参数值的两种方式
- MyBatis获取参数值的两种方式:${}和#{}
- ${}的本质就是字符串拼接,#{} 的本质就是占位符赋值
- ${}使用字符串拼接的方式拼接sql,若为字符串类型或日期类型的字段进行赋值时,需要手动加单引号;但是#{}使用占位符赋值的方式拼接sql,此时为字符串类型或日期类型的字段进行赋值时,可以自动添加单引号
1.1、单个字面量类型的参数
- 若mapper接口中的方法参数为单个的字面量类型,此时可以使用${}和#{}以任意的名称(最好见名识意)获取参数的值,注意${}需要手动加单引号
1 | <!--User getUserByUsername(String username);--> |
1 | <!--User getUserByUsername(String username);--> |
1.2、多个字面量类型的参数
若mapper接口中的方法参数为多个时,此时MyBatis会自动将这些参数放在一个map集合中
- 以arg0,arg1…为键,以参数为值;
- 以param1,param2…为键,以参数为值;
因此只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号。
- 使用arg或者param都行,要注意的是,arg是从arg0开始的,param是从param1开始的
1 | <!--User checkLogin(String username,String password);--> |
1 | <!--User checkLogin(String username,String password);--> |
1.3、map集合类型的参数
- 若mapper接口中的方法需要的参数为多个时,此时可以手动创建map集合,将这些数据放在map中只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号
1 | <!--User checkLoginByMap(Map<String,Object> map);--> |
1 |
|
1.4、实体类类型的参数
- 若mapper接口中的方法参数为实体类对象时此时可以使用${}和#{},通过访问实体类对象中的属性名获取属性值,注意${}需要手动加单引号
1 | <!--int insertUser(User user);--> |
1 |
|
1.5、使用@Param标识参数
可以通过@Param注解标识mapper接口中的方法参数,此时,会将这些参数放在map集合中
- 以@Param注解的value属性值为键,以参数为值;
- 以param1,param2…为键,以参数为值;
只需要通过${}和#{}访问map集合的键就可以获取相对应的值,注意${}需要手动加单引号
1 | <!--User CheckLoginByParam(@Param("username") String username, @Param("password") String password);--> |
1 |
|
总结
建议分成两种情况进行处理
- 实体类类型的参数
- 使用@Param标识参数
2、MyBatis的各种查询功能
- 如果查询出的数据只有一条,可以通过
- 实体类对象接收
- List集合接收
- Map集合接收,结果
{password=123456, sex=男, id=1, age=23, username=admin}
- 如果查询出的数据有多条,一定不能用实体类对象接收,会抛异常TooManyResultsException,可以通过
- 实体类类型的LIst集合接收
- Map类型的LIst集合接收
- 在mapper接口的方法上添加@MapKey注解
2.1、查询一个实体类对象
1 | /** |
1 | <!--User getUserById(@Param("id") int id);--> |
2.2、查询一个List集合
1 | /** |
1 | <!--List<User> getUserList();--> |
2.3、查询单个数据
1 | /** |
1 | <!--int getCount();--> |
2.4、查询一条数据为map集合
1 | /** |
1 | <!--Map<String, Object> getUserToMap(@Param("id") int id);--> |
2.5、查询多条数据为map集合
方法一
1 | /** |
1 | <!--Map<String, Object> getAllUserToMap();--> |
方法二
1 | /** |
1 | <!--Map<String, Object> getAllUserToMap();--> |
3、特殊SQL的执行
3.1、模糊查询
1 | /** |
1 | <!--List<User> getUserByLike(@Param("username") String username);--> |
- 其中
select * from t_user where username like "%"#{mohu}"%"
是最常用的
3.2、批量删除
- 只能使用${},如果使用#{},则解析后的sql语句为
delete from t_user where id in ('1,2,3')
,这样是将1,2,3
看做是一个整体,只有id为1,2,3
的数据会被删除。正确的语句应该是delete from t_user where id in (1,2,3)
,或者delete from t_user where id in ('1','2','3')
1 | /** |
1 | <delete id="deleteMore"> |
1 | //测试类 |
3.3、动态设置表名
- 只能使用${},因为表名不能加单引号
1 | /** |
1 | <!--List<User> getUserByTable(@Param("tableName") String tableName);--> |
3.4、添加功能获取自增的主键
使用场景
t_clazz(clazz_id,clazz_name)
- t_student(student_id,student_name,clazz_id)
- 添加班级信息
- 获取新添加的班级的id
- 为班级分配学生,即将某学的班级id修改为新添加的班级的id
在mapper.xml中设置两个属性
useGeneratedKeys:设置使用自增的主键
- keyProperty:因为增删改有统一的返回值是受影响的行数,因此只能将获取的自增的主键放在传输的参数user对象的某个属性中
1 | /** |
1 | <!--void insertUser(User user);--> |
1 | //测试类 |
4、自定义映射resultMap
4.1、resultMap处理字段和属性的映射关系
resultMap
:设置自定义映射- 属性:
id
:表示自定义映射的唯一标识,不能重复type
:查询的数据要映射的实体类的类型- 子标签:
id
:设置主键的映射关系result
:设置普通字段的映射关系- 子标签属性:
property
:设置映射关系中实体类中的属性名column
:设置映射关系中表中的字段名
- 属性:
- 若字段名和实体类中的属性名不一致,则可以通过resultMap设置自定义映射,即使字段名和属性名一致的属性也要映射,也就是全部属性都要列出来
1 | <resultMap id="empResultMap" type="Emp"> |
若字段名和实体类中的属性名不一致,但是字段名符合数据库的规则(使用_),实体类中的属性名符合Java的规则(使用驼峰)。此时也可通过以下两种方式处理字段名和实体类中的属性的映射关系
- 可以通过为字段起别名的方式,保证和实体类中的属性名保持一致
1
2
3
4<!--List<Emp> getAllEmp();-->
<select id="getAllEmp" resultType="Emp">
select eid,emp_name empName,age,sex,email from t_emp
</select> - 可以在MyBatis的核心配置文件中的
setting
标签中,设置一个全局配置信息mapUnderscoreToCamelCase,可以在查询表中数据时,自动将_类型的字段名转换为驼峰,例如:字段名user_name,设置了mapUnderscoreToCamelCase,此时字段名就会转换为userName。核心配置文件详解1
2
3<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
- 可以通过为字段起别名的方式,保证和实体类中的属性名保持一致
4.2、多对一映射处理
查询员工信息以及员工所对应的部门信息
1 | public class Emp { |
4.3、级联方式处理映射关系
1 | <resultMap id="empAndDeptResultMapOne" type="Emp"> |
4.4、使用association处理映射关系
- association:处理多对一的映射关系
- property:需要处理多对的映射关系的属性名
- javaType:该属性的类型
1 | <resultMap id="empAndDeptResultMapTwo" type="Emp"> |
4.5、分步查询
1. 查询员工信息
- select:设置分布查询的sql的唯一标识(namespace.SQLId或mapper接口的全类名.方法名)
- column:设置分步查询的条件
1 | //EmpMapper里的方法 |
1 | <resultMap id="empAndDeptByStepResultMap" type="Emp"> |
2. 查询部门信息
1 | //DeptMapper里的方法 |
1 | <!--此处的resultMap仅是处理字段和属性的映射关系--> |
4.6、一对多映射处理
1 | public class Dept { |
collection
- collection:用来处理一对多的映射关系
- ofType:表示该属性对饮的集合中存储的数据的类型
1 | <resultMap id="DeptAndEmpResultMap" type="Dept"> |
分步查询
1. 查询部门信息
1 | /** |
1 | <resultMap id="DeptAndEmpByStepOneResultMap" type="Dept"> |
2. 根据部门id查询部门中的所有员工
1 | /** |
1 | <!--List<Emp> getDeptAndEmpByStepTwo(@Param("did") Integer did);--> |
4.7、延迟加载
- 分步查询的优点:可以实现延迟加载,但是必须在核心配置文件中设置全局配置信息:
- lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
- aggressiveLazyLoading:当开启时,任何方法的调用都会加载该对象的所有属性。 否则,每个属性会按需加载
- lazyLoadingEnabled:延迟加载的全局开关。当开启时,所有关联对象都会延迟加载
- 此时就可以实现按需加载,获取的数据是什么,就只会执行相应的sql。此时可通过association和collection中的fetchType属性设置当前的分步查询是否使用延迟加载,fetchType=”lazy(延迟加载)|eager(立即加载)”
1 | <settings> |
1 |
|
- 关闭延迟加载,两条SQL语句都运行了
- 开启延迟加载,只运行获取emp的SQL语句
1 |
|
- 开启后,需要用到查询dept的时候才会调用相应的SQL语句
fetchType:当开启了全局的延迟加载之后,可以通过该属性手动控制延迟加载的效果,fetchType=”lazy(延迟加载)|eager(立即加载)”
1
2
3
4
5
6
7
8
9
10
11<resultMap id="empAndDeptByStepResultMap" type="Emp">
<id property="eid" column="eid"></id>
<result property="empName" column="emp_name"></result>
<result property="age" column="age"></result>
<result property="sex" column="sex"></result>
<result property="email" column="email"></result>
<association property="dept"
select="com.atguigu.mybatis.mapper.DeptMapper.getEmpAndDeptByStepTwo"
column="did"
fetchType="lazy"></association>
</resultMap>
评论
GitalkDisqus