1.MyBatis框架
課程目標(biāo)
1.理解ORM思想
2.掌握MyBatis框架配置數(shù)據(jù)庫(kù)的步驟
3.掌握Mybatis的代理方式實(shí)現(xiàn)持久層
4.掌握Mybatis框架對(duì)數(shù)據(jù)庫(kù)的CRUD操作
5.掌握動(dòng)態(tài)化SQL的操作
6.掌握Mybatis實(shí)現(xiàn)關(guān)聯(lián)查詢
課程重點(diǎn)
- Mybatis的代理方式寫(xiě)法
- Mybatis對(duì)數(shù)據(jù)庫(kù)的CRUD及動(dòng)態(tài)化SQL
- Mybatis的關(guān)聯(lián)查詢
軟件框架技術(shù)簡(jiǎn)介
軟件框架(software framework),通常指的是為了實(shí)現(xiàn)某個(gè)業(yè)界標(biāo)準(zhǔn)或完成特定基本任務(wù)的軟件組件規(guī)范,也指為了實(shí)現(xiàn)某個(gè)軟件組件規(guī)范時(shí),提供規(guī)范所要求之基礎(chǔ)功能的軟件產(chǎn)品。
框架的功能類似于基礎(chǔ)設(shè)施,與具體的軟件應(yīng)用無(wú)關(guān),但是提供并實(shí)現(xiàn)最為基礎(chǔ)的軟件架構(gòu)和體系。
- 為什么需要框架技術(shù):
a. 幫我們更快更好地構(gòu)建程序
b. 是一個(gè)應(yīng)用程序的半成品
c. 提供可重用的公共結(jié)構(gòu)
d. 按一定規(guī)則組織的一組組件 - 優(yōu)勢(shì):
a. 不用再考慮公共問(wèn)題
b. 專心在業(yè)務(wù)實(shí)現(xiàn)上
c. 結(jié)構(gòu)統(tǒng)一,易于學(xué)習(xí)、維護(hù)
d. 新手也可寫(xiě)出好程序
不要重復(fù)造輪子(Stop Trying to Reinvent the Wheel),已經(jīng)成為開(kāi)發(fā)人員的基本原則。
Java世界中的主流框架技術(shù): Spring、SpringMVC、MyBatis、Struts、Hibernate、SpringBoot等。
2.MyBatis簡(jiǎn)介
2.1.ORM思想的提出
數(shù)據(jù)庫(kù)中數(shù)據(jù)是以表的形式存在的,而java中使用的數(shù)據(jù)都是對(duì)象型的。所以不得不要將表數(shù)據(jù)轉(zhuǎn)換成對(duì)象數(shù)據(jù)。 這樣就會(huì)產(chǎn)生大量的沒(méi)有技術(shù)含量的純 “體力” 型代碼。
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);
}
上面代碼就屬于純 “體力” 型代碼。這些代碼工作量大、單調(diào)枯燥、占用大量開(kāi)發(fā)時(shí)間。為了解決這個(gè)問(wèn)題,出現(xiàn)了ORM思想。
ORM(對(duì)象-關(guān)系映射):完成對(duì)象數(shù)據(jù)到關(guān)系型數(shù)據(jù)映射的機(jī)制稱為對(duì)象-關(guān)系映射。
2.2.MyBatis框架
MyBatis就是一個(gè)ORM框架。當(dāng)然,也是一個(gè)持久層框架。
MyBatis封裝了JDBC, 將數(shù)據(jù)庫(kù)中的表數(shù)據(jù)自動(dòng)封裝到對(duì)象中。這樣就可以以面向?qū)ο蟮姆绞讲僮鲾?shù)據(jù)了。它的出現(xiàn),使得開(kāi)發(fā)工作量變小了,可以將精力集中在業(yè)務(wù)邏輯的處理上。代碼精簡(jiǎn)易讀。
MyBatis 本是apache的一個(gè)開(kāi)源項(xiàng)目iBatis, 2010年這個(gè)項(xiàng)目由apache software foundation 遷移到了google code,并且改名為MyBatis 。 也就是說(shuō):iBatis3.0之后都要改名為MyBatis 。
MyBatis 是支持普通 SQL查詢,存儲(chǔ)過(guò)程和高級(jí)映射的優(yōu)秀持久層框架。MyBatis 消除了幾乎所有的JDBC代碼和參數(shù)的手工設(shè)置以及結(jié)果集的檢索。MyBatis 使用簡(jiǎn)單的 XML或注解用于配置和原始映射,將接口和 Java 的POJOs(Plain Old Java Objects,普通的 Java對(duì)象)映射成數(shù)據(jù)庫(kù)中的記錄。
實(shí)際上,MyBatis最核心的功能,就是實(shí)現(xiàn)了輸入映射和輸出映射。
3.MyBatis實(shí)例
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.創(chuàng)建MyBatis配置文件
在 resources 文件夾中創(chuàng)建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> -->
<!-- 配置數(shù)據(jù)源相關(guān)屬性 -->
<environments default="development">
<!-- 可以配置多個(gè)數(shù)據(jù)源環(huán)境,默認(rèn)使用default中的值 -->
<environment id="development">
<!-- 使用jdbc的事務(wù)管理 -->
<transactionManager type="MANAGED" />
<!-- 配置數(shù)據(jù)源,并使用自帶數(shù)據(jù)庫(kù)連接池 -->
<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>
<!-- 配置映射文件,可配置多個(gè) -->
<mappers>
<mapper resource="" />
</mappers>
</configuration>
- transactionManager標(biāo)簽的type屬性有兩種取值:
a. JDBC:全部使用jdbc的事務(wù)管理
b. MANAGED:不使用事務(wù)管理,也從不提交 - dataSource標(biāo)簽的type屬性有三種取值:
a. POOLED:使用Mybatis自帶的數(shù)據(jù)庫(kù)連接池
b. UNPOOLED:不使用任何數(shù)據(jù)庫(kù)連接池
c. JNDI:jndi形式使用數(shù)據(jù)庫(kù)連接
3.4.創(chuàng)建日志配置文件
在 resources 文件夾中創(chuàng)建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.創(chuàng)建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.創(chuàng)建映射文件
在resources目錄下,創(chuàng)建映射文件: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標(biāo)簽:映射文件的根標(biāo)簽。 mapper標(biāo)簽namespace屬性:命名空間,對(duì)sql進(jìn)行分類管理
3.7.編寫(xiě)SQL語(yǔ)句
<insert id="insert" parameterType="com.neuedu.mybatis.po.Dept" >
insert into dept values (#{deptno},#{dname},#{loc})
</insert >
{}: 表示sql參數(shù),一個(gè)占位符。
當(dāng)parameterType屬性為對(duì)象類型時(shí):#{} 中的參數(shù)名為對(duì)象的屬性名。 當(dāng)parameterType類型為單個(gè)基本類型或String時(shí),參數(shù)名可以任意。
3.8.測(cè)試
在 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開(kāi)發(fā)DAO層有兩種方式:
- 原始dao方式
- mapper代理方式
4.1.原始dao方式
按照J(rèn)DBC課程中封裝dao層的方式,我們可以先封裝一個(gè) Util 工具類,在此工具類中封裝一個(gè)獲取SqlSessionFactory的方法。然后創(chuàng)建dao接口和實(shí)現(xiàn)類(使用繁瑣)
4.2.mapper代理方式
程序員只需要mapper接口和mapper.xml映射文件,Mybatis可以自動(dòng)生成mapper接口實(shí)現(xiàn)類代理對(duì)象。程序員編寫(xiě)mapper接口需要遵循一些開(kāi)發(fā)規(guī)范。
mapper代理方式開(kāi)發(fā)規(guī)范:
- 映射文件中的 namespace 必須是 mapper 接口的地址。
- 映射文件中 statement 的 id 必須與 mapper 接口中的方法名一致。
- 映射文件中 parameterType 必須與 mapper 接口中的方法參數(shù)類型一致。
- 映射文件中 resultType 必須與 mapper 接口中的返回值類型一致。
4.2.1.創(chuàng)建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.創(chuàng)建mapper映射文件
在src目錄下新建DeptMapper.xml映射文件
注意:接口名與映射文件名要一致
4.2.3.注冊(cè)mapper映射文件
<configuration>
<mappers>
<mapper resource="mappers/DeptMapper.xml" />
</mappers>
</configuration>
4.2.4.獲取代理對(duì)象測(cè)試
DeptMapper mapper=session.getMapper(DeptMapper.class);
5.MyBatis配置的優(yōu)化
5.1.批量定義類別名
在MyBatis中的配置文件中, parameterType和resultType都需要指定自定義類的全路徑。類的全路徑一般都很長(zhǎng),所以需要進(jìn)行優(yōu)化。也就是給類定義別名。
<configuration>
<typeAliases>
<package name=""/>
</typeAliases>
</configuration>
6.常用數(shù)據(jù)庫(kù)操作
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.轉(zhuǎn)義字符查詢
由于 <(小于號(hào))是標(biāo)簽關(guān)鍵詞,因此不能識(shí)別小于號(hào)。所以MyBatis中設(shè)計(jì)了一些轉(zhuǎn)義字符,來(lái)代替一些特殊字符:"<"(小于號(hào) <) ">"(大于號(hào) >)
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.動(dòng)態(tài)SQL
動(dòng)態(tài)SQL主要用于解決查詢條件不確定的情況。也就是說(shuō):在實(shí)際開(kāi)發(fā)中,經(jīng)常需要根據(jù)用戶是否輸入了某個(gè)值,來(lái)確定是否需要這個(gè)條件。 MyBatis中用于動(dòng)態(tài)sql的元素主要有:if、where、trim、set等
7.1.if+where標(biāo)簽
<select id="where" parameterType="emp" resultType="emp">
select * from emp
<!-- where+if實(shí)現(xiàn) 動(dòng)態(tài)條件 -->
<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會(huì)實(shí)現(xiàn)以下功能:
- 自動(dòng)添加where
- 不需要考慮where后是否加and,mybatis會(huì)自動(dòng)處理
- 不需要考慮是否加空格,mybatis會(huì)自動(dòng)處理
7.2.trim標(biāo)簽
trim標(biāo)簽主要用于插入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標(biāo)簽
set標(biāo)簽主要用于更新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的使用
當(dāng)sql語(yǔ)句中的列名與實(shí)體對(duì)象中的屬性名不一致時(shí),可以使用resultMap來(lái)顯式的進(jìn)行映射。
<!-- 定義一個(gè)resultMap,取一個(gè)唯一標(biāo)識(shí)id -->
<resultMap id="dept" type="dept">
<id column="no" property="deptno" />
<result column="name" property="dname" />
<result column="location" property="loc" />
</resultMap>
<!-- 使用resultMap屬性,指明使用哪一個(gè)resultMap -->
<select id="selectAll" resultMap="dept">
select deptno as no,dname as name,loc as location
from dept
</select>
9.MyBatis關(guān)聯(lián)查詢
我們知道,在數(shù)據(jù)庫(kù)中,表關(guān)系有:一對(duì)一、一對(duì)多。 那么在實(shí)際開(kāi)發(fā)中,很多查詢都是通過(guò)多個(gè)表之間的關(guān)系,進(jìn)行多表連接查詢的。
那么,在多表連接查詢中,MyBatis如何實(shí)現(xiàn)輸出映射呢,這就要使用MyBatis的關(guān)聯(lián)查詢。
9.1.一對(duì)一關(guān)聯(lián)查詢
修改實(shí)體類

使用resultMap映射關(guān)聯(lián)
<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標(biāo)簽 實(shí)現(xiàn)一對(duì)一的結(jié)果映射 -->
</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表示配置單個(gè)對(duì)象的關(guān)聯(lián)映射 :
- association標(biāo)簽中的property:添加的屬性名。
- association標(biāo)簽中的javaType:添加的屬性類型。
9.2.一對(duì)多關(guān)聯(lián)查詢
修改實(shí)體類

使用resultMap映射關(guān)聯(lián)
<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表示配置多個(gè)對(duì)象的集合的關(guān)聯(lián)映射 :
- collection標(biāo)簽中的property:添加的集合屬性名。
- collection標(biāo)簽中的ofType:添加的集合中的元素類型。
10.使用注解實(shí)現(xiàn)MyBatis映射
MyBatis也支持使用注解來(lái)配置映射語(yǔ)句。 主要有四種注解來(lái)實(shí)現(xiàn)增刪改查:@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();
}
注意:
- 當(dāng)使用基于注解的映射器接口時(shí),就不再需要映射配置文件了。
- 在實(shí)際開(kāi)發(fā)中,可以單獨(dú)使用映射文件,也可以單獨(dú)使用注解,也可以混合使用。
- 簡(jiǎn)單的sql使用注解,復(fù)雜的sql(動(dòng)態(tài)sql,多表查詢)使用映射文件