01 mybatis框架

  1. 回顾 jdbc:
    1.1 jdbc 操作步骤总结如下:

1、 加载数据库驱动
2、 创建并获取数据库链接
3、 创建 jdbc statement 对象
4、 设置 sql 语句
5、 设置 sql 语句中的参数(使用 preparedStatement)
6、 通过 statement 执行 sql 并获取结果
7、 对 sql 执行结果进行解析处理
8、 释放资源(resultSet、preparedstatement、connection)

1.2 jdbc 问题总结如下:

1、 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可解决此问题。
2、 Sql 语句写在代码中造成代码不易维护,实际应用 sql 变化的可能较大,sql 变动需要改变 java 代码。
3、 向 sql 语句传参数麻烦,因为 sql 语句的 where 条件不一定,可能多也可能少,占位符需要和参数一一对应。
对结果集解析麻烦,sql 变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成 pojo 对象解析比较方
便。

mybatis框架

在这里插入图片描述

1 MyBatis简介(了解)

1.1 什么是MyBatis

MyBatis 本是apache的一个开源项目iBatis, 2010年这个项目由apache software foundation 迁移到了google code,并且改名为MyBatis 。2013年11月迁移到Github。

MyBatis 是一款优秀的持久层框架,它对jdbc的操作数据库的过程进行封装,使开发者只需要关注 SQL 本身,而不需要花费精力去处理例如注册驱动、创建connection、创建statement、手动设置参数、结果集检索等jdbc繁杂的过程代码。它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java POJOs(Plain Ordinary Java Object,普通的 Java 对象)映射成数据库中的记录。

Mybatis通过xml或注解的方式将要执行的各种statement(statement、preparedStatemnt)配置起来,并通过java对象和statement中的sql进行映射生成最终执行的sql语句,最后由mybatis框架执行sql并将结果映射成java对象并返回。
总之,Mybatis对JDBC访问数据库的过程进行了封装,简化了JDBC代码,解决JDBC将结果集封装为Java对象的麻烦。

特点:

• 简单易学:本身就很小且简单。没有任何第三方依赖,最简单安装只要两个 jar 文件+配置几个 sql 映射文件易于学习,易于使用,通
过文档和源代码,可以比较完全的掌握它的设计思路和实现。
• 灵活:mybatis 不会对应用程序或者数据库的现有设计强加任何影响。 sql 写在 xml 里,便于统一管理和优化。通过 sql 语句可以满
足操作数据库的所有需求。
• 解除 sql 与程序代码的耦合:通过提供 DAO 层,将业务逻辑和数据访问逻辑分离,使系统的设计更清晰,更易维护,更易单元测试。
sql 和代码的分离,提高了可维护性。
• 提供映射标签,支持对象与数据库的 orm 字段关系映射
• 提供对象关系映射标签,支持对象关系组建维护
• 提供 xml 标签,支持编写动态 sql。 [2]

mybatis是持久层框架.Mybatis内部封装了JDBC.实现ORM(对象关系映射),从而简化了程序员操作数据库的过程.Mybatis是半自动化的ORM映射框架.

1.2 ORM

对象关系映射(Object Relational Mapping,简称ORM)是通过使用描述对象和数据库之间映射的元数据,将面向对象语言程序中的对象自动持久化到关系数据库中
本质上就是将数据从一种形式转换到另外一种形式。
这也同时暗示着额外的执行开销;然而,如果ORM作为一种中间件实现,则会有很多机会做优化,而这些在手写的持久层并不存在。 更重要的是用于控制转换的元数据需要提供和管理;但是同样,这些花费要比维护手写的方案要少;而且就算是遵守ODMG规范的对象数据库依然需要类级别的元数据。

  • ORM:
    1. 对象—表 以面向对象的方式操作数据库.
      对象名称------------表名
      对象的属性名称-------表中的字段
      userMapper.insert(user) sql操作自动完成!!!
    2. 表----对象 自动实现的
      例子:mybatis查询时,自动的返回了用户需要的对象
      利用resultType属性标识,查询操作返回对象的类型.由
      程序自动实现了数据的映射(封装)

在这里插入图片描述
(1)sqlMapConfig.xml是Mybatis的核心配置文件,通过其中的配置可以生成SqlSessionFactory,也就是SqlSession工厂
(2)基于SqlSessionFactory可以生成SqlSession对象
(3)SqlSession是一个既可以发送SQL去执行,并返回结果,类似于JDBC中的Connection对象,也是Mybatis中至关重要的一个对象。
(4)Executor是SqlSession底层的对象,用于执行SQL语句
(5)MapperStatement对象也是SqlSession底层的对象,用于接收输入映射(SQL语句中的参数),以及做输出映射(即将SQL查询的结果映射成相应的结果)

1.2 为什么要使用MyBatis

思考:在开始之前,思考下如何通过JDBC查询Emp表中的所有记录,并封装到一个List集合中返回。(演示:准备数据、导包、导入JDBC程序)

  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框架中几乎都得到了解决!!

2 MyBatis快速入门

2.1 Mybatis 环境搭建

<groupId>com.xkrj</groupId>
 <artifactId>mybatisless01</artifactId>
 <version>1.0-SNAPSHOT</version>
 <packaging>jar</packaging>
 <dependencies>
	 <dependency>
		 <groupId>org.mybatis</groupId>
		 <artifactId>mybatis</artifactId>
		 <version>3.4.5</version>
	 </dependency>
	 <dependency>
		 <groupId>mysql</groupId>
		 <artifactId>mysql-connector-java</artifactId>
		 <version>5.1.6</version>
	 </dependency>
	 <dependency>
		 <groupId>log4j</groupId>
		 <artifactId>log4j</artifactId>
		 <version>1.2.12</version>
	 </dependency>
	 <dependency>
		 <groupId>junit</groupId>
		 <artifactId>junit</artifactId>
		 <version>4.10</version>
	 </dependency>
 </dependencies>

2.2 数据库创建

CREATE TABLE `user` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `username` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '用户名称',
 `birthday` datetime DEFAULT NULL,
 `sex` char(1) COLLATE utf8_bin DEFAULT NULL,
 `address` varchar(256) COLLATE utf8_bin DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COLLATE=utf8_bi

2.3 实体对象 User 的创建

在这里插入图片描述

2.4 配置 log4j.properties 日志文件

### 设置###
log4j.rootLogger = debug,stdout,D,E
### 输出信息到控制抬 ###
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 = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

2.5 配置 Mybatis 的主配置文件:

配置位置在 resources 文件下面直接配置

<?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">
    
<!-- MyBatis的全局配置文件 -->
<configuration >
	<!-- 1.配置开发环境(需设置一个默认的环境) -->
	<environments default="develop">
		<!-- 这里可以配置多个环境,比如develop,test等 -->
		<environment id="develop">
			<!-- 1.1.配置事务管理方式:JDBC/MANAGED
			JDBC:将事务交给JDBC管理(推荐)
			MANAGED:自己管理事务
			  -->
			<transactionManager type="JDBC"></transactionManager>
			
			<!-- 1.2.配置数据源,即连接池方式:JNDI/POOLED/UNPOOLED
				JNDI:已过时
				POOLED:使用连接池(推荐)
				UNPOOLED:不使用连接池
			 -->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver"/>
				<property name="url" value="jdbc:mysql://localhost:3306/yonghedb?characterEncoding=utf-8"/>
				<property name="username" value="root"/>
				<property name="password" value="root"/>
			</dataSource>
		</environment>
	</environments>
	
	<!-- 2.加载Mapper配置文件(因mapper文件中配置了要执行的SQL语句) -->
	<mappers>
		<!-- 注意路径 -->
		<mapper resource="com/tedu/pojo/EmpMapper.xml"/>
	</mappers>
</configuration>

2.6 编写 dao 层接口

public interface UserDao {
 // @Select("select * from user")
 List<User> findAll();
}

