随心记录

Bug不空,誓不成佛

  menu
70 文章
14633 浏览
5 当前访客
ღゝ◡╹)ノ❤️

第六篇 初始MyBatis

一、什么是 MyBatis

    MyBatis(前身是 iBatis)是一个支持普通SQL 查询、存储过程以及高级映射的持久层框架,它消除了几乎所有的JDBC代码和参数的手动设置以及对结果集的检索,并使用简单的XML或注解进行配置和原始映射,用以将接口和Java的POJO(Plain Old Java Object,普通 java 对象)映射成数据库中的记录,使得 Java 开发人员可以使用面向对象的编程思想来操作数据库。
    MyBatis 框架也被称之为ORM(Object/Relational Mapping,即对象关系映射)框架。所谓的ORM就是一种为了解决面向对象与关系型数据库中数据类型不匹配的技术,它通过描述 Java 对象与数据库表之间的映射关系,自动将Java应用程序中的对象持久化到关系型数据库的表中,如图所示:
image.png
从图可以看出,使用ORM框架后,应用程序不再直接访问底层数据库,而是以面向对象的方式来操作持久化对象(Persisent Object, PO),而 ORM 框架则会通过映射关系将这些面向对象的操作转换成底层的SQL操作。
    当前的ORM框架产品有很多,常见的ORM框架有Hibernate 和 MyBatis。这两个框架的主要区别如下。

  • Hibernate:是一个全表映射的框架。通常开发者只需定义好持久化对象到数据库表的映射关系,就可以通过Hibernate 提供的方法完成持久层操作。开发者并不需要熟练地掌握SQL语句的编写,Hibernate 会根据制定的存储逻辑,自动的生成对应的SQL,并调用JDBC接口来执行,所以其开发效率会高于MyBatis。然而Hibernate自身也存在着一些缺点,例如它在多表关联时,对SQL查询的支持较差;更新数据时,需要发送所有字段;不支持存储过程;不能通过优化SQL来优化性能等。这些问题导致其只适合在场景不太复杂且对性能要求不高的项目中使用。
  • MyBatis:是一个半自动映射的框架。这里所谓的“半自动”是相对于Hibernate全表映射而言的,MyBatis 需要手动匹配提供POJO、SQL和映射关系,而Hibernate只需提供POJO和映射关系即可。与Hibernate相比,虽然使用MyBatis 手动编写SQL 要比使用Hibernate的工作量大,但MyBatis可以配置动态SQL并优化SQL,可以通过配置决定SQL的映射规则,它还支持存储过程等。对于一些复杂的和需要优化性能的项目来说,显然使用MyBatis更加合适。

二、MyBatis下载和使用

点我查看MyBatis各个版本和下载
image.png
这里只需下载mybatis-3.4.2.zip即可。在应用程序引入MyBatis的核心包和lib目录中的依赖包即可。

三、MyBatis 的工作原理

MyBatis的工作原理可以用如下图表示:
image.png
从图中可以看出,MyBatis 框架在操作数据库时,大体经过了8个步骤。具体如下:
(1)读取 MyBatis 配置文件 mybatis-config.xml。mybatis-config.xml 作为 MyBatis 的全局配置文件,配置了 MyBatis 的运行环境等信息,其中主要内容是获取数据库连接。
(2)加载映射文件 Mapper.xml。Mapper.xml 文件即SQL映射文件,该文件中配置了操作数据库的SQL语句,需要在mybatis-config.xml 中加载才能执行。mybatis-config.xml 可以加载多个配置文件,每个配置文件对应数据库中的一张表。
(3)构建会话工厂。通过 MyBatis 的环境等配置信息构建绘画工厂 SQLSessionFactory。
(4)构建SqlSession 对象。由会话工厂创建 SqlSession 对象,该对象中包含了执行 SQL 的所有方法。
(5)MyBatis 底层定义了一个Executor 接口来操作数据库,它会根据SqlSession 传递的参数动态地生成需要执行的 SQL 语句,同时负责查询缓存的维护。
(6)在Executor 接口的执行方法中,包含一个MappedStatement 类型的参数,该参数是对映射信息的封装,用于存储要映射的 SQL 语句的 id、参数等。Mapper.xml 文件中一个SQL 对应一个MappedStatement 对象,SQL 的 id 即是 MappedStatement 的 id。
(7)输入参数映射。在执行方法时,MappedStatement 对象会对用户执行 SQL 语句的输入参数进行定义(可以定义为Map、List类型、基本类型和POJO类型),Executor 执行器会通过MappedStatement 对象在执行SQL 前,将输入的 Java 对象映射到SQL语句中。这里对输入参数的映射过程就类似于 JDBC 编程中对 preparedStatement 对象设置参数的过程。
(8)输出结果映射。在数据库中执行完 SQL 语句后,MappedStatement 对象会对 SQL 执行输出的结果进行定义(可以定义为Map 和 List 类型、基本类型、POJO类型),Executor 执行器会通过MappedStatement 对象在执行SQL语句后,将输出结果映射至Java对象中。这种将输出结果映射到Java 对象的过程就类似于 JDBC 编程中对结果的解析处理过程。

