昔洛 的个人博客

Bug不空,誓不成佛

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

第七篇 MyBatis的核心配置

MyBatis 的核心对象

1、SqlSessionFactory

    SqlSessionFactory 是 MyBatis 框架中十分重要的对象,它是单个数据库映射关系经过编译后的内存镜像,其主要作用是创建 SqlSession。SqlSessionFactory 对象的实例可以通过 SqlSessionFactoryBuilder 对象来构建,而SQLSessionFactoryBuilder 则可以通过XML 配置文件或一个预先定义好的 Configuration 实例构建出 SqlSessionFactory 的实例。代码如下:

//读取配置文件
InputStream inputStream = Resources.getResourceAsStream("配置文件位置");
//根据配置文件构建 SqlSessionFactory
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

    SqlSessionFactory 对象是线程安全的,它一旦被创建,在整个应用执行期间都会存在。如果我们多次地创建同一个数据库的SqlSessionFactory,那么此数据库的资源将很容易被耗尽。为了解决此问题,通常每一个数据库都会只对应一个 SqlSessionFactory,所以在构建 SqlSessionFactory 实例时,建议使用单列模式。

2、SqlSession

    SqlSession 是 MyBatis 框架中另一个重要的对象,它是应用程序与持久层之间执行交互操作的一个单线程对象,其主要作用是执行持久操作。SqlSession 对象包含了数据库中所有执行SQL操作的方法,由于其底层封装了 JDBC 连接,所以可以直接使用其实例来执行已映射的SQL语句。
    每一个线程都应该有一个自己的 SqlSession 实例,并且该实例是不能被共享的。同时,SqlSession 实例也是线程不安全的,因此其使用范围最好在一次请求或一个方法中,绝不能将其放在一个类的静态字段、实例字段或任何类型的管理范围(如 Servlet 的 HTTPSession)中使用。使用完 SqlSession 对象之后,要及时地关闭它,通常可以将其放在 finally 块中关闭,代码如下:

SqlSession sqlSession = sqlSessionFactory.openSession();
try{
	//此处执行持久化操作
} finally {
	sqlSession.close();
}

SqlSession对象中包含了很多方法,其常用方法如下所示:

  • <T> T selectOne(String statement);
    查询方法。参数 statement 是在配置文件中定义的<select>元素的id。使用该方法后,会返回执行SQL语句查询结果的一条泛型对象。
  • <T> T selectOne(String statement, Object parameter);
    查询方法。参数 statement 是在配置文件中定义的<select>元素的id,parameter 是查询所需的参数。使用该方法后,会返回执行 SQL 语句查询的结果的一条泛型对象。
  • <E> List<E> selectList(String statement);
    查询方法。参数 statement 是在配置文件中定义的<select>元素的id。使用该方法后,会返回执行SQL语句查询结果的泛型对象的集合。
  • <E> List<E> selectList(String statement, Object parameter);
    查询方法。参数 statement 是在配置文件中定义的<select>元素的id,parameter 是查询所需的参数。使用该方法后,会返回执行SQL语句查询结果的泛型对象的集合。
  • <E> List<E> selectList(String statement,Object parameter,RowBounds rowBounds);
    查询方法。参数 statement 是在配置文件中定义的<select>元素的id,parameter 是查询所需的参数,rowBounds 是用于分页的参数对象。使用该方法后,会返回执行SQL语句查询结果的泛型对象集合。
  • void select(String statement,Object parameter,ResultHandler handler);
    查询方法。参数 statement 是在配置文件中定义的<select>元素的id,parameter 是查询所需的参数,ResultHandler 对象用于处理查询返回的复杂结果集,通常用于多表查询。
  • int insert(String statement);
    插入方法。参数 statement 是在配置文件中定义的<insert>元素的id。使用该方法后,会返回执行SQL语句所影响的行数。
  • int insert(String statement,Object parameter);
    插入方法。参数 statement 是在配置文件中定义的<insert>元素的id,parameter 是插入所需的参数。使用该方法后,会返回执行SQL语句所影响的行数。
  • int update(String statement);
    更新方法。参数 statement 是在配置文件中定义的<update>元素的id。使用该方法后,会返回执行SQL语句所影响的行数。
  • int update(String statement,Object parameter);
    更新方法。参数 statement 是在配置文件中定义的<update>元素的id,parameter 是更新所需的参数。使用该方法后,会返回执行SQL语句所影响的行数。
  • int delete(String statement);
    删除方法。参数 statement 是在配置文件中定义的<delete>元素的id。使用该方法后,会返回执行SQL语句所影响的行数。
  • int delete(String statement,Object parameter);
    删除方法。参数 statement 是在配置文件中定义的<delete>元素的id,parameter 是删除所需的参数。使用该方法后,会返回执行SQL语句所影响的行数。
  • void commit();
    提交事务的方法。
  • void rollback();
    回滚事务的方法;
  • void close();
    关闭 SqlSession 对象。
  • <T> T getMapper(Class<T> type);
    该方法会返回 Mapper 接口的代理对象,该对象关联的 SqlSession 对象,开发人员可以使用该对象直接调用方法操作数据库。参数 type 是 Mapper 的接口类型。MyBatis 官方推荐通过 Mapper 对象访问 MyBatis。
  • Connection getConnection();
    获取 JDBC 数据库连接对象的方法。

