一.概述
1.简介
- MyBatis是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis免除了几乎所有的JDBC代码以及设置参数和获取结果集的工作。MyBatis可以通过简单的XML或注解来配置和映射原始类型、接口和Java POJO(Plain Old Java Objects,普通老式Java对象)为数据库中的记录
2.maven构建
- 将MyBatis相关依赖导入项目,pom.xml添加如下配置
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.7</version> </dependency>
- 将Mysql相关依赖导入
<dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.37</version> </dependency>
- 将Junit相关依赖代入
<dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency>
- 将log4j相关依赖导入
<dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> log4j的配置文件名为log4j.xml,存放的位置是src/main/resources目录下:
二.相关概念
1.Mapper接口
MyBatis中的mapper接口相当于以前的dao。但是区别在于,mapper仅仅是接口,我们不需要提供实现类。
- Mapper接口的取名应该是和映射文件名保持一致
- 比如,某个实体类User,它的Mapper接口如下:
public interface UserMapper{ int insert(); }
- 在Mapper接口中可以提供一些抽象方法,用于增删改查
2.ORM思想
- ORM是指(Object Relationship Mapping)对象关系映射
- 其中
- 对象:Java的实体类对象
- 关系:关系型数据库
- 映射:二者之间的对应关系
- 体现
Java概念 |
数据库概念 |
类 |
表 |
属性 |
字段/列 |
对象 |
记录/行 |
三.映射配置文件
1.文件结构
- 命名规则:数据库表对应的类名+Mapper.xml
- 一个映射文件对应一个实体类,对应一个表中的操作
- 映射文件主要用于编写SQL、访问以及操作表中的数据
- 映射文件存放位置是maven工程下的src/main/resources/mappers目录下
- 映射配置文件要保证两个一致
- mapper接口的全类名和映射文件的命名空间namespace保持一致
- mapper接口中方法的方法名和映射文件中编写SQL的标签的id属性保持一致
- 映射文件的简易结构如下
<?xml version="1.0" encoding="UTF-8" ?> <!--DTD约束--> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="Mapper接口全类名"> <!--映射语句--> <insert id="Mapper接口方法名"> SQL语句 </insert> </mapper>
2.映射配置文件标签详解
- <insert>标签
- 用于书写插入数据的SQL语句
- id属性指定对应mapper接口的方法名
- 范例
<insert id="insertUser"> SQL语句 </insert>
- <delete>标签
- 用于删除表中的数据
- id属性指定对应mapper接口的方法名
- 范例
<delete id="deleteUser"> SQL语句 </delete>
- <update>标签
- 用于更新表中数据
- id属性指定对应mapper接口中的方法名
- 范例
<update id="updateUser"> SQL语句 </update>
- <select>标签
- 用于查询表中的数据
- id属性指定mapper接口中对应的方法名
- resultType属性表示自动映射,用于属性名和表中字段名一致的情况
- resultMap属性表示自定义映射,用于一对多或多对一或字段名和属性名不一致的情况
- 范例
查询一条数据:
<select id="getUserById" resultType="com.lxq.pojo.User"> SQL语句 </select>
查询多条数据到List集合:
3.SQL语句中参数的获取
(1)获取方式
- MyBatis获取参数值的两种方式:${}和#{}
- ${}的本质是字符串拼接,#{}的本质是占位符赋值
- ${}方式会将参数原原本本拼接入SQL语句中,如果参数是字符串类型的需要手动加上引号
`${参数}`
- #{}的方式会自动加上引号,如果是字符串类型的参数无需手动加上引号
#{参数}
(2)参数类型
- 单个字面量类型的参数
- 字面量是指基本数据类型以及包装类还有自定义类等
- 获取方式
<select id="getUserByUsername" resultType="User"> select * from t_user where username=#{username} </select>
- 此时获取参数时花括号里边的参数名不做要求,可以是随意的名称,如果使用${}的方式记得加引号
- 多个字面量类型的参数
- 若mapper接口中的方法参数为多个时,MyBatis会自动将这些参数放在一个map集合中,此时参数名不能是随意的名称
- 这个map集合存放参数的形式是
arg0,arg1...
为map中的键,参数值为map中的值;还有param1,param2...
为map中的键,参数值为map值的值 - arg和param是同时存在于同一个map中的,在SQL语句获取参数时只需指定这些键的名称即可
- 获取方式
<!--arg的方式--> <select id="checkLogin" resultType="User"> select * from t_user where username = #{arg0} and password = #{arg1} </select> <!--param的方式--> <select id="checkLogin" resultType="User"> select * from t_user where username = #{param1} and password = #{param2} </select>
- 如果使用${}的方式记得加引号
- map集合类型的参数
- 为了在SQL语句中指定一些有意义的参数名,我们可以自己提供一个map集合,自定义一些键的名称即可
- 通过自定义键的名称,我们在SQL语句里就可以使用自定义的参数名了
- 比如,这个自定义的map集合可以是
{("username","参数值"),("password","参数值")}
- 获取方式
<select id="checkLoginByMap" resultType="User"> select * from t_user where username = #{username} and password = #{password} </select>
- 如果使用${}的方式记得加引号
- 使用@Param注解标识的参数
- 如果每次使用多个参数时都要自定义map集合就太麻烦了,所以可以通过使用@Param注解在映射方法的形参中指定好参数名
- 比如,这个映射方法可以是
User getUserByParam(@Param("username") String username,@Param("password") String password);
- 获取方式
<select id="checkLoginByParam" resultType="User"> select * from t_user where username = #{username} and password = #{password} </select>
- 使用这种注解就可以方便获取参数了,如果使用${}的方式记得加引号
- 实体类型的参数
- 如果映射方法的形参是一个实体类型时,可以通过访问实体类对象中的属性名获取属性值
- 比如,这个实体类可以是
public class User { private Integer id; private String username; private String password; private Integer age; private String gender; private String email; //省略有参、无参构造方法以及toString()方法 public Integer getId() { return id; } public void setId(Integer id) { this.id = id; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public String getGender() { return gender; } public void setGender(String gender) { this.gender = gender; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
- 获取方式
<insert id="insertUser"> insert into t_user value(null,#{username},#{password},#{age},#{gender},#{email}) </insert>
- 如果使用${}的方式记得加引号
- 总结
- 上面所说的五种参数类型实际上可以分成两种类型,一种是使用@Param注解一种是使用实体类
- 就是说不论单个参数或者多个参数,都用注解的方式,如果是实体类那就用实体类属性的方式
4.各种SQL操作
- 查询一个实体类对象
映射方法:
User getUserById(@Param("id") int id);
映射文件:
- 查询一个List集合
映射方法:
List<User> getAllUser();
映射文件:
注意:当查询的数据为多条时,不能使用实体类作为返回值,否则会抛出异常 TooManyResultsException;但是若查询的数据只有一条,可以使用实体类或集合作为返回值
- 查询单个数据
映射方法:
int getCount();
映射文件:
- 查询一条数据到map集合
映射方法:
Map<String,Object> getUserToMap(@Param("id") int id);
映射文件:
注意:将一条数据查询到map集合中时,map的键是表中的字段名,map的值是表中数据
- 查询多条数据到map集合
- 方式一
映射方法:
List<Map<String,Object>> getAllUserToMap();
映射文件:
- 方式二
映射方法:
@MapKey("id") Map<String,Object> getAllUserToMap();
{ 1={password=123456, sex=男, id=1, age=23, username=admin}, 2={password=123456, sex=男, id=2, age=23, username=张三}, 3={password=123456, sex=男, id=3, age=23, username=张三} }
映射文件:
- 注意
- 方式一中每条查出来的数据都对应一个Map集合,然后再利用List集合将这些Map集合组织起来
- 方式二中每条查出来的数据都存放在一个Map集合中,但是这个Map集合的键由映射方法上方的@MapKey注解指定,而Map集合的值又是另外一个Map集合,作为值的Map集合中键对应表中字段名,值对应表中数据
- 方式二范例
- 模糊查询
映射方法:
List<User> getUserByLike(@Param("mohu") String mohu);
映射文件:
注意:不能使用like '%#{mohu}%'
的方式,因为#{}会被解析成?,这个问号会被当成字符串的一部分造成参数获取失败
- 批量删除
映射方法:
void deleteSomeUser(@Param("ids") String ids);
映射文件:
注意:这里获取参数的方式是${},因为#{}会自动添加引号,如果使用#{}的方式会造成SQL语句解析成delete from t_user where id in('ids')
从而报错
- 动态设置表名
映射方法:
List<User> getUserList(@Param("table") String table);
映射文件:
注意:这里使用${}是因为使用#{}时会自动添加引号,而表名不允许添加表名
- 执行添加功能时获取自增的主键
映射方法:
void insertUser(User user);
映射文件:
测试方法:
注意:这里的useGeneratedKeys设置使用自增主键为true,keyProperty是将获取的主键值赋给实体对象中的某个属性。这样,在添加这个实体对象后,自增的主键也能在实体对象中获得,而不需要进行查询
5.处理表字段和实体类属性名不一致的情况
- 方式一:给字段名取别名
- 如果表中字段名和实体类属性名不一致,可以在SQL语句中给字段名取别名
- 给字段取得别名必须和实体类属性名一致
- 方式二:在核心配置文件中配置驼峰映射
- 使用前提:表字段符合Mysql命名规范(使用下划线_分割单词),而实体类属性符合驼峰命名规范
- 使用方式:
- 在核心配置文件中使用<settings>标签,在该标签下使用<setting>子标签来配置
- 给子标签<setting>设置name属性值为mapUnderscoreToCamelCase,value属性值为true
- 范例
<settings> <setting name="mapUnderscoreToCamelCase" value="true" /> </settings>
- 在核心配置文件使用了如上配置后,在SQL语句中可以使用表的字段名而不用考虑表字段名和实体类属性名不一致的情况
- 方式三:在映射配置文件中使用<resultMap>标签自定义映射
- <resultMap>标签含有id属性和type属性,其中id属性是设置当前自定义映射的标识,type属性是映射的实体类
- <resultMap>标签下含有的子标签以及功能
select * from t_emp where emp_id = #{empId}
- <id>标签:设置主键字段的映射关系,使用column属性设置映射关系中表的字段名,使用property属性设置映射关系中实体类的属性名
- <result>标签:设置普通字段的映射关系,使用column属性设置映射关系中表的字段名,使用property属性设置映射关系中实体类的属性名
- 范例
select * from t_emp where emp_id = #{empId}
```
- 注意:SQL语句所在标签中的resultMap属性值必须是自定义映射的id
6.多对一映射关系的处理
- 这里多对一是指实体类中某个属性是以表中多个字段为属性构成的实体类,如员工类的部门属性,部门属性的类型是部门类,这个部门类有部门id,部门名称
- 方式一:使用级联
- <resultMap>配置
<resultMap id="getEmpAndDeptByEmpIdResultMap" type="Emp"> <id column="emp_id" property="empId"></id> <result column="emp_name" property="empName"></result> <result column="age" property="age"></result> <result column="gender" property="gender"></result> <result column="dept_id" property="dept.deptId"></result> <result column="dept_name" property="dept.deptName"></result> </resultMap> <select id="getEmpAndDeptByEmpId" resultMap="getEmpAndDeptByEmpIdResultMap"> select emp_id,emp_name,age,gender,t_dept.dept_id,dept_name from t_emp left join t_dept on t_emp.dept_id = t_dept.dept_id where emp_id = #{empId} </select>
- 方式二:使用<association>标签
- <resultMap>配置
<resultMap id="getEmpAndDeptByEmpIdResultMap" type="Emp"> <id column="emp_id" property="empId"></id> <result column="emp_name" property="empName"></result> <result column="age" property="age"></result> <result column="gender" property="gender"></result> <association property="dept" javaType="Dept"> <id column="dept_id" property="deptId"></id> <result column="dept_name" property="deptName"></result> </association> </resultMap> <select id="getEmpAndDeptByEmpId" resultMap="getEmpAndDeptByEmpIdResultMap"> select emp_id,emp_name,age,gender,t_dept.dept_id,dept_name from t_emp left join t_dept on t_emp.dept_id = t_dept.dept_id where emp_id = #{empId} </select>
注意:association标签中property属性是指映射实体类中属性的名称,javaType是它的类型,而association标签下的id标签和result标签中的property属性是指javaType指定的类中的属性名称,column属性指表中的字段名
- 方式三:使用分步查询
- <resultMap>配置
查询员工信息:
<resultMap id="getEmpAndDeptByEmpIdResultMap" type="Emp"> <id column="emp_id" property="empId"></id> <result column="emp_name" property="empName"></result> <result column="age" property="age"></result> <result column="gender" property="gender"></result> <association property="dept" select="com.liaoxiangqian.mapper.DeptMapper.getDeptByDeptId" column="dept_id"> </association> </resultMap> <select id="getEmpAndDeptByEmpId" resultMap="getEmpAndDeptByEmpIdResultMap"> select * from t_emp where emp_id = #{empId} </select>
根据员工的部门id查询部门信息
7.一对多映射关系的处理
- 这里一对多是指实体类中某个属性是由许多实体类构成的集合,如部门类中员工属性是一个List集合
- 方式一:使用<collection>标签
- <resultMap>配置
<resultMap id="getDeptAndEmpByDeptIdResultMap" type="Dept"> <id column="dept_id" property="deptId"></id> <result column="dept_name" property="deptName"></result> <collection property="emps" ofType="Emp"> <id column="emp_id" property="empId"></id> <result column="emp_name" property="empName"></result> <result column="age" property="age"></result> <result column="gender" property="gender"></result> </collection> </resultMap> <select id="getDeptAndEmpByDeptId" resultMap="getDeptAndEmpByDeptIdResultMap"> select * from t_dept left join t_emp on t_dept.dept_id = t_emp.dept_id where t_dept.dept_ id = #{deptId} </select>
- 方式二:使用分步查询
- <resultMap>配置
查询部门信息
<resultMap id="getDeptAndEmpByDeptIdResultMap" type="Dept"> <id column="dept_id" property="deptId"></id> <result column="dept_name" property="deptName"></result> <collection property="emps" select="com.liaoxiangqian.mapper.EmpMapper.getEmpByDeptId" column="dept_id"> </collection> </resultMap> <select id="getDeptAndEmpByDeptId" resultMap="getDeptAndEmpByDeptIdResultMap"> select * from t_dept where dept_id = #{deptId} </select>
根据部门id查询员工信息