四、MyBatis 入门程序

1、查询客户

① 根据客户编号查询客户信息

    根据客户编号查询客户信息主要是通过查询客户表中的主键(这里表示唯一的客户编号)来实现的,具体步骤如下:
(1)在 MySQL 数据库中,创建一个名为 mybatis 的数据库,在此数据库中创建一个 t_customer 表,同时预先插入几条数据。此操作所执行的 SQL 语句如下:

# 创建一个名称为 mybatis 的数据库
create database mybatis;
# 使用 mybatis 数据库
use mybatis;
# 创建一个名称为 t_customer 的表
create table t_customer (
	id int(32) PRIMARY KEY AUTO_INCREMENT,
	username varchar(50),
	jobs varchar(50),
	phone varchar(16)
);
# 插入3条数据
insert into t_customer values ('1' , 'joy' , 'doctor' , '13745874578');
insert into t_customer values ('2' , 'jack' , 'teacher' , '13521210112');
insert into t_customer values ('3' , 'tom' , 'worker' , '15179405961');

完成上述操作后数据库 t_customer 表中的数据如下图:
image.png
(2)在 Eclipse 中,创建一个新的Web项目,将 MyBatis 的核心 JAR 包、lib 目录中的依赖 JAR 包,以及 MySQL 数据库的驱动 JAR 包一同添加到项目的 lib 目录下,如下图所示:
image.png
(3)由于 MyBatis 默认使用 log4j 输出日志信息,所以如果要查看控制台的输出SQL语句,那么就需要在 classpath 路径下配置其日志文件。在项目 src 目录下创建 log4j.properties 文件,代码如下:

# Global logging configuration
log4j.rootLogger = ERROR, stdout
# MyBatis logging configuration...
log4j.logger.com.cn = DEBUG
# Console output...
log4j.appender.stdout = org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout = org.apache.log4j.PatternLayout
log4j.appender.stdout.layout.ConversionPattern = %5p [%t] - %m%n

上述配置中,包含了全局的日志配置、MyBatis 的日志配置和控制台输出,其中MyBatis 的日志配置用于将 com.cn 包下所有类的日志记录级别设置为DEBUG。

(4)在 src 目录下,创建一个 com.cn.po 包,在该包下创建持久化类 Customer,并在类中声明 id、username、jobs 和 phone 属性,及其对应的 getter/setter 方法,代码如下:

package com.cn.po;
/*
 * 客户持久化类
 */
public class Customer {
	private Integer id;		//主键id
	private String username;	//客户名称
	private String jobs;		//职业
	private String phone;		//电话
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getUsername() {
		return username;
	}
	public void setUsername(String username) {
		this.username = username;
	}
	public String getJobs() {
		return jobs;
	}
	public void setJobs(String jobs) {
		this.jobs = jobs;
	}
	public String getPhone() {
		return phone;
	}
	public void setPhone(String phone) {
		this.phone = phone;
	}
	@Override
	public String toString() {
		return "Customer [id=" + id + ", username=" + username + ", jobs=" + jobs + ", phone=" + phone + "]";
	}
}

