Mybatis3

目录

mybatis简介

mybatis下载

 第一个程序(普通编程和接口式编程)

总结

全局配置

映射文件

动态sql

缓存

整合spring

MyBatis逆向工程

mybatis运行原理

插件


mybatis简介

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

mybatis下载

要使用 MyBatis, 只需将 mybatis-x.x.x.jar 文件置于类路径(classpath)中即可。另外可以下载自己需要的jar包和其他文件

百度网盘链接:https://pan.baidu.com/s/1spcoL2O-yMCC_zuhh60l_A

提取码: jgn6。里面有需要的jar包

如果使用 Maven 来构建项目,则需将下面的依赖代码置于 pom.xml 文件中:

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

 第一个程序(普通编程和接口式编程)

(1)先引入需要的jar包 ,然后全部选中右键build path->add to path,在conf文件夹下导入log4j.xml文件

 (2)创建一个类,生成属性的set,get,tostring方法

package com.dgm.mybatis.bean;

import org.apache.ibatis.type.Alias;

//@Alias("emp")
public class Employee{
	public Integer id;
	public String lastName;
	public String email;
	public 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;
	}
	public String toString() {
		return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender + "]";
	}
	
}

(3)创建一个接口类,但不写接口的实现类 

package com.dgm.mybatis.dao;
import com.dgm.mybatis.bean.Employee;
public interface EmployeeMapper {
	public Employee getEmpById(Integer id);
}

(4)配置文件(全局配置文件和sql文件)

<?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.dgm.mybatis.dao.EmployeeMapper">    
     <select id="getEmpById" resultType="employee">
         select * from tbl_employee where id = #{id}
     </select>
</mapper>
<?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>
	 	<typeAlias type="com.dgm.mybatis.bean.Employee"/>	 	
<!-- 批量起别名的情况下,使用@Alias注解为 某个类型指定新的别名<package name="com.dgm.mybatis.bean"/>-->
	 </typeAliases>
	<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>
	<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
	<mappers>
		<mapper resource="EmployeeMapper.xml" />
	</mappers>
</configuration>

 (5)创建实现类,有两种方式实现

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

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

	@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.dgm.mybatis.dao.EmployeeMapper.selectId", 1);
			System.out.println(employee);
		} finally {
			openSession.close();
		}

	}
}

接口方式 (使用)

1.创建一个接口(EmployeeMapper.java),其中定义抽象方法
2.将EmployeeMapper.xml中的namespace改为接口的全类名
3.将标签中的"id"改为与接口中定义的抽象方法相同的方法名
4.第2、3步实现了动态绑定,在标签中写方法的具体实现
5.在测试方法中不需要创建接口的实现类,mybatis会为接口自动创建一个代理对象去执行增删改查方法

package com.dgm.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.dgm.mybatis.bean.Employee;
import com.dgm.mybatis.dao.EmployeeMapper;

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

	@Test
	public void test01() throws IOException {
		// 1、获取sqlSessionFactory对象
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		// 2、获取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();
		}

	}

}

总结

1、接口式编程
 原生:        Dao        ====>  DaoImpl
mybatis:    Mapper    ====>  xxMapper.xml 
2 、SqlSession代表和数据库的一次会话;用完必须关闭;
3、SqlSession和connection一样她都是非线程安全。每次使用都应该去获取新的对象。
4、mapper接口没有实现类,但是mybatis会为这个接口生成一个代理对象。(将接口和xml进行绑定)EmployeeMapper empMapper =    sqlSession.getMapper(EmployeeMapper.class);
 5、两个重要的配置文件:
         1)mybatis的全局配置文件:包含数据库连接池信息,事务管理器信息等...系统运行环境信息
         2)sql映射文件:保存了每一个sql语句的映射信息: 将sql抽取出来。    

全局配置

引入dtd约束

在mybatis-config.xml和EmployeeMapper.xml文件的头部标签中xxx.dtd文件是约束,规定了xml文件编写规则,联网状态下会自动加载,按Alt+/会有编写提示。也可以自己手动导入(具体方式可以百度)

properties配置文件:

mybatis可以使用properties来引入外部properties配置文件的内容

(1)先创建.prorerties配置文件

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

(2)然后在全局配置文件中引入

<properties resource="dbconfig.properties"></properties>
<environments default="development">
		<environment id="development">
			<transactionManager type="JDBC" />
			<dataSource type="POOLED">
				<property name="driver" value="${jdbc.driver}" />
				<property name="url" value="${jdbc.url}" />
				<property name="username" value="${jdbc.username}" />
				<property name="password" value="${jdbc.password}" />
			</dataSource>
		</environment>
	</environments>

setting运行文件:setting包含很多重要的设置项, seting用来设置每一个设置项( name设置项名, value设置项取值)

在全局配置文件引入settings(这里举例驼峰命名法,当属性lastname与数据库名称last_name不一样的时候不会报错)

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

typeAliases别名

 typeAliases:别名处理器,可以为我们的java类型起别名,别名不区分大小写
 typeAlias为某个java类型起别名,type指定要起别名的类型的全类名;默认别名就是类名小写, alias指定新的别名

<typeAliases>
	 	<typeAlias type="com.dgm.mybatis.bean.Employee" alias=""/>
</typeAliases>

package为某个包下的所有类批量起别名,name指定包名(为当前包及以下面所有的后代包的每一个类都起一个默认别名(类名小写))。批量起别名的情况下,使用@Alias注解为 某个类型指定新的别名

<typeAliases>
         <package name="com.dgm.mybatis.bean"/>
</typeAliases>

注意:不能和Java类型的已经存在的别名重复

enviroments运行环境

(1)enviroments运行环境

  • MyBatis可以配置多种环境,比如开发、测试和生 产环境需要有不同的配置。
  • •每种环境使用一个environment标签进行配置并指 定唯一标识符
  •  可以通过environments标签中的default属性指定 一个环境的标识符来快速的切换环境

(2)environment-指定具体环境

  • id:指定当前环境的唯一标识
  •  transactionManager:事务管理器(事务管理器的类型:JDBC,MANAGED)还可以自定义事务管理器(实现TransactionFactory接口,type指定为全类名)
  • dataSource:数据源类型(UNPOOLED,POOLED,JNDI)。还可以自定义数据源(实现DataSourceFactory接口,type是全类名)

databaseIdProvider多数据库支持

databaseIdProvider支持多数据库厂商,type=“DB_VENDOR”,VendorDatabaseIdProvider作用就是得到数据库厂商标(驱动getDatabaseProductName())识来执行不同的sql语句

先在全局配置文件中导入databaseIdProvider

<databaseIdProvider type="DB_VENDOR">
	 	<property name="Oracle" value="oracle"/>
	 	<property name="MySQL" value="mysql"/>
</databaseIdProvider>

 然后在sql配置文件中加入databaseId=""

<select id="getEmpById" resultType="employee" databaseId="mysql">
 		select * from tbl_employee where id = #{id}
