Mybatis


title: Mybatis
tag: Mybatis框架
categories: 分类
comment: 是否允许评论(true or false)
description: 描述
top_img: https://z3.ax1x.com/2021/10/06/4xq2s1.png

cover: https://z3.ax1x.com/2021/10/06/4xq2s1.png

Mybatis简介

  • Mybatis是支持定制化SQL、存储过程以及高级映射的优秀的持久层框架
  • Mybatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集
  • Mybatis可以使用简单的XML或注解用于配置和原始映射,将接口和Java的POJO(Plain Old Java Objects,普通的Java对象)映射成数据库中的记录

使用Mybatis原因

  • Mybatis是一个半自动化的持久化称框架
  • JDBC:SQL夹杂Java代码中,耦合度高导致硬编码内伤,维护不易且实际开发需求中sql是有变化,频繁修改的情况多见。
  • Hibernate和JPA:长难复杂SQL,对于Hibernate而言处理也不容易,内部自动生成的SQL,不容易做特殊化优化,基于全映射的自动化框架,大量字段的POJO进行部分映射时比较困难。导致数据库性能下降。
  • 对于开发人员而言核心SQL还是需要自己优化。
  • sql和java编码分开,功能边界清晰,一个专注业务、一个专注数据。

Mybatis-Helloworld

  • 创建一个测试表
  • 创建对应的javaBean
  • 创建mybatis配置文件,sql映射文件
  • 测试

Mybatis操作数据库

  1. 创建Mybatis全局配置文件

    mybatis的全局配置文件包含了影响Mybatis行为的设置(Settings)和属性(properties)信息,如数据库连接池信息等。指导着Mybatis进行工作。

  2. 创建SQL映射文件

    映射文件的作用相当于是定义DAO接口的实现类如何工作。

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

测试

  1. 利用全局配置文件,利用SqlSessionFactoryBuilder创建SqlSessionFactory

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

    SqlSessionFactory sqlSessionFactory = getSqlSessionFactory();
    
    SqlSession openSession = sqlSessionFactory.openSession();
    

接口式编程

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>

定义接口的抽象方法

package com.atguigu.mybatis.dao;

import com.atguigu.mybatis.bean.Employee;

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

}

测试

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

sqlsession

SqlSession的实例不是线程安全的,因此式是不能被共享的

SqlSession每次使用完成后需要正确关闭,这个关闭操作时必须的

SqlSession可以直接调用方法的id进行数据库操作,但是我们一般还是推荐使用SqlSession获取到Dao接口的代理类,执行代理对象的方法,可以更安全的进行类型检查。

作用域和生命周期

SqlsessionFactoryBuilder

这个类可以被实例化、使用和丢弃,一旦创建了SqlSessionFactory,就不再需要它。因此SqlSessionFactoryBuilder实例的最佳作用域是方法作用域。你可以重用SqlSessionFactoryBuilder来创建多个SqlSessionFactory实例。

SqlSessionFactory

SqlSessionFactory一旦被创建就应该在应用的运行期间一直存在,没有任何理由丢弃它或者创建另一个实例。使用SqlSessionFactory的最佳实践时在应用运行期间不需要重复创建多次,多次重建SqlSessionFactory被视为一种“坏习惯”。因此SqlSessionFactory的最佳作用域是应用作用域。可以使用单例模式或者静态单例模式可以做到。

SqlSession

每个线程都应该有自己的SqlSession实例。SqlSession的实例不是线程安全的,因此是不能被共享的,所以它的最佳的作用域是请求或方法作用域。绝对不能将SqlSession实例的引用放在一个类的静态域,甚至一个类的实例变量也不行。

Mybatis-全局配置文件(mybatis-config.xml)

Mybatis的配置文件包含了会深深影响Mybatis行为的设置和属性信息,

properties属性

76k6Fx.png

dbconfig.properties

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

mybatis-config.xml

<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>
<!--
		1、mybatis可以使用properties来引入外部properties配置文件的内容;
		resource:引入类路径下的资源
		url:引入网络路径或者磁盘路径下的资源
	  -->
	<properties resource="dbconfig.properties"></properties>

settings(设置)

mybatis中极为重要的调整设置,它们会改变Mybatis的运行时行为。