上述代码中,持久化类 Customer 与普通的 JavaBean 并没有什么区别,只是其属性字段与数据库中的表字段相对应。实际上,Customer 就是一个 POJO(普通 Java 对象),MyBatis 就是采用 POJO 作为持久化类来完成对数据库操作的。
(5)在 src 目录下,创建一个 com.cn.mapper 包,并在包中创建映射文件 CustomerMapper.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="com.cn.mapper.CustomerMapper">
	<!-- 根据客户编号获取客户信息 -->
	<select id = "findCustomerById" parameterType = "Integer"
		resultType = "com.cn.po.Customer">
		select * from t_customer where id = #{id}
	</select>
</mapper>

上述配置中,第2~3行是 MyBatis 的约束配置,第 5~11行是需要程序员编写的映射信息。其中<mapper>元素时配置文件的根元素,它包含一个 namespace 属性,该属性为这个<mapper>指定了唯一的命名空间,通常会设置成 “包名+SQL 映射文件名” 的形式。子元素<select>中的信息是用于执行查询操作的配置,其 id 属性是<select> 元素在映射文件中的唯一标识;parameterType 属性用于指定传入参数的类型,这里表示传递给执行SQL 的是一个Integer类型的参数;resultType 属性用于指定传入参数的类型,这里表示返回的数据是 Customer 类型。在定义的查询SQL 语句中,“#{}”用于表示一个占位符,相当于“?”,而 “#{id}” 表示该占位符待接收参数的名称 id。

(6)在 src 目录下,创建 MyBatis 的核心配置文件 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>
	<!-- 1.配置环境,默认的环境 id 为 mysql -->
	<environments default="mysql">
		<!-- 1.2.配置 id 为 mysql 的数据库环境 -->
		<environment id="mysql">
			<!-- 使用 JDBC 的事务管理 -->
			<transactionManager type="JDBC"/>
			<!-- 数据库连接池 -->
			<dataSource type="POOLED"> 
				<property name="driver" value="com.mysql.jdbc.Driver"/> 
				<property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
				<property name="username" value="root"/> 
				<property name="password" value="123456"/>  
			</dataSource>  
		</environment>
	</environments>
	<!-- 2.配置 Mapper 的位置 -->  
	<mappers>   
		<mapper resource="com/cn/mapper/CustomerMapper.xml"/>
	</mappers> 
</configuration>

上述配置中,第2~3行是 MyBatis 的配置文件约束信息,下面<configuration>元素中的内容就是开发人员需要编写的配置信息。这里按照<configuration>子元素的功能不同,将配置分为了两个步骤:第 1 步配置了环境,第 2 步配置了 Mapper 的位置。

(7)在 src 目录下,创建一个 com.cn.test 包,在该包下创建测试类 MybatisTest ,并在类中编写测试方法 findCustomerByIdTest(),代码如下:

package com.cn.test;

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.Test;
import com.cn.po.Customer;

public class MybatisTest {
	/*
	 * 根据客户编号查询客户信息
	 */
	@Test
	public void findCustomerbyIDTest() throws Exception{
		//1.读取配置文件
		InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
		//2.根据配置文件构建 SQLSessionFactory
		SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
		//3.通过 sqlSessionFactory 创建 SqlSession
		SqlSession sqlSession = sqlSessionFactory.openSession();
		//4.SqlSession 执行映射文件中定义的SQL,并返回映射结果
		Customer customer = sqlSession.selectOne("com.cn.mapper.CustomerMapper.findCustomerById",1);
		//打印输出结果
		System.out.println(customer.toString());
		//5.关闭sqlSession
		sqlSession.close();
	}
}

上述代码中,findCustomerByIdTest()方法中,首先通过输入流读取了配置文件,然后根据配置文件构建了 SqlSessionFactory 对象。接下来通过 SQLSessionFactory 对象又创建了 SqlSession 对象,并通过 SqlSession 对象的 selectOne() 方法执行查询操作。selectOne() 方法的第1个参数标识映射SQL 的标识字符串,它由 CustomerMapper.xml 中<mapper> 元素的namespace 属性值+<select>元素的 id 属性值组成;第2个参数标识查询所需要的参数,这里查询的是客户表中 id 为 1 的客户。测试完毕后关闭了 SqlSession。
测试结果如下图:
image.png