2.7 配置 Mybatis 的关系型映射文件

注意在配置映射文件的时候,映射文件的路径包需要和 dao 层接口的文件路径包保持一致:

<?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="UserDao">
 <select id="findAll" resultType="User">
 select * from user
 </select>
</mapper>

2.8 测试:

//读取配置文件
//创建 SqlSessionFactory 对象
//创建 SqlSession 对象
//使用 SqlSession 对象创建 Dao 代理对象
//使用代理对象执行方法

public void findAll() throws IOException{
	//1.读取sqlMapConfig.xml文件,获取其中的基本信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//2.根据配置信息生成SqlSessionFactory工厂对象, 
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//3.通过工厂获取一个SqlSession对象(用于执行SQL及返回结果)
	SqlSession session = factory.openSession();
	//4.执行SQL语句,查询emp表中的所有记录,封装到List集合中
	String sqlId = "com.tedu.pojo.EmpMapper.findAll";
	List<Emp> emplist = session.selectList( sqlId );
	//5.打印list集合
	for(Emp emp : emplist){
		System.out.println(emp);
	}
}

2.9. Mybatis 执行过程简单介绍

X

Eclipase版

2.1 准备数据,创建库和表

1、创建yonghedb库、emp表,并插入若干条记录

-- 1、删除 yonghedb 数据库(如果存在)
drop database if exists yonghedb;
-- 2、创建数据库 yonghedb 数据库
create database yonghedb charset utf8;
use yonghedb; -- 选择yonghedb数据库

-- 3、在 yonghedb 库中创建 emp 表
create table emp(
	id int primary key auto_increment,
	name varchar(50),
	job varchar(50),
	salary double
);
-- 4、往 emp 表中, 插入若干条记录
insert into emp values(null, '刘德华', '程序员', 3300);
insert into emp values(null, '周润发', '程序员', 2800);
insert into emp values(null, '周星驰', '程序员鼓励师', 3700);
insert into emp values(null, '刘备', '部门总监', 4200);
insert into emp values(null, '曹操', '程序员', 3000);
insert into emp values(null, '关羽', '程序员', 3500);
insert into emp values(null, '张飞', '程序员', 2700);
insert into emp values(null, '赵云', 'CEO', 5000);

2.2 创建工程,导入所需jar包、创建测试类

1、创建java工程
在这里插入图片描述
2、导入mybatis的jar包
在这里插入图片描述
其中mybatis-3.2.2是mybatis框架的核心包,其他则为依赖包。
3、导入mysql驱动包
在这里插入图片描述
4、引用所有jar包
在这里插入图片描述
5、创建com.tedu.test.TestMybatis测试类,并提供findAll方法(查询emp表中所有的员工信息),开发步骤如下:
在这里插入图片描述

/**
 * Mybatis的快速入门
 */
public class TestMybatis {
	/**
	 * 1.查询mybatisdb库中的emp表中所有员工,将所有员工记录以一个List 
	 * 集合(List<Emp>)返回
	 * @throws IOException 
	 */
	@Test
	public void findAll() throws IOException{
		//1.读取sqlMapConfig.xml文件,获取其中的基本信息
		
		//2. 根据配置信息生成SqlSessionFactory工厂对象, 
		
		//3.通过工厂获取一个SqlSession对象(用于执行SQL及返回结果)
		
		//4.执行SQL语句,查询emp表中的所有记录,封装到List集合中
		
		//5.打印list集合
		
	}
}

2.3 添加sqlMapConfig.xml文件

1、在src目录下,创建sqlMapConfig.xml文件 (MyBatis的核心配置文件)
在这里插入图片描述
2、sqlMapConfig.xml文件配置如下:
sqlMapConfig文件头信息如下:

<?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">
    
<!-- MyBatis的全局配置文件 -->
<configuration >
	
</configuration>

详细配置如下:

<?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">
    
<!-- MyBatis的全局配置文件 -->
<configuration >
	<!-- 1.配置开发环境(需设置一个默认的环境) -->
	<environments default="develop">
		<!-- 这里可以配置多个环境,比如develop,test等 -->
		<environment id="develop">
			<!-- 1.1.配置事务管理方式:JDBC/MANAGED
			JDBC:将事务交给JDBC管理(推荐)
			MANAGED:自己管理事务
			  -->
			<transactionManager type="JDBC"></transactionManager>
			
			<!-- 1.2.配置数据源,即连接池方式:JNDI/POOLED/UNPOOLED
				JNDI:已过时
				POOLED:使用连接池(推荐)
				UNPOOLED:不使用连接池
			 -->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver"/>
				<property name="url" value="jdbc:mysql://localhost:3306/yonghedb?characterEncoding=utf-8"/>
				<property name="username" value="root"/>
				<property name="password" value="root"/>
			</dataSource>
		</environment>
	</environments>
	
	<!-- 2.加载Mapper配置文件(因mapper文件中配置了要执行的SQL语句) -->
	<mappers>
		<!-- 注意路径 -->
		<mapper resource="com/tedu/pojo/EmpMapper.xml"/>
	</mappers>
</configuration>

2.4 编写Emp实体类

根据数据库中的字段来确定实体类中的成员变量。
注意:在当前实例中,Emp类中的属性和数据库表的字段名称必须一致,否则将会无法将结果集封装到Java对象中。
实现Emp类: 提供私有属性以及对应的getter方法、setter方法,并重写toString方法

package com.tedu.pojo;
/**
 * 实体类,用于封装Emp表中的一条用户信息
 */
public class Emp {
	//1.声明实体类中的属性
	private Integer id;
	private String name;
	private String job;
	private Double salary;
	
	//2.提供对应的getter和setter方法
	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;
	}
	
	//3.重写toString方法
	@Override
	public String toString() {
		return "Emp [id=" + id + ", name=" + name + ", job=" + job + ", salary=" + salary + "]";
	}
}

2.5 添加EmpMapper.xml文件

1、在src/com/tedu/pojo目录下,创建EmpMapper.xml文件 (实体类的映射文件)
在这里插入图片描述
2、EmpMapper.xml文件配置如下:
EmpMapper文件头信息如下:

<?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一般指定为当前文件的所在包路径+文件名(将来是接口名)
	在程序中通过[ namespace + id ]定位到执行哪一条SQL语句
 -->
<mapper namespace="">
	
	
</mapper>

详细配置如下:

<?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一般指定为当前文件的所在包路径+文件名
	将来在程序中通过[ namespace + id ]定位到执行哪一条SQL语句
 -->
<mapper namespace="com.tedu.pojo.EmpMapper">
	<!-- 通过select、insert、update、delete标签声明要执行的SQL -->
	<select id="findAll" resultType="com.tedu.pojo.Emp">
		select * from emp
	</select>
	<!-- 
	resultType:返回值类型,简单类型(例如:Integer,String,Emp等)
		如果返回集合(List<Emp>),只需配置集合中的元素类型即可!
	resultMap:复杂对象结构(例如多表关联查询等),后面用到再讲解
	 -->
</mapper>

将mapper文件引入到主配置文件当中。

2.6 实现测试类,并测试

1、实现findAll方法,代码如下:

public void findAll() throws IOException{
	//1.读取sqlMapConfig.xml文件,获取其中的基本信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//2.根据配置信息生成SqlSessionFactory工厂对象, 
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//3.通过工厂获取一个SqlSession对象(用于执行SQL及返回结果)
	SqlSession session = factory.openSession();
	//4.执行SQL语句,查询emp表中的所有记录,封装到List集合中
	String sqlId = "com.tedu.pojo.EmpMapper.findAll";
	List<Emp> emplist = session.selectList( sqlId );
	//5.打印list集合
	for(Emp emp : emplist){
		System.out.println(emp);
	}
}

2、执行findAll方法,输出结果为:

Emp [id=1, name=王海涛, job=程序员, salary=3300.0]
Emp [id=2, name=齐雷, job=程序员, salary=2800.0]
Emp [id=3, name=刘沛霞, job=程序员鼓励师, salary=3700.0]
Emp [id=4, name=陈子枢, job=部门总监, salary=4200.0]
Emp [id=5, name=刘昱江, job=程序员, salary=3000.0]
Emp [id=6, name=董长春, job=程序员, salary=3500.0]
Emp [id=7, name=张慎政, job=程序员, salary=2700.0]
Emp [id=8, name=夏侯惇, job=CEO, salary=5000.0]

2.7 原理分析

X

3 MyBatis入门细节

3.1 工程目录及文件介绍

在这里插入图片描述

1、pojo(Plain Ordinary Java Object): 简单的Java对象,实际就是普通JavaBean,也叫做实体类,用于封装一类信息。例如:提供Emp类,用于封装和用户相关的属性信息,比如id员工编号、name员工姓名、job员工职位、salary薪资等。再例如:提供Product类,用于封装和商品相关的属性信息,比如id商品编号、name商品名称、price单价等。
而属性一般是私有的,因此我们还会提供属性相关的setter和getter方法。
我们一般会把实体类,放在pojo目录下,而和实体类相关的mapper文件,也放在相同的目录下(Maven工程除外)

2、EmpMapper.xml:Emp实体类的映射文件,和Emp类放在同一目录下(Maven工程除外)

3、sqlMapConfig.xml:mybatis的全局配置文件,很多配置信息都会在这里进行配置,比如事务、连接数据库基本信息,连接池(数据源)等配置

4、lib及下的jar包:将所需jar包拷贝过来后,还需要引用jar包到工程,也就是选中jar包,右键 --> Build Path --> Add to Build Path即可!

3.2 sqlMapConfig.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">
    
<!-- MyBatis的全局配置文件 -->
<configuration >
	<!-- 配置环境,可配置多个环境(比如:develop开发、test测试) -->
	<environments default="develop">
		<environment id="develop">
			
			<!-- 配置事务管理方式:JDBC/MANAGED
			JDBC:将事务交给JDBC管理(推荐)
			MANAGED:自己管理事务
			  -->
			<transactionManager type="JDBC"></transactionManager>
			
			<!-- 配置数据源,即连接池 JNDI/POOLED/UNPOOLED
				JNDI:已过时
				POOLED:使用连接池(推荐)
				UNPOOLED:不使用连接池
			 -->
			<dataSource type="POOLED">
				<property name="driver" value="com.mysql.jdbc.Driver"/>
				<property name="url" value="jdbc:mysql://localhost:3306/mybatisdb?useUnicode=true&amp;characterEncoding=utf-8"/>
				<property name="username" value="root"/>
				<property name="password" value="root"/>
			</dataSource>
		</environment>
	</environments>
	
	<!-- 引入Mapper配置文件,可以配置多个 -->
	<mappers>
		<mapper resource="com/tedu/pojo/EmpMapper.xml"/>
	</mappers>

</configuration>

configuration是根标签,当前文件中所有的配置都在该标签内,注意其中配置的关键点:

默认的环境 ID(比如:default=“develop”)。
每个 environment 元素定义的环境 ID(比如:id=“develop”)。
事务管理器的配置(比如:type=“JDBC”)。
数据源的配置(比如:type=“POOLED”)。

(1)environments标签:该标签内部可以配置多个environment,即多种环境,每种环境可以做不同配置或连接不同数据库。例如,开发、测试、生产环境可能需要不同的配置,连接的数据库可能也不相同,因此我们可以配置三个environment,分别对应上面三种不同的环境。
但是要记住,environment可以配置多个,但是最终要使用的只能是其中一个!

SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment);

(2)environment标签:内部可以配置多种配置信息,下面介绍事务管理配置和数据源配置。
(3)transactionManage标签:事务管理配置,mybatis中有两种事务管理方式,也就是 type="[JDBC|MANAGED]。
JDBC:这个配置就是直接使用了 JDBC 的提交和回滚设置,它依赖于从数据源得到的连接来管理事务范围。推荐使用。
MANAGED:这个配置几乎没做什么。它从来不提交或回滚一个连接。需要自己手动添加并管理。不推荐使用。
(4)dataSource标签:数据源,也就是连接池配置。这里type指定数据源类型,有三种内建的类型:JNDI、POOLED、UNPOOLED
JNDI:已过时,不推荐使用!
POOLED:使用连接池,mybatis会创建连接池,并从连接池中获取连接访问数据库,在操作完成后,将会把连接返回连接池。
UNPOOLED:不使用连接池,该方式适用于只有小规模数量并发用户的简单应用程序上。
(5)mappers标签:用于导入mapper文件的位置,其中可以配置多个mapper,即可以导入多个mapper文件。

3.3 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">

<!-- 
	namespace一般指定为当前文件的所在包路径+文件名
	将来在程序中通过[ namespace + id ]定位到执行哪一条SQL语句
 -->
<mapper namespace="com.tedu.pojo.EmpMapper">
	<!-- 通过select、insert、update、delete标签声明要执行的SQL -->
	<select id="findAll" resultType="com.tedu.pojo.Emp">
		select * from emp
	</select>
	<!-- 
	resultType:返回值类型,简单类型(例如:Integer,String,Emp等)
		如果返回集合(List<Emp>),只需配置集合中的元素类型即可!
	resultMap:复杂对象结构(例如多表关联查询等),后面用到再讲解
	 -->
</mapper>

(1)第1行是xml的文档声明,用于声明xml的版本和编码
(2)第2、3、4行,引入了xml约束文档,当前xml文档将会按照mybatis-3-mapper.dtd文件所要求的规则进行书写。
(3)Mapper标签:根标签,其中namespace(名称空间,也叫命名空间),要求不能重复。其实就是一个名称,一般我们指定为"包名+文件名"。
(4)select标签:用于指定将来要执行的各种SQL语句。标签上可以声明属性,下面介绍常用的属性:id、resultType、resultMap
id属性:要求值不能重复。将来在执行SQL时,可以通过namespace + id找到指定SQL并执行。
resultType属性:从这条SQL语句中返回所期望类型的类的完全限定名称(包名+类名)。注意如果是集合情形,那应该是集合可以包含的类型,而不能是集合本身。
简而言之,resultType控制查询SQL执行后返回值的类型或集合中的泛型,例如查询emp表中的单条记录,返回值是一个Emp对象,因此,resultType=“com.tedu.pojo.Emp”;
如果查询emp表中的多条记录,返回值是一个List,此时resultType的值应该集合中的泛型,因此resultType=“com.tedu.pojo.Emp”;
resultMap属性:复杂对象结构(例如多表关联查询等)。 使用 resultType 或 resultMap,但不能同时使用。

4 MyBatis增删改查

4.1 新增员工

1、编辑EmpMapper.xml文件, 添加新增员工对应的sql.

<!-- 2.新增员工 -->
<insert id="insert">
	insert into emp(name,job,salary) values('韩少云', 'CEO', 5000)
</insert>

2、编写TestMybatis类,添加addEmp方法,实现新增员工操作。

/**
 * 2.新增:往emp表中添加一个新员工
 * @throws Exception
 */
public void addEmp() throws Exception{
	//1.读取sqlMapConfig.xml文件, 获取所有配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//2.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//3.通过工厂获取一个SqlSession对象
	SqlSession session = factory.openSession();
	//4.执行新增操作, 往emp表中添加一个新员工
	session.insert("com.tedu.pojo.EmpMapper.insert");
	//>>提交事务
	session.commit();
	//5.打印结果
	System.out.println("执行完成!");
}

4.2 修改员工

1、编辑EmpMapper.xml文件, 添加新增员工对应的sql.

<!-- 3.修改员工信息 -->
<update id="update">
	update emp set salary=6500 where name='韩少云'