</select>

mappers(sql映射注册到全局配置中)

mapper:注册一个sql映射

resource:引用类路径下的sql映射文件

url:引用网络路径或者磁盘路径下的sql映射文件(file:///var/mappers/AuthorMapper.xml)

<mappers>
		<mapper resource="mybatis/mapper/EmployeeMapper.xml />
</mappers>

class:引用(注册)接口

(1)有sql映射文件,映射文件必须和接口同名,并且放在与接口同一目录下

<mappers>
		<mapper class="com.dgm.mybatis.dao.EmployeeMapper" />
</mappers>

(2)没有sql映射文件,所有的sql都是利用注解写在接口上(不推荐使用)

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

package批量注册(映射文件必须和接口同名,并且放在与接口同一目录下)

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

 

映射文件

映射文件指导着MyBatis如何进行数据库增删改查, 有着非常重要的意义;

  • cache –命名空间的二级缓存配置
  • cache-ref – 其他命名空间缓存配置的引用。
  • resultMap – 自定义结果集映射
  • parameterMap – 已废弃!老式风格的参数映射
  • sql –抽取可重用语句块。
  • insert – 映射插入语句
  • update – 映射更新语句
  • delete – 映射删除语句
  • select – 映射查询语句

增删改查

(1)在接口添加增删改查方法 

 (2)在sql映射文件添加sql语句

(3)测试增删改查方法(这里测试添加方法,其他方法类似)

@Test
	public void test02() throws IOException {
		// 1、获取sqlSessionFactory对象
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		// 2、获取sqlSession对象
        //sqlSessionFactory.openSession()不会自动提交数据,需要手动提交
        //sqlSessionFactory.openSession(ture)会自动提交数据
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			// 3、获取接口的实现类对象
			//会为接口自动的创建一个代理对象,代理对象去执行增删改查方法
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			Employee emlpoyee = new Employee(null,"zs","zs@qq.com","1");
			mapper.addEmp(emlpoyee);
			//手动提交数据
			openSession.commit();
		} finally {
			openSession.close();
		}

(4)insert、update、delete元素

insert获取自增主键的值:

mysql支持自增主键,利用statement.getGenreatedKeys()获取自增主键值

主键生成方式

(1)若数据库支持自动生成主键的字段(比如 MySQL 和 SQL Server),则可以设置 useGeneratedKeys=”true”,然后再把 keyProperty 设置到目标属性上 (把主键值交给""里面的属性)

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

(2)而对于不支持自增型主键的数据库(例如 Oracle),则可以使用 selectKey 子元素: selectKey 元素将会首先运行,id 会被设置,然后插入语句会被调用

<!-- 
	获取非自增主键的值:
		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>

参数处理

(1)单个参数,多个参数,命名参数

  • 单个参数(#{  }) ----------可以接受基本类型,对象类型,集合类型的值。这种情况 MyBatis可直接使用这个参数,不需要经过任何处理。
  • 多个参数------------任意多个参数,都会被MyBatis重新包装成一个Map传入。 Map的key是param1,param2,3,4…(或者参数的索引也可以),值就是参数的值。

 

  •  命名参数 –------------为参数使用@Param起一个名字,MyBatis就会将这些参数封 装进map中,key就是我们自己指定的名字(#{指定的key}取出对应的参数值)
//在接口里面给参数使用注解
public Employee getEmpByIdAndLastName(@Param("id")Integer id,@Param("lastName")String lastName);
	

(2) POJO,Map,To

  • POJO ----------- 当这些参数属于我们业务POJO时,我们直接传递POJO 
  • Map -------------我们也可以封装多个参数为map,直接传递,如果多个参数不是业务模型中的数据,没有对应的pojo,不经常使用,为了方便,我们也可以传入map
        #{key}:取出map中对应的值
  • 如果多个参数不是业务模型中的数据,但是经常要使用,推荐来编写一个TO(Transfer Object)数据传输对象Page{int index; int size;}
//在接口的方法里面使用map
public Employee getEmpByMap(Map<String, Object> map);
@Test
	public void test04() throws IOException{
		
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		//1、获取到的SqlSession不会自动提交数据
		SqlSession openSession = sqlSessionFactory.openSession();
		
		try{
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			Map<String, Object> map = new HashMap<>();
			map.put("id", 2);
			map.put("lastName", "Tom");
			map.put("tableName", "tbl_employee");
			Employee employee = mapper.getEmpByMap(map);
			
			System.out.println(employee);
			
		}finally{
			openSession.close();
		}
	}

总结

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

    取值:id==>#{id/param1}   lastName==>#{param2}

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

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

##特别注意:如果是Collection(List、Set)类型或者是数组, 也会特殊处理。也是把传入的list或者数组封装在map中。key:Collection(collection),如果是List还可以使用这个key(list), 数组(array)

  • public Employee getEmpById(List<Integer> ids);

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

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

    取值:id==>#{key1}   lastName==>#{key2}

(3)#与$取值的区别

#{}:是以预编译的形式,将参数设置到sql语句中;PreparedStatement;防止sql注入

${}:取出的值直接拼装在sql语句中会有安全问题;大多情况下,我们去参数的值都应该使用#{}

原生jdbc不支持占位符的地方我们就可以使用${}进行取值。比如分表、排序........;按照年份分表拆分
            select * from ${year}_salary where xxx;
            select * from tbl_employee order by ${f_name} ${order}

(4)#取值时指定的相关规则

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

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

 由于全局配置中:jdbcTypeForNull=OTHER;oracle不支持;两种办法
        1、#{email,jdbcType=OTHER};
        2、jdbcTypeForNull=NULL
            <setting name="jdbcTypeForNull" value="NULL"/>

select(选择)

<!-- public List<Employee> getEmpsByLastNameLike(String lastName); -->
	<!--resultType:如果返回的是一个集合,要写集合中元素的类型  -->
	<select id="getEmpsByLastNameLike" resultType="com.dgm.mybatis.bean.Employee">
		select * from tbl_employee where last_name like #{lastName}
	</select>
@Test
	public void test04() throws IOException{
		
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		//1、获取到的SqlSession不会自动提交数据
		SqlSession openSession = sqlSessionFactory.openSession();
		try{
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			
			List<Employee> like = mapper.getEmpsByLastNameLike("%e%");
			for (Employee employee : like) {
				System.out.println(employee);
			}
			
		}finally{
			openSession.close();
		}
	}

(2)记录封装map

public interface EmployeeMapper {
	@MapKey("id")把id作为key
	public Map<String, Employee> getEmpByLastNameLikeReturnMap(String lastName);
	
	//返回一条记录的map;key就是列名,值就是对应的值
	public Map<String, Object> getEmpByIdReturnMap(Integer id);
	
}
<!--public Map<Integer, Employee> getEmpByLastNameLikeReturnMap(String lastName);  -->
 	<select id="getEmpByLastNameLikeReturnMap" resultType="com.dgm.mybatis.bean.Employee">
 		select * from tbl_employee where last_name like #{lastName}
 	</select>
 
 	<!--public Map<String, Object> getEmpByIdReturnMap(Integer id);  -->
 	<select id="getEmpByIdReturnMap" resultType="map">
 		select * from tbl_employee where id=#{id}
 	</select>
@Test
	public void test04() throws IOException{
		
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		//1、获取到的SqlSession不会自动提交数据
		SqlSession openSession = sqlSessionFactory.openSession();
		
		try{
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			Map<String, Object> map = mapper.getEmpByIdReturnMap(1);
			System.out.println(map);
			/*Map<String, Employee> map = mapper.getEmpByLastNameLikeReturnMap("%r%");
			System.out.println(map);
            */
			
		}finally{
			openSession.close();
		}
	}

(3)resultMap:外部resultMap的命名引用,和resultType属性不能同时使用

  • 自定义结果定义规则
package com.dgm.mybatis.dao;

import java.util.List;

import com.atguigu.mybatis.bean.Employee;

public interface EmployeeMapperPlus {
	
	public Employee getEmpById(Integer id);
	
	public Employee getEmpAndDept(Integer id);
	
	public Employee getEmpByIdStep(Integer id);
	
	public List<Employee> getEmpsByDeptId(Integer deptId);

}

 在EmployeeMapperPlus.xml里面配置sql映射文件

<resultMap type="com.dgm.mybatis.bean.Employee" id="MySimpleEmp">
		<!--指定主键列的封装规则
		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>
	
	<!-- resultMap:自定义结果集映射规则;  -->
	<!-- public Employee getEmpById(Integer id); -->
	<select id="getEmpById"  resultMap="MySimpleEmp">
		select * from tbl_employee where id=#{id}
	</select>
  • 关联查询,环境搭建,级联属性封装结果

 

package com.dgm.mybatis.dao;
import com.dgm.mybatis.bean.Department;
public interface DepartmentMapper {	
	public Department getDeptById(Integer id);	
	public Department getDeptByIdPlus(Integer id);
	public Department getDeptByIdStep(Integer id);
}


package com.dgm.mybatis.bean;
import java.util.List;
public class Department {	
	private Integer id;
	private String departmentName;
	private List<Employee> emps;
	public List<Employee> getEmps() {
		return emps;
	}
	public void setEmps(List<Employee> emps) {
		this.emps = emps;
	}
	public Integer getId() {
		return id;
	}
	public void setId(Integer id) {
		this.id = id;
	}
	public String getDepartmentName() {
		return departmentName;
	}
	public void setDepartmentName(String departmentName) {
		this.departmentName = departmentName;
	}
	@Override
	public String toString() {
		return "Department [id=" + id + ", departmentName=" + departmentName
				+ "]";
	}	
}


package com.dgm.mybatis.bean;

import org.apache.ibatis.type.Alias;

@Alias("emp")
public class Employee {
	private Integer id;
	private String lastName;
	private String email;
	private String gender;
	private Department dept;	
	public Employee() {
		super();
	}	
	public Employee(Integer id, String lastName, String email, String gender) {
		super();
		this.id = id;
		this.lastName = lastName;
		this.email = email;
		this.gender = gender;
	}
	public Department getDept() {
		return dept;
	}
	public void setDept(Department dept) {
		this.dept = dept;
	}
	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 + "]";
	}
}

 给员工表tbl_employee取别名e,给公司表tbl_dept取别名d,在EmployeeMapperPlus.xml里面配置sql映射

<resultMap type="com.dgm.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>
<!--  public Employee getEmpAndDept(Integer id);-->
<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定义关联对象封装规则
<!-- 
		使用association定义关联的单个对象的封装规则;
	 -->
	<resultMap type="com.dg,.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.dgm.mybatis.bean.Department">
			<id column="did" property="id"/>
			<result column="dept_name" property="departmentName"/>
		</association>
	</resultMap>
	<!--  public Employee getEmpAndDept(Integer id);-->
	<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分布查询

DepartmentMapper.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.dgm.mybatis.dao.DepartmentMapper">
	<!--public Department getDeptById(Integer id);  -->
	<select id="getDeptById" resultType="com.dgm.mybatis.bean.Department">
		select id,dept_name from tbl_dept where id=#{id}
	</select>
</mapper>

 EmployeeMapperPlus.xml

<!-- 使用association进行分步查询:
		1、先按照员工id查询员工信息
		2、根据查询员工信息中的d_id值去部门表查出部门信息
		3、部门设置到员工中;
	 -->
	 
	 <!--  id  last_name  email   gender    d_id   -->
	 <resultMap type="com.dgm.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指定的属性
	 	 -->
 		<association property="dept" 
	 		select="com.dgm.mybatis.dao.DepartmentMapper.getDeptById" column="d_id">
 		</association>
	 </resultMap>
	 <!--  public Employee getEmpByIdStep(Integer id);-->
	 <select id="getEmpByIdStep" resultMap="MyEmpByStep">
	 	select * from tbl_employee where id=#{id}
	 	<if test="_parameter!=null">
	 		and 1=1
	 	</if>
	 </select>
  • 关联查询,分布查询&延迟加载

全局配置文件里面设置setting

<settings>
		
		<!--显示的指定每个我们需要更改的配置的值,即使他是默认的。防止版本更新带来的问题  -->
		<setting name="lazyLoadingEnabled" value="true"/>
		<setting name="aggressiveLazyLoading" value="false"/>
</settings>
  • 关联查询,collection定义关联集合封装规则

DepartmentMapper.xml

<!-- 
	public class Department {
			private Integer id;
			private String departmentName;
			private List<Employee> emps;
	  did  dept_name  ||  eid  last_name  email   gender  
	 -->
	 
	<!--嵌套结果集的方式,使用collection标签定义关联的集合类型的属性封装规则  -->
	<resultMap type="com.dgm.mybatis.bean.Department" id="MyDept">
		<id column="did" property="id"/>
		<result column="dept_name" property="departmentName"/>
		<!-- 
			collection定义关联集合类型的属性的封装规则 
			ofType:指定集合里面元素的类型
		-->
		<collection property="emps" ofType="com.dgm.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>
	<!-- public Department getDeptByIdPlus(Integer id); -->
	<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>
  • 关联查询,collection分布查询&延迟加载

EmployeeMapperPlus.xml

<!-- public List<Employee> getEmpsByDeptId(Integer deptId); -->
	<select id="getEmpsByDeptId" resultType="com.dgm.mybatis.bean.Employee">
		select * from tbl_employee where d_id=#{deptId}
	</select>
	

 DepartmentMapper.xml

<!-- collection:分段查询 -->
	<resultMap type="com.dgm.mybatis.bean.Department" id="MyDeptStep">
		<id column="id" property="id"/>
		<id column="dept_name" property="departmentName"/>
		<collection property="emps" 
			select="com.dgm.mybatis.dao.EmployeeMapperPlus.getEmpsByDeptId"
			column="id">
        </collection>
	</resultMap>
	<!-- public Department getDeptByIdStep(Integer id); -->
	<select id="getDeptByIdStep" resultMap="MyDeptStep">
		select id,dept_name from tbl_dept where id=#{id}
	</select>
  • 分布查询传递多列值&fetchType

多列的值传递过去: 将多列的值封装map传递, column="{key1=column1,key2=column2}"

<collection property="emps" 
			select="com.atguigu.mybatis.dao.EmployeeMapperPlus.getEmpsByDeptId"
			column="{deptId=id}" fetchType="lazy">
</collection>

fetchType="lazy":表示使用延迟加载;
         (1) lazy:延迟
         (2)eager:立即

  • discriminator鉴别器(<discriminator javaType=""></discriminator>)

鉴别器:mybatis可以使用discriminator判断某列的值,然后根据某列的值改变封装行为
        封装Employee:
            如果查出的是女生:就把部门信息查询出来,否则不查询;
            如果是男生,把last_name这一列的值赋值给email;

EmployeeMapperPlus.xml

<resultMap type="com.dgm.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.dgm.mybatis.bean.Employee">
	 			<association property="dept" 
			 		select="com.dgm.mybatis.dao.DepartmentMapper.getDeptById"
			 		column="d_id">
		 		</association>
	 		</case>
	 		<!--男生 ;如果是男生,把last_name这一列的值赋值给email; -->
	 		<case value="1" resultType="com.dgm.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>

DepartmentMapper.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.dgm.mybatis.dao.DepartmentMapper">
	<!--public Department getDeptById(Integer id);  -->
	<select id="getDeptById" resultType="com.dgm.mybatis.bean.Department">
		select id,dept_name from tbl_dept where id=#{id}
	</select>
</mapper>

动态sql

  • 动态 SQL是MyBatis强大特性之一。极大的简化我们拼装 SQL的操作。
  •  动态 SQL 元素和使用 JSTL 或其他类似基于 XML 的文本处 理器相似。
  • MyBatis 采用功能强大的基于 OGNL 的表达式来简化操作。 

--------------------if

-------------------- choose (when, otherwise)

---------------------trim (where, set)

--------------------- foreach

if判断&OGNL

 <!-- 查询员工,要求,携带了哪个字段查询条件就带上这个字段的值 -->
	 <!-- public List<Employee> getEmpsByConditionIf(Employee employee); -->
	 <select id="getEmpsByConditionIf" resultType="com.dgm.mybatis.bean.Employee">
	 	select * from tbl_employee
	 	where
		 	<!-- test:判断表达式(OGNL)。
		 	  	 c:if  test
		 	从参数中取值进行判断	
		 	遇见特殊符号应该去写转义字符:
		 	-->
		 	<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>
	 	
	 </select>

where查询条件

查询的时候如果某些条件没带(主键为空)可能sql拼装会有问题,有两种解决方案
            1、给where后面加上1=1,以后的条件都and xxx.
            2、mybatis使用where标签来将所有的查询条件包括在内。mybatis就会将where标签中拼装的sql,多出来的and或者or去掉(where只会去掉第一个多出来的and或者or。)

 <!-- 查询员工,要求,携带了哪个字段查询条件就带上这个字段的值 -->
	 <!-- public List<Employee> getEmpsByConditionIf(Employee employee); -->
	 <select id="getEmpsByConditionIf" resultType="com.dgm.mybatis.bean.Employee">
	 	select * from tbl_employee
	 	<where>
		 	<!-- test:判断表达式(OGNL)。
		 	  	 c:if  test
		 	从参数中取值进行判断	
		 	遇见特殊符号应该去写转义字符:
		 	-->
		 	<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>

trim自定义字符串截取

 <!--public List<Employee> getEmpsByConditionTrim(Employee employee);  -->
	 <select id="getEmpsByConditionTrim" resultType="com.dgm.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>
	 

choose分支选择:如果new Employee()里面带了id就用id查,如果带了lastName就用lastName查;只会进入其中一个

<!-- public List<Employee> getEmpsByConditionChoose(Employee employee); -->
	 <select id="getEmpsByConditionChoose" resultType="com.dgm.mybatis.bean.Employee">
	 	select * from tbl_employee 
	 	<where>
	 		<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>
	 

set与if结合的动态更新

<!--public void updateEmp(Employee employee);  -->
	 <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>
        <!--根据id去更新数据-->
		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>

foreach遍历集合

<!--public List<Employee> getEmpsByConditionForeach(List<Integer> ids);  -->
	 <select id="getEmpsByConditionForeach" resultType="com.dgm.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>

MySQL下foreach批量插入的两种方式

<!-- 批量保存 -->
	 <!--public void addEmps(@Param("emps")List<Employee> emps);  -->
	 <!--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><!--   -->
	 
	 <!-- 这种方式需要数据库连接属性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> -->

Oracle下foreach批量插入的两种方式

<!-- Oracle数据库批量保存: 
	 	Oracle不支持values(),(),()
	 	Oracle支持的批量方式
	 	1、多个insert放在begin - end里面
	 		begin
			    insert into employees(employee_id,last_name,email) 
			    values(employees_seq.nextval,'test_001','test_001@atguigu.com');
			    insert into employees(employee_id,last_name,email) 
			    values(employees_seq.nextval,'test_002','test_002@atguigu.com');
			end;
		2、利用中间表:
			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
		       )	
	 -->
	 <insert id="addEmps" databaseId="oracle">
	 	<!-- 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> -->
	 	
	 	<!-- 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>

内置参数param&databaseId,bind绑定

<!-- 两个内置参数:
	 	不只是方法传递过来的参数可以被用来判断,取值。。。
	 	mybatis默认还有两个内置参数:
	 	_parameter:代表整个参数
	 		单个参数:_parameter就是这个参数
	 		多个参数:参数会被封装为一个map;_parameter就是代表这个map
	 	
	 	_databaseId:如果配置了databaseIdProvider标签。
	 		_databaseId就是代表当前数据库的别名oracle
	  -->
	  
	  <!--public List<Employee> getEmpsTestInnerParameter(Employee employee);  -->
	  <select id="getEmpsTestInnerParameter" resultType="com.dgm.mybatis.bean.Employee">
	  		<!-- bind:可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值
                //匹配%e%这类的,可以只写一个e
             -->
	  		<bind name="_lastName" value="'%'+lastName+'%'"/>
	  		<if test="_databaseId=='mysql'">
	  			select * from tbl_employee
	  			<if test="_parameter!=null">
	  				where last_name like #{lastName}
                    //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>

4sql抽取可重用的sql片段

<!-- 
	  	抽取可重用的sql片段。方便后面引用 
	  	1、sql抽取:经常将要查询的列名,或者插入用的列名抽取出来方便引用
	  	2、include来引用已经抽取的sql:
	  	3、include还可以自定义一些property,sql标签内部就能使用自定义的属性
	  			include-property:取值的正确方式${prop},
	  			#{不能使用这种方式}
	  -->
	  <sql id="insertColumn">
	  		<if test="_databaseId=='oracle'">
	  			employee_id,last_name,email,${testColomn}
	  		</if>
	  		<if test="_databaseId=='mysql'">
	  			last_name,email,gender,d_id
	  		</if>
	  </sql>


insert into tbl_employee(<include refid="insertColumn">
                              <property name="testColomn" value="abc"/>
                        </include>
                        ) 

缓存

MyBatis 包含一个非常强大的查询缓存特性,它可以非 常方便地配置和定制。缓存可以极大的提升查询效率。  MyBatis系统中默认定义了两级缓存。( 一级缓存和二级缓存。)

  • 默认情况下,只有一级缓存(SqlSession级别的缓存, 也称为本地缓存)开启。
  • 二级缓存(全局缓存)需要手动开启和配置,他是基于namespace级别的缓存。
  • 为了提高扩展性。MyBatis定义了缓存接口Cache。我们 可以通过实现Cache接口来自定义二级缓存

一级缓存:与数据库同一次会话期间查询到的数据会放在本地缓存中。以后如果需要获取相同的数据,直接从缓存中拿,没必要再去查询数据库

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

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

二级缓存(全局缓存)基于namespace级别的缓存:一个namespace对应一个二级缓存:
   (1) 工作机制:
            1、一个会话,查询一条数据,这个数据就会被放在当前会话的一级缓存中;
            2、如果会话关闭;一级缓存中的数据会被保存到二级缓存中;新的会话查询信息,就可以参照二级缓存中的内容;
            3、sqlSession===EmployeeMapper==>Employee, DepartmentMapper===>Department不同namespace查出的数据会放在自己对应的缓存中(map)
   (2) 效果:数据会从二级缓存中获取,查出的数据都会被默认先放在一级缓存中。只有会话提交或者关闭以后,一级缓存中的数据才会转移到二级缓存中
   (3) 使用:
                 1)、开启全局二级缓存配置:<setting name="cacheEnabled" value="true"/>
                 2)、去mapper.xml中配置使用二级缓存:<cache></cache>

                 3)、我们的POJO需要实现序列化接口