② 根据客户名模糊查询客户信息

模糊查询的实现非常简单,只需要在映射文件中通过<select>元素编写相应的SQL语句,并通过SqlSession 的查询方法执行该SQL即可,具体实现步骤如下:
(1)在映射文件 Customermapper.xml 中,添加根据客户名模拟查询客户信息列表的 SQL 语句,代码如下:

<!-- 根据客户名模糊查询客户信息列表 -->
<select id="findCustomerByName" parameterType="String"
	resultType="com.cn.po.Customer">
	select * from t_customer where username like '%${values}%'
</select>

与根据客户编号查询相比,上述配置代码中的属性 id、parameterType 和 SQL 语句都发生相应变化。其中,SQL 语句中的 “${}” 用来表示拼接 SQL 的字符串,即不加解释的原样输出。“\${}” 表示要拼接的是简单类型参数。

特别注意:

在使用 “${}” 进行 SQL 字符串拼接时,无法防止 SQL 注入问题。所以想要既能实现模糊查询,又要防止 SQL 注入,可以对上述映射文件 CustomerMapper.xml 中模糊查询的 select 语句进行修改,使用 MySQL 中的 concat() 函数进行字符串拼接。具体修改实例如下所示:
select * from t_customer where username like concat('%',#{value},'%')

(2)在测试类 MybatisTest 中,添加一个测试方法 findCustomerByNameTest(),代码如下:

/*
 * 根据用户名称来模糊查询用户信息列表
 */
@Test
public void findCustomerByNameTest() throws Exception{
	//1.读取配置文件
	InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
	//2.根据配置文件构建 SQLSessionFactory
	SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	//3.通过 sqlSessionFactory 创建 SqlSession
	SqlSession sqlSession = sqlSessionFactory.openSession();
	//4.SqlSession 执行映射文件中定义的SQL,并返回映射结果
	List<Customer> customers = sqlSession.selectList("com.cn.mapper.CustomerMapper.findCustomerByName","j");
	for (Customer customer : customers){
		//打印输出结果
		System.out.println(customer);
	}
	//5.关闭sqlSession
	sqlSession.close();
}

执行结果如下:
image.png
从图中可以看出,使用 MyBatis 框架已成功查询出了客户表中客户名称中带有“j”的两条客户信息。
至此,MyBatis 的操作大致可分为以下几个步骤:

  1. 读取配置文件。
  1. 根据配置文件构建 SqlSessionFactory。
  2. 通过 SqlSessionFactory 创建 SqlSession。
  3. 使用 SqlSession 对象操作数据库(包括查询、添加、修改、删除以及提交事务等)。
  4. 关闭 SqlSession。

2、添加客户

在 MyBatis 的映射文件中,添加操作是通过<insert>元素来实现的。例如,向数据库中的 t_customer 表中插入一条数据可以通过如下配置实现:

<!-- 添加客户信息 -->
<insert id="addCustomer" parameterType="com.cn.po.Customer">
	insert into t_customer(username,jobs,phone) values(#{username},#{jobs},#{phone})
</insert>

在测试类 MybatisTest 中,添加测试方法 addCustomerTest(),代码如下:

/*
 * 添加客户
 */
@Test
public void addCustomerTest() throws Exception{
	//1.读取配置文件
	InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
	//2.根据配置文件构建 SQLSessionFactory
	SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	//3.通过 sqlSessionFactory 创建 SqlSession
	SqlSession sqlSession = sqlSessionFactory.openSession();
	//4.SqlSession 执行添加操作
	//4.1创建Customer 对象,并向对象中添加数据
	Customer customer = new Customer();
	customer.setUsername("rose");
	customer.setJobs("student");
	customer.setPhone("1333533092");
	//4.2执行 SqlSession 的插入方法,返回的是 SQL 语句影响的行数
	int rows = sqlSession.insert("com.cn.mapper.CustomerMapper.addCustomer",customer);
	//4.3通过返回结果判断插入操作是否成功
	if(rows>0) {
		System.out.println("成功插入了"+rows+"条数据!");
	} else {
		System.out.println("执行插入操作失败!!!");
	}
	//4.4提交事务
	sqlSession.commit();
	//5.关闭sqlSession
	sqlSession.close();
}

上述代码的第4步操作中,首先创建了 Customer 对象,并向 Customer 对象中添加了属性值;然后通过 SqlSession 对象的 insert() 方法执行插入操作,并通过该操作返回的数据来判断插入操作是否执行成功;最后通过 SqlSession 的 commit()方法提交了事务,最后通过 close()方法关闭了SqlSession。
执行后的结果如图:
image.png
此时数据库中的t_customer表的内容如下图所示:
image.png

3、更新客户

MyBatis 的更新操作在映射文件中是通过配置<update>元素来实现的。代码如下:

<!-- 更新客户信息 -->
<update id="updateCustomer" parameterType="com.cn.po.Customer">
	update t_customer set username=#{username},jobs=#{jobs},phone=#{phone} where id=#{id}
</update>

在测试类 MybatisTest 中,添加测试方法 updateCustomerTest(),将 id 为 4 的用户职业修改为 "programmer",电话修改为“13311111111”,代码如下:

/*
 * 更新客户
 */
@Test
public void updateCustomerTest() throws Exception{
	//1.读取配置文件
	InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
	//2.根据配置文件构建 SQLSessionFactory
	SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	//3.通过 sqlSessionFactory 创建 SqlSession
	SqlSession sqlSession = sqlSessionFactory.openSession();
	//4.SqlSession 执行更新操作
	//4.1创建Customer 对象,对对象中的数据进行模拟更新
	Customer customer = new Customer();
	customer.setId(4);
	customer.setUsername("rose");
	customer.setJobs("programmer");
	customer.setPhone("13311111111");
	//4.2执行 SqlSession 的更新方法,返回的是 SQL 语句影响的行数
	int rows = sqlSession.insert("com.cn.mapper.CustomerMapper.updateCustomer",customer);
	//4.3通过返回结果判断更新操作是否成功
	if(rows>0) {
		System.out.println("成功修改了"+rows+"条数据!");
	} else {
		System.out.println("执行修改操作失败!!!");
	}
	//4.4提交事务
	sqlSession.commit();
	//5.关闭sqlSession
	sqlSession.close();
}

执行结果如下图:
image.png
此时 t_customer 表中的数据如下图:
image.png

4、删除客户

MyBatis 的删除操作在映射文件中是通过配置<delete>元素实现的,代码如下:

<!-- 删除客户信息 -->
<delete id="deleteCustomer" parameterType="Integer">
	delete from t_customer where id=${id}
</delete>

在测试类 MybatisTest 中添加方法 deleteCustomerTest(),代码如下:

/*
 * 删除客户
 */
@Test
public void deleteCustomerTest() throws Exception{
	//1.读取配置文件
	InputStream inputStream = Resources.getResourceAsStream("mybatis-config.xml");
	//2.根据配置文件构建 SQLSessionFactory
	SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
	//3.通过 sqlSessionFactory 创建 SqlSession
	SqlSession sqlSession = sqlSessionFactory.openSession();
	//4.SqlSession 执行删除操作
	//4.1执行 SqlSession 的删除方法,返回的是SQL语句影响的行数
	int rows = sqlSession.delete("com.cn.mapper.CustomerMapper.deleteCustomer",4);
	//4.3通过返回结果判断删除操作是否成功
	if(rows>0) {
		System.out.println("成功删除了"+rows+"条数据!");
	} else {
		System.out.println("执行删除操作失败!!!");
	}
	//4.4提交事务
	sqlSession.commit();
	//5.关闭sqlSession
	sqlSession.close();
}

运行结果如下:
image.png
此时表t_customer中的内容如下:
image.png

内事不懂问百度,外事不懂问谷歌~