设置名描述有效值默认值
cacheEnabled全局性地开启或关闭所有映射器配置文件中已配置的任何缓存。true | falsetrue
lazyLoadingEnabled延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态。true | falsefalse
aggressiveLazyLoading开启时,任一方法的调用都会加载该对象的所有延迟加载属性。 否则,每个延迟加载属性会按需加载(参考 lazyLoadTriggerMethods)。true | falsefalse (在 3.4.1 及之前的版本中默认为 true)
multipleResultSetsEnabled是否允许单个语句返回多结果集(需要数据库驱动支持)。true | falsetrue
useColumnLabel使用列标签代替列名。实际表现依赖于数据库驱动,具体可参考数据库驱动的相关文档,或通过对比测试来观察。true | falsetrue
useGeneratedKeys允许 JDBC 支持自动生成主键,需要数据库驱动支持。如果设置为 true,将强制使用自动生成主键。尽管一些数据库驱动不支持此特性,但仍可正常工作(如 Derby)。true | falseFalse
autoMappingBehavior指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示关闭自动映射;PARTIAL 只会自动映射没有定义嵌套结果映射的字段。 FULL 会自动映射任何复杂的结果集(无论是否嵌套)。NONE, PARTIAL, FULLPARTIAL
autoMappingUnknownColumnBehavior指定发现自动映射目标未知列(或未知属性类型)的行为。NONE: 不做任何反应WARNING: 输出警告日志('org.apache.ibatis.session.AutoMappingUnknownColumnBehavior' 的日志等级必须设置为 WARNFAILING: 映射失败 (抛出 SqlSessionException)NONE, WARNING, FAILINGNONE
defaultExecutorType配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(PreparedStatement); BATCH 执行器不仅重用语句还会执行批量更新。SIMPLE REUSE BATCHSIMPLE
defaultStatementTimeout设置超时时间,它决定数据库驱动等待数据库响应的秒数。任意正整数未设置 (null)
defaultFetchSize为驱动的结果集获取数量(fetchSize)设置一个建议值。此参数只可以在查询设置中被覆盖。任意正整数未设置 (null)
defaultResultSetType指定语句默认的滚动策略。(新增于 3.5.2)FORWARD_ONLY | SCROLL_SENSITIVE | SCROLL_INSENSITIVE | DEFAULT(等同于未设置)未设置 (null)
safeRowBoundsEnabled是否允许在嵌套语句中使用分页(RowBounds)。如果允许使用则设置为 false。true | falseFalse
safeResultHandlerEnabled是否允许在嵌套语句中使用结果处理器(ResultHandler)。如果允许使用则设置为 false。true | falseTrue
mapUnderscoreToCamelCase是否开启驼峰命名自动映射,即从经典数据库列名 A_COLUMN 映射到经典 Java 属性名 aColumn。true | falseFalse
localCacheScopeMyBatis 利用本地缓存机制(Local Cache)防止循环引用和加速重复的嵌套查询。 默认值为 SESSION,会缓存一个会话中执行的所有查询。 若设置值为 STATEMENT,本地缓存将仅用于执行语句,对相同 SqlSession 的不同查询将不会进行缓存。SESSION | STATEMENTSESSION
jdbcTypeForNull当没有为参数指定特定的 JDBC 类型时,空值的默认 JDBC 类型。 某些数据库驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。JdbcType 常量,常用值:NULL、VARCHAR 或 OTHER。OTHER
lazyLoadTriggerMethods指定对象的哪些方法触发一次延迟加载。用逗号分隔的方法列表。equals,clone,hashCode,toString
defaultScriptingLanguage指定动态 SQL 生成使用的默认脚本语言。一个类型别名或全限定类名。org.apache.ibatis.scripting.xmltags.XMLLanguageDriver
defaultEnumTypeHandler指定 Enum 使用的默认 TypeHandler 。(新增于 3.4.5)一个类型别名或全限定类名。org.apache.ibatis.type.EnumTypeHandler
callSettersOnNulls指定当结果集中值为 null 的时候是否调用映射对象的 setter(map 对象时为 put)方法,这在依赖于 Map.keySet() 或 null 值进行初始化时比较有用。注意基本类型(int、boolean 等)是不能设置成 null 的。true | falsefalse
returnInstanceForEmptyRow当返回行的所有列都是空时,MyBatis默认返回 null。 当开启这个设置时,MyBatis会返回一个空实例。 请注意,它也适用于嵌套的结果集(如集合或关联)。(新增于 3.4.2)true | falsefalse
logPrefix指定 MyBatis 增加到日志名称的前缀。任何字符串未设置
logImpl指定 MyBatis 所用日志的具体实现,未指定时将自动查找。SLF4J | LOG4J(deprecated since 3.5.9) | LOG4J2 | JDK_LOGGING | COMMONS_LOGGING | STDOUT_LOGGING | NO_LOGGING未设置
proxyFactory指定 Mybatis 创建可延迟加载对象所用到的代理工具。CGLIB | JAVASSISTJAVASSIST (MyBatis 3.3 以上)
vfsImpl指定 VFS 的实现自定义 VFS 的实现的类全限定名,以逗号分隔。未设置
useActualParamName允许使用方法签名中的名称作为语句参数名称。 为了使用该特性,你的项目必须采用 Java 8 编译,并且加上 -parameters 选项。(新增于 3.4.1)true | falsetrue
configurationFactory指定一个提供 Configuration 实例的类。 这个被返回的 Configuration 实例用来加载被反序列化对象的延迟加载属性值。 这个类必须包含一个签名为static Configuration getConfiguration() 的方法。(新增于 3.2.3)一个类型别名或完全限定类名。未设置
shrinkWhitespacesInSql从SQL中删除多余的空格字符。请注意,这也会影响SQL中的文字字符串。 (新增于 3.5.5)true | falsefalse
defaultSqlProviderTypeSpecifies an sql provider class that holds provider method (Since 3.5.6). This class apply to the type(or value) attribute on sql provider annotation(e.g. @SelectProvider), when these attribute was omitted.A type alias or fully qualified class nameNot set
nullableOnForEachSpecifies the default value of ‘nullable’ attribute on ‘foreach’ tag. (Since 3.5.9)true | falsefalse
<!-- 
		2、settings包含很多重要的设置项
		setting:用来设置每一个设置项
			name:设置项名
			value:设置项取值
	 -->
	<settings>
		<setting name="mapUnderscoreToCamelCase" value="true"/>
	</settings>

类型别名(typeAliases)

类型别名时Java类型设置一个短的名字,可以方便我们引用某个类。

<typeAliases>
		<!-- 1、typeAlias:为某个java类型起别名
				type:指定要起别名的类型全类名;默认别名就是类名小写;employee
				alias:指定新的别名
		 -->
		 <typeAlias type="com.atguigu.mybatis.bean.Employee" alias="emp"/> 
		
		<!-- 2、package:为某个包下的所有类批量起别名 
				name:指定包名(为当前包以及下面所有的后代包的每一个类都起一个默认别名(类名小写),)
		-->
		<package name="com.atguigu.mybatis.bean"/>
		
		<!-- 3、批量起别名的情况下,使用@Alias注解为某个类型指定新的别名 -->
</typeAliases>
@Alias("emp")
public class Employee {

需要注意的是,Mybatis已经为许多常见的Java类型内建立了相应的类型别名。它们都是大小写不敏感的,我们在起别名的时候千万不要占用已有的别名。

别名映射的类型
_bytebyte
_longlong
_shortshort
_intint
_integerint
_doubledouble
_floatfloat
_booleanboolean
stringString
byteByte
longLong
shortShort
intInteger
integerInteger
doubleDouble
floatFloat
booleanBoolean
dateDate
decimalBigDecimal
bigdecimalBigDecimal
objectObject
mapMap
hashmapHashMap
listList
arraylistArrayList
collectionCollection
iteratorIterator

插件(plugins)

Mybatis允许你在映射语句执行过程中的某一点进行拦截。默认情况下,Mybatis允许使用插件来拦截的方法调用包括:

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

在这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看mybatis的源代码。如果你想做的不仅仅是监控方法的调用,那么最好了解重写方法的行为。因为在试图修改和重写已有方法的行为时,很可能会破坏Mybatis的核心模块。这些都是更底层的类和方法,所以使用插件的时候要特别当心。

通过Mybatis提供的强大机制,使用插件时非常简单的,只需要实现Interceptor接口,并指定想要拦截的方法签名即可。

// ExamplePlugin.java
@Intercepts({@Signature(
  type= Executor.class,
  method = "update",
  args = {MappedStatement.class,Object.class})})
public class ExamplePlugin implements Interceptor {
  private Properties properties = new Properties();
  public Object intercept(Invocation invocation) throws Throwable {
    // implement pre processing if need
    Object returnObject = invocation.proceed();
    // implement post processing if need
    return returnObject;
  }
  public void setProperties(Properties properties) {
    this.properties = properties;
  }
}
<!-- mybatis-config.xml -->
<plugins>
  <plugin interceptor="org.mybatis.example.ExamplePlugin">
    <property name="someProperty" value="100"/>
  </plugin>
</plugins>

上面的插件将会拦截Executor实例中所有的"update"方法调用,这里的Executor时负责执行底层映射语句的内部对象。

覆盖配置类

除了用插件来修改Mybatis核心行为以外,还可以通过完全覆盖配置类来达到目的。只需要继承配置类后覆盖其中的某个方法,再把它传递到SqlSessionFactoryBuilder.build(myconfig)方法即可。再次重申,这可能会极大影响Mybatis的行为,勿请慎之又慎。

环境配置(envoronments)

Mybatis可以配置成适应多种环境,这种机制有助于将SQL映射应用于多种数据库之中,现实情况下有多种理由需要这么做,如,开发、测试和生产环境需要有不同的配置。

不过要记住:尽管可以配置多个环境,但每个SqlSessionFactory实例只能选择一种环境。

所以如果需要连接两个数据库,就需要创建两个SqlSesstionFactory实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依次类推,记起来很简单:

  • 每个数据库对应一个SqlSessionFactory实例

为了指定创建哪种环境,只要将它作为可选的参数传递给SqlSessionFactoryBuilder即可。可以接受环境配置的两个方法签名是:

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, environment, properties);

如果忽略了环境参数,那么将会加载默认环境,如下所示:

SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader);
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader, properties);

environments元素定义了如何配置环境。

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

注意一些点:

  • 默认使用的环境ID(比如:default= “development”)
  • 每个environment元素定义的环境ID
  • 事务管理器的配置
  • 数据源的配置

默认环境和环境ID顾名思义。环境可以随意命名,但务必保证默认的环境ID要匹配其中一个环境ID.

事务管理器

在mybatis中有两种类型的事务管理器(type="[JDBC|MANAGED]");

  • JDBC-这个配置直接使用了JDBC的提交和回滚设施,它依赖从数据源获得的连接来管理事务作用域。

  • MANAGED-这个配置几乎什么也不做。它从不提交或回滚一个连接,而是让容器来管理事务的整个周期。默认情况下它会关闭连接,然而一些容器并不希望被关闭,因此需要将closeConnection属性设置为false来阻止默认的关闭行为。例如:

    <transactionManager type="MANAGED">
      <property name="closeConnection" value="false"/>
    </transactionManager>
    

如果你正在使用Spring+Mybatis,则没有必要配置事务管理器,因为Spring模块会使用自带的管理器来覆盖前面的配置。

这两种事务管理器类型都不需要设置任何属性。它们其实是类型别名,换句话说,你可以用TransactionFactory接口实现类的全限定名或类型别名代替它们。

public interface TransactionFactory {
  default void setProperties(Properties props) { // 从 3.5.2 开始,该方法为默认方法
    // 空实现
  }
  Transaction newTransaction(Connection conn);
  Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}