使用工具类创建 SqlSession
    在上一篇的案例中,每个方法执行时都需要读取配置文件,并根据配置文件的信息构建 SqlSessionFactory 对象,然后创建 SqlSession 对象,这导致了大量的重复代码。为了简化开发,可以将上述重复代码封装到一个工具类中,然后通过工具类来创建 SqlSession,代码如下:

package com.cn.utils;
import java.io.Reader;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
/*
* 工具类
*/
public class MybatisUtils {
	private static SqlSessionFactory sqlSessionFactory = null;
	//初始化 SqlSessionFactory 对象
	static {
		try{
			//使用 MyBatis 提供的 Resources 类加载 MyBatis 的配置文件
			Reader reader = Resources.getResourceAsReader("mybatis-config.xml");
			//构建SqlSessionFactory工厂
			sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
	//获取 SqlSession 对象的静态方法
	public static SqlSession getSession() {
		return sqlSessionFactory.openSession();
	}
}

配置文件

    MyBatis 的核心配置文件中,包含了很多影响 MyBatis 行为的重要信息。这些信息通常在一个项目中只会在一个配置文件中编写,并且编写后也不会轻易改动。虽然在实际项目中需要开发人员编写或者修改的配置文件不多,但是熟悉配置文件中各个元素的功能还是十分重要的。

一、主要元素

    在 MyBatis 框架的核心配置文件中,<configuration>元素是配置文件的根元素,其他元素都要在<configuratrion>元素内配置。
MyBatis 配置文件中的主要元素如下所示:
image.png
\<configuration>的子元素必须按照上图中由上到下的顺序进行配置,否则 MyBatis 在解析 XML 配置文件时会报错。

二、<properties>元素

    <properties>是一个配置属性的元素,该元素通常用于将内部的配置外在化,即通过外部的配置来动态地替换内部定义的属性。例如,数据库的连接等属性,就可以通过典型的 Java 属性文件中的配置来替换,具体方式如下:
(1)在项目的src目录下,添加一个 db.properties 的配置文件,编辑后代码如下:

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

(2)在 MyBatis 配置文件 mybatis-config.xml 中配置<properties.../>元素,具体如下:

<properties resource="db.properties" />

(3)修改配置文件中数据库连接的信息,如下:

<dataSource type = "POOLED">
	<!-- 数据库驱动 -->
	<property name="driver" value="${jdbc.driver}" />
	<!-- 连接数据库的url -->
	<property name="url" value="${jdbc.url}" />
	<!-- 连接数据库的用户名 -->
	<property name="username" value="${jdbc.username}" />
	<!-- 连接数据库的密码 -->
	<property name="password" value="${jdbc.password}" />
</dataSource>

完成上述配置后,dataSource 中连接数据库的4个属性(driver、url、username 和 password)值将会由 db.properties 文件中对应的值来动态替换。这样就为配置提供了诸多灵活的选择。
除了可以向上述通过外部配置文件来定义属性值外,还可以通过配置<properties>元素的子元素<property>,以及通过方法参数传递的方式来获取属性值。由于使用properties 配置文件来配置属性值可以方便地在多个配置文件中使用这些属性值,并且方便日后的维护和修改,所以在实际开发中,使用 properties 配置文件来配置属性值是最常用的方式。

三、<settings>元素

    <settings>元素主要用于改变MyBatis运行时的行为,例如开启二级缓存、开启延迟加载等。虽然不配置<settings>元素,也可以正常运行MyBatis,但是熟悉<settings>的配置内容以及他们的作用还是十分必要的。
    <setting>元素中的常见配置及其描述如下:

设置参数描述有效值默认值
cacheEnabled该配置影响所有映射器中配置的缓存全局开关true|falsefalse
lazyLoadingEnabled延迟加载的全局开关。开启时,所有关联的对象都会延迟加载。特定关联关系中可以通过设置 fetchType 属性来覆盖该项的开关状态true|falsefalse
aggressiveLazyLoading关联对象属性的延迟加载开关。当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性都会按需加载true|falsetrue
multipleResultSetsEnabled是否允许单一语句返回多结果集(需要兼容驱动)true|falsetrue
useColumnLabel使用列标签代替列名。不同的驱动在这方面有不同的表现。具体可参考驱动文档或通过测试两种模式来观察所用驱动的行为true|falsetrue
useGeneratedKeys允许JDBC支持自动生成主键,需要驱动兼容。如果设置为true,则这个设置强制使用自动生成主键,尽管一些驱动不兼容但仍可正常工作true|falsefalse
autoMappingBehavior指定MyBatis应如何自动映射列到字段或属性。NONE表示取消自动映射;PARTIAL只会自动映射没有定义嵌套结果集映射的结果集;FULL会自动映射任意复杂的结果集(无论是否嵌套)NONE、PARTIAL、FULLPARTIAL
defaultExecutorType配置默认的执行器。SIMPLE就是普通的执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH 执行器将重用语句并执行批量更新SIMPLE、REUSE、BATCHSIMPLE
defaultStatementTimeout设置超时时间,它决定驱动等待数据库响应的秒数。当没有设置的时候,它取得就是驱动默认的时间任何正整数没有设置
mapUnderscoreToCamelCase是否开启自动驼峰命名规则(camel case)映射true|falsefalse
jdbcTypeForNull当没有为参数提供特定的JDBC类型时,为空值指定JDBC类型。某些驱动需要指定列的JDBC类型,多数情况直接用一般类型即可,比如NULL、VARCHAR或OTHERNULL、VARCHAR、OTHEROTHER

上表中介绍了<settings>元素中的常见配置,这些配置在配置文件中的使用方式如下:

<!-- 设置 -->
<settings>
	<setting name="cacheEnabled" value="true" />
	<setting name="lazyLoadingEnabled" value="true" />
	<setting name="multipleResultSetsEnabled" value="true" />
	<setting name="useColumnLabel" value="true" />
	<setting name="useGeneratedKeys" value="false" />
	<setting name="autoMappingBehavior" value="PARTIAL" />
	...
</settings>

四、<typeAliases>元素

    <typeAliases>元素用于为配置文件中的java类型设置一个简短的名字,即设置别名。别名的设置与XML配置相关,其使用的意义在于减少全限定类名的冗余。
使用<typeAliases>元素配置别名的方法如下:

<!-- 定义别名 -->
<typeAliases>
	<typeAlias alias="user" type="com.cn.po.User" />
</typeAliases>

上述示例中,<typeAliases>元素的子元素<typeAlias>中的type属性用于指定需要被定义别名的全限定名;alias 属性的属性值 user 就是自定义的别名,它可以代替 com.cn.po.User 使用在 MyBatis 文件的任何位置。如果省略 alias 属性,MyBatis 会默认将类名首字母小写后的名称作为别名。
当POJO类过多时,还可以通过自动扫描包的形式自定义别名,具体代码如下:

<!-- 使用自动扫描包来定义别名 -->
<typeAliases>
	<package name="com.cn.po" />
</typeAliases>

上述示例中,<typeAliases>元素的子元素<package>中的 name 属性用于指定要被定义别名的包,MyBatis 会将所有com.cn.po包中的POJO类以首字母小写的非限定类名来作为他的别名,比如 com.cn.po.User 的别名为 user,com.cn.po.Customer 的别名为 customer 等。
需要注意的是,上述方式的别名只适用于没有使用注解的情况。如果在程序中使用了注解,则别名为其注解的值,具体如下:

@Alias(value="user")
public class User{
	//User的属性和方法
	...
}

除了可以使用<typeAliases>元素自定义别名外,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

五、<typeHandler>元素

    MyBatis 在预处理语句(PreparedStatement)中设置一个参数或者从结果集(ResultSet)中取出一个值时,都会用其矿建内部注册了的 typeHandler(类型处理器)进行相关处理。typeHandler 的作用就是将预处理语句中传入的参数从 JavaType (Java类型)转换为 jdbcType(JDBC类型),或者从数据库取出结果时将 jdbcType 转换为 javaType。
为了方便转换,MyBatis 框架提供了一些默认的类型处理器,常用类型处理器如下:

类型处理器Java类型JDBC类型
BooleanTypeHandlerjava.lang.Boolean,boolean数据库兼容的BOOLEAN
ByteTypeHandlerjava.lang.Byte,byte数据库兼容的NUMERIC或BYTE
ShortTypeHandlerjava.lang.Short,short数据库兼容的NUMERIC或SHORT INTEGER
IntegerTypeHandlerjava.lang.Integer,int数据库兼容的NUMERIC或INTEGER
LongTypeHandlerjava.lang.Long,long数据库兼容的NUMERIC或LONG INTEGER
FloatTypeHandlerjava.lang.Float,float数据库兼容的NUMERIC或FLOAT
DoubleTypeHandlerjava.lang.Double,double数据库兼容的NUMERIC或DOUBLE
BigDecimalTypeHandlerjava.math.BigDecimal数据库兼容的NUMERIC或DECIMAL
StringTypeHandlerjava.lang.StringCHAR,VARCHAR
ClobTypeHandlerjava.lang.StringCLOB,LONGVARCHAR
ByteArrayTypeHandlerbyte[]数据库兼容的字节流类型
BlobTypeHandlerbyte[]BLOB,LONGVARBINARY
DateTypeHandlerjava.util.DateTIMESTAMP
SqlTimestampTypeHandlerjava.sql.TimestampTIMESTAMP
SqlDateTypeHandlerjava.sql.DateDATE
SqlTimeTypeHandlerjava.sql.TimeTIME

当 MyBatis 框架所提供的这些类型处理器不能够满足需求时,还可以通过自定义的方式对类型处理器进行扩展(自定义类型处理器可以通过实现 TypeHandler 接口或者继承 BaseTypeHandle 类来定义)。<typeHandler>元素就是用于配置文件中注册自定义的类型处理器的。它的使用方式有两种,具体如下:
1.注册一个类的类型处理器

<typeHandlers>
	<!-- 以单个类的形式配置 -->
	<typeHandler handler="com.cn.type.CustomtypeHandler" />
</typeHandlers>

2.注册一个包中所有的类型处理器

<typeHandlers>
	<!-- 注册一个包中所有的 typeHandler,系统在启动时会自动扫描包下的所有文件 -->
	<package name="com.cn.type" />
</typeHandlers>

上述代码中,子元素<package>的 name 属性用于指定类型处理器所在的包名,使用此种方式后,系统会在启动时自动地扫描 com.cn.type 包下所有的文件,并把它们作为类型处理器。

六、<objectFactory>元素

    MyBatis 框架每次创建结果对象的新实例时,都会使用一个对象工厂(ObjectFactory)的实例来完成。MyBatis 中默认的 ObjectFactory 的作用就是实例化目标类,它既可以通过默认构造方法实例化,也可以在参数映射存在的时候通过参数构造方法来实例化。
在通常情况下,使用默认的 ObjectFactory 即可,MyBatis 中默认的 ObjectFactor 由 org.apache.ibatis.reflection.factory.DefaultObjectFactory 来提供服务的。大部分场景下都不用配置和修改,但如果想覆盖 ObjectFactory 的默认行为,则可以通过自定义 ObjectFactory 来实现,具体方式如下:
(1)自定义一个对象工厂,自定义的对象工厂需要实现 ObjectFactory 接口,或者继承DefaultObjectFactory 类。由于 DefaultObjectFactory 类已经实现了 ObjectFactory 接口,所以通过继承 DefaultObjectFactory 类实现即可,代码如下:

//自定义工厂类
public class MyObjectFactory extends DefaultObjectFactory{
	private static final long serialVersionUID = -4114845625429965832L;
	public <T> T create(CLass<T> type){
		return super.create(type);
	}
	public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs){
		return super.create(type,constructorArgTypes,constructorArgs);
	}
	public void setProperties(Properties properties){
		super.setProperties(properties);
	}
	public <T> boolean isCollection(Class<T> type){
		return Collection.class.isAssignableFrom(type);
	}
}

(2)在配置文件中使用<objectFactory>元素配置自定义的ObjectFactory,如下:

<objectFactory type="com.cn.factory.MyObjecctFactory">
	<property name="name" value="MyObjectFactory" />
</objectFactory>

七、<plugins>元素