<?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.dgm.mybatis.dao.EmployeeMapper">

	<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接口即可;
	-->

<!-- 
namespace:名称空间;指定为接口的全类名
id:唯一标识
resultType:返回值类型
#{id}:从传递过来的参数中取出id值
public Employee getEmpById(Integer id);
 -->
	
</mapper>

缓存有关的设置以及属性

1)、cacheEnabled=true:false:关闭缓存(二级缓存关闭)(一级缓存一直可用的)

2)、每个select标签都有useCache="true": false:不使用缓存(一级缓存依然使用,二级缓存不使用)
3)、每个增删改标签的:flushCache="true":(一级二级都会清除)增删改执行完成后就会清楚缓存;
     测试:flushCache="true":一级缓存就清空了;二级也会被清除;
     查询标签:flushCache="false":
     如果flushCache=true;每次查询之后都会清空缓存;缓存是没有被使用的;
4)、sqlSession.clearCache();只是清除当前session的一级缓存;

 5)、localCacheScope:本地缓存作用域:(一级缓存SESSION);当前会话的所有数据保存在会话缓存中;STATEMENT:可以禁用一级缓存;                          

第三方缓存整合原理ehcache适配包下载

EhCache 是一个纯Java的进程内缓存框架,具有快速、精 干等特点,是Hibernate中默认的CacheProvider。 • MyBatis定义了Cache接口方便我们进行自定义扩展。

