環(huán)境準(zhǔn)備
- 數(shù)據(jù)庫
示例使用的使用MySQL8數(shù)據(jù)庫,創(chuàng)建一個(gè)用戶表user,包括以下兩個(gè)字段,一個(gè)是主鍵id,一個(gè)是用戶名username
| 列名 | 數(shù)據(jù)類型 | 其它 |
|---|---|---|
| id | int(11) | auto_increment |
| username | varchar(32) |
- 開發(fā)工具
示例使用的是Eclipse 2019-09 - 其它
可以使用Eclipse內(nèi)置的Maven3,也可以使用自行安裝的Maven。使用自行安裝的Maven,配置好Eclipse即可。
一個(gè)簡單實(shí)例
這個(gè)是比較傳統(tǒng)和普遍的做法,對于數(shù)據(jù)庫的API操作,按照分層習(xí)慣,會封裝在DAO里面。編碼的核心是聲明一個(gè)DAO接口,然后在DAO接口的實(shí)現(xiàn)類中調(diào)用MyBatis的API,從而實(shí)現(xiàn)對MyBatis的集成。而相關(guān)的所有SQL語句,則寫在xml格式的SQL映射文件中。
業(yè)務(wù)需求
一個(gè)簡單的用戶查詢?yōu)槔?,從MySQL數(shù)據(jù)庫中根據(jù)用戶id查詢用戶信息。
編碼流程
主要分成以下步驟
- 創(chuàng)建全局配置文件
全局配置文件的名字可以隨意,比如mybatis-config.xml。在全局配置文件中,可以配置數(shù)據(jù)庫連接、插件(如分頁插件)、SQL映射文件資源路徑等信息。對于數(shù)據(jù)庫連接,習(xí)慣做法會另外創(chuàng)建一個(gè)db.properties屬性文件單獨(dú)保存數(shù)據(jù)庫連接信息,然后在mybatis-config.xml文件中去引用。 - 創(chuàng)建數(shù)據(jù)表POJO對象
POJO對象主要用來傳遞語句的參數(shù)值,以及保存執(zhí)行SQL語句后數(shù)據(jù)庫返回給程序的操作結(jié)果。大多數(shù)情況下,POJO對象中的屬性名會與數(shù)據(jù)表列名一一對應(yīng)(也可以手動(dòng)屬性名和列名的映射關(guān)系)。比如可以創(chuàng)建User.java對應(yīng)user表。 - 創(chuàng)建數(shù)據(jù)表SQL映射文件
比如對用戶表user的相關(guān)SQL,可以創(chuàng)建一個(gè)UserMapper.xml來保存對用戶表的SQL語句。 - 創(chuàng)建DAO接口及其實(shí)現(xiàn)類
DAO接口聲明對數(shù)據(jù)庫的操作方法,在實(shí)現(xiàn)類中調(diào)用MyBatis的API實(shí)現(xiàn)對數(shù)據(jù)庫的操作。比如對user的操作,可以創(chuàng)建UserDao.java接口和UserDaoImpl.java實(shí)現(xiàn)類。
創(chuàng)建Maven工程
使用Eclipse創(chuàng)建一個(gè)Maven工程,編碼完成后,項(xiàng)目結(jié)構(gòu)大概如下

