MyBatis精讲

一、介绍

MyBatis介绍
MyBatis是一流的持久性框架,支持自定义SQL,存储过程和高级映射。MyBatis消除了几乎所有的JDBC代码以及参数的手动设置和结果检索。MyBatis可以使用简单的XML或注释进行配置,并将图元,映射接口和Java POJO(普通的旧Java对象)映射到数据库记录。
官网介绍:https://mybatis.org/mybatis-3/

为什么要使用MyBatis

  • MyBatis是一个半自动化的持久化层框架。
  • JDBC
    • SQL夹在Java代码块里,耦合度高导致硬编码内伤
    • 维护不易且实际开发需求中sql是有变化,频繁修改的情况多见 • Hibernate和JPA
    • 长难复杂SQL,对于Hibernate而言处理也不容易
    • 内部自动生产的SQL,不容易做特殊优化。
    • 基于全映射的全自动框架,大量字段的POJO进行部分映射时比较困难。
      导致数据库性能下降。 对开发人员而言,核心sql还是需要自己优化
  • MyBatis
    • sql和java编码分开,功能边界清晰,一个专注业务、一个专注数据
    • MyBatis 是支持定制化 SQL、存储过程以及高级映射的优秀的持久层框架。
    • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。
    • MyBatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录

下载MyBatis
https://github.com/mybatis/mybatis-3/
在这里插入图片描述
打开页面后,即可找到下载地址
在这里插入图片描述
如果是maven项目直接引入mybatis依赖即可

<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.2</version>
</dependency>

二、操作

2.1、原理介绍

根据全局配置文件,利用SqlSessionFactoryBuilder创建SqlSessionFactory

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = SqlSessionFactoryBuilder().build(inputStream);

使用SqlSessionFactory获取sqlSession对象。一个SqlSession对象代表和数据库的一次会话

SqlSession openSession = sqlSessionFactory.openSession();
try {
	// 3、获取接口的实现类对象
	//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
	EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
	Employee employee = mapper.getEmpById(1);
	System.out.println(mapper.getClass());
	System.out.println(employee);
} finally {
	openSession.close();
}

使用SqlSession根据方法id进行操作

try {
	Employee employee = openSession.selectOne(
			"com.atguigu.mybatis.EmployeeMapper.selectEmp", 1);
	System.out.println(employee);
} finally {
	openSession.close();
}

SqlSession

  • SqlSession 的实例不是线程安全的,因此是不能被共享的。
  • SqlSession每次使用完成后需要正确关闭,这个关闭操作是必须的。
  • SqlSession可以直接调用方法的id进行数据库操作,但是我们一般还是推荐使用SqlSession获取到Dao接口的代理类,执行代理对象的方法,可以更安全的进行类型检查操作。

2.2、代码实战

创建MyBatis全局配置文件mybatis-config.xml
MyBatis 的全局配置文件包含了影响 MyBatis 行为甚深的设置(settings)和属性(properties)信息、如数据库连接池信息等。指导着MyBatis进行工作。我们可以
参照官方文件的配置示例。下边文件的配置属性在官网都有介绍:官网配置介绍

<?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>
	<environments default="development">
		<environment id="development">
			<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>
	<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
	<mappers>
		<mapper resource="EmployeeMapper.xml" />
	</mappers>
</configuration>

properties属性:
在这里插入图片描述
如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:

  • 在 properties 元素体内指定的属性首先被读取。
  • 然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
  • 最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。

创建SQL映射文件EmployeeMapper.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="com.atguigu.mybatis.dao.EmployeeMapper">
<!-- 
namespace:名称空间;指定为接口的全类名
id:唯一标识
resultType:返回值类型
#{id}:从传递过来的参数中取出id值
public Employee getEmpById(Integer id);
-->
	<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee">
		select id,last_name lastName,email,gender from tbl_employee where id = #{id}
	</select>
</mapper>

创建DAO层Mapper接口EmployeeMapper

package com.atguigu.mybatis.dao;

import com.atguigu.mybatis.bean.Employee;

public interface EmployeeMapper {
	public Employee getEmpById(Integer id);
}

测试

package com.atguigu.mybatis.test;

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.Test;

import com.atguigu.mybatis.bean.Employee;
import com.atguigu.mybatis.dao.EmployeeMapper;

/**
 * @author sgw
 */
public class MyBatisTest {
	public SqlSessionFactory getSqlSessionFactory() throws IOException {
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		return new SqlSessionFactoryBuilder().build(inputStream);
	}

	/**
	 * 1、根据xml配置文件(全局配置文件)创建一个SqlSessionFactory对象 有数据源一些运行环境信息
	 * 2、sql映射文件;配置了每一个sql,以及sql的封装规则等。 
	 * 3、将sql映射文件注册在全局配置文件中
	 * 4、写代码:
	 * 		1)、根据全局配置文件得到SqlSessionFactory;
	 * 		2)、使用sqlSession工厂,获取到sqlSession对象使用他来执行增删改查
	 * 			一个sqlSession就是代表和数据库的一次会话,用完关闭
	 * 		3)、使用sql的唯一标志来告诉MyBatis执行哪个sql。sql都是保存在sql映射文件中的。
	 * 
	 * @throws IOException
	 */
	@Test
	public void test() throws IOException {

		// 2、获取sqlSession实例,能直接执行已经映射的sql语句
		// sql的唯一标识:statement Unique identifier matching the statement to use.
		// 执行sql要用的参数:parameter A parameter object to pass to the statement.
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();

		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			Employee employee = openSession.selectOne(
					"com.atguigu.mybatis.EmployeeMapper.selectEmp", 1);
			System.out.println(employee);
		} finally {
			openSession.close();
		}
	}

	@Test
	public void test01() throws IOException {
		// 1、获取sqlSessionFactory对象
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		// 2、获取sqlSession对象
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			// 3、获取接口的实现类对象
			//MyBatis会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			Employee employee = mapper.getEmpById(1);
			System.out.println(mapper.getClass());
			System.out.println(employee);
		} finally {
			openSession.close();
		}
	}
}

总结

  • 1、接口式编程
    原生: Dao ====> DaoImpl
    mybatis: Mapper ====> xxMapper.xml

  • 2、SqlSession
    SqlSession 代表和数据库的一次会话;用完必须关闭;
    SqlSession和Connection一样都是非线程安全的。每次使用都应该去获取新的对象(不要放在成员变量里)。

  • 3、mapper接口没有实现类,但是mybatis会为这个接口生成一个代理对象。
    (将接口和xml进行绑定)
    EmployeeMapper empMapper = sqlSession.getMapper(EmployeeMapper.class);

  • 5、两个重要的配置文件:
    mybatis的全局配置文件:包含数据库连接池信息,事务管理器信息等…系统运行环境信息
    sql映射文件:保存了每一个sql语句的映射信息,将sql抽取出来。

2.3、全局配置文件mybatis-config.xml

properties属性
官方介绍

  • mybatis可以使用properties来引入外部properties配置文件的内容;
    resource:引入类路径下的资源
    url:引入网络路径或者磁盘路径下的资源
<?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>
	<properties resource="dbconfig.properties"></properties>
	<environments default="dev_mysql">
		<environment id="dev_mysql">
			<transactionManager type="JDBC"></transactionManager>
			<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>
	<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
	<!-- 6、mappers:将sql映射注册到全局配置中 -->
	<mappers>
		<!-- 
			mapper:注册一个sql映射 
				注册配置文件
				resource:引用类路径下的sql映射文件
					mybatis/mapper/EmployeeMapper.xml
				url:引用网路路径或者磁盘路径下的sql映射文件
					file:///var/mappers/AuthorMapper.xml
					
				注册接口
				class:引用(注册)接口,
					1、有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
					2、没有sql映射文件,所有的sql都是利用注解写在接口上;
					推荐:
						比较重要的,复杂的Dao接口我们来写sql映射文件
						不重要,简单的Dao接口为了开发快速可以使用注解;
		-->
		<!-- <mapper resource="mybatis/mapper/EmployeeMapper.xml"/> -->
		<!-- <mapper class="com.atguigu.mybatis.dao.EmployeeMapperAnnotation"/> -->
		
		<!-- 批量注册: -->
		<package name="com.atguigu.mybatis.dao"/>
	</mappers>
</configuration>

对应的dbconfig.properties文件

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis
jdbc.username=root
jdbc.password=123456

settings属性

官方介绍

  • settings包含很多重要的设置项
    setting:用来设置每一个设置项
    name:设置项名
    value:设置项取值

例如:设置开启自动驼峰命名策略(mapUnderscoreToCamelCase默认值是false,不开启),即数据库字段是下划线A_COLUMN,而javaBean里是aColumn,开启驼峰命名后,这样就会把数据库里的字段映射到javaBean里了;