1)、导入第三方缓存包即可;
2)、导入与第三方缓存整合的适配包;官方有;
3)、mapper.xml中使用自定义缓存
<cache type="org.mybatis.caches.ehcache.EhcacheCache"></cache>

整合spring

整合适配包下载

1.查看不同MyBatis版本整合Spring时使用的适配包; http://www.mybatis.org/spring/

2.下载整合适配包 https://github.com/mybatis/spring/releases

所有需要的jar包的引入

 引入mybatis之前的配置

 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.dgm.mybatis">
		<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;
			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.dgm.mybatis.dao"/>
	<!-- <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
		<property name="basePackage" value="com.dgm.mybatis.dao"></property>
	</bean> -->
	
</beans>

  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只是控制网站跳转逻辑  -->
	<!-- 只扫描控制器 -->
	<context:component-scan base-package="com.dgm.mybatis" use-default-filters="false">
		<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>

 其他代码

package com.dgm.mybatis.controller;

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

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;

import com.dgm.mybatis.bean.Employee;
import com.dgm.mybatis.service.EmployeeService;

@Controller
public class EmployeeController {
	
	@Autowired
	EmployeeService employeeService;
	
	@RequestMapping("/getemps")
	public String emps(Map<String,Object> map){
		List<Employee> emps = employeeService.getEmps();
		map.put("allEmps", emps);
		return "list";
	}

}

