1.MyBatis框架
课程目标
1.理解ORM思想
2.掌握MyBatis框架配置数据库的步骤
3.掌握Mybatis的代理方式实现持久层
4.掌握Mybatis框架对数据库的CRUD操作
5.掌握动态化SQL的操作
6.掌握Mybatis实现关联查询
课程重点
- Mybatis的代理方式写法
- Mybatis对数据库的CRUD及动态化SQL
- Mybatis的关联查询
软件框架技术简介
软件框架(software framework),一般指的是为了实现某个业界标准或完成特定基本任务的软件组件规范,也指为了实现某个软件组件规范时,提供规范所要求之基础功能的软件产品。
框架的功能类似于基础设施,与具体的软件应用无关,但是提供并实现最为基础的软件架构和体系。
- 为什么需要框架技术:
a. 帮我们更快更好地构建程序
b. 是一个应用程序的半成品
c. 提供可重用的公共结构
d. 按必定规则组织的一组组件 - 优势:
a. 不用再思考公共问题
b. 专心在业务实现上
c. 结构统一,易于学习、维护
d. 新手也可写出好程序
不要重复造轮子(Stop Trying to Reinvent the Wheel),已经成为开发人员的基本原则。
Java世界中的主流框架技术: Spring、SpringMVC、MyBatis、Struts、Hibernate、SpringBoot等。
2.MyBatis简介
2.1.ORM思想的提出
数据库中数据是以表的形式存在的,而java中使用的数据都是对象型的。所以不得不要将表数据转换成对象数据。 这样就会产生大量的没有技术含量的纯 “体力” 型代码。
while(rs.next()) {
Business business = new Business();
business.setBusinessId(rs.getInt("businessId"));
business.setPassword(rs.getString("password"));
business.setBusinessName(rs.getString("businessName"));
business.setBusinessAddress(rs.getString("businessAddress"));
business.setBusinessExplain(rs.getString("businessExplain"));
business.setStarPrice(rs.getDouble("starPrice"));
business.setDeliveryPrice(rs.getDouble("deliveryPrice"));
list.add(business);
}
上面代码就属于纯 “体力” 型代码。这些代码工作量大、单调枯燥、占用大量开发时间。为了解决这个问题,出现了ORM思想。
ORM(对象-关系映射):完成对象数据到关系型数据映射的机制称为对象-关系映射。
2.2.MyBatis框架
MyBatis就是一个ORM框架。当然,也是一个持久层框架。
MyBatis封装了JDBC, 将数据库中的表数据自动封装到对象中。这样就可以以面向对象的方式操作数据了。它的出现,使得开发工作量变小了,可以将精力聚焦在业务逻辑的处理上。代码精简易读。
MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。 也就是说:iBatis3.0之后都要改名为MyBatis 。
MyBatis 是支持普通 SQL查询,存储过程和高级映射的优秀持久层框架。MyBatis 消除了几乎所有的JDBC代码和参数的手工设置以及结果集的检索。MyBatis 使用简单的 XML或注解用于配置和原始映射,将接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java对象)映射成数据库中的记录。
实际上,MyBatis最核心的功能,就是实现了输入映射和输出映射。
3.MyBatis实例
3.1.sql脚本
DROP TABLE IF EXISTS emp;
DROP TABLE IF EXISTS dept;
DROP TABLE IF EXISTS food;
DROP TABLE IF EXISTS student;
DROP TABLE IF EXISTS teacher;
CREATE TABLE dept (
DEPTNO int,
DNAME varchar(20),
LOC varchar(30) ,
PRIMARY KEY (DEPTNO)
) ;
CREATE TABLE emp (
EMPNO int ,
ENAME varchar(20) ,
JOB varchar(9) ,
SAL double ,
DEPTNO int DEFAULT NULL,
PRIMARY KEY (EMPNO),
FOREIGN KEY (DEPTNO) REFERENCES dept(DEPTNO)
) ;
CREATE TABLE food(
id int AUTO_INCREMENT PRIMARY KEY,
name varchar(20),
info varchar(30) ,
price double
) ;
CREATE TABLE teacher (
tid int NOT NULL AUTO_INCREMENT,
tname varchar(30),
PRIMARY KEY (tid)
) ;
CREATE TABLE student (
sid int NOT NULL AUTO_INCREMENT,
sname varchar(30) ,
age int ,
tid int ,
PRIMARY KEY (sid),
FOREIGN KEY (tid) REFERENCES teacher (tid)
) ;
insert into DEPT (DEPTNO, DNAME, LOC) values (10, ACCOUNTING , NEW YORK );
insert into DEPT (DEPTNO, DNAME, LOC) values (20, RESEARCH , DALLAS );
insert into DEPT (DEPTNO, DNAME, LOC) values (30, SALES , CHICAGO );
insert into DEPT (DEPTNO, DNAME, LOC) values (40, OPERATIONS , BOSTON );
insert into EMP (EMPNO, ENAME, JOB,SAL, DEPTNO) values (7369, SMITH , CLERK , 800,20);
insert into EMP (EMPNO, ENAME, JOB,SAL, DEPTNO)values (7499, ALLEN , SALESMAN ,1600, 30);
insert into EMP (EMPNO, ENAME, JOB,SAL, DEPTNO)values (7521, WARD , SALESMAN ,1250, 30);
insert into EMP (EMPNO, ENAME, JOB,SAL, DEPTNO)values (7566, JONES , MANAGER ,2975, 20);
insert into EMP (EMPNO, ENAME, JOB,SAL, DEPTNO)values (7654, MARTIN , SALESMAN ,1250, 30);
insert into EMP (EMPNO, ENAME, JOB,SAL, DEPTNO)values (7698, BLAKE , MANAGER ,2850, 30);
insert into EMP (EMPNO, ENAME, JOB,SAL, DEPTNO)values (7782, CLARK , MANAGER ,2450, 10);
insert into EMP (EMPNO, ENAME, JOB,SAL, DEPTNO)values (7788, SCOTT , ANALYST ,3000, 20);
insert into EMP (EMPNO, ENAME, JOB,SAL, DEPTNO)values (7839, KING , PRESIDENT ,5000, 10);
insert into EMP (EMPNO, ENAME, JOB,SAL, DEPTNO)values (7844, TURNER , SALESMAN ,1500, 30);
insert into EMP (EMPNO, ENAME, JOB,SAL, DEPTNO)values (7876, ADAMS , CLERK ,1100, 20);
insert into EMP (EMPNO, ENAME, JOB,SAL, DEPTNO)values (7900, JAMES , CLERK ,950, 30);
insert into EMP (EMPNO, ENAME, JOB,SAL, DEPTNO)values (7902, FORD , ANALYST ,3000, 20);
insert into EMP (EMPNO, ENAME, JOB,SAL, DEPTNO)values (7934, MILLER , CLERK ,1300, 10);
INSERT INTO teacher(tname)
VALUES ( 张雪峰 ),
( 孔子 );
INSERT INTO student(sname,age,tid)
VALUES ( 周杰伦 ,40,1),
( 易烊千玺 ,25,1),
( 檀健次 ,24,2),
( 罗云熙 ,25,2);
3.2.pom.xml
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.15</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<version>8.0.33</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
<scope>test</scope>
</dependency>
<!-- 使用log4j输出更多的日志信息 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.38</version>
</dependency>
</dependencies>
3.3.创建MyBatis配置文件
在 resources 文件夹中创建mybatis-config.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>
<!--
<typeAliases>
<package name=""/>
</typeAliases> -->
<!-- 配置数据源相关属性 -->
<environments default="development">
<!-- 可以配置多个数据源环境,默认使用default中的值 -->
<environment id="development">
<!-- 使用jdbc的事务管理 -->
<transactionManager type="MANAGED" />
<!-- 配置数据源,并使用自带数据库连接池 -->
<dataSource type="POOLED">
<property name="driver" value="com.mysql.cj.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/mybatis?characterEncoding=utf-8&serverTimezone=Asia/Shanghai" />
<property name="username" value="root" />
<property name="password" value="root" />
</dataSource>
</environment>
</environments>
<!-- 配置映射文件,可配置多个 -->
<mappers>
<mapper resource="" />
</mappers>
</configuration>
- transactionManager标签的type属性有两种取值:
a. JDBC:全部使用jdbc的事务管理
b. MANAGED:不使用事务管理,也从不提交 - dataSource标签的type属性有三种取值:
a. POOLED:使用Mybatis自带的数据库连接池
b. UNPOOLED:不使用任何数据库连接池
c. JNDI:jndi形式使用数据库连接
3.4.创建日志配置文件
在 resources 文件夹中创建log4j.properties配置文件
### direct log messages to stdout ###
log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.Target=System.out
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern=%d{yyyy-MM-dd HH:mm:ss} %m%n
log4j.rootLogger=debug,stdout
3.5.创建po类
在src新建Dept
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dept {
private Integer deptno;
private String dname;
private String loc;
}
3.6.创建映射文件
在resources目录下,创建映射文件:mappers/mapper.xml
<?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="">
</mapper>
注意: mapper标签:映射文件的根标签。 mapper标签namespace属性:命名空间,对sql进行分类管理
3.7.编写SQL语句
<insert id="insert" parameterType="com.neuedu.mybatis.po.Dept" >
insert into dept values (#{deptno},#{dname},#{loc})
</insert >
{}: 表明sql参数,一个占位符。
当parameterType属性为对象类型时:#{} 中的参数名为对象的属性名。 当parameterType类型为单个基本类型或String时,参数名可以任意。
3.8.测试
在 test文件夹下新建AppTest
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
public class AppTest
{
private SqlSession session=null;
@Before
public void before() throws Exception {
InputStream input = Resources.getResourceAsStream("mybatis-config.xml");
SqlSessionFactory ssf = new SqlSessionFactoryBuilder().build(input);
session= ssf.openSession();
}
@After
public void after() {
session.close();
}
@Test
public void testInsert() {
}
}
4.封装持久层
MyBatis开发DAO层有两种方式:
- 原始dao方式
- mapper代理方式
4.1.原始dao方式
按照JDBC课程中封装dao层的方式,我们可以先封装一个 Util 工具类,在此工具类中封装一个获取SqlSessionFactory的方法。然后创建dao接口和实现类(使用繁琐)
4.2.mapper代理方式
程序员只需要mapper接口和mapper.xml映射文件,Mybatis可以自动生成mapper接口实现类代理对象。程序员编写mapper接口需要遵循一些开发规范。
mapper代理方式开发规范:
- 映射文件中的 namespace 必须是 mapper 接口的地址。
- 映射文件中 statement 的 id 必须与 mapper 接口中的方法名一致。
- 映射文件中 parameterType 必须与 mapper 接口中的方法参数类型一致。
- 映射文件中 resultType 必须与 mapper 接口中的返回值类型一致。
4.2.1.创建mapper接口
import com.neuedu.mybatis.po.Dept;
import java.util.List;
public interface DeptMapper {
List<Dept> selectAll();
Dept selectByName(String dname);
int insert(Dept dept);
int update(Dept dept);
int deleteByDeptno(int deptno);
}
4.2.2.创建mapper映射文件
在src目录下新建DeptMapper.xml映射文件
注意:接口名与映射文件名要一致
4.2.3.注册mapper映射文件
<configuration>
<mappers>
<mapper resource="mappers/DeptMapper.xml" />
</mappers>
</configuration>
4.2.4.获取代理对象测试
DeptMapper mapper=session.getMapper(DeptMapper.class);
5.MyBatis配置的优化
5.1.批量定义类别名
在MyBatis中的配置文件中, parameterType和resultType都需要指定自定义类的全路径。类的全路径一般都很长,所以需要进行优化。也就是给类定义别名。
<configuration>
<typeAliases>
<package name=""/>
</typeAliases>
</configuration>
6.常用数据库操作
6.1.多条件查询
<select id="listEmp" parameterType="Emp" resultType="Emp">
select *
from emp
where job like concat( % ,#{job}, % ) and deptno=#{deptno}
order by empno
</select>
6.2.转义字符查询
由于 <(小于号)是标签关键词,因此不能识别小于号。所以MyBatis中设计了一些转义字符,来取代一些特殊字符:”<“(小于号 <) “>”(大于号 >)
6.3.插入(获取主键)
<insert id="insert" parameterType="food">
<selectKey keyProperty="id" resultType="int" order="AFTER">
select last_insert_id()
</selectKey>
insert into food(name,info,price)
values(#{name},#{info},#{price})
</insert>
7.动态SQL
动态SQL主要用于解决查询条件不确定的情况。也就是说:在实际开发中,常常需要根据用户是否输入了某个值,来确定是否需要这个条件。 MyBatis中用于动态sql的元素主要有:if、where、trim、set等
7.1.if+where标签
<select id="where" parameterType="emp" resultType="emp">
select * from emp
<!-- where+if实现 动态条件 -->
<where>
<if test="ename!=null and ename!= ">
and ename like concat( % ,#{ename}, % )
</if>
<if test="job!=null and job!= ">
and job = #{job}
</if>
<if test="sal!=null">
and sal < #{sal}
</if>
</where>
</select>
if+where会实现以下功能:
- 自动添加where
- 不需要思考where后是否加and,mybatis会自动处理
- 不需要思考是否加空格,mybatis会自动处理
7.2.trim标签
trim标签主要用于插入SQL
<insert id="insert" parameterType="emp">
insert into emp
<trim prefix="(" suffix=")" suffixOverrides=",">
empno,
<if test="ename!=null and ename!= ">
ename,
</if>
<if test="job!=null and job!= ">
job,
</if>
<if test="sal!=null">
sal,
</if>
<if test="deptno!=null">
deptno,
</if>
</trim>
<trim prefix="values(" suffix=")" suffixOverrides=",">
#{empno},
<if test="ename!=null and ename!= ">
#{ename},
</if>
<if test="job!=null and job!= ">
#{job},
</if>
<if test="sal!=null">
#{sal},
</if>
<if test="deptno!=null">
#{deptno},
</if>
</trim>
</insert>
7.3.set标签
set标签主要用于更新SQL
<update id="update" parameterType="emp">
update emp
<set>
<if test="ename!=null and ename!= ">
ename = #{ename},
</if>
<if test="sal!=null">
sal = #{sal},
</if>
</set>
where empno = #{empno}
</update>
8.MyBatis输出映射
8.1.resultMap的使用
当sql语句中的列名与实体对象中的属性名不一致时,可以使用resultMap来显式的进行映射。
<!-- 定义一个resultMap,取一个唯一标识id -->
<resultMap id="dept" type="dept">
<id column="no" property="deptno" />
<result column="name" property="dname" />
<result column="location" property="loc" />
</resultMap>
<!-- 使用resultMap属性,指明使用哪一个resultMap -->
<select id="selectAll" resultMap="dept">
select deptno as no,dname as name,loc as location
from dept
</select>
9.MyBatis关联查询
我们知道,在数据库中,表关系有:一对一、一对多。 那么在实际开发中,许多查询都是通过多个表之间的关系,进行多表连接查询的。
那么,在多表连接查询中,MyBatis如何实现输出映射呢,这就要使用MyBatis的关联查询。
9.1.一对一关联查询
修改实体类

使用resultMap映射关联
<resultMap id="onetoone" type="emp">
<id property="empno" column="empno"/>
<result property="ename" column="ename"/>
<result property="job" column="job"/>
<result property="sal" column="sal"/>
<result property="deptno" column="deptno"/>
<association property="dept" javaType="dept">
<id property="deptno" column="ddeptno"/>
<result property="dname" column="dname"/>
<result property="loc" column="loc"/>
</association>
<!-- 使用association标签 实现一对一的结果映射 -->
</resultMap>
<select id="selectAll" resultMap="onetoone">
SELECT e.*,d.DEPTNO as ddeptno,dname,loc
FROM emp e
JOIN dept d
ON e.deptno=d.deptno
</select>
association表明配置单个对象的关联映射 :
- association标签中的property:添加的属性名。
- association标签中的javaType:添加的属性类型。
9.2.一对多关联查询
修改实体类

使用resultMap映射关联
<resultMap type="Dept" id="deptResultMap">
<id property="deptno" column="deptno"/>
<result property="dname" column="dname"/>
<result property="loc" column="loc"/>
<collection property="empList" ofType="Emp">
<id property="empno" column="eempno"/>
<result property="ename" column="eename"/>
<result property="job" column="ejob"/>
<result property="hiredate" column="ehiredate"/>
<result property="sal" column="esal"/>
<result property="deptno" column="edeptno"/>
</collection>
</resultMap>
<select id="listDeptAll" resultMap="deptResultMap">
select d.*,
e.empno eempno,
e.ename eename,
e.job ejob,
e.hiredate ehiredate,
e.sal esal,
e.deptno edeptno
from dept d left join emp e
on d.deptno=e.deptno
</select>
collection表明配置多个对象的集合的关联映射 :
- collection标签中的property:添加的集合属性名。
- collection标签中的ofType:添加的集合中的元素类型。
10.使用注解实现MyBatis映射
MyBatis也支持使用注解来配置映射语句。 主要有四种注解来实现增删改查:@Select、@Insert、@Update、@Delete
public interface DeptMapper {
@Insert("insert into dept values(#{deptno},#{dname},#{loc})")
int insert(Dept dept);
@Delete("delete from dept where deptno = #{no}")
int deleteByDeptno(int deptno);
@Update("update dept set dname=#{dname},loc=#{loc} where deptno = #{deptno}")
int insert(Dept dept);
@Select("select * from dept")
List<Dept> selectAll();
}
注意:
- 当使用基于注解的映射器接口时,就不再需要映射配置文件了。
- 在实际开发中,可以单独使用映射文件,也可以单独使用注解,也可以混合使用。
- 简单的sql使用注解,复杂的sql(动态sql,多表查询)使用映射文件



