环境准备
- 数据库
示例使用的使用MySQL8数据库,创建一个用户表user,包括以下两个字段,一个是主键id,一个是用户名username
列名 | 数据类型 | 其它 |
---|---|---|
id | int(11) | auto_increment |
username | varchar(32) |
- 开发工具
示例使用的是Eclipse 2019-09 - 其它
可以使用Eclipse内置的Maven3,也可以使用自行安装的Maven。使用自行安装的Maven,配置好Eclipse即可。
一个简单实例
这个是比较传统和普遍的做法,对于数据库的API操作,按照分层习惯,会封装在DAO里面。编码的核心是声明一个DAO接口,然后在DAO接口的实现类中调用MyBatis的API,从而实现对MyBatis的集成。而相关的所有SQL语句,则写在xml格式的SQL映射文件中。
业务需求
一个简单的用户查询为例子,从MySQL数据库中根据用户id查询用户信息。
编码流程
主要分成以下步骤
- 创建全局配置文件
全局配置文件的名字可以随意,比如mybatis-config.xml。在全局配置文件中,可以配置数据库连接、插件(如分页插件)、SQL映射文件资源路径等信息。对于数据库连接,习惯做法会另外创建一个db.properties属性文件单独保存数据库连接信息,然后在mybatis-config.xml文件中去引用。 - 创建数据表POJO对象
POJO对象主要用来传递语句的参数值,以及保存执行SQL语句后数据库返回给程序的操作结果。大多数情况下,POJO对象中的属性名会与数据表列名一一对应(也可以手动属性名和列名的映射关系)。比如可以创建User.java对应user表。 - 创建数据表SQL映射文件
比如对用户表user的相关SQL,可以创建一个UserMapper.xml来保存对用户表的SQL语句。 - 创建DAO接口及其实现类
DAO接口声明对数据库的操作方法,在实现类中调用MyBatis的API实现对数据库的操作。比如对user的操作,可以创建UserDao.java接口和UserDaoImpl.java实现类。
创建Maven工程
使用Eclipse创建一个Maven工程,编码完成后,项目结构大概如下
其中pom.xml文件内容如下,主要是引入了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>
实例代码
按照上面的编码流程,按顺序先后创建的文件清单如下
- mybatis-config.xml
- db.properties
- User.java
- UserMapper.xml
- UserDao.java
- UserDaoImpl.java
- Test01.java //测试类
各个文件的具体代码分别如下
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>
<!-- 定义环境,默认使用development -->
<environments default="development">
<!-- 定义环境development,配置对应的JDBC数据库连接,相关的连接信息在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 {
/**
* 根据用户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();
/*
* 第一个statement参数,其格式为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全局配置,创建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数据库中添加以下3条数据
id | username |
---|---|
1 | admin |
2 | root |
3 | sqcheng |
添加完毕,运行Junit测试验证,正常将在控制台输入如下信息
user.id => 1
user.username => admin
使用Mapper接口代理
使用Mapper接口代理这种方式开发,需要开发人员只需要遵守一定的规范来编写接口和xml映射文件。接口编写完,实现类将在调用的时候交由MyBatis框架通过动态代理来生成,开发人员不需要自行编写接口的实现类。
编码流程
Mapper接口代理的方式编码流程如下
- 创建全局配置文件
- 创建数据表POJO对象
- 创建数据表SQL映射文件
- 创建Mapper接口
实例代码
在前面的实例工程基础上,新建一个com.fandou.mybatis.beginning.demo02包,然后再去编写相关的代码,编写完成后,项目的目录结构如下
其中pom.xml、db.properties、User.java和前面的实例是一样的,下面不再重复列出相关代码。以下是UserMapper.xml和UserMapper.java以及Test02.java的详细代码
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>
<!-- 定义环境,默认使用development -->
<environments default="development">
<!-- 定义环境development,配置对应的JDBC数据库连接,相关的连接信息在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 {
/**
* 根据用户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全局配置,创建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() {
/*
* 调用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());
}
}
对比代码,有以下三点发现
1、UserMapper接口的内容和UserDAO接口是一样的
2、UserMapper.xml中的命名空间namespace变成了全路径名
3、Test01.java中使用new方式实例化一个UserDaoImpl对象,Test02.java则使用MyBatis提供的动态代理方法直接获取生成的UserMapper接口实例
Mapper接口代理方式开发规范
以下是使用Mapper接口代理方式的一些开发规范,必须遵守
1、 Mapper接口的类路径与Mapper.xml中的namespace保持一致
2、Mapper接口中的方法名和Mapper.xml中每个statement的id相同
3、Mapper接口方法的入参类型和Mapper.xml中每个sql的parameterType相同
4、Mapper接口方法的返回值类型和Mapper.xml中定义的每个sql的resultType的类型相同
接下来,在Test02.java上运行Junit测试,正常情况下,控制台将输出如下结果
user.id => 2
user.username => root
纯注解方式
纯注解方式和使用Mapper接口代理的方式不同的地方是不需要写SQL映射文件,所有的SQL语句直接写在Mapper接口中。
编码流程
纯注解方式其编码流程如下
- 创建全局配置文件
- 创建数据表POJO对象
- 创建Mapper接口,使用注解并编写SQL语句
实例代码
在前面的实例工程基础上,新建一个com.fandou.mybatis.beginning.demo03包,然后再去编写相关的代码,编写完成后,项目的目录结构如下
其中pom.xml、db.properties、User.java和前面的实例是一样的,Test03.java与Test02.java相同,下面不重复列出。以下是相关文件的代码
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>
<!-- 定义环境,默认使用development -->
<environments default="development">
<!-- 定义环境development,配置对应的JDBC数据库连接,相关的连接信息在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 {
/**
* 根据用户id查询用户信息
* @param id
* @return
*/
@Select("select * from user where id = #{id}")
User findById(int id);
}
接下来,在Test03.java上运行Junit测试,假设查询的id为3,正常情况下,控制台将输出如下结果
user.id => 3
user.username => sqcheng
注意事项
编写select语句注意事项
- 如果parameter为简单类型
即基本类型和String类型,#{}中的参数名称可以任意 - 如果parameter普通对象或key-value键值对集合类对象
即POJO类型或Map/Set集合类型,#{}中的参数名称必须与POJO的属性名称或Map/Set对象中的键名一致 - 如果resultType为POJO类型
select语句中的列名和POJO对象属性名必须一致,或额外对属性和列名进行映射绑定
知识拓展
代理模式
代理模式是Java最常用的设计模式之一。它将对一个对象的直接访问,变为访问这个对象的代理对象,通过代理对象间接地访问原本的对象。Java中的代理模式分为动态代理和静态代理。静态代理是在编码阶段通过编码实现的;动态代理则是在运行期,动态地为指定的目标类生成代理类及其实例对象。以上实例中,使用MyBatis的session获取Mapper接口实例的方式,使用动态代理模式实现的。
- 动态代理
动态代理其主要原理是利用动态生成与装载字节码的技术,为指定的接口动态创建代理类来实现。相较于静态代理,动态代理更灵活。比较常见的有JDK和CGLIB两种,下面是两种方式的对比
对比项 | JDK动态代理 | CGLIB动态代理 |
---|---|---|
代理目标 | 代理有接口的类 | 通过子类继承父类方式进行代理 |
消耗时间 | 低 | 高 |
性能 | 低 | 高 |
使用场景 | 每次都需要新建的对象,或代理的类有实现接口 | 一次创建可多次使用的对象或代理的类没有实现接口 |
- 静态代理
静态代理要求在开发阶段,为目标类编写对应的代理。其优点是简单易行,执行效率高;缺点是实现起来比较死板,工作量大,难适应灵活多变的需求。