其中pom.xml文件內(nèi)容如下,主要是引入了mybatis、mysql以及junit依賴
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.fandou.mybatis</groupId>
<artifactId>mybatis-beginning</artifactId>
<version>0.0.1-SNAPSHOT</version>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.3</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.17</version>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
實(shí)例代碼
按照上面的編碼流程,按順序先后創(chuàng)建的文件清單如下
- mybatis-config.xml
- db.properties
- User.java
- UserMapper.xml
- UserDao.java
- UserDaoImpl.java
- Test01.java //測試類
各個(gè)文件的具體代碼分別如下
1、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>
<!-- 加載db.properties屬性文件 -->
<properties resource="db.properties"></properties>
<!-- 定義環(huán)境,默認(rèn)使用development -->
<environments default="development">
<!-- 定義環(huán)境development,配置對應(yīng)的JDBC數(shù)據(jù)庫連接,相關(guān)的連接信息在db.properties屬性文件中定義 -->
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/fandou/mybatis/beginning/demo01/mapper/UserMapper.xml"/>
</mappers>
</configuration>
2、db.properties
#//這里的屬性名與mybatis-config.xml文件中的保持一致
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://192.168.101.28:3306/vhr?characterEncoding=utf8
jdbc.username=sqcheng
jdbc.password=123456
3、User.java
package com.fandou.mybatis.beginning.demo01.po;
import java.io.Serializable;
public class User implements Serializable {
/**
* serialVersionUID
*/
private static final long serialVersionUID = -5944097591296647171L;
private int id;
private String username;
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
}
4、UserMapper.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">
<!-- 注意這里的namespace,目前隨意命名即可 -->
<mapper namespace="UserMapper">
<select id="findById" resultType="com.fandou.mybatis.beginning.demo01.po.User">
select * from user where id = #{id}
</select>
</mapper>
5、UserDao.java
package com.fandou.mybatis.beginning.demo01.dao;
import com.fandou.mybatis.beginning.demo01.po.User;
public interface UserDao {
/**
* 根據(jù)用戶id查詢用戶信息
* @param id
* @return
*/
User findById(int id);
}
6、UserDaoImpl.java
package com.fandou.mybatis.beginning.demo01.dao;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import com.fandou.mybatis.beginning.demo01.po.User;
public class UserDaoImpl implements UserDao {
private SqlSessionFactory sqlSessionFactory;
public UserDaoImpl(SqlSessionFactory sqlSessionFactory) {
this.sqlSessionFactory = sqlSessionFactory;
}
@Override
public User findById(int id) {
SqlSession session = null;
User user = null;
try {
session = sqlSessionFactory.openSession();
/*
* 第一個(gè)statement參數(shù),其格式為SQL映射文件中namespace.id
*/
user = (User) session.selectOne("UserMapper.findById", id);
} catch (Exception ex) {
System.err.println(ex.getMessage());
} finally {
session.close();
}
return user;
}
}
7、Test01.java
package com.fandou.mybatis.beginning.demo01;
import java.io.IOException;
import java.io.InputStream;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Before;
import org.junit.Test;
import com.fandou.mybatis.beginning.demo01.dao.UserDao;
import com.fandou.mybatis.beginning.demo01.dao.UserDaoImpl;
import com.fandou.mybatis.beginning.demo01.po.User;
public class Test01 {
private SqlSessionFactory sqlSessionFactory;
/**
* 加載MyBatis全局配置,創(chuàng)建sqlSessionFactory
* @throws IOException
*/
@Before
public void init() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindById() {
UserDao userDao = new UserDaoImpl(sqlSessionFactory);
User user = userDao.findById(1);
System.out.println("user.id => " + user.getId());
System.out.println("user.username => " + user.getUsername());
}
}
至此,所有的代碼編寫完畢,接下來,在mysql數(shù)據(jù)庫中添加以下3條數(shù)據(jù)
| id | username |
|---|---|
| 1 | admin |
| 2 | root |
| 3 | sqcheng |
添加完畢,運(yùn)行Junit測試驗(yàn)證,正常將在控制臺輸入如下信息
user.id => 1
user.username => admin
使用Mapper接口代理
使用Mapper接口代理這種方式開發(fā),需要開發(fā)人員只需要遵守一定的規(guī)范來編寫接口和xml映射文件。接口編寫完,實(shí)現(xiàn)類將在調(diào)用的時(shí)候交由MyBatis框架通過動(dòng)態(tài)代理來生成,開發(fā)人員不需要自行編寫接口的實(shí)現(xiàn)類。
編碼流程
Mapper接口代理的方式編碼流程如下
- 創(chuàng)建全局配置文件
- 創(chuàng)建數(shù)據(jù)表POJO對象
- 創(chuàng)建數(shù)據(jù)表SQL映射文件
- 創(chuàng)建Mapper接口
實(shí)例代碼
在前面的實(shí)例工程基礎(chǔ)上,新建一個(gè)com.fandou.mybatis.beginning.demo02包,然后再去編寫相關(guān)的代碼,編寫完成后,項(xiàng)目的目錄結(jié)構(gòu)如下