package com.dgm.mybatis.dao;
import java.util.List;

import com.dgm.mybatis.bean.Employee;
public interface EmployeeMapper {
	public Employee getEmpById(Integer id);

	public List<Employee> getEmps();
	
}

package com.dgm.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.dgm.mybatis.bean.Employee;
import com.dgm.mybatis.dao.EmployeeMapper;

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

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


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.dgm.mybatis.dao.EmployeeMapper">	
 	<!-- public Employee getEmpById(Integer id); -->
	<select id="getEmpById" resultType="com.atguigu.mybatis.bean.Employee">
		select * from tbl_employee where id=#{id}
	</select>
	
	<!--public List<Employee> getEmps();  -->
	<select id="getEmps" resultType="com.atguigu.mybatis.bean.Employee">
		select * from tbl_employee
	</select>
</mapper>
list.jsp


<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>员工列表</title>
</head>
<body>
	<table>
			<tr>
				<td>id</td>
				<td>lastName</td>
				<td>email</td>
				<td>gender</td>
			</tr>
		<c:forEach items="${allEmps }" var="emp">
			<tr>
				<td>${emp.id }</td>
				<td>${emp.lastName }</td>
				<td>${emp.email }</td>
				<td>${emp.gender }</td>
			</tr>
		</c:forEach>
	
	</table>

</body>
</html>


index.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
    pageEncoding="UTF-8"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>Insert title here</title>
</head>
<body>
<a href="getemps">查询所有员工</a>
</body>
</html>

MyBatis逆向工程

mbg简介

MyBatis Generator: 简称MBG,是一个专门为MyBatis框架使用者定制的代码生成器,可以快速的根据表生成对应的映射文件,接口,以及bean类。支持基本的增删 改查,以及QBC风格的条件查询。但是表连接、 存储过程等这些复杂sql的定义需要我们手工编写

