下面显示的是元素的一些属性:
1. update元素和delete元素
首先在映射接口中添加用于更新和删除的方法
1
2
3
4//更新方法
public boolean updateEmp(Employee emp);
//删除方法
public boolean deleteEmpById(Integer id);然后在映射器文件中添加对应的sql配置信息
1
2
3
4
5
6
7<!-- 参数类型可以省略 -->
<update id="updateEmp" parameterType="com.atguigu.mybatis.bean.Employee">
update tbl_employee set last_name=#{lastName},gender=#{gender},email=#{email} where id=#{id}
</update>
<delete id="deleteEmpById">
delete from tbl_employee where id=#{id}
</delete>进行测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void test02() throws IOException {
//1.根据MyBatis的配置文件,即mybatis-config.xml创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//2.获取session实例,能直接执行*已经映射的SQL语句*
SqlSession session = sqlSessionFactory.openSession(false);//设置默认不提交
try {
//3.获取接口的实现类对象
EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
//4.测试删除方法
boolean isDelete = employeeMapper.deleteEmpById(3);//输出true或者false
System.out.println(isDelete);
//5.手动提交
session.commit();
} finally {
//4.关闭会话session
session.close();
}
}
2. insert元素-插入语句
2.1 简单应用
首先在映射接口中添加用于插入的方法
1
2//增加方法
public void addEmp(Employee emp);然后在映射器文件中添加对应的sql配置信息
1
2
3<insert id="addEmp">
insert into tbl_employee(last_name,gender,email) values(#{lastName},#{gender},#{email})
</insert>测试代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void test02() throws IOException {
//1.根据MyBatis的配置文件,即mybatis-config.xml创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//2.获取session实例,能直接执行*已经映射的SQL语句*
SqlSession session = sqlSessionFactory.openSession(false);//设置默认不提交
try {
//3.获取接口的实现类对象
EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
//4.测试添加
Employee emp = new Employee(null, "张三", "1", "zhangsan@gmail.com");
employeeMapper.addEmp(emp);
//5.手动提交
session.commit();
} finally {
//4.关闭会话session
session.close();
}
}
2.2 获取自增主键的值
MySQL支持自增主键,自增主键值的获取,MyBatis也是利用statement.getGenreatedKey();
MyBatis使用insert元素的useGenreatedKeys=”true”来使用自增主键获取主键值策略;
使用insert元素的keyProperty来指定对应的主键属性,也就是MyBatis获取到主键值以后,将这个值封装给javaBean的对应属性。
修改映射器文件中的insert元素
1
2
3<insert id="addEmp" useGeneratedKeys="true" keyProperty="id">
insert into tbl_employee(last_name,gender,email) values(#{lastName},#{gender},#{email})
</insert>测试代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void test02() throws IOException {
//1.根据MyBatis的配置文件,即mybatis-config.xml创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//2.获取session实例,能直接执行*已经映射的SQL语句*
SqlSession session = sqlSessionFactory.openSession(false);//设置默认不提交
try {
//3.获取接口的实现类对象
EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
//4.测试添加
Employee emp = new Employee(null, "张三", "1", "zhangsan@gmail.com");
employeeMapper.addEmp(emp);
//输出员工的id
System.out.println(emp.getId());
//5.手动提交
session.commit();
} finally {
//4.关闭会话session
session.close();
}
}
3. select元素-查询语句
3.1 参数处理
3.1.1单个参数
比如我要根据employee的id值来查询对应的员工,这时候对应的查询方法的参数只有一个,就是id值。在映射器中的对应SQL语句中,#{参数名}中的参数名MyBatis不做特殊处理,也就是该参数名可以随便取名。
3.1.2 多个参数
3.1.2.1 使用注解传递多个参数
首先在映射接口中定义如下方法
1
2//根据员工id和姓名查询员工
public Employee gerEmpByIdAndName(Integer id, String lastName);然后在映射器文件中配置如下SQL
1
2
3<select id="gerEmpByIdAndName" resultType="com.atguigu.mybatis.bean.Employee">
select id,last_name,gender,email from tbl_employee where id=#{id} and last_name=#{lastName}
</select>运行测试之后会报如下错误
1
Cause: org.apache.ibatis.binding.BindingException: Parameter 'id' not found. Available parameters are [0, 1, param1, param2]
MyBatis在处理传入的多个参数时,会将多个参数封装成一个Map集合,对应的键值为:
key:param1…paramN,或者参数的索引0…N
value:我们传入的参数值
#{}就是从map中获取指定的key值,所以我们应该传入key值而不是value值。
解决:
使用命名参数,即明确指定封装参数时map的key,封装后的map集合的key值为使用@Param注解指定的值,value为参数值。
修改映射接口,指定map集合的键即可:
1 | //根据员工id和姓名查询员工 |
3.1.2.2 通过POJO传递多个参数
存在一个POJO-Employee
此时在映射接口定义如下方法:
1
2//根据POJO查询员工
public Employee getEmpByBean(Employee emp);在映射器配置如下SQL语句,这里是通过传入的Java Bean的id和lastName来查询。
1
2
3<select id="getEmpByBean" resultType="com.atguigu.mybatis.bean.Employee">
select id,last_name,gender,email from tbl_employee where id=#{id} and last_name=#{lastName}
</select>测试代码
3.1.2.3 通过Map集合传递多个参数
在映射接口定义如下方法
1
2//根据Map集合来查询员工
public Employee getEmpByMap(Map<String, Object> map);在映射器中配置SQL信息
1
2
3<select id="getEmpByMap" resultType="com.atguigu.mybatis.bean.Employee">
select id,last_name,gender,email from tbl_employee where id=#{id} and last_name=#{lastName}
</select>测试代码
3.1.2.4 混合使用
注意:集合和数组都有默认的key。3.2 #{}与${}的异同
相同点:两者都可以获取map中的值或者pojo对象属性的值。
不同点:#{}是以预编译的形式,将参数设置到sql语句中,类似于PreparedStatement,它可以防止sql注入。
${}是直接将取出的值拼接在sql语句中,类似于Statement,可能引发安全问题。
下面演示一下:
- 映射器文件的sql配置如下:
1
2
3<select id="getEmpByMap" resultType="com.atguigu.mybatis.bean.Employee">
select id,last_name,gender,email from tbl_employee where id=#{id} and last_name=#{lastName}
</select>
- 映射器文件的sql配置如下:
测试代码
1
2
3
4
5
6
7EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
//4.测试查询方法
Map<String,Object> map = new HashMap<>();
map.put("id", "1");
map.put("lastName", "Tom");
Employee emp = employeeMapper.getEmpByMap(map);
System.out.println(emp);运行结果
从输出的sql语句可以看出占位符?是存在的。
下面我们修改一下映射器文件的配置信息,更改为id=${id}:
1 | <select id="getEmpByMap" resultType="com.atguigu.mybatis.bean.Employee"> |
再次测试的输出结果为:
3.3 ${}的更多用法
比如我们需要按照年份进行分表拆分,然后使用传入的年份生成对应的表名,如
1 | select * from ${year}_salary where xxx; |
又比如我们通过字段名来对表的字段进行排序,那么我们也可以通过$来实现。如
1 | select * from tbl_employee order by ${last_name} ${order} |
显然,原生JDBC不支持占位符的地方我们就可以使用${}来进行取值。
3.4 #{}的更多用法
#{}还可以规定参数的一些规则,比如javaType、 jdbcType、 mode、 numericScale、
resultMap、 typeHandler、 jdbcTypeName。
下面来讲一下jdbcType的用法。
在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理(MyBatis默认将null设置为OTHER类型),比如Oracle数据库就无法识别OTHER类型。
我们可以在某个插入为null的数据来使用jdbcType设置。比如我要进入如下数据库操作:
1 | <insert id="addEmp" useGeneratedKeys="true" keyProperty="id"> |
但是我在传入数据的时候,email的值是null,那么Oracle数据库是无法识别而报错的。我们可以更改为如下语句则可以解决这个问题:
上面的配置只使用单条sql语句。我们可以在全局配置文件设置settings来实现全局配置,即:
3.5 查询返回数据封装
3.5.1 List封装查询到的数据
在映射接口编写查询方法
1
2//使用List封装查询到的数据
public List<Employee> getEmpsByLastNameLike(String lastName);在映射器文件中编写指定的SQL配置,注意:返回类型是Employee对象
1
2
3<select id="getEmpsByLastNameLike" resultType="com.atguigu.mybatis.bean.Employee">
select id,last_name,gender,email from tbl_employee where last_name like #{lastName}
</select>测试代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public void test04() throws IOException {
//1.根据MyBatis的配置文件,即mybatis-config.xml创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//2.获取session实例,能直接执行*已经映射的SQL语句*
SqlSession session = sqlSessionFactory.openSession(false);//设置默认不提交
try {
//3.获取接口的实现类对象
EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
//4.查询
List<Employee> empsList = employeeMapper.getEmpsByLastNameLike("%张%");
for (Employee employee : empsList) {
System.out.println(employee);
}
//5.手动提交
session.commit();
} finally {
//4.关闭会话session
session.close();
}
}
3.5.2 Map封装查询到的数据
需求1:将Employee中的属性封装到一个Map中,其中key为Employee中的属性名,value为对应的值。
编写映射接口方法
1
2//使用Map封装查询到的数据
public Map<String, Object> getEmpsByIdReturnMap(Integer id);在映射器文件编写对应的SQL配置
1
2
3
4<!-- 返回类型是Map集合,map是系统自定义的别名 -->
<select id="getEmpsByIdReturnMap" resultType="map">
select * from tbl_employee where id=#{id}
</select>测试代码
1
2
3
4EmployeeMapper employeeMapper = session.getMapper(EmployeeMapper.class);
//4.查询
Map<String, Object> empMap = employeeMapper.getEmpsByIdReturnMap(1);
System.out.println(empMap);运行结果
1
{gender=1, last_name=Tom, id=1, email=shotozheng .com}
需求2:将多个Employee对象封装到Map集合中,其中key为Employee中的id属性值,value为Employee对象。
同样需要在映射接口编写方法
1
2"id") (
public Map<Integer,Employee> getEmpsByLastNameLikeReturnMap(String lastName);注意:MapKey在于指定返回的Map集合的key为Employee的属性id
在映射器接口编写SQL配置
1
2
3
4<!-- 注意:这里的返回类型是Employee对象 -->
<select id="getEmpsByLastNameLikeReturnMap" resultType="com.atguigu.mybatis.bean.Employee">
select * from tbl_employee where last_name like #{lastName}
</select>运行测试即可,不再赘述。
4. resultMap元素
比如我们在定义Java Bean时,POJO的属性名若与数据库表的字段名不一样则会导致查询出来的数据为null。我们之前是用通过给SQL的的字段命和POJO对应属性一样的名称或者通过驼峰命名法来处理这个问题。但是这些可能会导致SQL语句不好理解或者POJO中的属性名不符合驼峰命名法规范等问题。
那么现在我们可以使用resultMap来定义映射规则来处理这个问题,当然它还可以进行级联更新和定制类型转化器等功能。定义映射规则就是SQL到Java Bean的映射关系定义。
下面演示一下resultMap自定义结果的功能:
创建一个POJO类Employee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19public class Employee {
private Integer id;
private String lastName;
private String gender;
private String email;
public Employee() {
super();
}
public Employee(Integer id, String lastName, String gender, String email) {
super();
this.id = id;
this.lastName = lastName;
this.gender = gender;
this.email = email;
}
//getter,setter,toString....
}注意:类中lastName属性对应的数据库表tbl_employee字段last_name
在映射接口EmployeePlus中定义如下方法
1
public Employee getEmpById(Integer id);
在映射器文件EmployeePlus.xml文件定义如下SQL配置
测试代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void test05() throws IOException {
//1.根据MyBatis的配置文件,即mybatis-config.xml创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//2.获取session实例,能直接执行*已经映射的SQL语句*
SqlSession session = sqlSessionFactory.openSession(false);//设置默认不提交
try {
//3.获取接口的实现类对象
EmployeeMapperPlus mapper = session.getMapper(EmployeeMapperPlus.class);
//4.查询
Employee emp = mapper.getEmpById(1);
System.out.println(emp);
//5.手动提交
session.commit();
} finally {
//4.关闭会话session
session.close();
}
}运行结果
在进行了自定义映射配置之后,即使Java Bean的属性名与数据库表的字段不一样,也可以形成映射关系。
5. 关联查询
需求:在查询出员工对象的同时,查询出该员工所在的部门。
5.1 级联属性封装结果集
创建两个Java Bean,分别为员工类Employee和部门类Department
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20public class Employee {
private Integer id;
private String lastName;
private String gender;
private String email;
private Department dept; //引用部门对象
public Employee() {
super();
}
public Employee(Integer id, String lastName, String gender, String email) {
super();
this.id = id;
this.lastName = lastName;
this.gender = gender;
this.email = email;
}
//getter,setter,toString
}1
2
3
4
5
6
7
8
9
10
11
12
13
14
15public class Department {
private Integer id; //部门编号
private String departmentName; //部门名称
public Department() {
super();
}
public Department(Integer id, String departmentName) {
super();
this.id = id;
this.departmentName = departmentName;
}
//getter,setter,toString....
}创建对应的数据库表tbl_employee和tbl_dept
1
2
3
4
5
6
7
8
9
10CREATE TABLE tbl_employee (
id int(11) NOT NULL AUTO_INCREMENT,
last_name varchar(255) DEFAULT NULL,
gender char(1) DEFAULT NULL,
email varchar(255) DEFAULT NULL,
d_id int(11) DEFAULT NULL,
PRIMARY KEY (id),
KEY FK_tbl_employee (d_id),
CONSTRAINT FK_tbl_employee FOREIGN KEY (d_id) REFERENCES tbl_dept (id)
)1
2
3
4CREATE TABLE tbl_dept(
id INT(11) PRIMARY KEY AUTO_INCREMENT,
dept_name VARCHAR(255)
)映射接口EmployeeMapperPlus中定义如下方法
1
public Employee getEmpAndDept(Integer id);
映射器文件EmployeeMapperPlus.xml进行如下配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<!-- 联合查询,**级联属性封装结果集** -->
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp">
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="gender" property="gender"/>
<result column="email" property="email"/>
<!-- 下面的是关于部门表的内容,可以使用Employee的属性dept来引用 -->
<result column="did" property="dept.id"/>
<result column="dept_name" property="dept.departmentName"/>
</resultMap>
<!-- 查询两个表的所有字段的值 resultMap的值即为上方resultMap元素的id-->
<select id="getEmpAndDept" resultMap="MyDifEmp">
SELECT e.id id, e.last_name last_name,e.gender gender, e.email email,
e.d_id d_id, d.id did, d.dept_name dept_name FROM tbl_employee e, tbl_dept d
WHERE e.d_id=d.id AND e.id=#{id}
</select>运行测试
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public void test05() throws IOException {
//1.根据MyBatis的配置文件,即mybatis-config.xml创建SqlSessionFactory
SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
//2.获取session实例,能直接执行*已经映射的SQL语句*
SqlSession session = sqlSessionFactory.openSession(false);//设置默认不提交
try {
//3.获取接口的实现类对象
EmployeeMapperPlus mapper = session.getMapper(EmployeeMapperPlus.class);
//4.查询
Employee emp = mapper.getEmpAndDept(1);
System.out.println(emp);//连同部门信息也会打印出来
//5.手动提交
session.commit();
} finally {
//4.关闭会话session
session.close();
}
}刚才对映射器文件EmployeeMapperPlus.xml进行配置时使用的是级联属性封装结果集 的方式,下面我们也可以使用association的方式来定义关联的单个对象的封装规则。
5.2 association元素
5.2.1 association嵌套结果集
在原有的基础上,对映射器文件EmployeeMapperPlus.xml的resultMap元素进行如下更改配置:
5.2.2 association分步查询
我们还可以使用association来进行分步查询。
具体思路:
- 先按照员工的id去查询员工的信息
- 根据查询员工信息中的d_id值去部门表查出部门信息
- 将查询到的部门信息设置到员工信息中
具体步骤:
为了根据员工的id去查询员工的信息,我们需要先在映射接口EmployeeMapperPlus中定义如下方法:
1
public Employee getEmpByIdStep(Integer id);
然后需要在映射器文件EmployeeMapperPlus.xml文件进行如下配置
1
2
3
4<!-- 查出员工的信息 -->
<select id="getEmpByIdStep" resultMap="MyEmpByStep">
select * from tbl_employee where id=#{id}
</select>因为需要根据员工信息中的d_id值去部门表查出部门信息 ,所以现在需要在映射接口DepartmentMapper中定义如下方法:
1
public Department getDeptById(Integer id);
同样地,在映射器文件中进行如下配置:
1
2
3
4
5<mapper namespace="com.atguigu.mybatis.mapper.DepartmentMapper">
<select id="getDeptById" resultType="com.atguigu.mybatis.bean.Department">
select id,dept_name departmentName from tbl_dept where id=#{id}
</select>
</mapper>对映射器文件EmployeeMapperPlus.xml做如下的resultMap元素配置:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpByStep">
<!-- 员工基本属性信息映射配置 -->
<id column="id" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
<!-- association定义关联对象的封装规则
property的内容的Employee关联的对象属性名dept
select表明当前属性调用select指定的方法查出的结果,内容的namespace+方法id
column指定将哪一列的值传给这个方法,这里要根据d_id去查询部门信息,所以为d_id
流程:使用select指定的方法并通过传入column指定的这列参数的值去查出对象,
并封装给property指定的属性,即dept
-->
<association property="dept"
select="com.atguigu.mybatis.mapper.DepartmentMapper.getDeptById"
column="d_id">
</association>
</resultMap>运行测试
5.2.3 延迟加载问题
从以上的输出结果可以知道,系统会进行通过员工id查询员工信息,并且通过员工的id去查询对应的部门号。但是如果我们在未使用到部门的信息时,也就是我们只输出员工本身的一些与部门无关的信息,系统依旧会进行部门信息的查询。这样会浪费系统资源,我们可以通过设置延迟加载来处理这个问题。
在全局配置文件进行如下配置:
1 | <!-- 开启延迟加载和关闭属性按需加载 --> |
5.3 collection元素
需求:根据id查询部门的时候,同时查询出该部门的所有员工。
5.3.1 collection嵌套结果集
更改JavaBean类Department,增加字段以及对应的getter和setter方法。具体如下所示:
在映射接口DepartmentMapper中添加如下方法
1
public Department getDeptAndEmpsByDeptId(Integer id);
在映射器文件DepartmentMapper.xml进行如下配置
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22<!-- type的内容即为返回值类型,即查询到的部门对象 -->
<resultMap type="com.atguigu.mybatis.bean.Department" id="MyDept">
<id column="did" property="id"/>
<result column="dept_name" property="departmentName"/>
<!-- collection定义关联集合类型的属性的封装规则
ofType指定集合里面的元素类型
-->
<collection property="emps" ofType="com.atguigu.mybatis.bean.Employee">
<!-- 定义这个集合中元素的封装规则,这里的是员工 -->
<id column="eid" property="id"/>
<result column="last_name" property="lastName"/>
<result column="email" property="email"/>
<result column="gender" property="gender"/>
</collection>
</resultMap>
<select id="getDeptAndEmpsByDeptId" resultMap="MyDept">
select d.id did,d.dept_name,e.id eid,e.last_name,e.email,e.gender
from tbl_dept d
LEFT JOIN tbl_employee e
ON d.id=e.d_id
WHERE d.id=#{id}
</select>测试代码
5.3.2 collection分步查询
具体思路:
先按照部门的id去查询部门的信息
根据查询到的部门信息中的id值去查询员工表的员工信息
将查询到的员工信息设置到部门信息的属性集合中
具体步骤:
在映射接口文件DepartmentMapper中定义如下方法,即根据部门id查询部门信息:
1
public Department getDeptByIdStep(Integer id);
在映射器文件DepartmentMapper.xml进行如下配置:
1
2
3<select id="getDeptByIdStep" resultMap="MyDeptStep">
select id,dept_name departmentName from tbl_dept where id=#{id}
</select>在映射接口EmployeeMapperPlus中定义如下方法,即根据部门id查询所有员工:
1
public List<Employee> getEmpsByDeptId(Integer deptId);
在映射器EmployeeMapperPlus.xml中配置如下信息
1
2
3
4
5<!--注意:如果Employee中的字段与数据库表的字段名不一样,需要给字段命和POJO中对应属性一样的名称,
比如下面的last_name字段必须需命名为lastName,否则无法完成一一映射。-->
<select id="getEmpsByDeptId" resultType="com.atguigu.mybatis.bean.Employee">
select id,last_name lastName,gender,email from tbl_employee where d_id=#{deptId}
</select>在映射器文件DepartmentMapper.xml中配置resultMapper信息,完成关联对象的封装规则:
补充:我们上面的例子只在column属性中传递了一个部门的id值。如下图示:
我们在分步查询时也可以进行多列传值,MyBatis会将多列的值封装为map进行传递。
格式为column=”{key1=column1,key2=column2}”
比如可以写成如下形式:
其中的键deptId对应映射器EmployeeMapperPlus.xml中的SQL的传入的id名称。即
6. sql 元素
sql元素的作用在于可以定义一条SQL的一部分,方便后面的SQL引用它。假设映射器文件中有如下配置信息:
我们可以使用sql将其中的id,last_name lastName,gender,email抽取出来。如下所示:
更改原有的配置信息,引用sql标签的配置。
sql元素还支持变量的传递,映射器的配置信息如下:
在include元素中定义了一个命名为e的变量,其值是SQL中标tbl_employee的别名emp,然后sql元素就能够使用这个变量名了。这个用法常用于多表查询并需要给不同表起别名的情况。