其中pom.xml、db.properties、User.java和前面的實(shí)例是一樣的,下面不再重復(fù)列出相關(guān)代碼。以下是UserMapper.xml和UserMapper.java以及Test02.java的詳細(xì)代碼
1、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>
<!-- 加載db.properties屬性文件 -->
<properties resource="db.properties"></properties>
<!-- 定義環(huán)境,默認(rèn)使用development -->
<environments default="development">
<!-- 定義環(huán)境development,配置對應(yīng)的JDBC數(shù)據(jù)庫連接,相關(guān)的連接信息在db.properties屬性文件中定義 -->
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<mapper resource="com/fandou/mybatis/beginning/demo02/mapper/UserMapper.xml"/>
</mappers>
</configuration>
2、UserMapper.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">
<!-- 注意這里的namespace,需要使用UserMapper.java接口類的完整名 -->
<mapper namespace="com.fandou.mybatis.beginning.demo02.mapper.UserMapper">
<select id="findById" resultType="com.fandou.mybatis.beginning.demo02.po.User">
select * from user where id = #{id}
</select>
</mapper>
3、UserMapper.java
package com.fandou.mybatis.beginning.demo02.mapper;
import com.fandou.mybatis.beginning.demo02.po.User;
public interface UserMapper {
/**
* 根據(jù)用戶id查詢用戶信息
* @param id
* @return
*/
User findById(int id);
}
4、Test02.java
package com.fandou.mybatis.beginning.demo02;
import java.io.IOException;
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.Before;
import org.junit.Test;
import com.fandou.mybatis.beginning.demo02.po.User;
import com.fandou.mybatis.beginning.demo02.mapper.UserMapper;
public class Test02 {
private SqlSessionFactory sqlSessionFactory;
/**
* 加載MyBatis全局配置,創(chuàng)建sqlSessionFactory
* @throws IOException
*/
@Before
public void init() throws IOException {
String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
}
@Test
public void testFindById() {
/*
* 調(diào)用UserMapper接口
*/
SqlSession session = sqlSessionFactory.openSession();
UserMapper userMapper = session.getMapper(UserMapper.class);
User user = userMapper.findById(2);
System.out.println("user.id => " + user.getId());
System.out.println("user.username => " + user.getUsername());
}
}
對比代碼,有以下三點(diǎn)發(fā)現(xiàn)
1、UserMapper接口的內(nèi)容和UserDAO接口是一樣的
2、UserMapper.xml中的命名空間namespace變成了全路徑名
3、Test01.java中使用new方式實(shí)例化一個(gè)UserDaoImpl對象,Test02.java則使用MyBatis提供的動(dòng)態(tài)代理方法直接獲取生成的UserMapper接口實(shí)例
Mapper接口代理方式開發(fā)規(guī)范
以下是使用Mapper接口代理方式的一些開發(fā)規(guī)范,必須遵守
1、 Mapper接口的類路徑與Mapper.xml中的namespace保持一致
2、Mapper接口中的方法名和Mapper.xml中每個(gè)statement的id相同
3、Mapper接口方法的入?yún)㈩愋秃蚆apper.xml中每個(gè)sql的parameterType相同
4、Mapper接口方法的返回值類型和Mapper.xml中定義的每個(gè)sql的resultType的類型相同
接下來,在Test02.java上運(yùn)行Junit測試,正常情況下,控制臺將輸出如下結(jié)果
user.id => 2
user.username => root
純注解方式
純注解方式和使用Mapper接口代理的方式不同的地方是不需要寫SQL映射文件,所有的SQL語句直接寫在Mapper接口中。
編碼流程
純注解方式其編碼流程如下
- 創(chuàng)建全局配置文件
- 創(chuàng)建數(shù)據(jù)表POJO對象
- 創(chuàng)建Mapper接口,使用注解并編寫SQL語句
實(shí)例代碼
在前面的實(shí)例工程基礎(chǔ)上,新建一個(gè)com.fandou.mybatis.beginning.demo03包,然后再去編寫相關(guān)的代碼,編寫完成后,項(xiàng)目的目錄結(jié)構(gòu)如下