<?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>

	<properties resource="dbconfig.properties"></properties>

	<settings>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
	</settings>

	<environments default="dev_mysql">
		<environment id="dev_mysql">
			<transactionManager type="JDBC"></transactionManager>
			<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.atguigu.mybatis.dao"/>
	</mappers>
</configuration>

typeAliases别名属性
官方文档
类型别名是为 Java 类型设置一个短的名字。 它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。例如:

<typeAliases>
  <typeAlias alias="Author" type="domain.blog.Author"/>
  <typeAlias alias="Blog" type="domain.blog.Blog"/>
  <typeAlias alias="Comment" type="domain.blog.Comment"/>
  <typeAlias alias="Post" type="domain.blog.Post"/>
  <typeAlias alias="Section" type="domain.blog.Section"/>
  <typeAlias alias="Tag" type="domain.blog.Tag"/>
</typeAliases>

typeAliases:别名处理器:可以为我们的java类型起别名 ,别名不区分大小写;

不指定别名,使用默认的别名:

<typeAliases>
		<!-- 1、typeAlias:为某个java类型起别名
				type:指定要起别名的类型全类名;默认别名就是类名小写;employee
		 -->
		 <typeAlias type="com.atguigu.mybatis.bean.Employee" /> 
</typeAliases>

指定别名:

<typeAliases>
	<!-- alias:指定新的别名-->
	<typeAlias type="com.atguigu.mybatis.bean.Employee" alias="emp"/> 
</typeAliases>

为某个包下的所有类批量起别名:

<typeAliases>
		<!-- 2、package:为某个包下的所有类批量起别名 
				name:指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认别名(别名是类名小写))
		-->
		<package name="com.atguigu.mybatis.bean"/>
</typeAliases>

使用上边package批量起别名的前提下,在javaBean上使用@Alias注解为每个实体类起自己的起别名:

//别名
Alias("emp")
public class Employee {
	
	private Integer id;
	private String lastName;
	private String email;
	private String gender;
	
	
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}
	public String getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public String getGender() {
		return gender;
	}
	public void setGender(String gender) {
		this.gender = gender;
	}
	@Override
	public String toString() {
		return "Employee [id=" + id + ", lastName=" + lastName + ", email="
				+ email + ", gender=" + gender + "]";
	}
}

在mybatis-config.xml配置了别名后,在Mapper.xml的文件中,所有写com.atguigu.mybatis.bean.Employee的地方(一般是resultType的值)都可以换成别名了
在这里插入图片描述

MyBatis已经起好的别名:
在这里插入图片描述
注意:我们自己的别名不可以与上边的重复

typeHandlers类型处理器
官方文档
typeHandlers是架起javaBean与数据库的桥梁,即通过typeHandlers将java里的数据类型与数据库里的数据类型进行一一映射;
在这里插入图片描述
typeHandlers后边会细讲;

plugins插件
官方文档
主要是在拦截以下四大对象前后做一些事情

  • Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
  • ParameterHandler (getParameterObject, setParameters)
  • ResultSetHandler (handleResultSets, handleOutputParameters)
  • StatementHandler (prepare, parameterize, batch, update, query)

plugins插件后边会详细介绍;

environments环境配置
官方文档

MyBatis 可以配置成适应多种环境,这种机制有助于将 SQL 映射应用于多种数据库之中;

  • environments:环境们,mybatis可以配置多种环境 ,default指定使用某种环境。可以达到快速切换环境。
    • environment:配置一个具体的环境信息;必须有两个标签;id代表当前环境的唯一标识
    • transactionManager:事务管理器(项目中的事务一般交给了Spring去管理,不在MyBatis配置);
      • type:事务管理器的类型(有以下两种事务管理器):
        JDBC(JdbcTransactionFactory) | MANAGED(ManagedTransactionFactory)
        自定义事务管理器:实现TransactionFactory接口.type指定为全类名
    • dataSource:数据源
      • type:数据源类型
        UNPOOLED(UnpooledDataSourceFactory)不使用连接池
        POOLED(PooledDataSourceFactory)使用连接池
        JNDI(JndiDataSourceFactory)使用连接池
        自定义数据源:实现DataSourceFactory接口,type是全类名
<environments default="dev_mysql">
	<environment id="dev_mysql">
		<transactionManager type="JDBC"></transactionManager>
		<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>

	<environment id="dev_oracle">
		<transactionManager type="JDBC" />
		<dataSource type="POOLED">
			<property name="driver" value="${orcl.driver}" />
			<property name="url" value="${orcl.url}" />
			<property name="username" value="${orcl.username}" />
			<property name="password" value="${orcl.password}" />
		</dataSource>
	</environment>
</environments>

databaseIdProvider数据库厂商标识
官方文档
MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的databaseId 属性;
在全局平配置文件里进行配置来支持多数据库厂商

  • type=“DB_VENDOR”:VendorDatabaseIdProvider
    作用就是得到数据库厂商的标识(驱动getDatabaseProductName()),mybatis就能根据数据库厂商标识来执行不同的sql;

MySQL,Oracle,SQL Server,xxxx

<databaseIdProvider type="DB_VENDOR">
	<!-- 为不同的数据库厂商起别名 value值是别名 -->
	<property name="MySQL" value="mysql"/>
	<property name="Oracle" value="oracle"/>
	<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>

全局配置好后,在Mapper.xml里,通过databaseId的值告诉MyBatis当前sql是那种数据库的语句

<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee"
		databaseId="oracle">
		select EMPLOYEE_ID id,LAST_NAME	lastName,EMAIL email 
		from employees where EMPLOYEE_ID=#{id}
</select>

<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee"
		databaseId="mysql">
		select * from tbl_employee where id = #{id}
</select>

mappers映射器
官方文档

mappers:将sql映射注册到全局配置中

mapper:注册一个sql映射

方式一:注册配置文件

  1. resource:引用类路径下的sql映射文件
mybatis/mapper/EmployeeMapper.xml
  1. url:引用网路路径或者磁盘路径下的sql映射文件
file:///var/mappers/AuthorMapper.xml
<mappers>
	<mapper resource="mybatis/mapper/EmployeeMapper.xml"/>
</mappers>

方式二:注册接口
class:引用(注册)接口,

  1. 有sql映射文件,映射文件名必须和接口同名,并且放在与接口同一目录下;
  2. 没有sql映射文件,所有的sql都是利用注解写在接口上;
    sql利用注解写在接口上的方式:
package com.atguigu.mybatis.dao;

import org.apache.ibatis.annotations.Select;

import com.atguigu.mybatis.bean.Employee;

public interface EmployeeMapperAnnotation {
	@Select("select * from tbl_employee where id=#{id}")
	public Employee getEmpById(Integer id);
}

全局配置文件:

<mappers>
    <mapper class="com.atguigu.mybatis.dao.EmployeeMapperAnnotation"/>
</mappers>

以上两种方式可以同时配置

<mapper resource="mybatis/mapper/EmployeeMapper.xml"/> 
<mapper class="com.atguigu.mybatis.dao.EmployeeMapperAnnotation"/> 

方式三:批量注册
映射文件名必须和接口同名,并且放在与接口同一目录下(注解版不需要);

<mappers>
	<package name="com.atguigu.mybatis.dao"/>
</mappers>

推荐:
比较重要的,复杂的Dao接口我们来写sql映射文件;
不重要,简单的Dao接口为了开发快速可以使用注解的方式;

一个比较完整的全局配置文件:

<?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>
	
	<properties resource="dbconfig.properties"></properties>
	
	
	<settings>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
	</settings>
	
	<typeAliases>
		<package name="com.atguigu.mybatis.bean"/>
	</typeAliases>
		
	<environments default="dev_mysql">
		<environment id="dev_mysql">
			<transactionManager type="JDBC"></transactionManager>
			<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>
	
		<environment id="dev_oracle">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${orcl.driver}" />
				<property name="url" value="${orcl.url}" />
				<property name="username" value="${orcl.username}" />
				<property name="password" value="${orcl.password}" />
			</dataSource>
		</environment>
	</environments>
	
	<databaseIdProvider type="DB_VENDOR">
		<!-- 为不同的数据库厂商起别名 -->
		<property name="MySQL" value="mysql"/>
		<property name="Oracle" value="oracle"/>
		<property name="SQL Server" value="sqlserver"/>
	</databaseIdProvider>
	
	<mappers>
		<package name="com.atguigu.mybatis.dao"/>
	</mappers>
</configuration>

注意,配置文件里的标签是有顺序的,比如 <databaseIdProvider >标签不可以放在<environments>之前
标签的顺序如下:
在这里插入图片描述

三、MyBatis的sql映射文件Mapper.xml

官方文档

3.1、MyBatis实现增删改操作

接口

package com.atguigu.mybatis.dao;

import java.util.List;
import java.util.Map;

import org.apache.ibatis.annotations.MapKey;
import org.apache.ibatis.annotations.Param;

