MyBatis 本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
总之,Mybatis对JDBC访问数据库的过程进行了封装,简化了JDBC代码,解决JDBC将结果集封装为Java对象的麻烦。
一、Mybatis简介
什么是MyBatis
MyBatis 本是apache的一个开源项目iBatis,2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。
MyBatis是一个优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注SQL本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。
Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
总之,Mybatis对JDBC访问数据库的过程进行了封装,简化了JDBC代码,解决JDBC将结果集封装为Java对象的麻烦。
下图是MyBatis架构图:
- (1)mybatis-config.xml是Mybatis的核心配置文件,通过其中的配置可以生成SqlSessionFactory,也就是SqlSession工厂
- (2)基于SqlSessionFactory可以生成SqlSession对象
- (3)SqlSession是一个既可以发送SQL去执行,并返回结果,类似于JDBC中的Connection对象,也是Mybatis中至关重要的一个对象。
- (4)Executor是SqlSession底层的对象,用于执行SQL语句
- (5)MapperStatement对象也是SqlSession底层的对象,用于接收输入映射(SQL语句中的参数),以及做输出映射(即将SQL查询的结果映射成相应的结果)
为什么要使用MyBatis
原始的JDBC操作
谈及mybatis,必然需要先了解Java和数据库的连接技术——JDBC(Java DataBase Connectivity)。
jdbc代码:
try {
Class.forName("com.mysql.jdbc.Driver");
try {
connection = DriverManager.getConnection("jdbc:mysql://db.itest.ren:3306/testdb", "xxxx", "xxxx");
String sql = "Select * from testTable WHERE orderid = '" + orderId + "'";
PreparedStatement preparedStatement = connection.prepareStatement(sql);
preparedStatement.execute();
try {
resultSet = preparedStatement.executeQuery(sql);
} catch (SQLException e) {
e.printStackTrace();
}
try {
while (resultSet.next()) {
sellerId = resultSet.getLong("sid");
list.add(sellerId);
}
} catch (SQLException e) {
e.printStackTrace();
}
} catch (SQLException e) {
System.out.println("连接数据库失败");
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
- 1、使用传统方式JDBC访问数据库:
- (1)使用JDBC访问数据库有大量重复代码(比如注册驱动、获取连接、获取传输器、释放资源等);
- (2)JDBC自身没有连接池,会频繁的创建连接和关闭连接,效率低;
- (3)SQL是写死在程序中,一旦修改SQL,需要对类重新编译;
- (4)对查询SQL执行后返回的ResultSet对象,需要手动处理,有时会特别麻烦;
- …
- 2、使用mybatis框架访问数据库:
- (1)Mybatis对JDBC对了封装,可以简化JDBC代码;
- (2)Mybatis自身支持连接池(也可以配置其他的连接池),因此可以提高程序的效率;
- (3)Mybatis是将SQL配置在mapper文件中,修改SQL只是修改配置文件,类不需要重新编译。
- (4)对查询SQL执行后返回的ResultSet对象,Mybatis会帮我们处理,转换成Java对象。
- …
总之,JDBC中所有的问题(代码繁琐、有太多重复代码、需要操作太多对象、释放资源、对结果的处理太麻烦等),在Mybatis框架中几乎都得到了解决!!
二、MyBatis快速入门 需求:使用mybatis查询表中的所有数据
表信息:
CREATE TABLE `emp` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(50) DEFAULT NULL,
`job` varchar(50) DEFAULT NULL,
`salary` double DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8
pom.xml
<dependencies>
<dependency>
<groupId>org.testng</groupId>
<artifactId>testng</artifactId>
<version>6.9.10</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.18</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.2.8</version>
</dependency>
</dependencies>
resource/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>
<!-- 默认指定的数据库环境为MySQL,引用其中一个environment元素的id -->
<environments default="mysql">
<!-- 配置名为mysql(名称可任意取)的环境 -->
<environment id="mysql">
<!-- 配置事务管理器,JDBC代表使用JDBC自带的事务提交和回滚 -->
<transactionManager type="JDBC"/>
<!-- datasource配置数据源,此处使用MyBatis内置的数据源 -->
<dataSource type="POOLED">
<!-- 配置连接数据库的驱动、URL、用户名和密码 -->
<property name="driver" value="com.mysql.jdbc.Driver"/>
<property name="url" value="jdbc:mysql://db.itest.ren:3306/testdb"/>
<property name="username" value="xxxxx"/>
<property name="password" value="xxxxx"/>
</dataSource>
</environment>
</environments>
<mappers>
<!-- 配置MyBatis需要加载的Mapper -->
<mapper resource="EmpMapper.xml"/>
</mappers>
</configuration>
编写实体类pojo/Emp.java:
public class Emp {
//1.声明实体类中的属性
private Integer id;
private String name;
private String job;
private Double salary;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getJob() {
return job;
}
public void setJob(String job) {
this.job = job;
}
public Double getSalary() {
return salary;
}
public void setSalary(Double salary) {
this.salary = salary;
}
@Override
public String toString() {
return "Emp{" +
"id=" + id +
", name='" + name + '\'' +
", job='" + job + '\'' +
", salary=" + salary +
'}';
}
}
编写Mapper文件EmpMapper.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="EmpMapper">
<select id="findAll" resultType="com.itest.mybatis.pojo.Emp">
select * from emp
</select>
</mapper>
编写测试代码访问所有数据:
public class TestMybatis01 {
/** 练习1(快速入门): 查询emp表中的所有员工, 返回一个List<Emp>集合
* @throws IOException */
@Test
public void findAll() throws IOException {
//1.读取mybatis的核心配置文件(mybatis-config.xml)
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.通过配置信息获取一个SqlSessionFactory工厂对象
SqlSessionFactory fac = new SqlSessionFactoryBuilder().build(in);
//3.通过工厂获取一个SqlSession对象
SqlSession session = fac.openSession();
//4.通过namespace+id找到要执行的sql语句并执行sql语句
List<Emp> list = session.selectList("EmpMapper.findAll");
//5.输出结果
for(Emp e : list) {
System.out.println( e );
}
}
}
输出如下:
Emp{id=1, name='李四', job='程序员', salary=2800.0}
Emp{id=2, name='王五', job='程序员鼓励师', salary=2700.0}
Emp{id=3, name='王二', job='部门总监', salary=4200.0}
Emp{id=4, name='麻子', job='程序员', salary=3000.0}
Emp{id=5, name='高启强', job='程序员', salary=3500.0}
三、配置文件
3.1 mybatis-config.xml配置文件
详细参考:https://mybatis.org/mybatis-3/zh/configuration.html
MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:
- configuration(配置)
- properties(属性)
- settings(设置)
- typeAliases(类型别名)
- typeHandlers(类型处理器)
- objectFactory(对象工厂)
- plugins(插件)
- environments(环境配置)
- environment(环境变量)
- transactionManager(事务管理器)
- dataSource(数据源)
- databaseIdProvider(数据库厂商标识)
- mappers(映射器)
- environment(环境变量)
3.1.1 environments环境配置
MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中, 现实情况下有多种理由需要这么做。例如,开发、测试和生产环境需要有不同的配置;或者想在具有相同 Schema 的多个生产数据库中使用相同的 SQL 映射。还有许多类似的使用场景。
不过要记住:尽管可以配置多个环境,但每个 SqlSessionFactory 实例只能选择一种环境。
所以,如果你想连接两个数据库,就需要创建两个 SqlSessionFactory 实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推,记起来很简单:
每个数据库对应一个 SqlSessionFactory 实例
为了指定创建哪种环境,只要将它作为可选的参数传递给 SqlSessionFactoryBuilder 即可。
3.1.2 事务管理器(transactionManager)
在 MyBatis 中有两种类型的事务管理器(也就是 type=”[JDBC|MANAGED]”):
- JDBC – 这个配置直接使用了 JDBC 的提交和回滚功能,它依赖从数据源获得的连接来管理事务作用域。推荐使用
- MANAGED – 这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。不推荐使用
3.1.3 数据源(dataSource)
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
有三种内建的数据源类型(也就是 type=”[UNPOOLED | POOLED | JNDI]”): |
- UNPOOLED– 这个数据源的实现会每次请求时打开和关闭连接。
- POOLED– 这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求。
- JNDI – 这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。
3.1.4 映射器(mappers)
用于导入mapper文件的位置,其中可以配置多个mapper,即可以导入多个mapper文件
可以使用相对于类路径的资源引用,或完全限定资源定位符(包括 file:/// 形式的 URL),或类名和包名等
3.2 Mapper.xml配置文件
详细参考:https://mybatis.org/mybatis-3/zh/sqlmap-xml.html
MyBatis 的真正强大在于它的语句映射,这是它的魔力所在。由于它的异常强大,映射器的 XML 文件就显得相对简单。如果拿它跟具有相同功能的 JDBC 代码进行对比,你会立即发现省掉了将近 95% 的代码。MyBatis 致力于减少使用成本,让用户能更专注于 SQL 代码。
SQL 映射文件只有很少的几个顶级元素(按照应被定义的顺序列出):
- cache – 该命名空间的缓存配置。
- cache-ref – 引用其它命名空间的缓存配置。
- resultMap – 描述如何从数据库结果集中加载对象,是最复杂也是最强大的元素。
- parameterMap – 老式风格的参数映射。此元素已被废弃,并可能在将来被移除!请使用行内参数映射。文档中不会介绍此元素。
- sql – 可被其它语句引用的可重用语句块。
- insert – 映射插入语句。
- update – 映射更新语句。
- delete – 映射删除语句。
- select – 映射查询语句。
3.2.1 上述项目中的EmpMapper.xml配置文件解释
(1)第1行是xml的文档声明,用于声明xml的版本和编码
(2)第2、3、4行,引入了xml约束文档,当前xml文档将会按照mybatis-3-mapper.dtd文件所要求的规则进行书写。
(3)Mapper标签:根标签,其中namespace(名称空间,也叫命名空间),要求不能重复。在程序中通过【namespace + id 】定位到要执行哪一条SQL语句
(4)select标签:用于指定将来要执行的各种SQL语句。标签上可以声明属性,下面介绍常用的属性:id、resultType、resultMap
- id属性:要求值不能重复。将来在执行SQL时,可以通过【namespace + id】找到指定SQL并执行。
- resultType属性:从这条SQL语句中返回所期望类型的类的完全限定名称(包名+类名)。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。 简而言之,resultType控制查询SQL执行后返回值的类型或集合中的泛型,例如查询emp表中的单条记录,返回值是一个Emp对象,因此,resultType=”com.itest.mybatis.pojo.Emp”; 如果查询emp表中的多条记录,返回值是一个List,此时resultType的值应该集合中的泛型,因此resultType=”com.itest.mybatis.pojo.Emp”;
- resultMap属性:复杂对象结构(例如多表关联查询等)。 使用 resultType 或 resultMap,但不能同时使用。
3.2.2 mybatis中的占位符
在上面的增删改查操作中,SQL语句中的值是写死在SQL语句中的,而在实际开发中,此处的值往往是用户提交过来的值,因此这里我们需要将SQL中写死的值替换为占位符。
在mybatis中占位符有两个,分别是 #{} 占位符 和 ${} 占位符:
- #{}:相当于JDBC中的问号(?)占位符,是为SQL语句中的参数值进行占位,大部分情况下都是使用#{}占位符;并且当#{}占位符是为字符串或者日期类型的值进行占位时,在参数值传过来替换占位符的同时,会进行转义处理(在字符串或日期类型的值的两边加上单引号);
- ${}:是为SQL片段(字符串)进行占位,将传过来的SQL片段直接拼接在 ${} 占位符所在的位置,不会进行任何的转义处理。(由于是直接将参数拼接在SQL语句中,因此可能会引发SQL注入攻击问题) 需要注意的是:使用 ${} 占位符为SQL语句中的片段占位时,即使只有一个占位符,需要传的也只有一个参数,也需要将参数先封装再传递!
3.2.3 动态SQL标签
if、where标签
-
标签:是根据test属性中的布尔表达式的值,从而决定是否执行包含在其中的SQL片段。如果判断结果为true,则执行其中的SQL片段;如果结果为false,则不执行其中的SQL片段 -
标签:用于对包含在其中的SQL片段进行检索,在需要时可以生成where关键字,并且在需要时会剔除多余的连接词(比如and或者or)
3.2. 4 foreach标签
foreach标签:可以对传过来的参数数组或集合进行遍历
3.3 Mybatis 增删改查
3.3.1 新增
编辑EmpMapper.xml
<!-- 练习2: 新增员工信息: 赵云 保安 6000
增删改的标签上不用指定resultType, 因为返回值都是int类型
-->
<update id="insert" >
insert into emp value(null, '赵云', '保安', 6000)
</update>
实现和测试:
/*
新增员工信息: 赵云 保安 6000
*/
@Test
public void testInsert() throws IOException {
//1.读取mybatis的核心配置文件(mybatis-config.xml)
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.通过配置信息获取一个SqlSessionFactory工厂对象
SqlSessionFactory fac = new SqlSessionFactoryBuilder().build(in);
//3.通过工厂获取一个SqlSession对象
SqlSession session = fac.openSession();
//执行sql语句, 返回执行结果
int rows = session.update("EmpMapper.insert");
//提交事务
session.commit();
System.out.println("影响的行数: "+rows);
}
3.3.2 修改
编辑EmpMapper.xml
<!-- 练习3:修改员工信息:赵云 保镖 20000 -->
<update id="update">
update emp set job='保镖', salary=20000 where name='赵云'
</update>
实现和测试:
/*
练习3: 修改员工信息, 将赵云的job改为'保镖',salary改为20000
*/
@Test
public void testUpdate() throws IOException {
//1.读取mybatis的核心配置文件(mybatis-config.xml)
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.通过配置信息获取一个SqlSessionFactory工厂对象
SqlSessionFactory fac = new SqlSessionFactoryBuilder().build(in);
//3.通过工厂获取一个SqlSession对象
SqlSession session = fac.openSession();
//执行sql语句, 返回执行结果
int rows = session.update("EmpMapper.update");
//提交事务
session.commit();
System.out.println("影响行数:"+rows);
}
3.3.3 删除
编辑EmpMapper.xml
<!-- 练习4: 删除name为'赵云'的记录 -->
<update id="delete">
delete from emp where name='赵云'
</update>
实现和测试:
/*
练习4: 删除name为'赵云'的记录
*/
@Test
public void testDelete() throws IOException {
//1.读取mybatis的核心配置文件(mybatis-config.xml)
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.通过配置信息获取一个SqlSessionFactory工厂对象
SqlSessionFactory fac = new SqlSessionFactoryBuilder().build(in);
//3.通过工厂获取一个SqlSession对象
SqlSession session = fac.openSession();
//执行sql语句, 返回执行结果
int rows = session.update("EmpMapper.delete");
//提交事务
session.commit();
System.out.println("影响行数:"+rows);
}
四、Mapper接口开发
Mapper接口开发介绍
在上面的Mybatis案例中, 通过SqlSession对象调用方法进行增删改查操作时,方法中需要传入的第一个参数是一个字符串值,该值对应的内容为: (Mapper文件中的)namespace + id, 通过这种方式,找到Mapper文件中映射的SQL语句并执行!!
这种方式由于传入的是字符串值, 很容易发生字符串拼写错误且编译时期不会提示。
这里我们将会讲解比上面更加简单的方式,也是我们企业开发中最常用的方式,即使用mapper接口开发。使用mapper接口开发需要注意以下几点:
- 1、创建一个接口,接口的全限定类名和mapper文件的namespace值要相同
- 2、mapper文件中每条要执行的SQL语句,在接口中要添加一个对应的方法,并且接口中的方法名和SQL标签上的id值相同
- 3、Mapper接口中方法接收的参数类型,和mapper.xml中定义的sql的接收的参数类型要相同
- 4、接口中方法的返回值类型和SQL标签上的resultType即返回值类型相同(如果方法返回值是集合,resultType只需要指定集合中的泛型)
Mapper接口开发实现
下面将使用mapper接口开发的方式,实现根据id查询指定的员工信息
1、创建Mapper接口(命名为EmpMapper2),并提供方法
public interface EmpMapper2 {
/**
* 根据id查询员工信息
*
* @param id
* @return
*/
public Emp findById(Integer id);
}
2、创建mapper.xml(EmpMapper2.xml),
注意:namespace要和mapper接口全限定名称相同
注意:id要和接口中的方法名相同
注意:返回类型要和接口中对应方法的返回类型相同
<?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="com.itest.mybatis.dao.EmpMapper2">
<select id="findById" resultType="com.itest.mybatis.pojo.Emp">
select * from emp where id = #{id}
</select>
</mapper>
新创建的mapper.xml需要加入到mybatis-config.xml中mappers标签中
<mappers>
<!-- 配置MyBatis需要加载的Mapper -->
<mapper resource="EmpMapper.xml"/>
<mapper resource="EmpMapper2.xml"/>
</mappers>
3、提供实现类,测试Emp接口中的根据id查询员工的方法
public class TestMybatisInf {
@Test
public void testFindById() throws Exception{
//1.读取mybatis的核心配置文件(mybatis-config.xml)
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.通过配置信息获取一个SqlSessionFactory工厂对象
SqlSessionFactory fac = new SqlSessionFactoryBuilder().build(in);
//3.通过工厂获取一个SqlSession对象
SqlSession session = fac.openSession();
//4.获取Mapper接口对象
EmpMapper2 map = session.getMapper(EmpMapper2.class);
//5.调用接口对象的方法进行查询
Emp e = map.findById(2);
//6.输出结果
System.out.println(e);
}
}
参考资料:
https://mybatis.org/mybatis-3/zh/getting-started.html