</update>

2、编写TestMybatis类,添加updateEmp方法,实现修改员工信息。

/**
 * 3.修改:修改emp表中的员工信息
 * @throws Exception
 */
@Test
public void updateEmp() throws Exception{
	//1.读取sqlMapConfig.xml文件, 获取所有配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//2.创建工厂对象, 用于创建SqlSession对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//3.获取一个SqlSession对象
	SqlSession session  = factory.openSession();
	//4.执行更新操作
	session.update("com.tedu.pojo.EmpMapper.update");
	//>>提交事务
	session.commit();
	
	//5.打印结果
	System.out.println("执行完成!");
}

4.3 删除员工

1、编辑EmpMapper.xml文件, 添加新增员工对应的sql.

<!-- 4.删除员工信息 -->
<update id="delete">
	delete from emp where name='韩少云'
</update>

2、编写TestMybatis类,添加deleteEmp方法,实现删除员工。

/**
 * 4.:删除emp表中的一条员工信息
 * @throws Exception
 */
@Test
public void deleteEmp() throws Exception{
	//1.读取sqlMapConfig.xml文件, 获取所有配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//2.创建工厂对象, 用于创建SqlSession对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//3.获取一个SqlSession对象
	SqlSession session  = factory.openSession();
	//4.执行删除操作
	session.update("com.tedu.pojo.EmpMapper.delete");
	//>>提交事务
	session.commit();
	
	//5.打印结果
	System.out.println("执行完成!");
}

4.4 查询员工

1、编辑EmpMapper.xml文件, 添加查询员工对应的sql.

<!-- 5.查询指定id员工信息 -->
<update id="findById">
	select * from emp where id=2
</update>

2、编写TestMybatis类,添加findById方法,实现根据id查询员工信息。

public void findById() throws IOException{
	//0.加载sqlMapConfig.xml文件,获取所有的配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//1.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//2.通过工厂创建一个SqlSession对象
	SqlSession session = factory.openSession();
	//3.执行SQL语句(namespace+Id)
	Emp emp = session.selectOne("com.tedu.pojo.EmpMapper.findById");
	//4.输出结果
	System.out.println(emp);
}

4.5 #{}占位符

在上面的增删改查操作中,SQL语句中的值是写死在SQL中,而在实际开发中,此处的值往往是用户提交过来的值,因此这里我们需要将SQL中写死的值替换为占位符。
使用占位符完成上面的增删改查练习
1、新增:往emp表中添加一个新员工
mapper文件配置:

<!-- 6.新增员工信息 -->
<insert id="insert2">
	insert into emp(name,job,salary) values(#{name}, #{job}, #{salary})
</insert>

java代码示例:

public void testInsert() throws IOException{
	//0.加载sqlMapConfig.xml文件,获取所有的配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//1.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//2.通过工厂创建一个SqlSession对象
	SqlSession session = factory.openSession();
	//3.执行SQL语句(namespace+SQLId)
	//>>准备参数
	Emp emp = new Emp();
	emp.setName("马云");
	emp.setJob("阿里CEO");
	emp.setSalary(10000.0);
	//>>执行新增
	session.insert("com.tedu.pojo.EmpMapper.insert2",emp);
	//4.提交事务
	session.commit();
	System.out.println("执行完成!");
}

2、查询:查询指定id的员工信息
mapper文件配置:

<!-- 7.查询Emp表中指定id的员工信息 -->
<mapper namespace="com.tedu.pojo.EmpMapper">
	<select id="findById2" resultType="com.tedu.pojo.Emp">
		select * from emp where id=#{id}
	</select>
	...

java代码示例:

public void testFindEmpById() throws IOException{
	//0.加载sqlMapConfig.xml文件,获取所有的配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//1.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//2.通过工厂创建一个SqlSession对象
	SqlSession session = factory.openSession();
	//3.执行SQL语句(namespace+Id)
	Emp emp = session.selectOne("com.tedu.pojo.EmpMapper.findById2",1);
	//4.输出结果
	System.out.println(emp);
}

3、修改:修改emp表中指定id的员工信息
mapper文件配置:

<mapper namespace="com.tedu.pojo.EmpMapper">
	<!-- 8.修改:修改emp表中指定id的员工信息 -->
	<update id="update2" parameterType="com.tedu.pojo.Emp">
		update emp set name=#{name},job=#{job},salary=#{salary}
		where id=#{id}
	</update>
	...

java代码示例:

public void testUpdate() throws IOException{
	//0.加载sqlMapConfig.xml文件,获取所有的配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//1.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//2.通过工厂创建一个SqlSession对象
	SqlSession session = factory.openSession();
	//3.执行SQL语句(namespace+SQLId)
	//>>准备参数
	Emp emp = new Emp();
	emp.setId(1);
	emp.setName("王五");
	emp.setJob("架构师");
	emp.setSalary(8000.0);
	//>>执行修改
	session.update("com.tedu.pojo.EmpMapper.update2",emp);
	//>>提交事务
	session.commit();
	System.out.println("执行完成!");
}

4、删除:删除指定name的员工信息
mapper文件配置:

<!-- 9.删除:删除emp表指定name的员工信息 -->
<insert id="delete2" parameterType="String">
	delete from emp
	where name=#{name}
</insert>

java代码示例:

public void testDelete() throws IOException{
	//0.加载sqlMapConfig.xml文件,获取所有的配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//1.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//2.通过工厂创建一个SqlSession对象
	SqlSession session = factory.openSession();
	//3.执行SQL语句(namespace+SQLId)
	//>>执行删除
	session.delete("com.tedu.pojo.EmpMapper.delete2", "马云");
	//>>提交事务
	session.commit();
	System.out.println("执行完成!");
}

5 MyBatis扩展(了解)

5.1 #{}和${}

在上面的增删改查练习中,当SQL语句中包含的参数值是传递过来的,在SQL语句中我们会通过 #{} 占位符进行占位,在SQL语句真正执行时,再将传递过来的值放在SQL语句指定的位置,和SQL语句一起执行!
其实,#{} 就是JDBC中的问号(?)占位符,因此为了安全考虑,在执行时会对传递过来的值进行转译处理。
例如:查询指定name的员工信息,SQL语句为:

select * from emp where name=#{name}

其实就等价于JDBC中: select * from emp where name=?,如果传过来的参数值为:王海涛,那么最终执行的SQL语句为:

select * from emp where name='王海涛'

那么如果我们在传递的时候不是一个参数值,而是SQL语句本身呢?
例如在查询时,我们想动态的传递查询的列:

select 查询的列?? from emp

此时传递过来的应该是一个SQL片段,不同于上面的参数值,如果此时还用 #{},也会像上面一样被转译处理,这不是我们希望看到的。
如果不想让传过来的值被转译处理,那么这里可以使用 ${},例如:

select ${columns} from emp

示例:查询emp表中所有员工的名称(name)、职位(job):
mapper文件配置:

<!-- 10.查询:查询Emp表中所有员工的名称,职位 -->
<select id="findAll2" resultType="com.tedu.pojo.Emp">
	select ${cols} from emp
</select>

java代码示例:

public void testFindAll2() throws IOException{
	//0.加载sqlMapConfig.xml文件,获取所有的配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//1.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//2.通过工厂创建一个SqlSession对象
	SqlSession session = factory.openSession();
	//3.执行SQL语句(namespace+SQLId)
	Map map = new HashMap();
	map.put("cols", "name, job");
	List<Emp> list = session.selectList(
			"com.tedu.pojo.EmpMapper.findAll2",map);
	//4.输出结果
	for(Emp emp : list){
		System.out.println(emp);
	}
}

需要注意的是,在传递 ${}对应的值时,需要将值存入map集合中!!

Mybatis 的 CURD 操作

5.2 parameterType:

将要传入语句的参数的完全限定类名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器推断出具体传入语句的参数,
默认值为未设置(unset)

5.3 resultType

从这条语句中返回的期望类型的类的完全限定名或别名。 注意如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身。可
以使用 resultType 或 resultMap,但不能同时使用。

5.4 获取添加时 ID 的值:

<selectKey keyProperty="id" resultType="int" order="BEFORE">
	SELECT LAST_INSERT_ID();
</selectKey>

5.5 . 使用实体类作为查询条件

实体对象 QueryVo:
在这里插入图片描述

5.6 映射文件:

在这里插入图片描述

5.7 ResultMap 接收结果映射

默认情况下,MyBatis 会在幕后自动创建一个 ResultMap,再基于属性名来映射列到 JavaBean 的属性上。如果列名和属性名没有精
确匹配,可以在 SELECT 语句中对列使用别名(这是一个基本的 SQL 特性)来匹配标签。比如:

<select id="selectUsers" resultType="User">
select user_id as "id", user_name as "userName", hashed_password as "hashedPassword" from some_table where id = #{id} 
</select>

ResultMap 最优秀的地方在于,虽然你已经对它相当了解了,但是根本就不需要显式地用到他们。 上面这些简单的示例根本不需要下
面这些繁琐的配置。 但出于示范的原因,让我们来看看最后一个示例中,如果使用外部的 resultMap 会怎样,这也是解决列名不匹配
的另外一种方式。

<resultMap id="userResultMap" type="User">
	<id property="id" column="user_id" />
	<result property="username" column="user_name"/>
	<result property="password" column="hashed_password"/>
</resultMap>

而在引用它的语句中使用 resultMap 属性就行了(注意我们去掉了 resultType 属性)。比如:

<select id="selectUsers" resultMap="userResultMap">
	select user_id, user_name, hashed_password from some_table where id = #{id} 
</select>

5.8 类型别名(typeAliases)

类型别名是为 Java 类型设置一个短的名字。 它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如
在这里插入图片描述
指定别名后 类型是忽略大小写的:
也可以指定一个包名,MyBatis 会在包名下面搜索需要的 Java Bean,比如:
在这里插入图片描述

5.9. 连接池的作用

1)通过数据库连接池,应用程序可以重用池中现有的连接中现有的连接,而不必反复与数据库建立新的连接。
2)使用连接池可显著提高应用程序的可伸缩,因为有限数量的数据库连接可以为数量大得多的客户端提供服务。例如:数据库应用里面,上万的用户可能只需要少量的连接,因为客户端不可能同时并发的大量的访问数据库。故少量的连接可以支持大量的服务,提高程序的可伸缩性。
3)同时,由于可以节省建立新连接所需的大由于可以节省建立新连接所需的大量时间,使用连接池还能够改善性能。