在事务管理器实例化后,所有在XML种配置的属性将会被传递给setProperties()方法。你的实现还需要创建一个Transaction接口的实现类,这个接口如下:

public interface Transaction {
  Connection getConnection() throws SQLException;
  void commit() throws SQLException;
  void rollback() throws SQLException;
  void close() throws SQLException;
  Integer getTimeout() throws SQLException;
}

使用这两个接口,可以完全自定义Mybatis对事务的处理。

数据源(dataSource)

dataSource元素使用标准的JDBC数据源接口来配置JDBC连接对象的资源。

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

UNPOOLED

这个数据源的实现会每次请求时打开和关闭连接。虽然有点慢,但对那些数据库连接的可用性要求不高的简单应用程序来说,是一个很好的选择。性能表现则依赖于使用的数据库,对某些数据库来说,使用连接池并不重要,这个配置就很适合。

UNPOOLED 类型的数据源仅仅需要配置以下 5 种属性:

  • driver – 这是 JDBC 驱动的 Java 类全限定名(并不是 JDBC 驱动中可能包含的数据源类)。
  • url – 这是数据库的 JDBC URL 地址。
  • username – 登录数据库的用户名。
  • password – 登录数据库的密码。
  • defaultTransactionIsolationLevel – 默认的连接事务隔离级别。
  • defaultNetworkTimeout – 等待数据库操作完成的默认网络超时时间(单位:毫秒)。查看 java.sql.Connection#setNetworkTimeout() 的 API 文档以获取更多信息。

作为可选项,你也可以传递属性给数据库驱动。只需在属性名加上“driver.”前缀即可,例如:

  • driver.encoding=UTF8

这将通过 DriverManager.getConnection(url, driverProperties) 方法传递值为 UTF8encoding 属性给数据库驱动。

POOLED

这种数据源的实现利用“池”的概念将JDBC连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。这种处理方式很流行,能使并发web应用快速响应请求。

  • poolMaximumActiveConnections – 在任意时间可存在的活动(正在使用)连接数量,默认值:10
  • poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。
  • poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
  • poolTimeToWait – 这是一个底层设置,如果获取连接花费了相当长的时间,连接池会打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直失败且不打印日志),默认值:20000 毫秒(即 20 秒)。
  • poolMaximumLocalBadConnectionTolerance – 这是一个关于坏连接容忍度的底层设置, 作用于每一个尝试从缓存池获取连接的线程。 如果这个线程获取到的是一个坏的连接,那么这个数据源允许这个线程尝试重新获取一个新的连接,但是这个重新尝试的次数不应该超过 poolMaximumIdleConnectionspoolMaximumLocalBadConnectionTolerance 之和。 默认值:3(新增于 3.4.5)
  • poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否正常工作并准备接受请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动出错时返回恰当的错误消息。
  • poolPingEnabled – 是否启用侦测查询。若开启,需要设置 poolPingQuery 属性为一个可执行的 SQL 语句(最好是一个速度非常快的 SQL 语句),默认值:false。
  • poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的频率。可以被设置为和数据库连接超时时间一样,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)。

JNDI

这个数据源实现是为了能在如EJB或应用服务器这类容器中使用,容器以集中或在外部配置数据源,然后放置一个JNDI上下文的数据源引用。这种数据源配置只需要两个属性:

  • initial_context – 这个属性用来在 InitialContext 中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么将会直接从 InitialContext 中寻找 data_source 属性。
  • data_source – 这是引用数据源实例位置的上下文路径。提供了 initial_context 配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找。

数据库厂商标识

Mybatis可以根据不同的数据库厂商执行不同的语句。这种多厂商的支持是基于映射语句中的databaseId属性。

<!-- 5、databaseIdProvider:支持多数据库厂商的;
		 type="DB_VENDOR":VendorDatabaseIdProvider
		 	作用就是得到数据库厂商的标识(驱动getDatabaseProductName()),mybatis就能根据数据库厂商标识来执行不同的sql;
		 	MySQL,Oracle,SQL Server,xxxx
	  -->
	<databaseIdProvider type="DB_VENDOR">
		<!-- 为不同的数据库厂商起别名 -->
		<property name="MySQL" value="mysql"/>
		<property name="Oracle" value="oracle"/>
		<property name="SQL Server" value="sqlserver"/>
	</databaseIdProvider>
  • Type:DB_VENDOR

    使用Mybatis提供的VendorDatabasedProvider解析数据库厂商标识。也可以实现DatabaseIdProvider接口来定义。

  • Property-name:数据库厂商标识

  • Property-value:为标识起一个别名,方便SQL语句使用databaseId属性引用

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

Mybatis匹配规则如下:

  1. 如果没有配置databaseIdProvider标签,那么databaseId=null
  2. 如果配置了databaseIdProvider标签,使用标签配置的name去匹配数据库信息,匹配上设置databaseId=配置指定的值,否则依旧为null。
  3. 如果databaseId不为null,他只会找到配置databaseId的sql语句
  4. Mybatis会加载不带databaseId属性和带有匹配当前数据库databaseId属性的所有语句。如果同时找到带有databaseId和不带databaseId的相同语句,则后者会被抛弃。

映射器(mappers)

  • mapper逐个注册SQL映射文件
<!-- 使用相对于类路径的资源引用 -->
<mappers>
  <mapper resource="org/mybatis/builder/AuthorMapper.xml"/>
  <mapper resource="org/mybatis/builder/BlogMapper.xml"/>
  <mapper resource="org/mybatis/builder/PostMapper.xml"/>
</mappers>
<!-- 使用完全限定资源定位符(URL) -->
<mappers>
  <mapper url="file:///var/mappers/AuthorMapper.xml"/>
  <mapper url="file:///var/mappers/BlogMapper.xml"/>
  <mapper url="file:///var/mappers/PostMapper.xml"/>
</mappers>
<!-- 使用映射器接口实现类的完全限定类名 -->
<mappers>
  <mapper class="org.mybatis.builder.AuthorMapper"/>
  <mapper class="org.mybatis.builder.BlogMapper"/>
  <mapper class="org.mybatis.builder.PostMapper"/>
</mappers>
  • 使用批量注册
<!-- 将包内的映射器接口实现全部注册为映射器 -->
<mappers>
  <package name="com.atguigu.dao"/>
</mappers>

Mybatis-XML映射文件

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

select

查询语句是Mybatis中最常用的元素之一。

<!-- 
namespace:名称空间;指定为接口的全类名
id:唯一标识
resultType:返回值类型
#{id}:从传递过来的参数中取出id值
public Employee getEmpById(Integer id);
 -->
<select id="getEmpById"  paramterType = "int"resultType="com.atguigu.mybatis.bean.Employee">
		select * from tbl_employee where id = #{id}
	</select>

这个语句名为getEmpById,接受一个int类型的参数,并返回一个Employee类型的对象

select元素的属性

属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
resultType期望从这条语句中返回结果的类全限定名或别名。 注意,如果返回的是集合,那应该设置为集合包含的类型,而不是集合本身的类型。 resultType 和 resultMap 之间只能同时使用一个。
resultMap对外部 resultMap 的命名引用。结果映射是 MyBatis 最强大的特性,如果你对其理解透彻,许多复杂的映射问题都能迎刃而解。 resultType 和 resultMap 之间只能同时使用一个。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:false。
useCache将其设置为 true 后,将会导致本条语句的结果被二级缓存缓存起来,默认值:对 select 元素为 true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
fetchSize这是一个给驱动的建议值,尝试让驱动程序每次批量返回的结果行数等于这个设置值。 默认值为未设置(unset)(依赖驱动)。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
resultSetTypeFORWARD_ONLY,SCROLL_SENSITIVE, SCROLL_INSENSITIVE 或 DEFAULT(等价于 unset) 中的一个,默认值为 unset (依赖数据库驱动)。
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。
resultOrdered这个设置仅针对嵌套结果 select 语句:如果为 true,将会假设包含了嵌套结果集或是分组,当返回一个主结果行时,就不会产生对前面结果集的引用。 这就使得在获取嵌套结果集的时候不至于内存不够用。默认值:false
resultSets这个设置仅适用于多结果集的情况。它将列出语句执行后返回的结果集并赋予每个结果集一个名称,多个名称之间以逗号分隔。