    MyBatis 允许在已映射语句执行过程中的某一点进行拦截调用,这种拦截调用是通过插件来实现的。<plugins>元素的作用就是配置用户所开发的插件。如果用户想要进行插件开发,必须要先了解其内部运行原理,因为在视图修改或重写已有方法的行为时,很可能会破坏 MyBatis 原有的核心模块。

八、<environments>元素

    在配置文件中,<environments>元素用于对环境进行配置。MyBatis 的环境配置实际上就是数据源的配置,我们可以通过<environments>元素配置多种数据源,即配置多种数据库。
使用<environments>元素进行环境配置的示例如下:

<environments default="development">
	<environment id="development">
		<!-- 使用JDBC事务管理 -->
		<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>元素是环境配置的根元素,它包含一个 default 属性,该属性用于指定默认的环境 ID。<environment>是<environments>元素的子元素,它可以定义多个,其 id 属性用于表示所定义环境的 ID 值。在<environment>元素内,包含事务管理和数据源的配置信息,其中<transactionManager>元素用于配置事务管理,它的 type 属性用于指定事务管理的方式,即使用哪种事务管理器;<dataSource>元素用于配置数据源,它的 type 属性用于指定使用哪种数据源。
    在 MyBatis 中,可以配置两种类型的事务管理器,分别是 JDBC 和 MANAGED。介绍如下:

  • JDBC:此配置直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务的作用域。
  • MANAGED:此配置从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期。在默认情况下,它会关闭连接,但一些容器并不希望这样,为此可以将 closeConnection 属性设置为 false 来阻止它默认的关闭行为。

注意:
    如果项目中使用的是 Spring+ MyBatis,则没有必要在 MyBatis 中配置事务管理器,因为实际开发中,会使用 Spring 自带的管理器来实现事务管理。

对于数据源的配置,MyBatis 框架提供了 UNPOOLED、POOLED 和 JNDI 三种数据源类型,具体如下:
1、UNPOOLED
配置数据源类型后,在每次被请求时会打开和关闭连接。它对没有性能要求的简单应用程序是一个很好的选择。
UNPOOLED 类型的数据源需要配置5种属性,如下表:

属性说明
driverJDBC驱动的Java类的完全限定名(并不是JDBC驱动中可能包含的是数据源类)
url数据域的URL地址
username登录数据库的用户名
password登录数据库的密码
defaultTransactionIsolationLevel默认的连接事务隔离级别

2、POOLEd
    此数据源利用“池”的概念将JDBC连接对象组织起来,避免了在创建新的连接实例时所需要初始化和认证时间。这种方式使得并发Web应用可以快速地相应请求,配置此数据源类型时,可以配置除上表外更多的属性,如下表:

属性说明
poolMaximumActiveConnections在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10
poolMaximumIdleConnections任意时间可能存在的空闲连接数
poolMaximumCheckoutTime在被强制返回之前,池中连接被检出(checked out)时间,默认:20000毫秒
poolTimeToWait如果获取连接话费的时间较长,它会给连接池打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直处于无提示的失败),默认值:20000
poolPingQuery发送到数据库的侦测查询,用于检验连接是否处在正常工作秩序中。默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一定的错误消息
poolPingEnabled是否启用侦测查询。若开启,必须使用一个可执行的SQL语句设置poolPingQuery属性(最好是一个非常快的SQL),默认值:false
poolPingConnectionsNotUserFor配置poolPingQuery的使用频度。可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认值:0(表示所有连接每一时刻都被侦测,只有poolPingEnabled的属性值为true时适用)