Mybatis 中使用链接池

有三种内建的数据源类型(也就是 type=”[UNPOOLED|POOLED|JNDI]”):

POOLED:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这
是一种使得并发 Web 应用快速响应请求的流行处理方式
UNPOOLED:这个数据源的实现只是每次被请求时打开和关闭连接。虽然有点慢,但对于在数据库连接可用性方面没有太高要求的简单
应用程序来说,是一个很好的选择。 不同的数据库在性能方面的表现也是不一样的,对于某些数据库来说,使用连接池并不重要,这个
配置就很适合这种情形
JNDI:服务器提供的技术来实现获取 DataSource 对象不同的服务器所获取的 DataSource 值不一样 ,如果不是 maven 或者 war 工程不能使用

6 动态SQL

6.1 if元素

示例:查询emp表中的所有员工信息,如果传递了name参数,则根据name进行模糊查询,否则将查询所有员工信息
mapper文件配置:

<!-- 11.查询:查询emp表中的所有员工信息,如果传递了name参数,则根据name进行模糊查询,否则将查询所有员工信息 -->
<select id="findAll3" resultType="com.tedu.pojo.Emp">
	select * from emp 
	where 1=1 
	<if test="name != null">
		and name like '%${name}%'
	</if>
</select>

java代码示例: 查询name中包含"刘"字的所有员工

public void testFindAll3() throws IOException{
	//0.加载sqlMapConfig.xml文件,获取所有的配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//1.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//2.通过工厂创建一个SqlSession对象
	SqlSession session = factory.openSession();
	//3.执行SQL语句(namespace+SQLId)
	Map map = new HashMap();
	map.put("name", "刘");//查询name中包含"刘"字的所有员工
	List<Emp> list = session.selectList(
			"com.tedu.pojo.EmpMapper.findAll3",map);
	//4.输出结果
	for(Emp emp : list){
		System.out.println(emp);
	}
}

示例:或者不传递name参数,则查询所有员工薪资

public void testFindAll3() throws IOException{
	//0.加载sqlMapConfig.xml文件,获取所有的配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//1.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//2.通过工厂创建一个SqlSession对象
	SqlSession session = factory.openSession();
	//3.执行SQL语句(namespace+SQLId)
	//不传递name参数,则查询所有员工薪资
	//Map map = new HashMap();
	//map.put("name", "刘"); 
	List<Emp> list = session.selectList(
			"com.tedu.pojo.EmpMapper.findAll3");
	//4.输出结果
	for(Emp emp : list){
		System.out.println(emp);
	}
}

6.2 where元素

在这里插入图片描述
示例:查询emp表中所有员工的信息,另:
如果传递了minSal(最低薪资)和maxSal(最高薪资),则查询薪资大于minSal和-小–于maxSal的员工信息;
如果只传递了minSal,则查询薪资大于minSal的所有员工信息;
如果只传递了maxSal,则查询薪资小于maxSal的所有员工信息;
mapper文件配置:

<!-- 12.查询emp表中所有员工的信息,另:
	如果传递了minSal(最低薪资)和maxSal(最高薪资),则查询薪资大于minSal和小于maxSal的员工信息;
	如果只传递了minSal,则查询薪资大于minSal的所有员工信息;
	如果只传递了maxSal,则查询薪资小于maxSal的所有员工信息;
 -->
<select id="findAll4" resultType="com.tedu.pojo.Emp">
	select * from emp 
	<where>
		<if test="minSal != null">
			salary > #{minSal} 
		</if>
		<if test="maxSal != null">
			and salary <![CDATA[ < ]]> #{maxSal} 
		</if>
	</where>
</select>

java代码示例:查询薪资大于3000和小于4000的员工

public void testFindAll4() throws IOException{
	//0.加载sqlMapConfig.xml文件,获取所有的配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//1.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//2.通过工厂创建一个SqlSession对象
	SqlSession session = factory.openSession();
	//3.执行SQL语句(namespace+SQLId)
	Map map = new HashMap();
	//查询薪资大于3000和小于4000的员工
	map.put("minSal", 3000);
	map.put("maxSal", 4000);
	List<Emp> list = session.selectList(
			"com.tedu.pojo.EmpMapper.findAll4",map);
	//4.输出结果
	for(Emp emp : list){
		System.out.println(emp);
	}
}

java代码示例:查询薪资大于3000的员工

public void testFindAll4() throws IOException{
	//0.加载sqlMapConfig.xml文件,获取所有的配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//1.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//2.通过工厂创建一个SqlSession对象
	SqlSession session = factory.openSession();
	//3.执行SQL语句(namespace+SQLId)
	Map map = new HashMap();
	//查询薪资大于3000的员工
	map.put("minSal", 3000);
	//map.put("maxSal", 4000);
	List<Emp> list = session.selectList(
			"com.tedu.pojo.EmpMapper.findAll4",map);
	//4.输出结果
	for(Emp emp : list){
		System.out.println(emp);
	}
}

java代码示例:查询薪资小于4000的员工