insert、update、delete

<!-- public void addEmp(Employee employee); -->
	<!-- 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>
	<!-- public void updateEmp(Employee employee);  -->
	<update id="updateEmp">
		update tbl_employee 
		set last_name=#{lastName},email=#{email},gender=#{gender}
		where id=#{id}
	</update>
<!-- public void deleteEmpById(Integer id); -->
	<delete id="deleteEmpById">
		delete from tbl_employee where id=#{id}
	</delete>

元素属性

属性描述
id在命名空间中唯一的标识符,可以被用来引用这条语句。
parameterType将会传入这条语句的参数的类全限定名或别名。这个属性是可选的,因为 MyBatis 可以通过类型处理器(TypeHandler)推断出具体传入语句的参数,默认值为未设置(unset)。
parameterMap用于引用外部 parameterMap 的属性,目前已被废弃。请使用行内参数映射和 parameterType 属性。
flushCache将其设置为 true 后,只要语句被调用,都会导致本地缓存和二级缓存被清空,默认值:(对 insert、update 和 delete 语句)true。
timeout这个设置是在抛出异常之前,驱动程序等待数据库返回请求结果的秒数。默认值为未设置(unset)(依赖数据库驱动)。
statementType可选 STATEMENT,PREPARED 或 CALLABLE。这会让 MyBatis 分别使用 Statement,PreparedStatement 或 CallableStatement,默认值:PREPARED。
useGeneratedKeys(仅适用于 insert 和 update)这会令 MyBatis 使用 JDBC 的 getGeneratedKeys 方法来取出由数据库内部生成的主键(比如:像 MySQL 和 SQL Server 这样的关系型数据库管理系统的自动递增字段),默认值:false。
keyProperty(仅适用于 insert 和 update)指定能够唯一识别对象的属性,MyBatis 会使用 getGeneratedKeys 的返回值或 insert 语句的 selectKey 子元素设置它的值,默认值:未设置(unset)。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn(仅适用于 insert 和 update)设置生成键值在表中的列名,在某些数据库(像 PostgreSQL)中,当主键列不是表中的第一列的时候,是必须设置的。如果生成列不止一个,可以用逗号分隔多个属性名称。
databaseId如果配置了数据库厂商标识(databaseIdProvider),MyBatis 会加载所有不带 databaseId 或匹配当前 databaseId 的语句;如果带和不带的语句都有,则不带的会被忽略。

插入语句的规则更加丰富,在插入语句里面有一些额外的属性和子元素用来处理主键的生成,并且提供了多种生成方式。

首先,如果你的数据库支持自动生成主键的字段,那么你可以将useGeneratedKeys=”true”,然后再把 keyProperty 设置为目标属性就 OK 了。如下所示:

<insert id="insertAuthor" useGeneratedKeys="true"
    keyProperty="id">
  insert into Author (username,password,email,bio)
  values (#{username},#{password},#{email},#{bio})
</insert>

如果你的数据库还支持多行插入,你可以传入一个Author数组或集合。

<insert id="insertAuthor" useGeneratedKeys="true"
    keyProperty="id">
  insert into Author (username, password, email, bio) values
  <foreach item="item" collection="list" separator=",">
    (#{item.username}, #{item.password}, #{item.email}, #{item.bio})
  </foreach>
</insert>

对于不支持自动生成主键列的数据库和可能不支持自动生成主键的JDBC驱动,Mybatis有另外一种方法来生成主键。

<insert id="insertAuthor">
  <selectKey keyProperty="id" resultType="int" order="BEFORE">
    select CAST(RANDOM()*1000000 as INTEGER) a from SYSIBM.SYSDUMMY1
  </selectKey>
  insert into Author
    (id, username, password, email,bio, favourite_section)
  values
    (#{id}, #{username}, #{password}, #{email}, #{bio}, #{favouriteSection,jdbcType=VARCHAR})
</insert>

selectKey元素的属性

属性描述
keyPropertyselectKey 语句结果应该被设置到的目标属性。如果生成列不止一个,可以用逗号分隔多个属性名称。
keyColumn返回结果集中生成列属性的列名。如果生成列不止一个,可以用逗号分隔多个属性名称。
resultType结果的类型。通常 MyBatis 可以推断出来,但是为了更加准确,写上也不会有什么问题。MyBatis 允许将任何简单类型用作主键的类型,包括字符串。如果生成列不止一个,则可以使用包含期望属性的 Object 或 Map。
order可以设置为 BEFOREAFTER。如果设置为 BEFORE,那么它首先会生成主键,设置 keyProperty 再执行插入语句。如果设置为 AFTER,那么先执行插入语句,然后是 selectKey 中的语句 - 这和 Oracle 数据库的行为相似,在插入语句内部可能有嵌入索引调用。
statementType和前面一样,MyBatis 支持 STATEMENTPREPAREDCALLABLE 类型的映射语句,分别代表 Statement, PreparedStatementCallableStatement 类型。

参数

对于大多数简单的使用场景,你都不需要使用复杂的参数,如:

<select id="selectUsers" resultType="User">
  select id, username, password
  from users
  where id = #{id}
</select>

对于参数类型(paramterType)会被自动设置为int,这个参数可以随意命名。原始类型或简单数据类型因为没有其他属性,会用它们的值作为参数。然而,如果传入一个复杂的对象,行为就会有点不一样了。如:

<insert id="insertUser" parameterType="User">
  insert into users (id, username, password)
  values (#{id}, #{username}, #{password})
</insert>

如果User类型的参数对象传递到了语句中,会查找id、username和password属性,然后将它们的值传入预处理语句的参数中。

字符串转换

默认情况下,使用#{}参数语法时,Mybatis会创建PreparedStatement参数占位符,并通过占位符安全地设置参数(就像使用?一样)。这样做更安全,更迅速,通常也是首选做法,不过有时你想直接在SQL语句中直接插入一个不转义的字符串。

当SQL语句中的元数据(如表名或列名)是动态生成的时候,字符串替换将会非常有用。如果你想select一个表任意一列的数据时,不需要这样写:

@Select("select * from user where id = #{id}")
User findById(@Param("id") long id);

@Select("select * from user where name = #{name}")
User findByName(@Param("name") String name);

@Select("select * from user where email = #{email}")
User findByEmail(@Param("email") String email);

// 其它的 "findByXxx" 方法

而是只写这样一个方法:

@Select("select * from user where ${column} = #{value}")
User findByColumn(@Param("column") String column, @Param("value") String value);

其中${column}会被直接替换,而#{value}会使用?预处理。这样,就能完成同样的任务:

User userOfId1 = userMapper.findByColumn("id", 1L);
User userOfNameKid = userMapper.findByColumn("name", "kid");
User userOfEmail = userMapper.findByColumn("email", "noone@nowhere.com");

结果映射

result元素时Mybatis中最重要最强大的元素。它可以让你从90%的JDBCResultType数据提取代码中解放出来,并在一些情形下允许你进行一些JDBC不支持的操作。实际上,在为一些比如连接的复杂语句编写映射代码的时候,一份resultMap能够代替实现同等功能的数千行代码。ResultMap的设计思想是,对简单的语句做到零配置,对于复杂的语句,只需要描述语句之间的关系就行了。

一个简单的没有resultMap类型的语句:

<select id="selectUsers" resultType="map">
  select id, username, hashedPassword
  from some_table
  where id = #{id}
</select>

上述语句只是简单地将所有的列映射到HashMap的键上,这由resultType属性指定。虽然在大部分情况下是都够用的,但是HashMap并不是一个很好的领域模型。你的程序更可能会使用JavaBean或者POJO作为领域模型。

package com.someapp.model;
public class User {
  private int id;
  private String username;
  private String hashedPassword;

  public int getId() {
    return id;
  }
  public void setId(int id) {
    this.id = id;
  }
  public String getUsername() {
    return username;
  }
  public void setUsername(String username) {
    this.username = username;
  }
  public String getHashedPassword() {
    return hashedPassword;
  }
  public void setHashedPassword(String hashedPassword) {
    this.hashedPassword = hashedPassword;
  }
}

基于 JavaBean 的规范,上面这个类有 3 个属性:id,username 和 hashedPassword。这些属性会对应到 select 语句中的列名。

这样的一个 JavaBean 可以被映射到 ResultSet,就像映射到 HashMap 一样简单。

<select id="selectUsers" resultType="com.someapp.model.User">
  select id, username, hashedPassword
  from some_table
  where id = #{id}
</select

显示的使用resultMap,可以解决列名不匹配的另外一种方式:

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

然后再引用它的语句中设置resultMap属性就行了。如:

<select id="selectUsers" resultMap="userResultMap">
  select user_id, user_name, hashed_password
  from some_table
  where id = #{id}
</select>
高级结果映射

例如下面一个复杂的语句:

<!-- 非常复杂的语句 -->
<select id="selectBlogDetails" resultMap="detailedBlogResultMap">
  select
       B.id as blog_id,
       B.title as blog_title,
       B.author_id as blog_author_id,
       A.id as author_id,
       A.username as author_username,
       A.password as author_password,
       A.email as author_email,
       A.bio as author_bio,
       A.favourite_section as author_favourite_section,
       P.id as post_id,
       P.blog_id as post_blog_id,
       P.author_id as post_author_id,
       P.created_on as post_created_on,
       P.section as post_section,
       P.subject as post_subject,
       P.draft as draft,
       P.body as post_body,
       C.id as comment_id,
       C.post_id as comment_post_id,
       C.name as comment_name,
       C.comment as comment_text,
       T.id as tag_id,
       T.name as tag_name
  from Blog B
       left outer join Author A on B.author_id = A.id
       left outer join Post P on B.id = P.blog_id
       left outer join Comment C on P.id = C.post_id
       left outer join Post_Tag PT on PT.post_id = P.id
       left outer join Tag T on PT.tag_id = T.id
  where B.id = #{id}
</select>

对应的复杂结果映射

!-- 非常复杂的结果映射 -->
<resultMap id="detailedBlogResultMap" type="Blog">
  <constructor>
    <idArg column="blog_id" javaType="int"/>
  </constructor>
  <result property="title" column="blog_title"/>
  <association property="author" javaType="Author">
    <id property="id" column="author_id"/>
    <result property="username" column="author_username"/>
    <result property="password" column="author_password"/>
    <result property="email" column="author_email"/>
    <result property="bio" column="author_bio"/>
    <result property="favouriteSection" column="author_favourite_section"/>
  </association>
  <collection property="posts" ofType="Post">
    <id property="id" column="post_id"/>
    <result property="subject" column="post_subject"/>
    <association property="author" javaType="Author"/>
    <collection property="comments" ofType="Comment">
      <id property="id" column="comment_id"/>
    </collection>
    <collection property="tags" ofType="Tag" >
      <id property="id" column="tag_id"/>
    </collection>
    <discriminator javaType="int" column="draft">
      <case value="1" resultType="DraftPost"/>
    </discriminator>
  </collection>
</resultMap>

一一来解开这个面纱

  • constructor
    

    - 用于在实例化类时,注入结果到构造方法中

    • idArg - ID 参数;标记出作为 ID 的结果可以帮助提高整体性能
    • arg - 将被注入到构造方法的一个普通结果
  • id – 一个 ID 结果;标记出作为 ID 的结果可以帮助提高整体性能

  • result – 注入到字段或 JavaBean 属性的普通结果

  • association
    

    – 一个复杂类型的关联;许多结果将包装成这种类型

    • 嵌套结果映射 – 关联可以是 resultMap 元素,或是对其它结果映射的引用
  • collection
    

    – 一个复杂类型的集合

    • 嵌套结果映射 – 集合可以是 resultMap 元素,或是对其它结果映射的引用
  • discriminator
    

    – 使用结果值来决定使用哪个

    resultMap
    
    • case 
      

      – 基于某些值的结果映射

      • 嵌套结果映射 – case 也是一个结果映射,因此具有相同的结构和元素;或者引用其它的结果映射

ResultMap的属性列表

属性描述
id当前命名空间中的一个唯一标识,用于标识一个结果映射。
type类的完全限定名, 或者一个类型别名(关于内置的类型别名,可以参考上面的表格)。
autoMapping如果设置这个属性,MyBatis 将会为本结果映射开启或者关闭自动映射。 这个属性会覆盖全局的属性 autoMappingBehavior。默认值:未设置(unset)
id&result
<id property="id" column="post_id"/>
<result property="subject" column="post_subject"/>

这些元素是结果映射的基础。id和result元素都将一个列的值映射到一个简单的数据类型的属性或字段。

这两者之间唯一不同的是,id元素对应的属性会被标记为对象昂的标识符,在比较对象实例时使用。

id和result的属性

属性描述
property映射到列结果的字段或属性。如果 JavaBean 有这个名字的属性(property),会先使用该属性。否则 MyBatis 将会寻找给定名称的字段(field)。 无论是哪一种情形,你都可以使用常见的点式分隔形式进行复杂属性导航。 比如,你可以这样映射一些简单的东西:“username”,或者映射到一些复杂的东西上:“address.street.number”。
column数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。
javaType一个 Java 类的全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
jdbcTypeJDBC 类型,所支持的 JDBC 类型参见这个表格之后的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可以为空值的列指定这个类型。
typeHandler我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的全限定名,或者是类型别名。

支持的JDBC类型

Mybatis通过内置的jdbcType枚举类型支持下面的JDBC类型。

BITFLOATCHARTIMESTAMPOTHERUNDEFINED
TINYINTREALVARCHARBINARYBLOBNVARCHAR
SMALLINTDOUBLELONGVARCHARVARBINARYCLOBNCHAR
INTEGERNUMERICDATELONGVARBINARYBOOLEANNCLOB
BIGINTDECIMALTIMENULLCURSORARRAY
构造方法

通过修改对象属性的方式,可以满足大多数的数据传输对象以及绝大部分领域模型的要求。但是有些情况下你想使用不可变的类。一般来说,很少改变或基本不变的包含引用或数据的表,很适合使用不可变类。构造方法注入允许你在初始化时为类设置属性的值,而不用暴露共有方法。Mybatis也支持私有属性和私有JavaBean属性来完成注入,但有一些人更青睐于通过构造方法注入。constructor元素就是为此而生的。

下面看这个构造方法:

public class User {
   //...
   public User(Integer id, String username, int age) {
     //...
  }
//...
}

为了将结果注入构造方法,Mybatis需要通过某种定位方式定位相应的构造方法。在下面的例子中,MyBatis 搜索一个声明了三个形参的构造方法,参数类型以 java.lang.Integer, java.lang.Stringint 的顺序给出。

<constructor>
   <idArg column="id" javaType="int"/>
   <arg column="username" javaType="String"/>
   <arg column="age" javaType="_int"/>
</constructor>
属性描述
column数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。
javaType一个 Java 类的完全限定名,或一个类型别名(关于内置的类型别名,可以参考上面的表格)。 如果你映射到一个 JavaBean,MyBatis 通常可以推断类型。然而,如果你映射到的是 HashMap,那么你应该明确地指定 javaType 来保证行为与期望的相一致。
jdbcTypeJDBC 类型,所支持的 JDBC 类型参见这个表格之前的“支持的 JDBC 类型”。 只需要在可能执行插入、更新和删除的且允许空值的列上指定 JDBC 类型。这是 JDBC 的要求而非 MyBatis 的要求。如果你直接面向 JDBC 编程,你需要对可能存在空值的列指定这个类型。
typeHandler我们在前面讨论过默认的类型处理器。使用这个属性,你可以覆盖默认的类型处理器。 这个属性值是一个类型处理器实现类的完全限定名,或者是类型别名。
select用于加载复杂类型属性的映射语句的 ID,它会从 column 属性中指定的列检索数据,作为参数传递给此 select 语句。具体请参考关联元素。
resultMap结果映射的 ID,可以将嵌套的结果集映射到一个合适的对象树中。 它可以作为使用额外 select 语句的替代方案。它可以将多表连接操作的结果映射成一个单一的 ResultSet。这样的 ResultSet 将会将包含重复或部分数据重复的结果集。为了将结果集正确地映射到嵌套的对象树中,MyBatis 允许你 “串联”结果映射,以便解决嵌套结果集的问题。想了解更多内容,请参考下面的关联元素。
name构造方法形参的名字。从 3.4.3 版本开始,通过指定具体的参数名,你可以以任意顺序写入 arg 元素。参看上面的解释。
discriminator鉴别器

可以用来判断某列的值,然后自定义根据得到某列的不同值做出不同的封装行为

<!-- discriminator:鉴别器,可以用来判断某列的值,然后根据某列的值改变封装行为
		假设:
			如果查询结果是女性,则把部门信息查询出来,否则不查询
			如果是男性,把last_name这一列的值赋值给email
-->
<resultMap type="com.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"/>
 		
 		<association property="dept"
 		select="com.dao.DepartmentMapper.getDeptById"
 		column="d_id">
 		</association>
 		
 		<!-- 
 			column:指定要判断的列名 
 			javaType:列值对应的java类型
 		-->
 		<discriminator javaType="string" column="gender">
 			<!-- 女生 -->
 			<!-- resultType不可缺少,也可以使用resutlMap -->
 			<case value="0" resultType="com.bean.Employee">
 				<association property="dept"
 					select="com.dao.DepartmentMapper.getDeptById"
 					column="d_id">
 				</association>
 			</case>
 			<!-- 男生 -->
 			<case value="1" resultType="com.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>

关联
<association property="author" column="blog_author_id" javaType="Author">
  <id property="id" column="author_id"/>
  <result property="username" column="author_username"/>
</association>

在我们的示例中,一个博客有一个用户。关联结果映射和其他类型的映射工作方式差不多。你需要指定目标属性名以及属性的javaType。

关联的不同之处是,你需要告诉Mybatis如何进行加载关联。Mybatis有两种不同的方式加载关联:

  • 嵌套 Select 查询:通过执行另外一个 SQL 映射语句来加载期望的复杂类型。
  • 嵌套结果映射:使用嵌套的结果映射来处理连接结果的重复子集

关联嵌套的Select查询

属性描述
column数据库中的列名,或者是列的别名。一般情况下,这和传递给 resultSet.getString(columnName) 方法的参数一样。 注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得 prop1prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。
select用于加载复杂类型属性的映射语句的 ID,它会从 column 属性指定的列中检索数据,作为参数传递给目标 select 语句。 具体请参考下面的例子。注意:在使用复合主键的时候,你可以使用 column="{prop1=col1,prop2=col2}" 这样的语法来指定多个传递给嵌套 Select 查询语句的列名。这会使得 prop1prop2 作为参数对象,被设置为对应嵌套 Select 语句的参数。
fetchType可选的。有效值为 lazyeager。 指定属性后,将在映射中忽略全局配置参数 lazyLoadingEnabled,使用属性的值。

示例:

<resultMap id="blogResult" type="Blog">
  <association property="author" column="author_id" javaType="Author" select="selectAuthor"/>
</resultMap>

<select id="selectBlog" resultMap="blogResult">
  SELECT * FROM BLOG WHERE ID = #{id}
</select>

<select id="selectAuthor" resultType="Author">
  SELECT * FROM AUTHOR WHERE ID = #{id}
</select>

一个用来加载博客,另外一个用来加载作者。而且博客的结果映射描述了应用使用selectAuthor语句加载author属性。

集合
<collection property="posts" ofType="domain.blog.Post">
  <id property="id" column="post_id"/>
  <result property="subject" column="post_subject"/>
  <result property="body" column="post_body"/>
</collection>

集合元素和关联元素几乎是一样的,它们相似程度很高。

每个部门有多个员工,即在部门中员工是一个集合属性

使用collection嵌套封装结果集

collection定义关联的集合

ofType:指定集合里的元素类型

<resultMap type="com.bean.Department" id="MyDept">
		...
		<collection property="emps" ofType="com.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>

使用collection进行分步查询、延迟加载与association类似

扩展
要是在使用association或者collection时需要将多个列的值传递给调用的方法,可以将多列的值封装到map中进行传递column="{key1 = column1, key2 = column2}"

被调用的方法

<select id="getEmpsByDeptId" resultMap="xxx">
		select xxx from tbl_employee where id = #{deptId}
</select>

调用该方法的xml文件中:

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

即使全局开启了延迟加载,仍然可以在association或者collection标签中修改fetchType的值来开启或关闭延迟加载。

动态SQL

动态SQL是Mybatis的强大特性之一。如果你使用过JDBC或其他类似的框架,你应该能理解根据不同条件拼接SQL语句有多痛苦。

if

使用动态SQL最常见的情景是根据条件包含where子句的一部分。如:

<select id="findActiveBlogWithTitleLike"
     resultType="Blog">
  SELECT * FROM BLOG
  WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
</select>

如果希望通过“title”和“author”两个参数进行可选搜索怎么办呢?

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG WHERE state = ‘ACTIVE’
  <if test="title != null">
    AND title like #{title}
  </if>
  <if test="author != null and author.name != null">
    AND author_name like #{author.name}
  </if>
</select

choose、when、otherwise

有时候,我们不想使用所有的条件,而只是想从多个条件中选择一个使用。针对这种情况,Mybatis提供了choose元素,有点像java中的switch语句。

<!-- public List<Employee> getEmpsByConditionChoose(Employee employee); -->
	 <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>

trim、where、set

<select id="findActiveBlogLike"
     resultType="Blog">
  SELECT * FROM BLOG
  <where>
    <if test="state != null">
         state = #{state}
    </if>
    <if test="title != null">
        AND title like #{title}
    </if>
    <if test="author != null and author.name != null">
        AND author_name like #{author.name}
    </if>
  </where>
</select>

where元素只会在子元素返回任何内容的情况下才插入“WHERE”子句。而且,若子句的开头为AND或OR,where元素也会将它们去除。

如果where元素与你期望的不太一样,你也可以通过自定义trim元素来定制where元素的功能。如

<trim prefix="WHERE" prefixOverrides="AND |OR ">
  ...
</trim>

prefixOverrides属性会忽略通过管道符分割的文本序列。上述例子会移除所有prefixOverrides属性中指定的内容,并且插入prefix属性中指定的内容。

<!--public List<Employee> getEmpsByConditionTrim(Employee employee);  -->
	 <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>

用于动态更新语句的类似解决方案叫做set,set元素可以用于动态包含需要更新的列,忽略其他不更新的列。

 <!--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>
		where id=#{id} 
	 </update>

foreach

动态SQL的另一个常见使用场景是对集合进行遍历,如:

 <!--public List<Employee> getEmpsByConditionForeach(List<Integer> ids);  -->
	 <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>

foreach元素的功能非常强大,它允许你指定一个集合,声明可以在元素体内是使用的集合项(item)和(index)变量。它允许你指定开头与结尾的字符串以及集合项迭代之间的分割符。这个元素也不会错误地添加多余的分隔符。

bind

bind元素允许你在OGNL表达式以外创建一个变量,并将其绑定到当前的上下文,如:

 <!--public List<Employee> getEmpsTestInnerParameter(Employee employee);  -->
	  <select id="getEmpsTestInnerParameter" resultType="com.atguigu.mybatis.bean.Employee">
	  		<!-- bind:可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值 -->
	  		<bind name="_lastName" value="'%'+lastName+'%'"/>
	  		<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>

多数据库支持

如果配置了databaseIdProvider,你就可以在动态代码中使用名“_databased”的变量来为不同的数据库构建特定的语句。如下:

<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);  -->
	  <select id="getEmpsTestInnerParameter" resultType="com.atguigu.mybatis.bean.Employee">
	  		<!-- bind:可以将OGNL表达式的值绑定到一个变量中,方便后来引用这个变量的值 -->
	  		<bind name="_lastName" value="'%'+lastName+'%'"/>
	  		<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>

缓存机制

Mybatis包含一个非常强大的查询缓存特性,它可以非常方便地配置和定制。缓存可以极大的提升查询效率。

Mybatis系统中默认定义了两级缓存。

一级缓存和二级缓存

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

一级缓存

本地缓存,作用域默认为SqlSession.当Session flush或close后,该Session中的所有Cache将被清空。

本地缓存不能被关闭,但可以调用clearCache()来清空本地缓存,或者改变缓存的作用域。

同一次会话期间只要查询过的数据都会保存在当前SqlSession的一个Map中

  • key:hashCode + 查询色SqlId+编写的sql查询语句+参数

一级缓存失效的四种情况

  • 不同的SqlSession对应不同的一级缓存
  • 同一个SqlSession但是查询条件不同
  • 同一个SqlSession两次查询期间执行了任何一次增删改操作
  • 同一个SqlSession两次查询期间手动清空了缓存

二级缓存

二级缓存,全局作用缓存

二级缓存默认不开启,需要手动配置

Mybatis提供二级缓存的接口以及实现,缓存实现要求POJO是实现Serializable接口

二级缓存在SqlSession关闭或提交之后才会生效

使用步骤:

  1. 在全局配置文件中开启二级缓存

  2. 需要使用二级缓存的映射文件处使用cache配置缓存

  3. 注意:POJO需要实现Serializable接口

缓存相关属性

eviction=“FIFO”:缓存回收策略

  • LRU-最近最少使用的:移除最长时间不被使用的对象
  • FIFO-先进先出:按对象进入缓存的顺序来移除它们。
  • SOFT-软引用:移除基于垃圾回收器状态和软引用规则的对象
  • WEAK-弱引用:更积极地移除基于垃圾收集器和弱引用规则的对象。
  • 默认是LRU。

缓存有关设置

  • 全局setting的cacheEnable:

    配置二级缓存的开关。一级缓存一直是打开的

  • select标签的useCache属性:

    配置这个select是否使用二级缓存。一级缓存一直是使用的

  • sql标签的flushCache属性

    增删改默认flushCache=true.sql执行以后,会同时清空一级和二级缓存。查询默认flushCache=false.

  • sqlSession.clearCache():

    只是用来清除一级缓存

  • 当在某一个作用域(一级缓存Session/二级缓存Namespace)进行了C/U/D操作后,默认该作用域下所有select中的缓存将被clear.

Mybatis-逆向工程(Generator生成器使用)IDEA

环境版本

Mysql-8.0
springboot-2.1.6
jdk-1.8

1.新建项目

2.在pom文件的<build>下的<plugins>添加以下配置

记住mysql连接一定添加在plugins中

72gx0I.png

<plugin>
                <groupId>org.mybatis.generator</groupId>
                <artifactId>mybatis-generator-maven-plugin</artifactId>
                <version>1.3.5</version>
                <configuration>
                    <!-- 在控制台打印执行日志 -->
                    <verbose>true</verbose>
                    <!-- 重复生成时会覆盖之前的文件-->
                    <overwrite>true</overwrite>
                    <configurationFile>src/main/resources/generatorConfig.xml</configurationFile>
                </configuration>
                <!-- 数据库连接选择8.0以上的,因为用的mysql8.0-->
            <dependencies>
                <dependency>
                    <groupId>mysql</groupId>
                    <artifactId>mysql-connector-java</artifactId>
                    <version>8.0.16</version>
                </dependency>
            </dependencies>
            </plugin>

3.新建后面需要使用到的包

7221c4.png

4.在mysql中建立测试数据库wg_inset

722fC8.png

5.在resources下创建generatorConfig.xml

72R0I0.png

72WCQg.png

具体内容:

<?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>
    <!-- context 是逆向工程的主要配置信息 -->
    <!-- id:起个名字 -->
    <!-- targetRuntime:设置生成的文件适用于那个 mybatis 版本 -->
    <context id="default" targetRuntime="MyBatis3">
        <!--optional,指在创建class时,对注释进行控制-->
        <commentGenerator>
            <property name="suppressDate" value="true"/>
            <!-- 是否去除自动生成的注释 true:是 : false:否 -->
            <property name="suppressAllComments" value="true"/>
        </commentGenerator>
        <!--jdbc的数据库连接 wg_insert 为数据库名字-->
        <jdbcConnection driverClass="com.mysql.cj.jdbc.Driver"
                        connectionURL="jdbc:mysql://localhost:3306/wg_insert?useUnicode=true&amp;characeterEncoding=utf-8&amp;serverTimezone=UTC" userId="root"
                        password="123456"></jdbcConnection>
        <!--非必须,类型处理器,在数据库类型和java类型之间的转换控制-->
        <javaTypeResolver>
            <!-- 默认情况下数据库中的 decimal,bigInt 在 Java 对应是 sql 下的 BigDecimal 类 -->
            <!-- 不是 double 和 long 类型 -->
            <!-- 使用常用的基本类型代替 sql 包下的引用类型 -->
            <property name="forceBigDecimals" value="false"/>
        </javaTypeResolver>
        <!-- targetPackage:生成的实体类所在的包 -->
        <!-- targetProject:生成的实体类所在的硬盘位置 -->
        <javaModelGenerator targetPackage="com.wglvzyx.mybatisredis.entity"
                            targetProject="src/main/java">
            <!-- 是否允许子包 -->
            <property name="enableSubPackages" value="false"/>
            <!-- 是否对modal添加构造函数 -->
            <property name="constructorBased" value="true"/>
            <!-- 是否清理从数据库中查询出的字符串左右两边的空白字符 -->
            <property name="trimStrings" value="true"/>
            <!-- 建立modal对象是否不可改变 即生成的modal对象不会有setter方法,只有构造方法 -->
            <property name="immutable" value="false"/>
        </javaModelGenerator>
        <!-- targetPackage 和 targetProject:生成的 mapper 文件的包和位置 -->
        <sqlMapGenerator targetPackage="mapper"
                         targetProject="src/main/resources">
            <!-- 针对数据库的一个配置,是否把 schema 作为字包名 -->
            <property name="enableSubPackages" value="false"/>
        </sqlMapGenerator>
        <!-- targetPackage 和 targetProject:生成的 interface 文件的包和位置 -->
        <javaClientGenerator type="XMLMAPPER"
                             targetPackage="com.wglvzyx.mybatisredis.dao" targetProject="src/main/java">
            <!-- 针对 oracle 数据库的一个配置,是否把 schema 作为字包名 -->
            <property name="enableSubPackages" value="false"/>
        </javaClientGenerator>
        <!-- tableName是数据库中的表名,domainObjectName是生成的JAVA模型名,后面的参数不用改,要生成更多的表就在下面继续加table标签 -->
        <table tableName="student" domainObjectName="Student"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false"></table>
        <!-- tableName是数据库中的表名,domainObjectName是生成的JAVA模型名,后面的参数不用改,要生成更多的表就在下面继续加table标签 -->
        <table tableName="post" domainObjectName="Post"
               enableCountByExample="false" enableUpdateByExample="false"
               enableDeleteByExample="false" enableSelectByExample="false"
               selectByExampleQueryId="false"></table>
    </context>
</generatorConfiguration>

6.跑起来看是否成功

72fZBd.png

[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 4.562 s
[INFO] Finished at: 2019-07-29T11:55:44+08:00
[INFO] Final Memory: 17M/187M
[INFO] ------------------------------------------------------------------------

Process finished with exit code 0
控制台出现这个。表示成功了

查看生成的文件

72hwdA.png

Mybatis运行原理

72TTr8.png

  1. SqlMapConfig.xml,此文件作为MyBatis的全局配置文件,配置了mybatis的运行环境等信息。Mapper.xml文件即SQL映射文件,文件中配置了操作数据库的sql语句。此文件需要在SqlMapConfig.xml中加载。
  2. 通过MyBatis环境等配置信息构造SqlSessionFactory即会话工厂
  3. 由会话工厂创建SqlSession即会话,操作数据库需要通过SqlSession进行。
  4. Mybatis底层自定义了Executor执行器接口操作数据库,Executor接口有两个实现,一个是基本执行器、一个是缓存执行器。
  5. Mapped Statement也是MyBatis一个底层封装对象,它包装了MyBatis配置信息及sql映射信息等。mapper.xml文件中一个sql对应一个Mapped Statement对象,sql的id即是Mapped statement的id。
  6. Mapped Statement对sql执行输入参数进行定义,包括HashMap、基本类型、pojo,Executor通过Mapped Statement在执行sql前将输入的java对象映射至sql中,输入参数映射就是jdbc编程中对PreparedStatement设置参数。
  7. Mapped Statement对sql执行输出结果进行定义,包括HashMap、基本类型、Pojo,Executor通过Mapped Statement在执行sql后将输出结果映射至Java对象中,输出结果映射过程相当于Jdbc编程中对结果的解析处理过程

1、根据配置文件创建SQLSessionFactory

727Dij.png

2、返回SqlSession的实现类DefaultSqlSession对象。他里面包含了Executor和Configuration;Executor会在这一步被创建

72ODxA.png

3、getMapper返回接口的代理对象包含了SqlSession对象

72Ol8J.png

4、查询流程

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tkKOUMVL-1644221812317)(C:\Users\QJS\AppData\Roaming\Typora\typora-user-images\image-20220121142901726.png)]

Mybatis-插件开发

1 PageHelper作用

官网:https://pagehelper.github.io/

PageHelper是一个Mybatis的分页插件, 负责将已经写好的sql语句, 进行分页加工。官网说法:如果你也在用 MyBatis,建议尝试该分页插件,这一定是最方便使用的分页插件。分页插件支持任何复杂的单表、多表分页。

优点:无需你自己去封装以及关心sql分页等问题,使用很方便,前端取数据也很方便。

2 导入PageHelper插入依赖

<!--MyBatis的PageHelper插件-->
<dependency>
    <groupId>com.github.pagehelper</groupId>
    <artifactId>pagehelper</artifactId>
    <version>5.0.3</version>
</dependency>

3 添加分页插件配置

3.1SqlMapConfig.xml配置

如果是单独的Mybatis框架,需要在SqlMapConfig.xml添加分页插件的配置

<!--
    plugins在配置文件中的位置必须符合要求,否则会报错,顺序如下:
    properties?, settings?,
    typeAliases?, typeHandlers?,
    objectFactory?,objectWrapperFactory?,
    plugins?,
    environments?, databaseIdProvider?, mappers?
-->
<plugins>
    <!-- com.github.pagehelper为PageHelper类所在包名 -->
    <plugin interceptor="com.github.pagehelper.PageInterceptor">
        <!--数据库方言-->
        <property name="helperDialect" value="mysql"/>
        <!--合理化分页-->
        <property name="reasonable" value="true"/>
    </plugin>
</plugins>

必须注意配置的顺序,顺序出错,会执行报错!

3.2applicationContext.xml

如果是Mybatis与Spring整合,需要修改Spring的applicationContext.xml

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">  
  <!-- 注意:这里还有其他Spring相关配置 -->
  <property name="plugins">
    <array>
      <bean class="com.github.pagehelper.PageInterceptor">
        <property name="properties">
          <value>
            helperDialect=mysql
            reasonable=true
          </value>
        </property>
      </bean>
    </array>
  </property>
</bean>

4 编写Dao方法

package com.yiidian.dao;
import com.yiidian.domain.Customer;
import java.util.List;

/**
 * Dao接口
 */
public interface CustomerDao {
    /**
     * 查询所有用户
     */
    public List<Customer> findAll();
}

5、编写测试类

package com.yiidian.mybatis;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.yiidian.dao.CustomerDao;
import com.yiidian.domain.Customer;
import com.yiidian.utils.MyBatisUtils;
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 java.io.IOException;
import java.io.InputStream;
import java.util.List;

/**
 * MyBatis测试类 - PageHelper分页插件的使用
 */
public class TestCustomerDao {
    @Test
    public void test1(){
        //1.获取SqlSession对象
        SqlSession sqlSession = MyBatisUtils.getSession();

        //2.生成Dao代理对象
        CustomerDao customerDao = sqlSession.getMapper(CustomerDao.class);

        //3.设置分页参数
        int pageNun = 1; // 页码
        int pageSize = 2;// 每页显示条数
        PageHelper.startPage(pageNun,pageSize);

        //4. 查询用户数据
        List<Customer> list = customerDao.findAll();

        //5.把用户数据封装到PageInfo分页结果对象
        PageInfo<Customer> page = new PageInfo<>(list);

        //6.取出PageInfo的属性
        //测试PageInfo全部属性
        //PageInfo包含了非常全面的分页属性
        System.out.println("当前页码="+page.getPageNum());
        System.out.println("每页显示条数="+page.getPageSize());
        System.out.println("当前页起始行号="+page.getStartRow());
        System.out.println("当前页结束行号="+page.getEndRow());
        System.out.println("总记录数="+page.getTotal());
        System.out.println("总页数="+page.getPages());
        System.out.println("是否为第1页="+page.isIsFirstPage());
        System.out.println("是否为最后1页="+page.isIsLastPage());
        System.out.println("是否有上一页="+page.isHasPreviousPage());
        System.out.println("是否有下一页="+page.isHasNextPage());
        System.out.println("当前页数据=");
        for(Customer c:page.getList()){
            System.out.println(c);
        }
        //7.关闭连接
        sqlSession.close();
    }
}

6、测试结果

PageHelper把所有分页结果数据封装到PageInfo对象,下面是打印出常用的属性

7R9BuQ.png

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.junit.Test;

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

/**

  • MyBatis测试类 - PageHelper分页插件的使用
    */
    public class TestCustomerDao {
    @Test
    public void test1(){
    //1.获取SqlSession对象
    SqlSession sqlSession = MyBatisUtils.getSession();

     //2.生成Dao代理对象
     CustomerDao customerDao = sqlSession.getMapper(CustomerDao.class);
    
     //3.设置分页参数
     int pageNun = 1; // 页码
     int pageSize = 2;// 每页显示条数
     PageHelper.startPage(pageNun,pageSize);
    
     //4. 查询用户数据
     List<Customer> list = customerDao.findAll();
    
     //5.把用户数据封装到PageInfo分页结果对象
     PageInfo<Customer> page = new PageInfo<>(list);
    
     //6.取出PageInfo的属性
     //测试PageInfo全部属性
     //PageInfo包含了非常全面的分页属性
     System.out.println("当前页码="+page.getPageNum());
     System.out.println("每页显示条数="+page.getPageSize());
     System.out.println("当前页起始行号="+page.getStartRow());
     System.out.println("当前页结束行号="+page.getEndRow());
     System.out.println("总记录数="+page.getTotal());
     System.out.println("总页数="+page.getPages());
     System.out.println("是否为第1页="+page.isIsFirstPage());
     System.out.println("是否为最后1页="+page.isIsLastPage());
     System.out.println("是否有上一页="+page.isHasPreviousPage());
     System.out.println("是否有下一页="+page.isHasNextPage());
     System.out.println("当前页数据=");
     for(Customer c:page.getList()){
         System.out.println(c);
     }
     //7.关闭连接
     sqlSession.close();
    

    }
    }


### 6、测试结果

PageHelper把所有分页结果数据封装到PageInfo对象,下面是打印出常用的属性

[[外链图片转存中...(img-PIIaQsIL-1644221812318)]](https://imgtu.com/i/7R9BuQ)
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值