import com.atguigu.mybatis.bean.Employee;

public interface EmployeeMapper {

	public Long addEmp(Employee employee);

	public boolean updateEmp(Employee employee);

	public void deleteEmpById(Integer 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.atguigu.mybatis.dao.EmployeeMapper">
 
	<!-- parameterType:参数类型,可以省略, 
	获取自增主键的值:
		mysql支持自增主键,自增主键值的获取,mybatis也是利用statement.getGenreatedKeys();
		useGeneratedKeys="true";使用自增主键获取主键值策略
		keyProperty;指定对应的主键属性,也就是mybatis获取到主键值以后,将这个值封装给javaBean的哪个属性
	-->
	<insert id="addEmp" parameterType="com.atguigu.mybatis.bean.Employee"
		useGeneratedKeys="true" keyProperty="id" databaseId="mysql">
		insert into tbl_employee(last_name,email,gender) 
		values(#{lastName},#{email},#{gender})
	</insert>
	
	<!-- 
	获取非自增主键的值:
		Oracle不支持自增;Oracle使用序列来模拟自增;
		每次插入的数据的主键是从序列中拿到的值;如何获取到这个值;
	 -->
	<insert id="addEmp" databaseId="oracle">
		<!-- 
		keyProperty:查出的主键值封装给javaBean的哪个属性
		order="BEFORE":当前sql在插入sql之前运行
			   AFTER:当前sql在插入sql之后运行
		resultType:查出的数据的返回值类型
		
		BEFORE运行顺序:
			先运行selectKey查询id的sql;查出id值封装给javaBean的id属性
			在运行插入的sql;就可以取出id属性对应的值
		AFTER运行顺序:
			先运行插入的sql(从序列中取出新值作为id);
			再运行selectKey查询id的sql-->
		<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
			<!-- 编写查询主键的sql语句 -->
			<!-- BEFORE-->
			select EMPLOYEES_SEQ.nextval from dual 
			<!-- AFTER:
			 select EMPLOYEES_SEQ.currval from dual -->
		</selectKey>
		
		<!-- 插入时的主键是从序列中拿到的 -->
		<!-- BEFORE:-->
		insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL) 
		values(#{id},#{lastName},#{email<!-- ,jdbcType=NULL -->}) 
		<!-- AFTER:
		insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL) 
		values(employees_seq.nextval,#{lastName},#{email}) -->
	</insert>
	
	<update id="updateEmp">
		update tbl_employee 
		set last_name=#{lastName},email=#{email},gender=#{gender}
		where id=#{id}
	</update>
	
	<delete id="deleteEmpById">
		delete from tbl_employee where id=#{id}
	</delete>
</mapper>

测试:

    /**
	 * 	测试增删改
	 * @throws IOException 
	 */
	@Test
	public void test03() throws IOException{
		
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		//1、获取到的SqlSession不会自动提交数据
		SqlSession openSession = sqlSessionFactory.openSession();
		try{
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			//测试添加
			Employee employee = new Employee(null, "jerry4",null, "1");
			mapper.addEmp(employee);
			System.out.println(employee.getId());
			
			//测试修改
			//Employee employee = new Employee(1, "Tom", "[email protected]", "0");
			//boolean updateEmp = mapper.updateEmp(employee);
			//System.out.println(updateEmp);
			//测试删除
			//mapper.deleteEmpById(2);
			//2、手动提交数据
			openSession.commit();
		}finally{
			openSession.close();
		}
	}
  1. mybatis允许增删改直接定义以下类型返回值
    Integer、Long、Boolean、void
  2. 有以下两种SqlSession
    SqlSession openSession =sqlSessionFactory.openSession();——>需要手动提交
    SqlSession openSession =sqlSessionFactory.openSession(true);——>自动提交

上边的插入操作,主键值是自增的,当执行完插入操作后,想得到返回的主键时,MyBatis利用statement.getGenrentedKeys()来获取,只需要在<insert>节点里配置useGeneratedKeys="true" 即可
,默认是false;keyProperty="id"指定主键对应的属性,即MyBatis获取到主键值后保存到JavaBean的id属性里;

<insert id="addEmp" parameterType="com.atguigu.mybatis.bean.Employee"
		useGeneratedKeys="true" keyProperty="id" databaseId="mysql">
		insert into tbl_employee(last_name,email,gender) 
		values(#{lastName},#{email},#{gender})
</insert>

oracle不支持自增,需要使用序列来实现自增,每次插入的数据的主键是从序列中拿到的值,那如何获取到这个值?

<insert id="addEmp" databaseId="oracle">
		<!-- 
		keyProperty:查出的主键值封装给javaBean的哪个属性
		order="BEFORE":当前sql在插入sql之前运行
			   AFTER:当前sql在插入sql之后运行
		resultType:查出的数据的返回值类型
		
		BEFORE运行顺序:
			先运行selectKey查询id的sql;查出id值封装给javaBean的id属性
			再运行插入的sql;就可以取出id属性对应的值
		AFTER运行顺序:
			先运行插入的sql(从序列中取出新值作为id);
			再运行selectKey查询id的sql-->
		<selectKey keyProperty="id" order="BEFORE" resultType="Integer">
			<!-- 编写查询主键的sql语句 -->
			<!-- BEFORE-->
			select EMPLOYEES_SEQ.nextval from dual 
			<!-- AFTER:
			 select EMPLOYEES_SEQ.currval from dual -->
		</selectKey>
		
		<!-- 插入时的主键是从序列中拿到的 -->
		<!-- BEFORE:-->
		insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL) 
		values(#{id},#{lastName},#{email<!-- ,jdbcType=NULL -->}) 
		<!-- AFTER:
		insert into employees(EMPLOYEE_ID,LAST_NAME,EMAIL) 
		values(employees_seq.nextval,#{lastName},#{email}) -->
</insert>

3.2、MyBatis对参数的处理

单个参数
mybatis不会做特殊处理,#{参数名/任意名}:取出参数值。注意,大括号里写任意值都行,没必要与接口方法的参数名保持一致
接口:

import java.util.List;

public interface EmployeeMapper {
	public Employee getEmpById(Integer id);
}

映射文件

<!--注意大括号里,是可以随便写的,因为只有一个参数-->
<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee">
		select * from tbl_employee where id = #{idabc}
</select>

多个参数

接口

public Employee getEmpByIdAndLastName(Integer id,String lastName);

映射文件

<select id="getEmpByIdAndLastName" resultType="com.atguigu.mybatis.bean.Employee">
 		select * from tbl_employee where id = #{id} and last_name=#{lastName}
</select>

上边这样是会报错的:

org.apache.ibatis.binding.BindingException:
Parameter ‘id’ not found.
Available parameters are [1, 0, param1, param2]

这是因为MyBatis遇到多个参数的时候会做特殊处理,多个参数会被封装成 一个map,key的值是param1…paramN,或者参数的索引也可以,value的值是传入的参数值,所以,接口如果不变的情况下,映射文件正确的写法应该是:

<select id="getEmpByIdAndLastName" resultType="com.atguigu.mybatis.bean.Employee">
 		select * from tbl_employee where id = #{param1} and last_name=#{param2}
</select>

上边这样写没有问题,但是阅读起来很不友好,所以采用@Param命名参数的方式,明确指定封装参数时map的key;@Param(“id”),多个参数会被封装成 一个map,key:使用@Param注解指定的值,value:参数值,#{指定的key}取出对应的参数值;

接口:

public Employee getEmpByIdAndLastName(@Param("id")Integer id,@Param("lastName")String lastName);

映射文件:

<select id="getEmpByIdAndLastName" resultType="com.atguigu.mybatis.bean.Employee">
 		select * from tbl_employee where id = #{id} and last_name=#{lastName}
</select>

POJO作为参数
如果多个参数正好是我们业务逻辑的数据模型(javaBean),我们就可以直接传入pojo;
#{属性名}:即可取出传入的pojo的属性值

Map作为参数
如果多个参数不是业务模型中的数据(javaBean),没有对应的pojo,不经常使用,为了方便,我们也可以传入map
#{key}:取出map中对应的值;

接口

public Employee getEmpByMap(Map<String, Object> map);

映射文件

<select id="getEmpByMap" resultType="com.atguigu.mybatis.bean.Employee">
 	select * from ${tableName} where id=${id} and last_name=#{lastName}
</select>

几种特殊参数形式,对于的mapper取值的写法
1、接口

public Employee getEmp(@Param("id")Integer id,String lastName);

对应的mapper取值:

id==>#{id/param1}   lastName==>#{param2}

2、接口

public Employee getEmp(Integer id,@Param("e")Employee emp);

对应的mapper取值:

id==>#{param1}    lastName===>#{param2.lastName/e.lastName}

3、特别注意:如果参数是Collection(List、Set)类型、或者是数组类型,mybatis也会特殊处理,会把传入的list或者数组封装在map中
map里key的值:

  • 参数是Collection:key就是collection,如果Collection是List,key可以使用这个list
  • 参数是数组Array:key就是array

例如接口:

public Employee getEmpById(List<Integer> ids);

对应mapper里的取值:

取出第一个id的值:   #{list[0]}

mybatis中#与$的区别
$会自动将参数填充到指定位置,而#会使用占位符填充
在这里插入图片描述
区别:

  • #{}:是以预编译的形式,将参数设置到sql语句中;PreparedStatement预编译,可以防止sql注入;
  • ${}:取出的值直接拼装在sql语句中,存在安全问题;

大多情况下,我们取参数的值都应该去使用#{};

原生jdbc不支持占位符的地方我们就可以使用${}进行取值,比如分表、排序。。。;按照年份分表拆分

# "#"只能获取参数里的值,sql里表名的地方不是参数,不可以使用#来获取表名
select * from ${year}_salary where xxx;
# 表名、排序,不支持预编译,即不可以使用#来取值
select * from tbl_employee order by ${f_name} ${order}

#{}:更丰富的用法:
规定参数的一些规则:
javaType、 jdbcType、 mode(存储过程)、 numericScale、
resultMap、 typeHandler、 jdbcTypeName、 expression(未来准备支持的功能);

jdbcType通常需要在某种特定的条件下被设置:
在我们数据为null的时候,有些数据库可能不能识别mybatis对null的默认处理。比如Oracle(报错):
JdbcType OTHER:无效的类型;因为mybatis对所有的null都映射的是原生Jdbc的OTHER类型,oracle不能正确处理;

由于全局配置中:jdbcTypeForNull=OTHER;oracle不支持;如果email字段为空,有两种解决办法
1、mapper文件里取值的地方:#{email,jdbcType=OTHER};
2、修改全局配置:jdbcTypeForNull=NULL

<setting name="jdbcTypeForNull" value="NULL"/>

在这里插入图片描述

3.3、select查询元素

返回类型是List集合
如果返回的是一个list集合类型,则resultType要写集合中元素的类型
接口:

public List<Employee> getEmpsByLastNameLike(String lastName);

mapper文件

<select id="getEmpsByLastNameLike" resultType="com.atguigu.mybatis.bean.Employee">
		select * from tbl_employee where last_name like #{lastName}
</select>

返回类型是Map集合
1、返回结果只有一条Map记录
如果返回的是一个Map集合类型,且只有一条记录,则resultType就是map(mybatis为Map集合取的别名)
接口:

//返回一条记录的map;key就是列名,值就是对应的值
public Map<String, Object> getEmpByIdReturnMap(Integer id);

mapper文件

<select id="getEmpByIdReturnMap" resultType="map">
 	select * from tbl_employee where id=#{id}
</select>

2、返回结果只有多条Map记录
如果返回的是一个Map集合类型,且有多条记录,则resultType是集合里元素的类型

接口:

//多条记录封装一个map:Map<Integer,Employee>:键是这条记录的主键,值是记录封装后的javaBean
//@MapKey:告诉mybatis封装这个map的时候使用哪个属性作为map的key
@MapKey("lastName")
public Map<String, Employee> getEmpByLastNameLikeReturnMap(String lastName);

mapper.xml:

<select id="getEmpByLastNameLikeReturnMap" resultType="com.atguigu.mybatis.bean.Employee">
 	select * from tbl_employee where last_name like #{lastName}
</select>

resultType自定义结果集映射
数据库列名与javaBean属性名对应不上的话,可以有三种解决方式
1、起别名
2、全局配置文件里开启驼峰命名(前提是数据库列符合驼峰规则)
3、使用resultMap来自定义映射
切记:resultType与resultType只能使用其中的一个

mapper.xml里自定义映射规则

<!--自定义某个javaBean的封装规则
	type:自定义规则的Java类型
	id:唯一id,方便引用
-->
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MySimpleEmp">
		<!--指定主键列的封装规则
		id定义主键会底层有优化;
		column:指定哪一列
		property:指定对应的javaBean属性
		  -->
		<id column="id" property="id"/>
		<!-- 定义普通列封装规则 -->
		<result column="last_name" property="lastName"/>
		<!-- 其他不指定的列会自动封装:我们只要写了resultMap,就把全部的映射规则都写上。 -->
		<result column="email" property="email"/>
		<result column="gender" property="gender"/>
</resultMap>

<select id="getEmpById"  resultMap="MySimpleEmp">
		select * from tbl_employee where id=#{id}
</select>

场景:
查询Employee员工的同时,查询该员工对应的部门
Employee===Department;
一个员工有与之对应的部门信息;

接口:

public Employee getEmpAndDept(Integer id);

方式一、级联属性封装结果集
resultMap 结果集:

<!--
	联合查询:级联属性封装结果集
-->
<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp">
		<id column="id" property="id"/>
		<result column="last_name" property="lastName"/>
		<result column="gender" property="gender"/>
		<result column="did" property="dept.id"/>
		<result column="dept_name" property="dept.departmentName"/>
</resultMap>

查询sql:

<select id="getEmpAndDept" resultMap="MyDifEmp">
		SELECT e.id id,e.last_name last_name,e.gender gender,e.d_id d_id,
		d.id did,d.dept_name dept_name FROM tbl_employee e,tbl_dept d
		WHERE e.d_id=d.id AND e.id=#{id}
</select>

方式二、使用association(联合)定义关联的单个对象的封装规则
resultMap 结果集:

<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyDifEmp2">
		<id column="id" property="id"/>
		<result column="last_name" property="lastName"/>
		<result column="gender" property="gender"/>
		
		<!--  association可以指定联合的javaBean对象
		property="dept":指定哪个属性是联合的对象
		javaType:指定这个属性对象的类型[不能省略]
		-->
		<association property="dept" javaType="com.atguigu.mybatis.bean.Department">
			<id column="did" property="id"/>
			<result column="dept_name" property="departmentName"/>
		</association>
</resultMap>

方式三、使用association进行分步查询
1、先按照员工id查询员工信息
2、根据查询员工信息中的d_id值去部门表查出部门信息
3、将部门信息设置到员工属性中;

定义部门的接口(之前只有员工的接口):

public Department getDeptById(Integer id);

对应的部门mapper.xml:

<select id="getDeptById" resultType="com.atguigu.mybatis.bean.Department">
		select id,dept_name departmentName from tbl_dept where id=#{id}
</select>

员工接口:

public Employee getEmpByIdStep(Integer id);

员工mapper.xml:

<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpByStep">
	 <id column="id" property="id"/>
	 <result column="last_name" property="lastName"/>
	 <result column="email" property="email"/>
	 <result column="gender" property="gender"/>
	 <!-- association定义关联对象的封装规则
	 		select:表明当前属性是调用select指定的方法查出的结果
	 		column:指定将哪一列的值传给这个方法
	 		
	 		流程:使用select指定的方法(传入column指定的这列参数的值)查出对象,并封装给property指定的属性(dept)
	 	 -->
 	 <association property="dept" 
	 		select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
	 		column="d_id">
 	</association>
</resultMap>

 <select id="getEmpByIdStep" resultMap="MyEmpByStep">
	 	select * from tbl_employee where id=#{id}
	 	<if test="_parameter!=null">
	 		and 1=1
	 	</if>
</select>

association 的延迟加载(懒加载):
上边的场景里,我们每次查询Employee对象的时候,都将Department一起查询出来了。
延迟加载:部门信息在我们使用的时候再去查询;
要实现懒加载,在之前association 分段查询的基础之上,在全局配置文件里加上以下两个配置即可:

<settings>
	<!--显示的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题  -->
	<setting name="lazyLoadingEnabled" value="true"/>
	<setting name="aggressiveLazyLoading" value="false"/>
</settings>

javaBean里的属性是集合类型
方式一:嵌套集合
场景:查询部门的时候,将当前部门里所有员工都查出来(部门的javaBean里,员工属性是集合类型)

部门接口:

public Department getDeptByIdPlus(Integer id);

部门mapper.xml:

<!--嵌套结果集的方式,使用collection标签定义关联的集合类型的属性封装规则  -->
<resultMap type="com.atguigu.mybatis.bean.Department" id="MyDept">
		<id column="did" property="id"/>
		<result column="dept_name" property="departmentName"/>
		<!-- 
			collection定义关联集合类型的属性的封装规则 
			ofType:指定集合里面元素的类型
		-->
		<collection property="emps" ofType="com.atguigu.mybatis.bean.Employee">
			<!-- 定义这个集合中元素的封装规则 -->
			<id column="eid" property="id"/>
			<result column="last_name" property="lastName"/>
			<result column="email" property="email"/>
			<result column="gender" property="gender"/>
		</collection>
</resultMap>

<select id="getDeptByIdPlus" resultMap="MyDept">
		SELECT d.id did,d.dept_name dept_name,
				e.id eid,e.last_name last_name,e.email email,e.gender gender
		FROM tbl_dept d
		LEFT JOIN tbl_employee e
		ON d.id=e.d_id
		WHERE d.id=#{id}
</select>

方式二:分步查询

接口:

public Department getDeptByIdStep(Integer id);

mapper.xml:

<!-- collection:分段查询 -->
<resultMap type="com.atguigu.mybatis.bean.Department" id="MyDeptStep">
		<id column="id" property="id"/>
		<id column="dept_name" property="departmentName"/>
		<collection property="emps" 
			select="com.atguigu.mybatis.dao.EmployeeMapperPlus.getEmpsByDeptId"
			column="{id}" fetchType="lazy">
		</collection>
</resultMap>

<select id="getDeptByIdStep" resultMap="MyDeptStep">
		select id,dept_name from tbl_dept where id=#{id}
</select>

扩展:多列的值传递过去,将多列的值封装map传递,如下:

column="{key1=column1,key2=column2}"
即:
<collection property="emps" 
	select="com.atguigu.mybatis.dao.EmployeeMapperPlus.getEmpsByDeptId"
	column="{deptId=id}" fetchType="lazy">
</collection>

fetchType=“lazy”:表示使用延迟加载,即懒加载;

  • lazy:延迟
  • eager:立即

discriminator鉴别器
mybatis可以使用discriminator判断某列的值,然后根据该列的值改变封装行为

场景:如果查出的员工是女生:就把部门信息查询出来,否则不查询部门信息;如果是男生,把last_name这一列的值赋值给email;

mapper.xml:

<resultMap type="com.atguigu.mybatis.bean.Employee" id="MyEmpDis">
	 	<id column="id" property="id"/>
	 	<result column="last_name" property="lastName"/>
	 	<result column="email" property="email"/>
	 	<result column="gender" property="gender"/>
	 	<!--
	 		column:指定判定的列名
	 		javaType:列值对应的java类型  -->
	 	<discriminator javaType="string" column="gender">
	 		<!--女生  resultType:指定封装的结果类型;不能缺少。/resultMap-->
	 		<case value="0" resultType="com.atguigu.mybatis.bean.Employee">
	 			<association property="dept" 
			 		select="com.atguigu.mybatis.dao.DepartmentMapper.getDeptById"
			 		column="d_id">
		 		</association>
	 		</case>
	 		<!--男生 ;如果是男生,把last_name这一列的值赋值给email; -->
	 		<case value="1" resultType="com.atguigu.mybatis.bean.Employee">
		 		<id column="id" property="id"/>
			 	<result column="last_name" property="lastName"/>
			 	<result column="last_name" property="email"/>
			 	<result column="gender" property="gender"/>
	 		</case>
	 	</discriminator>
</resultMap>

四、动态sql

4.1 MyBatis之if标签

场景:当查询条件有多个字段时,则sql里就将那些不为空的字段作为查询条件
接口:

public List<Employee> getEmpsByConditionIf(Employee employee);

mapper.xml:

<select id="getEmpsByConditionIf" resultType="com.atguigu.mybatis.bean.Employee">
	 	select * from tbl_employee
	 	<!-- where -->
	 	<where>
		 	<!-- test:判断表达式(OGNL)
		 	OGNL参照官方文档。
		 	  	 c:if  test
		 	从参数中取值进行判断
		 	
		 	遇见特殊符号应该去写转义字符:
		 	&quot;&quot:指的是'',即空字符;
		 	&amp;&amp:指的是&&,即条件与;
		 	-->
		 	<if test="id!=null">
		 		id=#{id}
		 	</if>
		 	<if test="lastName!=null &amp;&amp; lastName!=&quot;&quot;">
		 		and last_name like #{lastName}
		 	</if>
		 	<if test="email!=null and email.trim()!=&quot;&quot;">
		 		and email=#{email}
		 	</if> 
		 	<!-- ognl会进行字符串与数字的转换判断  "0"==0 -->
		 	<if test="gender==0 or gender==1">
		 	 	and gender=#{gender}
		 	</if>
	 	</where>
</select>

4.2 MyBatis之where标签

4.1章节中,已经使用了where条件,mybatis会将where标签中拼装的sql,多出来的and或者or去掉,where只会去掉第一个多出来的and或者or,即and或or只能放在if标签里的最前边。

4.3 MyBatis之trim标签

trim标签可以自定义字符串的截取规则
接口:

public List<Employee> getEmpsByConditionTrim(Employee employee);

mapper.xml:

<select id="getEmpsByConditionTrim" resultType="com.atguigu.mybatis.bean.Employee">
	 	select * from tbl_employee
	 	<!-- 后面多出的and或者or where标签不能解决 
	 	prefix="":前缀:trim标签体中是整个字符串拼串 后的结果。
	 			prefix给拼串后的整个字符串加一个前缀 
	 	prefixOverrides="":
	 			前缀覆盖: 去掉整个字符串前面多余的字符
	 	suffix="":后缀
	 			suffix给拼串后的整个字符串加一个后缀 
	 	suffixOverrides=""
	 			后缀覆盖:去掉整个字符串后面多余的字符
	 			
	 	-->
	 	<!-- 自定义字符串的截取规则 -->
	 	<trim prefix="where" suffixOverrides="and">
	 		<if test="id!=null">
		 		id=#{id} and
		 	</if>
		 	<if test="lastName!=null &amp;&amp; lastName!=&quot;&quot;">
		 		last_name like #{lastName} and
		 	</if>
		 	<if test="email!=null and email.trim()!=&quot;&quot;">
		 		email=#{email} and
		 	</if> 
		 	<!-- ognl会进行字符串与数字的转换判断  "0"==0 -->
		 	<if test="gender==0 or gender==1">
		 	 	gender=#{gender}
		 	</if>
	   </trim>
</select>

4.4 MyBatis之choose(when, otherwise)标签

choose标签,即分支选择,相当于java里带了break的swtich-case;

场景:
如果id不为空,则使用id作为查询条件,如果lastName不为空,则使用lastName作为查询条件,即最后只使用其中一种作为条件;

接口:

public List<Employee> getEmpsByConditionChoose(Employee employee);

mapper.xml:

<select id="getEmpsByConditionChoose" resultType="com.atguigu.mybatis.bean.Employee">
	 	select * from tbl_employee 
	 	<where>
	 		<!-- 如果带了id就用id查,如果带了lastName就用lastName查;只会进入其中一个 -->
	 		<choose>
	 			<when test="id!=null">
	 				id=#{id}
	 			</when>
	 			<when test="lastName!=null">
	 				last_name like #{lastName}
	 			</when>
	 			<when test="email!=null">
	 				email = #{email}
	 			</when>
	 			<otherwise>
	 				gender = 0
	 			</otherwise>
	 		</choose>
	 	</where>
</select>

4.5 MyBatis之set标签

where是用来封装查询条件的, 而set是用来封装修改条件的;

接口:

public void updateEmp(Employee employee);

mapper.xml:

<update id="updateEmp">
	 	<!-- Set标签的使用 -->
	 	update tbl_employee 
		<set>
			<if test="lastName!=null">
				last_name=#{lastName},
			</if>
			<if test="email!=null">
				email=#{email},
			</if>
			<if test="gender!=null">
				gender=#{gender}
			</if>
		</set>
		where id=#{id} 
<!-- 		
		Trim:更新拼串
		update tbl_employee 
		<trim prefix="set" suffixOverrides=",">
			<if test="lastName!=null">
				last_name=#{lastName},
			</if>
			<if test="email!=null">
				email=#{email},
			</if>
			<if test="gender!=null">
				gender=#{gender}
			</if>
		</trim>
		where id=#{id}  -->
</update>

4.6 MyBatis之foreach标签

foreach标签用来遍历集合

4.6.1、foreach遍历查询

接口:

public List<Employee> getEmpsByConditionForeach(@Param("ids")List<Integer> ids);

mapper.xml:

<select id="getEmpsByConditionForeach" resultType="com.atguigu.mybatis.bean.Employee">
	 	select * from tbl_employee
	 	<!--
	 		collection:指定要遍历的集合:
	 			list类型的参数会特殊处理封装在map中,map的key就叫list
	 		item:将当前遍历出的元素赋值给指定的变量
	 		separator:每个元素之间的分隔符
	 		open:遍历出所有结果拼接一个开始的字符
	 		close:遍历出所有结果拼接一个结束的字符
	 		index:索引。遍历list的时候index就是索引,item就是当前值
	 			  遍历map的时候index表示的就是map的key,item就是map的值
	 		
	 		#{变量名}就能取出变量的值也就是当前遍历出的元素
	 	  -->
	 	<foreach collection="ids" item="item_id" separator=","
	 		open="where id in(" close=")">
	 		#{item_id}
	 	</foreach>
</select>

4.6.2、foreach遍历插入

接口:

public void addEmps(@Param("emps")List<Employee> emps);

场景一:MySql数据库
mapper.xml;

<!--MySQL下批量保存:可以foreach遍历   mysql支持values(),(),()语法-->
<insert id="addEmps">
	 	insert into tbl_employee(
	 		last_name,email,gender,d_id
	 	) 
		values
		<foreach collection="emps" item="emp" separator=",">
			(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
		</foreach>
</insert>

mapper.xml也可以这样写(执行多条插入语句):

<!--这种方式需要数据库连接属性allowMultiQueries=true;
	这种分号分隔多个sql可以用于其他的批量操作(删除,修改)-->
<insert id="addEmps">
	 	<foreach collection="emps" item="emp" separator=";">
	 		insert into tbl_employee(last_name,email,gender,d_id)
	 		values(#{emp.lastName},#{emp.email},#{emp.gender},#{emp.dept.id})
	 	</foreach>
</insert>

在properties里配置allowMultiQueries=true:

jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true

场景二:Oracle数据库

方式一:begin-end方式
即在oracle直接执行的语句就是:

begin
	insert into employees(employee_id,last_name,email) 
			    values(employees_seq.nextval,'test_001','[email protected]');
	insert into employees(employee_id,last_name,email) 
			    values(employees_seq.nextval,'test_002','[email protected]');
end;

mapper.xml:

<insert id="addEmps" databaseId="oracle">
	<foreach collection="emps" item="emp" open="begin" close="end;">
		 		insert into employees(employee_id,last_name,email) 
				    values(employees_seq.nextval,#{emp.lastName},#{emp.email});
	</foreach>
</insert>

方式二:利用中间表
即在oracle直接执行的语句就是:

insert into employees(employee_id,last_name,email)
	select employees_seq.nextval,lastName,email from(
		   select 'test_a_01' lastName,'test_a_e01' email from dual
		   union
		   select 'test_a_02' lastName,'test_a_e02' email from dual
		   union
		   select 'test_a_03' lastName,'test_a_e03' email from dual
     )	

mapper.xml:

<insert id="addEmps" databaseId="oracle">
	 	<!-- oracle第二种批量方式  -->
	 	insert into employees(
	 		employee_id,last_name,email
	 	) 
	 	select employees_seq.nextval,lastName,email from
	    (
			<foreach collection="emps" item="emp" separator="union">
				 	select #{emp.lastName} lastName,#{emp.email} email from dual
			</foreach>
	    )
</insert>

上边mapper.xml优化:

<insert id="addEmps" databaseId="oracle">
    insert into employees(
	 		employee_id,last_name,email
	 	) 
	<foreach collection="emps" item="emp" separator="union"
		 				open="select employees_seq.nextval,lastName,email from("
		 				close=")">
		 				select #{emp.lastName} lastName,#{emp.email} email from dual
		 			</foreach>
</insert>

4.7 两个内置参数

不只是方法传递过来的参数可以被用来判断、取值,mybatis默认还有两个内置参数

参数一 :_parameter
_parameter代表整个参数
接口传递单个参数:_parameter就是这个参数
接口传递多个参数:参数会被封装为一个map,_parameter就是代表这个map

参数二 :_databaseId
如果全局配置文件配置了databaseIdProvider标签,则_databaseId就是代表当前数据库的别名,如oracle、mysql等
全局配置文件:

<databaseIdProvider type="DB_VENDOR">
	<!-- 为不同的数据库厂商起别名 -->
	<property name="MySQL" value="mysql"/>
	<property name="Oracle" value="oracle"/>
	<property name="SQL Server" value="sqlserver"/>
</databaseIdProvider>

接口:

public List<Employee> getEmpsTestInnerParameter(Employee employee);

mapper.xml:

<select id="getEmpsTestInnerParameter" resultType="com.atguigu.mybatis.bean.Employee">
	  		<if test="_databaseId=='mysql'">
	  			select * from tbl_employee
	  			<if test="_parameter!=null">
	  				where last_name like #{lastName}
	  			</if>
	  		</if>
	  		<if test="_databaseId=='oracle'">
	  			select * from employees
	  			<if test="_parameter!=null">
	  				where last_name like #{_parameter.lastName}
	  			</if>
	  		</if>
</select>

bind属性
可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值

<select id="getEmpsTestInnerParameter" resultType="com.atguigu.mybatis.bean.Employee">
	  	<!-- bind:将lastName的值取到后,拼接上模糊查询%,赋值给_lastName-->
	  		<bind name="_lastName" value="'%'+lastName+'%'"/>
	  		<if test="_databaseId=='mysql'">
	  			select * from tbl_employee
	  			<if test="_parameter!=null">
	  				where last_name like #{_lastName}
	  			</if>
	  		</if>
</select>

4.8 MyBatis之sql标签与include标签

sql标签抽取可重用的sql片段,方便后面引用(使用include引用)

<sql id="insertColumn">
	  employee_id,last_name,email
</sql>

引用上边的标签

<insert id="addEmps" databaseId="oracle">
	 	insert into employees(
	 		<!-- 引用外部定义的sql -->
	 		<include refid="insertColumn"></include>
	 	)
	 	<foreach collection="emps" item="emp" separator="union"
	 			open="select employees_seq.nextval,lastName,email from("
	 			close=")">
	 		select #{emp.lastName} lastName,#{emp.email} email from dual
	 	</foreach>
</insert>

动态判断

<sql id="insertColumn">
  		<if test="_databaseId=='oracle'">
  			employee_id,last_name,email
  		</if>
  		<if test="_databaseId=='mysql'">
  			last_name,email,gender,d_id
  		</if>
</sql>

include还可以自定义一些property,sql标签内部就能使用自定义的属性
include-property:取值的正确方式${prop},#{不能使用这种方式}

<insert id="addEmps" databaseId="oracle">
	 	insert into employees(
	 		<!-- 引用外部定义的sql -->
	 		<include refid="insertColumn">
	 			<property name="testColomn" value="abc"/>
	 		</include>
	 	 )
	 	<foreach collection="emps" item="emp" separator="union"
	 			open="select employees_seq.nextval,lastName,email from("
	 			close=")">
	 		select #{emp.lastName} lastName,#{emp.email} email from dual
	 	</foreach>
</insert>

五、MyBatis缓存机制

MyBatis包含非常强大的缓存机制,可以进行方便的配置和定义,极大的提高查询效率;MyBatis定义了两级缓存,默认开启一级缓存;

5.1 一级缓存

一级缓存也叫本地缓存,是sqlSession级别的缓存。一级缓存是一直开启的,无法关闭;
SqlSession级别的一个Map:即MyBatis查出数据后,会将数据放在当前SqlSession的Map里;
与数据库同一次会话期间查询到的数据会放在本地缓存中。
以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库;

一级缓存失效情况(没有使用到当前一级缓存的情况,效果就是,还需要再向数据库发出查询):

  1. sqlSession不同。
  2. sqlSession相同,查询条件不同.(当前一级缓存中还没有这个数据)。
  3. sqlSession相同,两次查询之间执行了增删改操作(这次增删改可能对当前数据有影响)。
  4. sqlSession相同,手动清除了一级缓存(缓存清空:openSession.clearCache())。

5.2 二级缓存

二级缓存是全局缓存,基于namespace级别的缓存,即一个namespace对应一个二级缓存;
工作机制:
1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;
2、如果会话关闭,一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以查找二级缓存中的内容;
3、假如sqlSession里有两大块内容:EmployeeMapper(Employee)与DepartmentMapper(Department),则二级缓存会根据不同namespace的值将这两部门内容放在不同的Map里存储起来;不同namespace查出的数据会放在自己对应的缓存中(map);
效果:数据会从二级缓存中获取,查出的数据都会被默认先放在一级缓存中。

注意:
只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中

开启二级缓存的步骤:

  1. 开启全局二级缓存配置(默认已开启):
    如果设置为false的话,关闭的是二级缓存,不是一级缓存,一级缓存无法关闭;
<setting name="cacheEnabled" value="true"/>
  1. 去要开启二级缓存的mapper.xml中配置使用二级缓存:
	<cache></cache>

在这里插入图片描述
也可以配置缓存的回收策略,即缓存太多后,删除哪些缓存

<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>
<!-- <cache eviction="FIFO" flushInterval="60000" readOnly="false" size="1024"></cache> -->
	<!--  
	eviction:缓存的回收策略:
		• LRU(默认策略) – 最近最少使用的:移除最长时间不被使用的对象。
		• FIFO – 先进先出:按对象进入缓存的顺序来移除它们。
		• SOFT – 软引用:移除基于垃圾回收器状态和软引用规则的对象。
		• WEAK – 弱引用:更积极地移除基于垃圾收集器状态和弱引用规则的对象。
		• 默认的是 LRU。
	flushInterval:缓存刷新间隔
		缓存多长时间清空一次,默认不清空,设置一个毫秒值
	readOnly:是否只读:
		true:只读;mybatis认为所有从缓存中获取数据的操作都是只读操作,不会修改数据。
				 mybatis为了加快获取速度,直接就会将数据在缓存中的引用交给用户。不安全,速度快
		false:非只读:mybatis觉得获取的数据可能会被修改。
				mybatis会利用序列化&反序列的技术克隆一份新的数据给你。安全,速度慢
	size:缓存存放多少元素;
	type="":指定自定义缓存的全类名;
			缓存类实现Cache接口即可;
	-->
  1. 我们的POJO需要实现序列化接口

关于缓存的设置/属性讲解

  1. cacheEnabled=false:false关闭的是二级缓存,没有关闭一级缓存,一级缓存一直可用
  2. 每个select标签都有一个属性useCache=“true”:
    设置为false:不使用二级缓存,一级缓存依然使用
  3. 每个增删改标签的:flushCache=“true”:(一级二级都会清除)
    增删改执行完成后就会清楚缓存;
    测试:flushCache=“true”:一级缓存与二级都会被清除;
    查询标签:flushCache=“false”:
    如果flushCache=true;每次查询之后都会清空缓存,缓存是没有被使用的;
  4. sqlSession.clearCache()方法:只是清除当前session的一级缓存;
  5. localCacheScope:本地缓存作用域,有以下两种取值:SESSION与STATEMENT;默认是SESSION,SESSION是一级缓存,将当前会话的所有数据保存在会话缓存中;STATEMENT:可以禁用一级缓存;

MyBatis查询数据的顺序:
先找二级缓存(因为二级缓存范围广),再找一级缓存,都没有的话再去数据库查数据;

六、MyBatis与Spring(以及SpringMvc)整合

6.1 介绍

mybatis官网:https://github.com/mybatis
MyBatis整合Spring需要的jar包
在这里插入图片描述
整合文档
在这里插入图片描述
各个版本对应表格
在这里插入图片描述
整合包下载:
在这里插入图片描述

6.2 SSM整合

导入需要的jar包
Spring与SpringMvc需要的jar包
在这里插入图片描述
MyBatis需要的jar包:
在这里插入图片描述
Spring与MyBatis整合的jar包
在这里插入图片描述
数据库驱动需要的jar包
在这里插入图片描述
编写配置文件
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>
	<settings>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
		<setting name="jdbcTypeForNull" value="NULL"/>
		
		<!--显式的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题  -->
		<setting name="cacheEnabled" value="true"/>
		<setting name="lazyLoadingEnabled" value="true"/>
		<setting name="aggressiveLazyLoading" value="false"/>
	</settings>
	
	<databaseIdProvider type="DB_VENDOR">
		<property name="MySQL" value="mysql"/>
		<property name="Oracle" value="oracle"/>
		<property name="SQL Server" value="sqlserver"/>
	</databaseIdProvider>
</configuration>

数据库配置文件dbconfig.properties

jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true
jdbc.username=root
jdbc.password=123456

orcl.driver=oracle.jdbc.OracleDriver
orcl.url=jdbc:oracle:thin:@localhost:1521:orcl
orcl.username=scott
orcl.password=123456

MyBatis接口:

package com.maltose.mybatis.dao;

import java.util.List;
import com.atguigu.mybatis.bean.Employee;

public interface EmployeeMapper {
	
	public Employee getEmpById(Integer id);
	
	public List<Employee> getEmps();
}

MyBatis的mapper.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="com.atguigu.mybatis.dao.EmployeeMapper">

	<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee">
		select * from tbl_employee where id=#{id}
	</select>
	
	<select id="getEmps" resultType="com.atguigu.mybatis.bean.Employee">
		select * from tbl_employee
	</select>
</mapper>

配置web.xml,使得SpringMvc的IOC容器跟随项目一起启动

<context-param>
	<param-name>contextConfigLocation</param-name>
	<param-value>classpath:applicationContext.xml</param-value>
</context-param>

<!-- Bootstraps the root web application context before servlet initialization -->
<listener>
	<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

配置web.xml,添加SpringMvc的配置文件

<servlet>
		<servlet-name>spring</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<load-on-startup>1</load-on-startup>
</servlet>

	<!-- Map all requests to the DispatcherServlet for handling -->
<servlet-mapping>
		<servlet-name>spring</servlet-name>
		<url-pattern>/</url-pattern>
</servlet-mapping>

spring配置文件applicationContext.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mybatis-spring="http://mybatis.org/schema/mybatis-spring"
	xmlns:tx="http://www.springframework.org/schema/tx"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring-1.2.xsd
		http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<!-- Spring希望管理所有的业务逻辑组件,等。。。 -->
	<context:component-scan base-package="com.maltose.mybatis">
	    <!--除了Controller控制器不扫描以外,其他都扫描-->
		<context:exclude-filter type="annotation"
			expression="org.springframework.stereotype.Controller" />
	</context:component-scan>

	<!-- 引入数据库的配置文件 -->
	<context:property-placeholder location="classpath:dbconfig.properties" />
	<!-- Spring用来控制业务逻辑。数据源、事务控制、aop -->
	<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
		<property name="jdbcUrl" value="${jdbc.url}"></property>
		<property name="driverClass" value="${jdbc.driver}"></property>
		<property name="user" value="${jdbc.username}"></property>
		<property name="password" value="${jdbc.password}"></property>
	</bean>
	<!-- spring事务管理 -->
	<bean id="dataSourceTransactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
		<property name="dataSource" ref="dataSource"></property>
	</bean>

	<!-- 开启基于注解的事务 -->
	<tx:annotation-driven transaction-manager="dataSourceTransactionManager"/>
	
	<!-- 
	整合mybatis 
		目的:1、spring管理所有组件,包含mapper的实现类。
				service==>Dao   @Autowired:自动注入mapper,不需要再自己创建openSession了;
			 2、spring用来管理事务,spring声明式事务
	-->
	<!--创建出SqlSessionFactory对象  -->
	<bean id="sqlSessionFactoryBean" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource"></property>
		<!-- configLocation指定全局配置文件的位置 -->
		<property name="configLocation" value="classpath:mybatis-config.xml"></property>
		<!--mapperLocations: 指定mapper文件的位置-->
		<property name="mapperLocations" value="classpath:mybatis/mapper/*.xml"></property>
	</bean>
	
	<!--配置一个可以进行批量执行的sqlSession  -->
	<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
		<constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg>
		<constructor-arg name="executorType" value="BATCH"></constructor-arg>
	</bean>
	
	<!-- 扫描所有的mapper接口的实现,让这些mapper能够自动注入;
	base-package:指定mapper接口的包名
	 -->
	<mybatis-spring:scan base-package="com.atguigu.mybatis.dao"/>
	<!-- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.atguigu.mybatis.dao"></property>
	</bean> -->
	
</beans>

在web.xml同级目录创建SpringMvc的配置文件spring-servlet.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns:context="http://www.springframework.org/schema/context"
	xmlns:mvc="http://www.springframework.org/schema/mvc"
	xsi:schemaLocation="http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
		http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd">

	<!--SpringMVC只是控制网站跳转逻辑,即只管Controller  -->
	<!-- 只扫描控制器 -->
	<context:component-scan base-package="com.maltose.mybatis" use-default-filters="false">
	    <!--只扫描当前包下标了@Controller注解的类-->
		<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
	</context:component-scan>
	
	<!-- 视图解析器,解析页面地址 -->
	<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
		<property name="prefix" value="/WEB-INF/pages/"></property>
		<property name="suffix" value=".jsp"></property>
	</bean>
	
	<mvc:annotation-driven></mvc:annotation-driven>
	<mvc:default-servlet-handler/>
</beans>

6.3 MyBatis逆向工程—MBG

MyBatis逆向工程,即代码生成器,根据数据库里的表,创建出对应的javaBean、接口、mapper.xml文件

下载代码生成器

官网地址:https://github.com/mybatis
在这里插入图片描述
官方使用文档以及下载代码生成器:

在这里插入图片描述
点击下载即可:
在这里插入图片描述
使用MyBatis代码生成器

解压后将下边这个包导入到项目里
在这里插入图片描述
官方使用介绍:http://mybatis.org/generator/quickstart.html

在项目根目录下创建配置文件mbg.xml:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE generatorConfiguration
  PUBLIC "-//mybatis.org//DTD MyBatis Generator Configuration 1.0//EN"
  "http://mybatis.org/dtd/mybatis-generator-config_1_0.dtd">
<generatorConfiguration>

	<!-- 
		targetRuntime="MyBatis3Simple":生成简单版的CRUD
		MyBatis3:豪华版
	
	 -->
  <context id="DB2Tables" targetRuntime="MyBatis3Simple">
  	<!-- jdbcConnection:指定如何连接到目标数据库 -->
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
        connectionURL="jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true"
        userId="root"
        password="123456">
    </jdbcConnection>

	<!-- java类型解析器 -->
    <javaTypeResolver >
      <property name="forceBigDecimals" value="false" />
    </javaTypeResolver>

	<!-- javaModelGenerator:指定javaBean的生成策略 
	targetPackage="test.model":生成的目标javaBean包名
	targetProject="\MBGTestProject\src":目标工程
	-->
    <javaModelGenerator targetPackage="com.maltose.mybatis.bean" 
    		targetProject=".\src">
      <property name="enableSubPackages" value="true" />
      <property name="trimStrings" value="true" />
    </javaModelGenerator>

	<!-- sqlMapGenerator:sql映射生成策略: -->
    <sqlMapGenerator targetPackage="com.atguigu.mybatis.dao"  
    	targetProject=".\conf">
      <property name="enableSubPackages" value="true" />
    </sqlMapGenerator>

	<!-- javaClientGenerator:指定mapper接口所在的位置 -->
    <javaClientGenerator type="XMLMAPPER" targetPackage="com.atguigu.mybatis.dao"  
    	targetProject=".\src">
      <property name="enableSubPackages" value="true" />
    </javaClientGenerator>

	<!-- 指定要逆向分析哪些表:根据表要创建javaBean,domainObjectName只对应的javaBean -->
    <table tableName="tbl_dept" domainObjectName="Department"></table>
    <table tableName="tbl_employee" domainObjectName="Employee"></table>
  </context>
</generatorConfiguration>

新建一个测试类,加入如下方法:

package com.maltose.mybatis.test;

import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

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 org.mybatis.generator.api.MyBatisGenerator;
import org.mybatis.generator.config.Configuration;
import org.mybatis.generator.config.xml.ConfigurationParser;
import org.mybatis.generator.internal.DefaultShellCallback;

import com.atguigu.mybatis.bean.Employee;
import com.atguigu.mybatis.bean.EmployeeExample;
import com.atguigu.mybatis.bean.EmployeeExample.Criteria;
import com.atguigu.mybatis.dao.EmployeeMapper;

public class MyBatisTest {
	@Test
	public void testMbg() throws Exception {
		List<String> warnings = new ArrayList<String>();
		boolean overwrite = true;
		File configFile = new File("mbg.xml");
		ConfigurationParser cp = new ConfigurationParser(warnings);
		Configuration config = cp.parseConfiguration(configFile);
		DefaultShellCallback callback = new DefaultShellCallback(overwrite);
		MyBatisGenerator myBatisGenerator = new MyBatisGenerator(config,
				callback, warnings);
		myBatisGenerator.generate(null);
	}
}

执行完上边的方法后,就会生成相应的文件;

代码生成器生成复杂逻辑的文件
以上是生成简单的增删改查文件,其实代码生成器也支持生成复杂的逻辑,如带条件的查询等等,将配置文件里的targetRuntime="MyBatis3Simple"改为targetRuntime="MyBatis3"即可;

七、MyBatis分页插件之PageHelper

官网:https://github.com/pagehelper/Mybatis-PageHelper
在这里插入图片描述
添加依赖

<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>x.x.x</version>
</dependency>

接口:

public List<Employee> getEmps();

mapper.xml:

<select id="getEmps" resultType="com.atguigu.mybatis.bean.Employee">
	select id,last_name lastName,email,gender from tbl_employee
</select>

在MyBatis全局配置文件里配置拦截器

<plugins>
    <plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>

使用PageHelper实现分页:

EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
//获取第2页,每页显示三条数据
Page<Object> page = PageHelper.startPage(2, 3);
//执行下边的查询方法之前,执行上边的分页逻辑,得到的结果就是分页后的数据	
List<Employee> emps = mapper.getEmps();
System.out.println("当前页码:"+page.getPageNum());
System.out.println("总记录数:"+page.getTotal());
System.out.println("每页的记录数:"+page.getPageSize());
System.out.println("总页码:"+page.getPages());

使用PageInfo获取更多分页数据:

EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
Page<Object> page = PageHelper.startPage(5, 1);
List<Employee> emps = mapper.getEmps();
//第二个参数:传入要连续显示多少页
PageInfo<Employee> info = new PageInfo<>(emps, 5);

System.out.println("当前页码:"+info.getPageNum());
System.out.println("总记录数:"+info.getTotal());
System.out.println("每页的记录数:"+info.getPageSize());
System.out.println("总页码:"+info.getPages());
System.out.println("是否第一页:"+info.isIsFirstPage());

八、MyBatis批量操作—BATCH

MyBatis未与Spring整合时实现批量操作

获取openSession 时,传入BATCH参数

    @Test
	public void testBatch() throws IOException{
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		
		//可以执行批量操作的sqlSession
		SqlSession openSession = sqlSessionFactory.openSession(ExecutorType.BATCH);
		long start = System.currentTimeMillis();
		try{
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			for (int i = 0; i < 10000; i++) {
				mapper.addEmp(new Employee(UUID.randomUUID().toString().substring(0, 5), "b", "1"));
			}
			openSession.commit();
			long end = System.currentTimeMillis();
			//批量操作耗时:(预编译sql一次==>设置参数===>10000次===>执行(1次))
			//Parameters: 616c1(String), b(String), 1(String)==>4598
			//非批量操作耗时:(预编译sql=设置参数=执行)==》10000    10200
			System.out.println("执行时长:"+(end-start));
		}finally{
			openSession.close();
		}
	}

MyBatis与spring整合之后,实现批量操作

applicationContext.xml里添加批量操作的配置

<!--配置一个可以进行批量执行的sqlSession  -->
<bean id="sqlSession" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg name="sqlSessionFactory" ref="sqlSessionFactoryBean"></constructor-arg>
	<constructor-arg name="executorType" value="BATCH"></constructor-arg>
</bean>

配置完后,代码里使用:

package com.maltose.mybatis.service;

import java.util.List;

import org.apache.ibatis.session.SqlSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import com.maltose.mybatis.bean.Employee;
import com.maltose.mybatis.dao.EmployeeMapper;

@Service
public class EmployeeService {

	@Autowired
	private SqlSession sqlSession;
	
	public List<Employee> getEmps(){
		EmployeeMapper mapper = sqlSession.getMapper(EmployeeMapper.class);
		return mapper .getEmps();
	}
}

九、MyBatis操作存储过程

场景:oracle实现分页

分页javaBean

package com.maltose.mybatis.bean;

import java.util.List;

/**
 * 封装分页查询数据
 * @author sgw
 *
 */
public class OraclePage {
	
	private int start;
	private int end;
	private int count;
	private List<Employee> emps;
	
	public int getStart() {
		return start;
	}
	public void setStart(int start) {
		this.start = start;
	}
	public int getEnd() {
		return end;
	}
	public void setEnd(int end) {
		this.end = end;
	}
	public int getCount() {
		return count;
	}
	public void setCount(int count) {
		this.count = count;
	}
	public List<Employee> getEmps() {
		return emps;
	}
	public void setEmps(List<Employee> emps) {
		this.emps = emps;
	}
}

在oracle里编写一个分页的存储过程
在这里插入图片描述
MyBatis调用存储过程来完成分页

接口:

public void getPageByProcedure(OraclePage page);

mapper.xml:

<!--
	1、使用select标签定义调用存储过程
	2、statementType="CALLABLE":表示要调用存储过程
	3、{call procedure_name(params)}
-->
<select id="getPageByProcedure" statementType="CALLABLE" databaseId="oracle">
		{call hello_test(
			#{start,mode=IN,jdbcType=INTEGER},
			#{end,mode=IN,jdbcType=INTEGER},
			#{count,mode=OUT,jdbcType=INTEGER},
			#{emps,mode=OUT,jdbcType=CURSOR,javaType=ResultSet,resultMap=PageEmp}
		)}
</select>

<resultMap type="com.atguigu.mybatis.bean.Employee" id="PageEmp">
	<id column="EMPLOYEE_ID" property="id"/>
	<result column="LAST_NAME" property="email"/>
	<result column="EMAIL" property="email"/>
</resultMap>

代码调用的实现

    @Test
	public void testProcedure() throws IOException{
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		SqlSession openSession = sqlSessionFactory.openSession();
		try{
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			OraclePage page = new OraclePage();
			page.setStart(1);
			page.setEnd(5);
			mapper.getPageByProcedure(page);
			
			System.out.println("总记录数:"+page.getCount());
			System.out.println("查出的数据:"+page.getEmps().size());
			System.out.println("查出的数据:"+page.getEmps());
		}finally{
			openSession.close();
		}
     }
发布了46 篇原创文章 · 获赞 12 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/qq_33417321/article/details/103903021
今日推荐