public void testFindAll4() throws IOException{
	//0.加载sqlMapConfig.xml文件,获取所有的配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//1.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//2.通过工厂创建一个SqlSession对象
	SqlSession session = factory.openSession();
	//3.执行SQL语句(namespace+SQLId)
	Map map = new HashMap();
	//查询薪资小于4000的员工
	//map.put("minSal", 3000);
	map.put("maxSal", 4000);
	List<Emp> list = session.selectList(
			"com.tedu.pojo.EmpMapper.findAll4",map);
	//4.输出结果
	for(Emp emp : list){
		System.out.println(emp);
	}
}

6.3 set元素

示例:修改emp表中指定id的员工信息,如果传递了name、job、salary列的值,则修改,否则不修改
mapper文件配置:

<!-- 13.修改emp表中指定id的员工信息,如果传递了name、job、salary列的值,则修改,否则不修改
 -->
<update id="updateEmpById">
	update emp
	<set>
		<if test="name != null">name=#{name},</if>
		<if test="job != null">job=#{job},</if>
		<if test="salary != null">salary=#{salary}</if>
	</set>
	where id=#{id}
</update>

java代码示例:

public void testUpdateById() throws IOException{
	//0.加载sqlMapConfig.xml文件,获取所有的配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//1.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//2.通过工厂创建一个SqlSession对象
	SqlSession session = factory.openSession();
	//3.执行SQL语句(namespace+SQLId)
	Emp emp = new Emp();
	emp.setId(1);
	emp.setName("王海涛xx");
	emp.setJob("金牌讲师");
	emp.setSalary(4999.0);
	//4.执行更新
	session.update("com.tedu.pojo.EmpMapper.updateEmpById",emp);
	//>>提交事务
	session.commit();
	System.out.println("执行完成!");
}

可以尝试不传递name或job或salary中的某些值,运行程序,查询修改后的结果!!

6.4 foreach元素

示例: 查询emp表中所有指定范围id的员工信息
mapper文件配置:

<!-- 14.查询emp表中所有指定id的员工信息 -->
<select id="findByIds" resultType="com.tedu.pojo.Emp">
	select * from emp where id in
	<foreach collection="array" open="(" close=")" item="id" separator=",">
		#{id}
	</foreach>	
</select>

Java代码示例: 查询指定id的所有员工信息

public void testfindByIds() throws IOException{
	//0.加载sqlMapConfig.xml文件,获取所有的配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//1.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//2.通过工厂创建一个SqlSession对象
	SqlSession session = factory.openSession();
	//3.执行SQL语句(namespace+SQLId)
	//查询指定id的所有员工信息
	int[] ids = {1,3,5};
	//4.执行更新
	List<Emp> list = session.selectList("com.tedu.pojo.EmpMapper.findByIds",ids);
	//5.输出结果
	for (Emp e : list) {
		System.out.println(e);
	}
}

批量删除示例: 删除emp表中所有指定id的员工信息

mapper文件配置:

<!-- 15.批量删除emp表中所有指定id的员工信息 -->
<delete id="deleteByIds">
	delete from emp where id in
	<foreach collection="array" open="(" close=")" item="id" separator=",">
		#{id}
	</foreach>	
</delete>

Java代码示例: 删除定id的所有员工信息