mbg配置文件编写

1) jdbcConnection 配置数据库连接信息
2) javaModelGenerator 配置javaBean的生成策略
3) sqlMapGenerator 配置sql映射文件生成策略
4) javaClientGenerator 配置Mapper接口的生成策略
5) table 配置要逆向解析的数据表
        tableName:表名
        domainObjectName:对应的javaBean名
然后运行代码生成器生成代码
注意: Context标签 targetRuntime=“MyBatis3 可以生成带条件的增删改查 targetRuntime=“MyBatis3Simple 可以生成基本的增删改查 如果再次生成,建议将之前生成的数据删除,避免xml向后追加内容出现的问题。
使用mbg 逆向生成所有代码和配置
<?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="MyBatis3">
  	<!-- jdbcConnection:指定如何连接到目标数据库 -->
    <jdbcConnection driverClass="com.mysql.jdbc.Driver"
        connectionURL="jdbc:mysql://localhost:3306/mybatis?allowMultiQueries=true"
        userId="root"
        password="*****">
    </jdbcConnection>

	<!--  -->
    <javaTypeResolver >
      <property name="forceBigDecimals" value="false" />
    </javaTypeResolver>

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

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

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

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

测试代码

public class MyBatisTest {

	public SqlSessionFactory getSqlSessionFactory() throws IOException {
		String resource = "mybatis-config.xml";
		InputStream inputStream = Resources.getResourceAsStream(resource);
		return new SqlSessionFactoryBuilder().build(inputStream);
	}

	@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);
	}
	

测试简单查询,测试带条件的复杂查询

@Test
	public void testMyBatis3() throws IOException{
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		SqlSession openSession = sqlSessionFactory.openSession();
		try{
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			//xxxExample就是封装查询条件的
			//1、查询所有
			//List<Employee> emps = mapper.selectByExample(null);
			//2、查询员工名字中有e字母的,和员工性别是1的
			//封装员工查询条件的example
			EmployeeExample example = new EmployeeExample();
			//创建一个Criteria,这个Criteria就是拼装查询条件
			//select id, last_name, email, gender, d_id from tbl_employee 
			//WHERE ( last_name like ? and gender = ? ) or email like "%e%"
			Criteria criteria = example.createCriteria();
			criteria.andLastNameLike("%e%");
			criteria.andGenderEqualTo("1");
			
			Criteria criteria2 = example.createCriteria();
			criteria2.andEmailLike("%e%");
			example.or(criteria2);
			
			List<Employee> list = mapper.selectByExample(example);
			for (Employee employee : list) {
				System.out.println(employee.getId());
			}
			
		}finally{
			openSession.close();
		}
	}

mybatis运行原理

框架分层架构

mybatis工作原理

SQLSessionFactory的初始化:解析文件的每一个信息保存在Configuration中,返回包含Configuration的DefaultSqlSession;
注意:【MappedStatement】:代表一个增删改查的详细信息

opensession获取sqlsession对象:返回一个DefaultSQlSession对象,包含Executor和Configuration; 这一步会创建Executor对象;

getMapper获取到接口的代理对象:getMapper,使用MapperProxyFactory创建一个MapperProxy的代理对象,代理对象里面包含了,DefaultSqlSession(Executor)

mybatis原理总结

1、根据配置文件(全局,sql映射)初始化出Configuration对象

2、创建一个DefaultSqlSession对象,他里面包含Configuration以及Executor(根据全局配置文件中的defaultExecutorType创建出对应的Executor)

 3、DefaultSqlSession.getMapper():拿到Mapper接口对应的MapperProxy;

4、MapperProxy里面有(DefaultSqlSession);

5、执行增删改查方法:
     1)、调用DefaultSqlSession的增删改查(Executor);
     2)、会创建一个StatementHandler对象。(同时也会创建出ParameterHandler和ResultSetHandler)
     3)、调用StatementHandler预编译参数以及设置参数值; 使用ParameterHandler来给sql设置参数
     4)、调用StatementHandler的增删改查方法;
     5)、ResultSetHandler封装结果
     注意:四大对象每个创建的时候都有一个interceptorChain.pluginAll(parameterHandler);

插件

plugins插件

插件是MyBatis提供的一个非常强大的机制,我们 可以通过插件来修改MyBatis的一些核心行为。插 件通过动态代理机制,可以介入四大对象的任何 一个方法的执行。

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

插件原理

在四大对象创建的时候
     1、每个创建出来的对象不是直接返回的,而是interceptorChain.pluginAll(parameterHandler);
     2、获取到所有的Interceptor(拦截器)(插件需要实现的接口);调用interceptor.plugin(target);返回target包装后的对象
     3、插件机制,我们可以使用插件为目标对象创建一个代理对象;AOP(面向切面)我们的插件可以为四大对象创建出代理对象;代理对象就可以拦截到四大对象的每一个执行;

插件编写&单个插件原理

 1、编写Interceptor的实现类

  • Intercept:拦截目标方法执行
  • plugin:生成动态代理对象,可以使用MyBatis提 供的Plugin类的wrap方法
  • setProperties:注入插件配置时设置的属性

 2、使用@Intercepts注解完成插件签名
 3、将写好的插件注册到全局配置文件中

package com.dgm.mybatis.dao;

import java.util.Properties;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

/**
 * 完成插件签名:告诉MyBatis当前插件用来拦截哪个对象的哪个方法
 */
@Intercepts(
		{
			@Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
		})
public class MyFirstPlugin implements Interceptor{

	/**
	 * intercept:拦截:
	 * 		拦截目标对象的目标方法的执行;
	 */
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		                    System.out.println("MyFirstPlugin...intercept:"+invocation.getMethod());
		//执行目标方法
		Object proceed = invocation.proceed();
		//返回执行后的返回值
		return proceed;
	}

	/**
	 * plugin:
	 * 		包装目标对象的:包装:为目标对象创建一个代理对象
	 */
	@Override
	public Object plugin(Object target) {
		//我们可以借助Plugin的wrap方法来使用当前Interceptor包装我们目标对象
		System.out.println("MyFirstPlugin...plugin:mybatis将要包装的对象"+target);
		Object wrap = Plugin.wrap(target, this);
		//返回为当前target创建的动态代理
		return wrap;
	}

	/**
	 * setProperties:
	 * 		将插件注册时 的property属性设置进来
	 */
	@Override
	public void setProperties(Properties properties) {
		System.out.println("插件配置的信息:"+properties);
	}

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

	<!--plugins:注册插件  -->
	<plugins>
		<plugin interceptor="com.dgm.mybatis.dao.MyFirstPlugin">
			<property name="username" value="root"/>
			<property name="password" value="123456"/>
		</plugin>
		<plugin interceptor="com.dgm.mybatis.dao.MySecondPlugin">                      
        </plugin>
	</plugins>
	<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>