其中pom.xml、db.properties、User.java和前面的實(shí)例是一樣的,Test03.java與Test02.java相同,下面不重復(fù)列出。以下是相關(guān)文件的代碼
1、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>
<!-- 加載db.properties屬性文件 -->
<properties resource="db.properties"></properties>
<!-- 定義環(huán)境,默認(rèn)使用development -->
<environments default="development">
<!-- 定義環(huán)境development,配置對應(yīng)的JDBC數(shù)據(jù)庫連接,相關(guān)的連接信息在db.properties屬性文件中定義 -->
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 掃描接口的注解 -->
<package name="com.fandou.mybatis.beginning.demo03/mapper"/>
</mappers>
</configuration>
2、UserMapper.java
package com.fandou.mybatis.beginning.demo03.mapper;
import org.apache.ibatis.annotations.Select;
import com.fandou.mybatis.beginning.demo03.po.User;
public interface UserMapper {
/**
* 根據(jù)用戶id查詢用戶信息
* @param id
* @return
*/
@Select("select * from user where id = #{id}")
User findById(int id);
}
接下來,在Test03.java上運(yùn)行Junit測試,假設(shè)查詢的id為3,正常情況下,控制臺將輸出如下結(jié)果
user.id => 3
user.username => sqcheng
注意事項(xiàng)
編寫select語句注意事項(xiàng)
- 如果parameter為簡單類型
即基本類型和String類型,#{}中的參數(shù)名稱可以任意 - 如果parameter普通對象或key-value鍵值對集合類對象
即POJO類型或Map/Set集合類型,#{}中的參數(shù)名稱必須與POJO的屬性名稱或Map/Set對象中的鍵名一致 - 如果resultType為POJO類型
select語句中的列名和POJO對象屬性名必須一致,或額外對屬性和列名進(jìn)行映射綁定
知識拓展
代理模式
代理模式是Java最常用的設(shè)計(jì)模式之一。它將對一個(gè)對象的直接訪問,變?yōu)樵L問這個(gè)對象的代理對象,通過代理對象間接地訪問原本的對象。Java中的代理模式分為動(dòng)態(tài)代理和靜態(tài)代理。靜態(tài)代理是在編碼階段通過編碼實(shí)現(xiàn)的;動(dòng)態(tài)代理則是在運(yùn)行期,動(dòng)態(tài)地為指定的目標(biāo)類生成代理類及其實(shí)例對象。以上實(shí)例中,使用MyBatis的session獲取Mapper接口實(shí)例的方式,使用動(dòng)態(tài)代理模式實(shí)現(xiàn)的。
- 動(dòng)態(tài)代理
動(dòng)態(tài)代理其主要原理是利用動(dòng)態(tài)生成與裝載字節(jié)碼的技術(shù),為指定的接口動(dòng)態(tài)創(chuàng)建代理類來實(shí)現(xiàn)。相較于靜態(tài)代理,動(dòng)態(tài)代理更靈活。比較常見的有JDK和CGLIB兩種,下面是兩種方式的對比
| 對比項(xiàng) | JDK動(dòng)態(tài)代理 | CGLIB動(dòng)態(tài)代理 |
|---|---|---|
| 代理目標(biāo) | 代理有接口的類 | 通過子類繼承父類方式進(jìn)行代理 |
| 消耗時(shí)間 | 低 | 高 |
| 性能 | 低 | 高 |
| 使用場景 | 每次都需要新建的對象,或代理的類有實(shí)現(xiàn)接口 | 一次創(chuàng)建可多次使用的對象或代理的類沒有實(shí)現(xiàn)接口 |
- 靜態(tài)代理
靜態(tài)代理要求在開發(fā)階段,為目標(biāo)類編寫對應(yīng)的代理。其優(yōu)點(diǎn)是簡單易行,執(zhí)行效率高;缺點(diǎn)是實(shí)現(xiàn)起來比較死板,工作量大,難適應(yīng)靈活多變的需求。