3、JNDI
    此数据源可以在EJB或应用服务器等容器中使用。容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。
配置JNDI数据源时,只需要配置两个属性,如下表:

属性说明
initial_context此属性主要用于在InitalContext中寻找上下文(即initialContext.lookup(initial_context))该属性为可选属性,在忽略时,data_source 属性会直接从InitialContext中寻找
data_source此属性表示引用数据源实例位置的上下文的路径,如果提供了initial_context配置,那么程序会在其返回的上下文中进行查找;如果没有提供,则直接在InitialContext中查找

九、<mappers>元素

    在配置文件中,<mappers>元素用于指定MyBatis 映射文件的位置,一般可以使用一下4种方式引入映射器文件,具体如下:
1、使用类路径引入

<mappers>
	<mapper resource="com/cn/mapper/UserMapper.xml" />
</mappers>

2、使用本地文件路径引入

<mappers>
	<mapper url="file:///D:/com/cn/mapper/UserMapper.xml" />
</mappers>

3、使用接口类引入

<mappers>
	<mapper class="com.cn.mapper.UserMapper" />
<mappers>

4、使用包名引入

<mappers>
	<package name="com.cn.mappers" />
</mappers>

映射文件

一、主要元素

在映射文件中,<mapper>元素是映射文件的根元素,其他元素都是它的子元素。这些子元素及其作用如图所示:
image.png

二、<select>元素

    <select>元素用于映射查询语句,它可以帮助我们从数据库中读取数据,并组装数据给业务开发人员。
使用<select>元素执行查询操作非常简单,如下:

<select id="findCustomerById" parameterType="Integer"
	resultType="com.cn.po.Customer">
	select * from t_customer where id = #{id}
</select>