多个插件运行流程

插件会产生目标对象的代理对象,多个插件就会产生多层代理。创建动态代理的时候,是按照插件配置顺序创建层层代理对象。执行目标方法之后,按照逆行顺序执行

开发插件的例子

package com.dgm.mybatis.dao;

import java.util.Properties;

import org.apache.ibatis.executor.parameter.ParameterHandler;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.plugin.Interceptor;
import org.apache.ibatis.plugin.Intercepts;
import org.apache.ibatis.plugin.Invocation;
import org.apache.ibatis.plugin.Plugin;
import org.apache.ibatis.plugin.Signature;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;

/**
 * 完成插件签名:告诉MyBatis当前插件用来拦截哪个对象的哪个方法
 */
@Intercepts(
		{
			@Signature(type=StatementHandler.class,method="parameterize",args=java.sql.Statement.class)
		})
public class MyFirstPlugin implements Interceptor{

	/**
	 * intercept:拦截:
	 * 		拦截目标对象的目标方法的执行;
	 */
	@Override
	public Object intercept(Invocation invocation) throws Throwable {
		                    System.out.println("MyFirstPlugin...intercept:"+invocation.getMethod());
		//动态的改变一下sql运行的参数:以前1号员工,实际从数据库查询3号员工
		Object target = invocation.getTarget();
		System.out.println("当前拦截到的对象:"+target);
		//拿到:StatementHandler==>ParameterHandler===>parameterObject
		//拿到target的元数据
		MetaObject metaObject = SystemMetaObject.forObject(target);
		Object value = metaObject.getValue("parameterHandler.parameterObject");
		System.out.println("sql语句用的参数是:"+value);
		//修改完sql语句要用的参数
		metaObject.setValue("parameterHandler.parameterObject", 11);
		//执行目标方法
		Object proceed = invocation.proceed();
		//返回执行后的返回值
		return proceed;
	}

	/**
	 * plugin:
	 * 		包装目标对象的:包装:为目标对象创建一个代理对象
	 */
	@Override
	public Object plugin(Object target) {
		//我们可以借助Plugin的wrap方法来使用当前Interceptor包装我们目标对象
		System.out.println("MyFirstPlugin...plugin:mybatis将要包装的对象"+target);
		Object wrap = Plugin.wrap(target, this);
		//返回为当前target创建的动态代理
		return wrap;
	}

	/**
	 * setProperties:
	 * 		将插件注册时 的property属性设置进来
	 */
	@Override
	public void setProperties(Properties properties) {
		System.out.println("插件配置的信息:"+properties);
	}

}

PageHelpler分页插件的使用

1、导入相关包 pagehelper-x.x.x.jar jsqlparser- 0.9.5.jar
2、在MyBatis全局配置文件中配置分页插件。
<plugins>
		<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
</plugins>
3、使用PageHelper提供的方法进行分页
@Test
	public void test01() throws IOException {
		// 1、获取sqlSessionFactory对象
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		// 2、获取sqlSession对象
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			Page<Object> page = PageHelper.startPage(1, 5);
			
			List<Employee> emps = mapper.getEmps();
			//传入要连续显示多少页
			for (Employee employee : emps) {
				System.out.println(employee);
			}
			System.out.println("当前页码:"+page.getPageNum());
			System.out.println("总记录数:"+page.getTotal());
			System.out.println("每页的记录数:"+page.getPageSize());
			System.out.println("总页码:"+page.getPages());
		
		} finally {
			openSession.close();
		}

	}
4、可以使用更强大的PageInfo封装返回结果
@Test
	public void test01() throws IOException {
		// 1、获取sqlSessionFactory对象
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		// 2、获取sqlSession对象
		SqlSession openSession = sqlSessionFactory.openSession();
		try {
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			Page<Object> page = PageHelper.startPage(1, 5);
			
			List<Employee> emps = mapper.getEmps();
			//传入要连续显示多少页
			PageInfo<Employee> info = new PageInfo<>(emps, 5);
			for (Employee employee : emps) {
				System.out.println(employee);
			}
			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());
			System.out.println("连续显示的页码:");
			int[] nums = info.getNavigatepageNums();
			for (int i = 0; i < nums.length; i++) {
				System.out.println(nums[i]);
			}
			
			
			//xxxx
		} finally {
			openSession.close();
		}

	}
批量操作
@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();
		}
		
	}

与Spring整合中进行批量操作时在applicationContext.xml配置一个可以进行批量执行的sqlSession,然后在employeeService里面设置sqlSession

<!--配置一个可以进行批量执行的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.dgm.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.dgm.mybatis.bean.Employee;
import com.dgm.mybatis.dao.EmployeeMapper;

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

}

调用存储过程

实际开发中,我们通常也会写一些存储过程, MyBatis也支持对存储过程的调用
一个最简单的存储过程
delimiter $$
create procedure test()
begin
select 'hello';
end $$
delimiter ;
存储过程的调用
1、select标签中statementType=“CALLABLE”
2、标签体中调用语法:
{call procedure_name(#{param1_info},#{param2_info})}

oracle创建一个带游标的存储过程

MyBatis对存储过程的游标提供了一个JdbcType=CURSOR 的支持, 可以智能的把游标读取到的数据,映射到我们声明的结果集中
调用实例:
package com.dgm.mybatis.bean;

import java.util.List;

/**
 * 封装分页查询数据
 * @author lfy
 *
 */
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;
	}
	
	

}
<?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);
 -->
	
	<!-- public void getPageByProcedure(); 
	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>

</mapper>
<?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>
	<plugins>
		<plugin interceptor="com.dgm.pagehelper.PageInterceptor"></plugin>
	</plugins>	
		<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>
	<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
	<mappers>
		<mapper resource="EmployeeMapper.xml" />
	</mappers>
</configuration>
/**
	 * oracle分页:
	 * 		借助rownum:行号;子查询;
	 * 存储过程包装分页逻辑
	 * @throws IOException 
	 */
	@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();
		}
		
	}

自定义TypeHandler处理枚举

typeHandlers类型处理器无论是 MyBatis 在预处理语句(PreparedStatement)中 设置一个参数时,还是从结果集中取出一个值时, 都会 用类型处理器将获取的值以合适的方式转换成 Java 类型。我们可以通过自定义TypeHandler的形式来在设置参数或 者取出结果集的时候自定义参数封装策略。

步骤:
1、实现TypeHandler接口或者继承BaseTypeHandler
2、使用@MappedTypes定义处理的java类型 使用@MappedJdbcTypes定义jdbcType类型
3、在自定义结果集标签或者参数处理的时候声明使用自定义 TypeHandler进行处理,或者在全局配置TypeHandler要处理的javaType
package com.dgm.mybatis.typehandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;

import com.atguigu.mybatis.bean.EmpStatus;

/**
 * 1、实现TypeHandler接口。或者继承BaseTypeHandler
 * @author lfy
 *
 */