public void testDeleteByIds() throws IOException{
	//0.加载sqlMapConfig.xml文件,获取所有的配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//1.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//2.通过工厂创建一个SqlSession对象
	SqlSession session = factory.openSession();
	//3.执行SQL语句(namespace+SQLId)
	//删除指定id的员工信息
	int[] ids = {1,3,5};
	//4.执行更新
	session.delete("com.tedu.pojo.EmpMapper.deleteByIds",ids);
	//>>提交事务
	session.commit();
	System.out.println("执行完成!");

7 Mapper接口开发

7.1 Mapper接口开发介绍

在上面的Mybatis案例中, 通过SqlSession对象调用方法进行增删改查操作时, 方法中需要传入的第一个参数是一个字符串值, 该值对应的内容为: (Mapper文件中的) namespace + id, 通过这种方式, 找到Mapper文件中映射的SQL语句并执行!!
这种方式由于传入的是字符串值, 很容易发生字符串拼写错误且编译时期不会提示。
这里我们将会讲解比上面更加简单的方式,也是我们企业开发中最常用的方式,即使用mapper接口开发。使用mapper接口开发需要注意以下几点:

1、Mapper接口的全路径名+方法名,和mapper.xml文件中定义namespace+id值必须相同
2、Mapper接口中方法接收的参数类型,和mapper.xml中定义的sql的parameterType的类型相同
3、Mapper接口中方法的返回值类型,和mapper.xml中定义的sql的resultType的类型相同

7.2 Mapper接口开发实现

下面将使用mapper接口开发的方式,实现根据id查询指定的员工信息
1、创建com.tedu.dao.EmpMapper接口
由于接口的全路径名(com.tedu.dao.EmpMapper)要和EmpMapper.xml的namespace值保持一致, 因此, 这里将namespace的值改为com.tedu.dao.EmpMapper:

<?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.tedu.dao.EmpMapper">

</mapper>

2、在接口中提供findById方法

/**
 * 根据id查询员工信息
 * @param id
 * @return Emp
 */
public Emp findById(Integer id);

注意:方法的名字要和映射的sql标签的id值保持一致
方法的返回值类型和resultType的类型要一致

<!-- 1.查询:查询Emp表中指定id的员工信息 -->
<select id="findById" resultType="com.tedu.pojo.Emp">
	select * from emp where id=#{id}
</select>

3、提供实现类,测试Emp接口中的根据id查询员工的方法
(1)创建com.tedu.test.TestMybatisInf类, 并提供testFindById方法

public class TestMybatisInf {
	@Test
	public void testFindById() throws Exception{}
}

实现testFindById方法并测试

@Test
public void testFindById() throws Exception{
	//0.加载sqlMapConfig.xml文件,获取所有的配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//1.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//2.通过工厂创建一个SqlSession对象
	SqlSession session = factory.openSession();
	//3.获取Mapper接口对象
	EmpMapper map = session.getMapper(EmpMapper.class);
	//4.调用接口对象的方法进行查询
	Emp e = map.findById(2);
	//5.输出结果
	System.out.println(e);
}

5、在接口中提供findAll方法

/**
 * 查询所有的员工信息
 * @return List<Emp>
 */
public List<Emp> findAll();

注意:方法的名字要和映射的sql标签的id值保持一致
方法的返回值类型和resultType的类型要一致, 例如:

<!-- 2.查询Emp表中所有员工的信息 -->
<select id="findAll" resultType="com.tedu.pojo.Emp">
	select * from emp
</select>

6、提供实现类,测试Emp接口中的查询所有员工的方法
(1)创建com.tedu.test.TestMybatisInf类, 并提供testFindAll方法

public class TestMybatisInf {
	。。。
	@Test
	public void testFindAll () throws Exception{}
}

实现testFindAll方法并测试

@Test
public void testFindAll() throws Exception{
	//0.加载sqlMapConfig.xml文件,获取所有的配置信息
	InputStream in = Resources.getResourceAsStream("sqlMapConfig.xml");
	//1.创建工厂对象
	SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(in);
	//2.通过工厂创建一个SqlSession对象
	SqlSession session = factory.openSession();
	//3.获取Mapper接口对象
	EmpMapper map = session.getMapper(EmpMapper.class);
	//4.调用接口对象的方法进行查询
	List<Emp> list = map.findAll();
	//5.输出结果
	for (Emp e : list) {
		System.out.println(e);
	}
}

8. Mybatis 多表查询

一个用户对应多个订单(多个订单对应一个用户)
一个用户对应一个身份证
一个学生对应多个老师,一个老师对应多个学生

8.1 创建实验表

CREATE TABLE `account` (
 `ID` int(11) NOT NULL,
`UID` int(11) DEFAULT NULL,
 `MONEY` double DEFAULT NULL,
 PRIMARY KEY (`ID`),
 KEY `FK_Reference` (`UID`),
 CONSTRAINT `FK_Reference` FOREIGN KEY (`UID`) REFERENCES `user` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

8.2 创建实体类对象 Account.java

在这里插入图片描述

8.3 因为 Account 类中没有用户的信息 所以一编写 AccountUser 继承 Account 类

在这里插入图片描述

8.4 配置映射文件:

在这里插入图片描述
以上是通过继承的方式编写的,比较麻烦不推荐使用:

8.5 关联多对一关系

更改实体对象 Account 类:

在这里插入图片描述
更改配置文件信息

在这里插入图片描述
多对一测试

@Test
 public void findAll(){
	 List<Account> accounts = accountDao.findAll();
	 for(Account account : accounts){
	 System.out.println(account+" "+account.getUser());
	 }
 }

8.6 关联一对多关系

关联实体类修改
在这里插入图片描述
修改 User 的映射文件
在这里插入图片描述
测试执行
在这里插入图片描述

8.7 关联多对多关系

多对多的关系最终还是两个一对多 引入中间表完成只是 sql 语句发生了变化;
一个用户对应多个角色,一个角色对应多个用户

创建实验表
Role 表:

CREATE TABLE `role` (
 `id` int(11) NOT NULL,
 `role_name` varchar(30) COLLATE utf8_bin DEFAULT NULL,
 `role_desc` varchar(60) COLLATE utf8_bin DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

用户表 User

CREATE TABLE `user` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `username` varchar(32) COLLATE utf8_bin NOT NULL COMMENT '用户名称',
 `birthday` datetime DEFAULT NULL,
 `sex` char(1) COLLATE utf8_bin DEFAULT NULL,
 `address` varchar(256) COLLATE utf8_bin DEFAULT NULL,
 PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=10 DEFAULT CHARSET=utf8 COLLATE=utf8_bin

中间表:

CREATE TABLE `user_role` (
 `uid` int(11) NOT NULL,
 `rid` int(11) NOT NULL,
 PRIMARY KEY (`uid`,`rid`),
 KEY `rid` (`rid`),
 CONSTRAINT `user_role_ibfk_1` FOREIGN KEY (`uid`) REFERENCES `user` (`id`),
 CONSTRAINT `user_role_ibfk_2` FOREIGN KEY (`rid`) REFERENCES `role` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_bin

配置一个角色对应多个用户
注意:此处配置完成后必须要在主配置文件中进行映射
在这里插入图片描述
测试查询

@test
public void queryAll() throws Exception {
 List<Role> roles = roleDao.findAll();
 for (Role r : roles) {
 System.out.println(r);
 }
 session.close();
 }

演示一个用户对应多个角色 省略

8 几个可以优化的地方

9.1 加入log4j日志框架

在项目中加入log4j的配置文件,用于打印日志信息,便于开发调试。
在src(或相似的目录)下创建log4j.properties如下:
在这里插入图片描述

# Global logging configuration
log4j.rootLogger=DEBUG, stdout
# 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默认使用log4j作为输出日志信息。
只要将该文件放在指定的位置,log4j工具会自动到指定位置加载上述文件,读取文件中的配置信息并使用!

9.2 可重复使用的SQL片段

在编写查询SQL时,我们书写的SQL往往为:

select * from emp
select * from emp where id=#{id}
...

其实这里书写星号(*)不如直接写列名速度快,因为在查询时,还需要将星号(*)转换为表列名,如果直接写列名则省去了转换操作。
因此下面在查询指定id的员工和查询所有员工的SQL中,将查询的列由星号(*)替换为了列名。

<!-- 0.查询指定id的员工 -->
<select id="findById" parameterType="int" resultType="com.tedu.pojo.Emp">
	select id,name,job,salary from emp
	where id=#{id}
</select>

<!-- 1.查询所有员工 -->
<select id="findAll" resultType="com.tedu.pojo.Emp">
	select id,name,job,salary from emp
</select>

但,如果直接写列名替换星号(*),可能也会带来一些麻烦:
(1)假设这里还有很多查询,都要查询所有列,每次都书写所有列名,麻烦!
(2)如果表中的列有几十个,书写起来会更加麻烦,SQL语句的长度也会暴增!
(3)如果后期需求发生变化,要修改查询的列,可能要修改多处,可维护性差!
要解决上面的问题,可以使用sql片段,例如:

声明一个sql片段,指定要查询的列

<!-- 声明一个SQL片段,指定要查询的列 -->
<sql id="empCols">id,name,job,salary</sql>

将下面查询SQL中的列名替换为SQL片段

<!-- 0.查询指定id的员工 -->
<select id="findById" parameterType="int" resultType="com.tedu.pojo.Emp">
	select <include refid="empCols"/> from emp
	where id=#{id}
</select>

<!-- 1.查询所有员工 -->
<select id="findAll" resultType="com.tedu.pojo.Emp">
	select <include refid="empCols"/> from emp
</select>

9.3 SQL语句中的特殊符号

示例:添加一个查询功能:查询薪资小于3500的所有员工。
1、编辑EmpMapper.xml文件, 添加查询对应的sql.

<!-- 查询薪资小于3500的所有员工 -->
<select id="findBySal">
	select <include refid="empCols"/> from emp
	where salary < 3500
</select>

2、但在书写完后,xml文件提示有错误:在这里插入图片描述
原来,小于号(<)在xml文件中是特殊字符,被xml文件当成了标签的开始符号。

3、解决方法:将特殊符号包含在CDATA区()中,这是因为放在CDATA区中的内容,只会被xml解析器当作普通文本来处理。而不是被当成标签的一部分处理。

<!-- 查询薪资小于3500的所有员工 -->
<select id="findBySal">
	select <include refid="empCols"/> from emp
	where salary <![CDATA[ < ]]> 3500
</select>

9.4 jdbc.properties文件

在开发中,通常我们会将连接数据库的配置信息单独放在一个properties文件中(方便管理和维护), 然后在MyBatis的mapper文件中引入properties文件的配置信息即可!
1、在src目录下创建一个名称为jdbc.properties的文件
在这里插入图片描述
2、jdbc.properties文件内容如下:

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf-8
jdbc.username=root
jdbc.password=root

3、在sqlMapConfig.xml文件中引入jdbc.properties文件
在这里插入图片描述
1、其中 标签用于引入jdbc.properties文件,默认到classpath即类目录下寻找指定的文件;
2、properties标签上value属性中配置的 ${jdbc.xxx}:
${jdbc.driver}:其实就是jdbc.properties文件中的 jdbc.driver的值,即:

com.mysql.jdbc.Driver

${jdbc.url}:其实就是jdbc.properties文件中的 jdbc.url的值,即:

jdbc:mysql://localhost:3306/mybatisdb?characterEncoding=utf-8

${jdbc.username}:其实就是jdbc.properties文件中的 jdbc.username的值,即:

root

${jdbc.password}:其实就是jdbc.properties文件中的 jdbc.password的值,即:

root

9.5 Mybatis 的延迟加载和立即加载

9.5.1 立即加载:

就是立马执行 sql 语句:
下面的日志一次会执行所有的 sql 语句
在这里插入图片描述

9.5.2 延迟加载

根据需求 加载需要的 sql 语句:
开启延迟加载:
在这里插入图片描述
配置 account 的映射文件:
在这里插入图片描述
配置 User 的映射配置文件

在这里插入图片描述
测认:
在这里插入图片描述
生成的日志文件只有查询 account 的 sql 语句
在这里插入图片描述

9.6. Mybatis 缓存技术

mybatis 提供查询缓存,用于减轻数据库压力,提高数据库性能
9.6.1什么样的数据使用缓存:
⚫ 经常查询的数据;
⚫ 不经常更改的数据
⚫ 不是很重要的数据
9.6.2据不适应缓存
⚫ 经常改变的数据
⚫ 数据很重要
⚫ 不经常查询的数据
9.6.3作原理
sqlsession 级别的缓存

在操作数据库时,需要构造 sqlsession 对象,在对象中有一个数据结构(HashMap)用于存储缓存数据
不同的 sqlsession 之间的缓存区域是互相不影响的。一级缓存自动开启不需要配置任何信息

在这里插入图片描述

第一次发起查询 sql 查询用户 id 为 1 的用户,先去找缓存中是否有 id 为 1 的用户,如果没有,再去数据库查询用户信息。得到用户信息,将
用户信息存储到一级缓存中。
如果 sqlsession 执行了 commit 操作(插入,更新,删除),会清空 sqlsession 中的一级缓存,避免脏读
第二次发起查询 id 为 1 的用户,缓存中如果找到了,直接从缓存中获取用户信息
mybatis 默认支持一级缓存。

测试:
在这里插入图片描述
结果:
在这里插入图片描述
打印的对象是属于同一个对象,sql 语句只执行一次:
当 Session 关闭 缓存中的数据 就不存在了

在这里插入图片描述
获取得到的结果为 false;两次不一样执行多次 sql 语句:
在这里插入图片描述
现原理
在这里插入图片描述
测试案例:多个 sqlSession 执行同一个方法:
配置开启二级缓存
在这里插入图片描述
还需要在 Mapper 对应的 xml 中添加 cache 标签,表示对哪个 mapper 开启缓存

<!-- 表示 DEPT 表查询结果保存到二级缓存(共享缓存) --> <cache/>

在这里插入图片描述
在这里插入图片描述
获取的日志为:
在这里插入图片描述
二级缓存存储的是数据不是对象;

10 Mybatis 注解开发

@Insert:实现新增
@Update:实现更新
@Delete:实现删除
@Select:实现查询
@Result:实现结果集封装
@Results:可以与@Result 一起使用,封装多个结果集
@ResultMap:实现引用@Results 定义的封装
@One:实现一对一结果集封装

在这里插入图片描述
@Many:实现一对多结果集封装
在这里插入图片描述

11 扩展内容

11.1 Jdbc回顾

通过JDBC查询Emp表中的所有记录,并封装到一个List集合中返回
1、创建TestJdbc类,完成查询所有员工:

package com.tedu;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.List;
import com.tedu.pojo.Emp;
/** Jdbc回顾 */
public class TestJdbc {
	public static void main(String[] args) {
		/* 查询emp表中的所有员工信息,将每个员工信息的封装到一个
		 * Emp对象中,再将封装了员工信息所有Emp对象存入List集合
		 * 中,并遍历输出所有的员工信息 
		 */
		List<Emp> empList = findAll();
		for(Emp emp : empList){
			System.out.println(emp);
		}
	}
	
	/**
	 * 查询emp表中的所有员工信息,封装到List集合并返回
	 */
	private static List<Emp> findAll() {
		Connection conn = null;
		Statement stat = null;
		ResultSet rs = null;
		try {
			//1.注册数据库驱动
			Class.forName("com.mysql.jdbc.Driver");
			//2.获取数据库连接(Connection)
			conn = DriverManager.getConnection(
					"jdbc:mysql:///yonghedb", 
					"root", "root");
			//3.获取传输器
			stat = conn.createStatement();
			//4.利用传输器发送sql到数据库执行,并返回执行结果
			String sql = "select * from emp";
			rs = stat.executeQuery(sql);
			//5.处理结果
			//5.1.声明List集合,用于封装所有的员工信息
			List<Emp> empList = new ArrayList();
			//5.2.遍历ResultSet结果集
			while(rs.next()) {
				//5.3.获取结果集中的每一条员工信息
				int id = rs.getInt("id");
				String name = rs.getString("name");
				String job = rs.getString("job");
				double salary = rs.getDouble("salary");
				//5.4.将每一条员工信息封装到一个Emp对象中
				Emp emp = new Emp();
				emp.setId(id);
				emp.setName(name);
				emp.setJob(job);
				emp.setSalary(salary);
				//5.5.将Emp对象存入List集合中
				empList.add(emp);
			}
			return empList;
		} catch (Exception e) {
			e.printStackTrace();
			System.out.println("查询失败!");
		} finally{
			//6.释放资源
			if(rs != null){
				try {
					rs.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}finally{
					rs = null;
				}
			}
			if(stat != null){
				try {
					stat.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}finally{
					stat = null;
				}
			}
			if(conn != null){
				try {
					conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}finally{
					conn = null;
				}
			}
		}
		return null;
	}
}

2、声明Emp实体类,用于封装员工信息:

package com.tedu.pojo;

public class Emp {
	//声明私有属性
	private Integer id;		//员工编号
	private String name;	//员工姓名
	private String job;		//员工职位
	private double salary;	//员工薪资
	
	//提供getter和setter方法
	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;
	}
	
	//提供toString方法
	@Override
	public String toString() {
		return "Emp [id=" + id + ", name=" + name + ", job=" + job + ", salary=" + salary + "]";
	}
}

11.2 sqlMapConfig文件没有提示的解决办法

如果在没有网络(外网)的情况下,编写sqlMapConfig.xml文件没有提示,可以按照下面的步骤进行配置:
(1)找到mybatis-3-config.dtd的文件的位置,例如:

在这里插入图片描述
(2)复制下面的url地址:

http://mybatis.org/dtd/mybatis-3-config.dtd

(3)在eclipse菜单栏中: window --> Preferences --> 在搜索框中搜索 [ xml ]
XML --> XML Catalog --> User Specified Entries --> Add…
在这里插入图片描述
(4)在弹出的窗口中:在这里插入图片描述

11.3 Mapper文件没有提示的解决办法

如果在没有网络(外网)的情况下,编写XxxMapper.xml文件没有提示,可以按照下面的步骤进行配置:
(1)找到mybatis-3-mapper.dtd的文件的位置,例如:
(2)复制上面的url地址,即:
(2)复制上面的url地址,即:

http://mybatis.org/dtd/mybatis-3-mapper.dtd

(3)在eclipse菜单栏中: window --> Preferences --> 在搜索框中搜索 [ xml ]
XML --> XML Catalog --> User Specified Entries --> Add…
在这里插入图片描述(4)在弹出的窗口中:在这里插入图片描述

11.4 配置XML Schema代理服务器

1、打开Eclipse的配置首选项
在这里插入图片描述
2、找到 XML -> XML Catalog, 添加配置项目:
在这里插入图片描述
3、添加URI代理配置, 比如:配置Spring Schema
在这里插入图片描述
4、可以配置的代理有
http://ibatis.apache.org http://doc.tedu.cn
http://www.springframework.org http://doc.tedu.cn
http://mybatis.org http://doc.tedu.cn
http://hibernate.sourceforge.net, http://www.hibernate.org http://doc.tedu.cn
http://struts.apache.org http://doc.tedu.cn

11.5 sql脚本

-- 1、删除 yonghedb 数据库(如果存在)
drop database if exists yonghedb;
-- 2、创建数据库 yonghedb 数据库
create database yonghedb charset utf8;
use yonghedb; -- 选择yonghedb数据库

-- 3、在 yonghedb 库中创建 emp 表
create table emp(
	id int primary key auto_increment,
	name varchar(50),
	job varchar(50),
	salary double
);
-- 4、往 emp 表中, 插入若干条记录
insert into emp values(null, '霸王', '程序员', 3300);
insert into emp values(null, '惊雷', '程序员', 2800);
insert into emp values(null, '刘德华', '程序员鼓励师', 3700);
insert into emp values(null, '甄子丹', '部门总监', 4200);
insert into emp values(null, '杨坤', '程序员', 3000);
insert into emp values(null, '周润发', '程序员', 3500);
insert into emp values(null, '张三丰', '程序员', 2700);
insert into emp values(null, '太乙真人', 'CEO', 5000);
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XYDrestart

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值