上述语句中的唯一标识为 findCustomerById,它接收一个 Integer 类型的参数,并返回一个 Customer 类型的对象。
<select> 元素中,除了上述示例代码中的几个属性外,还有其他一些可以配置的属性,如下所示:

属性说明
id表示命名空间中的唯一标识符,常与命名空间组合起来使用。组合后如果不唯一, MyBatis 会抛出异常
parameterType该属性表示传入 SQL 语句的参数类的全限定名或者别名。它是一个可选属性,因为 MyBatis 可以通过 TypeHandler 推断出具体传入语句的参数。其默认值是 unset(依赖于驱动)
resultType从SQL语句中返回的类型的类的全限定名或者别名。如果是集合类型,那么返回的应该是集合可以包含的类型,而不是集合本身。返回时可以使用 resultType 或 resultMap 之一
flushCache表示在调用 SQL 语句之后,是否需要 MyBatis 清空之前查询的本地缓存和二级缓存。其值为布尔类型(true|false),默认值为false。如果设置为true,则任何时候只要 SQL 语句被调用,都会清空本地缓存和二级缓存
useCache用于控制二级缓存的开启和关闭。其值为布尔类型(true|false),默认值为true,表示将查询结果存入二级缓存中
timeout用于设置超时参数,单位为秒。超时时将抛出异常
fetchSize获取记录的总条数设定,其默认值是unset(依赖于驱动)
statementType用于设置 MyBatis 使用哪个 JDBC 的 Statement 工作,其值为 STATEMENT、PREPARED(默认值)或 CALLABLE,分别对应 JDBC 中的 Statement、PreparedStatement 和 CallableStatement
resultSetType表示结果集的类型;其值可设置为 FORWARD_ONLY、SCROLL_SENSITIVE 或 SCROLL_INSENSITIVE,它的默认值是unset(依赖于驱动)

三、<insert>元素

    <insert>元素用于映射插入语句,在执行完元素中定义的 SQL 语句后,会返回一个表示插入记录数的整数。
<insert>元素的配置示例如下:

<insert
	id="addCustomer"
	parameterType="com.cn.po.Customer"
	flushCache="true"
	statementType="PREPARED"
	keyProperty=""
	keyColumn=""
	useGeneratedKeys=""
	timeout="20">

从上述代码中可以看出,<insert>元素的属性与<select>元素的属性大部分相同,但还包含了3个特有属性,这3个属性的描述如下表所示:

属性说明
keyProperty(仅对 insert 和 update 有用)此属性的作用是将插入或更新操作时的返回值给 PO 类的某个属性,通常会设置为主键对应的属性。如果需要设置联合主键,可以在多个值之间用逗号隔开
keyColumn(仅对 insert 和 update 有用)此属性用于设置第几列是主键,当主键列不是表中的第一列时需要设置。在需要主键联合时,值可以用逗号隔开
useGeneratedKeys(仅对 insert 和 update 有用)此属性会使 MyBatis 使用 JDBC 的 getGeneratedKeys() 方法来获取由数据库内部产生的主键,如 MySQL 和 SQL Server 等自动递增的字段,其默认值为false

执行插入操作后,很多时候需要返回插入成功的数据生成的主键值,此时就可以拖过上面的3个属性来实现。
如果使用的数据库支持主键自动增长(如MySQL),那么可以通过 keyProperty 属性指定 PO 类的某个属性接收主键返回值(通常会设置到 id 属性上),然后将 useGeneratedKeys 的属性设置为true,其使用示例如下:

<insert id="addCustomer" parameterType="com.cn.po.Customer"
	keyProperty="id" useGeneratedKeys="true" >
	insert into t_customer(username,jobs,phone)
	values(#{username},#{jobs},#{phone})
</insert>

使用上述配置执行插入后,会返回插入成功的行数,以及插入行的主键值。可通过以下代码测试:

@Test
public void addCustomerTest(){
	//获取SqlSession
	SqlSession sqlSession = MyBatisUtils.getSession();
	Customer customer = new Customer();
	customer.setUsername("rose");
	customer.setJobs("student");
	customer.setPhone("13333533092");
	int rows = sqlSession.insert("com.cn.mapper." + "CustomerMapper.addCustomer", customer);
	if(rows > 0){
		System.out.println("您成功插入了"+rows+"条数据!");
	} else {
		System.out.println("执行插入操作失败!");
	}
	sqlSession.commit();
	sqlSession.close();
}

执行程序后,控制台输出结果如下:
image.png
如果使用的数据库不支持主键自动增长(如 Oracle),或者支持增长的数据库取消了主键自增的规则时,也可以使用 MyBatis 提供的另一种方式来自定义生成主键,具体配置如下:

<insert id="insertCustomer" parameterType="com.cn.po.Customer">
	<selectKey keyProperty="id" resultType="Integer" order="BEFORE">
		select if(max(id) is null, 1, max(id) +1) as newId from t_customer
	</selectKey>
	insert info t_customer(id,username,jobs,phone)
	values(#{id},#{username},#{jobs},#{phone})
</insert>

在执行上述代码时,<selectKey>元素会首先运行,它会通过自定义的语句来设置数据表中的主键(如果 t_customer 表中没有记录,则将 id 设置为1,否则就将 id 的最大值加1,来作为新的主键),然后再调用插入语句。
<selectKey>元素在使用时可以设置一下几种属性。

<selectKey
	keyProperty="id"
	resultType="Integer"
	order="BEFORE"
	statementType="PREPARED">

在上述<selectKey>元素的几个属性中,keyProperty、resultType 和 statementType 的作用与前面相同,order 属性可以被设置为 BEFORE 和 AFTER。如果设置为BEFORE,那么它会首先执行<selectKey>元素中的配置来设置主键,然后执行插入语句;如果设置为AFTER,那么它会先执行插入语句,然后执行<selectKey>元素中的配置内容。

四、<update>元素和<delete>元素

    <update>和<delete>元素的使用比较简单,他们的属性配置也基本相同,常用属性如下所示:

<update
	id="updateCustomer"
	parameterType="com.cn.po.Customer"
	flushCache="true"
	statementType="PREPARED"
	timeout="20">
<delete
	id="deleteCustomer"
	parameterType="com.cn.po.Customer"
	flushCache="true"
	statementType="PREPARED"
	timeout="20">

从上述配置代码中可以看出,<update> 和 <delete>元素的属性基本与<select>元素中的属性一致。与<insert>元素一样,<update>和<delete>元素在执行完之后,也会返回一个表示影响记录条数的整数,使用实例如下:

<!-- 更新信息 -->
<update id="updateCustomer" parameterType="com.cn.po.Customer">
	update t_customer
	set username=#{username},jobs=#{jobs},phone=#{phone}
	where id=#{id}
</update>
<!-- 删除信息 -->
<delete id="deleteCustomer" parameterType="Integer">
	delete from t_customer where id=#{id}
</delete>

五、<sql>元素

在一个映射文件中,通常需要定义多条 SQL 语句,这些 SQL 语句的组成可能有一部分是相同的(如多条select语句中都查询相同的id、username、jobs 字段),如果每一个 SQL 语句都重写一遍相同的部分,势必会增加代码量,导致映射文件过于臃肿。可以在映射文件中使用 MyBatis 所提供的<sql>元素来解决该问题
<sql>元素的作用就是定义可重用的 SQL 代码片段,然后在其他语句中引用这一代码片段。例如,定义一个包含 id、username、jobs 和 phone 字段的代码片段如下。

<sql id="customerColumns">id,username,jobs,phone</sql>

这一代码片段可以包含在其他语句中使用,具体如下:

<select id="findCustomerById" parameterType="Integer"
		resultType="com.cn.po.Customer">
	select <include refid="customerColumns"/>
	from t_customer
	where id = #{id}
</select>

在上述代码中,使用<include>元素的 refid 属性引用了自定义的代码片段,refid 的属性值为自定义代码片段的 id。
实际开发中,可以更加灵活地定义 SQL 片段,示例如下:

<!-- 定义表的前缀名 -->
<sql id="tablename">
	${prefix}customer
</sql>
<!-- 定义要查询的表 -->
<sql id="someinclude">
	from
	<include refid="${include_target}"/>
</sql>
<!-- 定义查询列 -->
<sql id="customerColumns">
	id,username,jobs,phone
</sql>
<!-- 根据 id 查询客户信息 -->
<select id="findCustomerById" parameterType="Integer"
		resultType="com.cn.po.Customer">
	select
	<include refid="customerColumns"/>
	<include refid="someinclude">
		<property name="prefix" value="t_"/>
		<property name="include_target" value="tablename"/>
	</include>
	where id = #{id}
</select>

上述代码中,定义了3个代码片段,分别为表的前缀名、要查询的表和需要查询的列。前两个代码片段中,分别获取了<include>子元素<property>中的值,其中第 1 个代码片段中的"${prefix}"会获取name为prefix的值"t_",获取后所组成的表名为"t_customer";而第2个代码片段中的"${include_target}"会获取name为include_target的值"tablename",由于 tablename 为第一个SQL片段的id值,所以最后要查询的表为"t_customer"。所有的SQL片段执行程序后的结果如图:
image.png

六、<resultMap>元素

    <resultMap>元素表示结果映射集,是 MyBatis 中最重要也是最强大的元素。它的主要作用是定义映射规则、级联的更新以及定义类型转化器等。
<resultMap>元素中包含了一些子元素,他的元素结构如下所示:

<!-- resultMap 的元素结构 -->
<resultMap type="" id="">
	<constructor> 	<!-- 类在实例化时,用来注入结果到构造方法中 -->
		<idArg/>	<!-- ID 参数;标记结果作为 ID -->
		<arg/>	<!-- 注入到构造方法的一个普通结果 -->
	</constructor>
	<id/>		<!-- 用于表示哪个列是主键 -->
	<result/>		<!-- 注入到字段或JavaBean属性的普通结果-->
	<association property=""/>		<!-- 用于一对一关联 -->
	<collection property="" />		<!-- 用于一对多关联 -->
	<discriminator javaType="">	<!-- 使用结果值来决定使用哪个结果映射 -->
		<case value="" />
	</discriminator>
</resultMap>

    <resultMap>元素的type属性表示需要映射的POJO,id属性是这个resultMap的唯一标识。它的子元素<constructor>用于配置构造方法(当一个POJO中未定义无参的构造方法时,就可以使用<constructor>元素进行配置)。子元素<id>用于表示哪个列是主键,而<result>用于表示POJO和数据表中普通列的映射关系。<association>和<collection>用于处理多表时的关联关系,而<discriminator>元素主要用于处理一个单独的数据库查询返回很多不同数据类型结果集的情况。
在默认情况下,MyBatis 程序在运行时会自动地将查询到的第数据与需要返回的对象的属性进行匹配赋值(需要表中的列名与对象的属性名称完全一致)。然而实际开发时,数据表中的列和需要返回的对相关的属性可能不会完全一致,这种情况下 MyBatis 是不会自动赋值的。此时,就可以使用<resultMap>元素进行处理。
接下来,通过一个具体的示例演示<resultMap>元素在此种情况的使用,具体步骤如下:
(1)在 mybatis 数据库中,创建一个 t_user 表,并插入几条测试数据。

USE mybatis;
CREATE TABLE t_user{
	t__id INT PRIMARY KEY AUTO_INCREMENT,
	t_name VARCHAR(50),
	t_age INT
);
INSERT INTO t_user(t_name,t_age) VALUES('Lucy',25);
INSERT INTO t_user(t_name,t_age) VALUES('Lili',20);
INSERT INTO t_user(t_name,t_age) VALUES('Jim',20);

(2)在com.cn.po包中,创建持久化类 User,并在类中定义 id、name 和 age属性,以及getter/setter方法和toString()方法,如下所示:

package com.cn.po;
public class User{
	private Integer id;
	private String name;
	private Integer age;
	public Integer getId(){
		return id;
	}
	public void setId(Integer id){
		this.id = id;
	}
	public String getName(){
		return name;
	}
	public void setName(String name){
		this.name = name;
	}
	public Integer getAge(){
		return age;
	}
	public void setAge(Integer age){
		this.age = age;
	}
	@Override
	public String toString(){
		return "User [id =" + id + ", name=" + name + ", age=" + age + "]";
	}
}

(3)在com.cn.mapper包下,创建映射文件UserMapper.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.cn.mapper.UserMapper">
	<resultMap type="com.cn.po.User" id="resultMap">
		<id property="id" column="t_id"/>
		<result property="name" column="t_name"/>
		<result property="age" column="t_age"/>
	</resultMap>
	<select id="findAllUser" resultMap="resultMap">
		select * from t_user
	</select>
</mapper>

在上述中,<resultMap>的子元素<id>和<result>的property属性表示User类的属性名,column属性表示数据表t_user 的列名。<select>元素的 resultMap属性表示引用上面定义的resultMap。
(4)在配置文件 mybatis-config.xml 中,引入 UserMapper.xml。
(5)在测试类,编写测试方法 findAllUserTest(),代码如下:

@Test
public void findAllUserTest(){
	//获取SqlSession
	SqlSession sqlSession = MybatisUtils.getSession();
	//SqlSession 执行映射文件中定义的SQL,并返回映射结果
	List<User> list = sqlSession.selectList("com.cn.mapper.UserMapper.findAllUser");
	for(User uesr : list){
		System.out.println(user);
	}
	//关闭 SqlSession
	sqlSession.close();
}

测试结果如图所示:
image.png
从图中可以看出,虽然 t_user 表的列名与User对象的属性名完全不一样,但查询出的数据还是被正确地封装到了User对象中。
除此之外,还可以通过<resultMap>元素中的<association>和<collection>处理多表时的关联关系。

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