public class MyEnumEmpStatusTypeHandler implements TypeHandler<EmpStatus> {

	/**
	 * 定义当前数据如何保存到数据库中
	 */
	@Override
	public void setParameter(PreparedStatement ps, int i, EmpStatus parameter,
			JdbcType jdbcType) throws SQLException {
		System.out.println("要保存的状态码:"+parameter.getCode());
		ps.setString(i, parameter.getCode().toString());
	}

	@Override
	public EmpStatus getResult(ResultSet rs, String columnName)
			throws SQLException {
		//需要根据从数据库中拿到的枚举的状态码返回一个枚举对象
		int code = rs.getInt(columnName);
		System.out.println("从数据库中获取的状态码:"+code);
		EmpStatus status = EmpStatus.getEmpStatusByCode(code);
		return status;
	}

	@Override
	public EmpStatus getResult(ResultSet rs, int columnIndex)
			throws SQLException {
		int code = rs.getInt(columnIndex);
		System.out.println("从数据库中获取的状态码:"+code);
		EmpStatus status = EmpStatus.getEmpStatusByCode(code);
		return status;
	}

	@Override
	public EmpStatus getResult(CallableStatement cs, int columnIndex)
			throws SQLException {
		int code = cs.getInt(columnIndex);
		System.out.println("从数据库中获取的状态码:"+code);
		EmpStatus status = EmpStatus.getEmpStatusByCode(code);
		return status;
	}

}
package com.dgm.mybatis.bean;

import com.sun.org.apache.bcel.internal.generic.RETURN;

/**
 * 希望数据库保存的是100,200这些状态码,而不是默认0,1或者枚举的名
 * @author lfy
 *
 */
public enum EmpStatus {
	LOGIN(100,"用户登录"),LOGOUT(200,"用户登出"),REMOVE(300,"用户不存在");
	
	
	private Integer code;
	private String msg;
	private EmpStatus(Integer code,String msg){
		this.code = code;
		this.msg = msg;
	}
	public Integer getCode() {
		return code;
	}
	
	public void setCode(Integer code) {
		this.code = code;
	}
	public String getMsg() {
		return msg;
	}
	public void setMsg(String msg) {
		this.msg = msg;
	}
	
	//按照状态码返回枚举对象
	public static EmpStatus getEmpStatusByCode(Integer code){
		switch (code) {
			case 100:
				return LOGIN;
			case 200:
				return LOGOUT;	
			case 300:
				return REMOVE;
			default:
				return LOGOUT;
		}
	}
	
	
}
package com.dgm.mybatis.bean;

public class Employee {
	
	private Integer id;
	private String lastName;
	private String email;
	private String gender;
	//员工状态
	private EmpStatus empStatus=EmpStatus.LOGOUT;
	
	
	public Employee(String lastName, String email, String gender) {
		super();
		this.lastName = lastName;
		this.email = email;
		this.gender = gender;
	}
	public Employee() {
		super();
		// TODO Auto-generated constructor stub
	}
	
	
	public EmpStatus getEmpStatus() {
		return empStatus;
	}
	public void setEmpStatus(EmpStatus empStatus) {
		this.empStatus = empStatus;
	}
	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 + "]";
	}
	
	

}
<?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.dgm.mybatis.dao.EmployeeMapper">
<!-- 
namespace:名称空间;指定为接口的全类名
id:唯一标识
resultType:返回值类型
#{id}:从传递过来的参数中取出id值

public Employee getEmpById(Integer id);
 -->
 	
	<select id="getEmpById" resultType="com.dgm.mybatis.bean.Employee">
		select id,last_name lastName,email,gender,empStatus from tbl_employee where id = #{id}
	</select>
	
	<!--public List<Employee> getEmps();  -->
	<select id="getEmps" resultType="com.dgm.mybatis.bean.Employee">
		select id,last_name lastName,email,gender from tbl_employee
	</select>
	
	<!--public Long addEmp(Employee employee);  -->
	<insert id="addEmp" useGeneratedKeys="true" keyProperty="id">
		insert into tbl_employee(last_name,email,gender,empStatus) 
		values(#{lastName},#{email},#{gender},#{empStatus})
	</insert>
	
</mapper>
<?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>
	<typeHandlers>
		<!--1、配置我们自定义的TypeHandler  -->
		<typeHandler handler="com.dgm.mybatis.typehandler.MyEnumEmpStatusTypeHandler" javaType="com.dgm.mybatis.bean.EmpStatus"/>
		<!--2、也可以在处理某个字段的时候告诉MyBatis用什么类型处理器
				保存:#{empStatus,typeHandler=xxxx}
				查询:
					<resultMap type="com.dgm.mybatis.bean.Employee" id="MyEmp">
				 		<id column="id" property="id"/>
				 		<result column="empStatus" property="empStatus" typeHandler=""/>
				 	</resultMap>
				注意:如果在参数位置修改TypeHandler,应该保证保存数据和查询数据用的TypeHandler是一样的。
		  -->
	</typeHandlers>
	<plugins>
		<plugin interceptor="com.github.pagehelper.PageInterceptor"></plugin>
	</plugins>
	<environments default="dev_mysql">
		<environment id="dev_mysql">
			<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="*****" />
			</dataSource>
		</environment>
	</environments>
	
	<databaseIdProvider type="DB_VENDOR">
		<property name="MySQL" value="mysql"/>
		<property name="Oracle" value="oracle"/>
		<property name="SQL Server" value="sqlserver"/>
	</databaseIdProvider>
	<!-- 将我们写好的sql映射文件(EmployeeMapper.xml)一定要注册到全局配置文件(mybatis-config.xml)中 -->
	<mappers>
		<mapper resource="EmployeeMapper.xml" />
	</mappers>
</configuration>
@Test
	public void testEnumUse(){
		EmpStatus login = EmpStatus.LOGIN;
		System.out.println("枚举的索引:"+login.ordinal());
		System.out.println("枚举的名字:"+login.name());
		
		System.out.println("枚举的状态码:"+login.getCode());
		System.out.println("枚举的提示消息:"+login.getMsg());
	}
	
	/**
	 * 默认mybatis在处理枚举对象的时候保存的是枚举的名字:EnumTypeHandler
	 * 改变使用:EnumOrdinalTypeHandler:
	 * @throws IOException
	 */
	@Test
	public void testEnum() throws IOException{
		SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
		SqlSession openSession = sqlSessionFactory.openSession();
		try{
			EmployeeMapper mapper = openSession.getMapper(EmployeeMapper.class);
			Employee employee = new Employee("test_enum", "enum@atguigu.com","1");
			//mapper.addEmp(employee);
			//System.out.println("保存成功"+employee.getId());
			//openSession.commit();
			Employee empById = mapper.getEmpById(30026);
			System.out.println(empById.getEmpStatus());
		}finally{
			openSession.close();
		}
	}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值