MyBtis(一)——MyBatis简介、MyBatis入门程序
上篇博文主要总结了MyBatis的一些基础用法(普通的增删改),现在我们来对MyBatis的全局配置文件进行更深入的探讨。
回顾一下,配置文件对于MyBatis来说非常重要,MyBatis总共有两类配置文件:
一类用于指定数据源、事务属性以及其他一些参数配置信息(通常是一个独立的文件,可以称之为全局配置文件);
另一类则用于 指定数据库表和程序之间的映射信息(可能不止一个文件,我们称之为映射文件)
MyBatis 的配置文件包含了影响整个MyBatis运行的信息。
一个完整的全局配置文件会包含以下内容:
properties(属性)
settings(配置)
typeAliases(类型别名)
typeHandlers(类型处理器)
objectFactory(对象工厂)
plugins(插件)
environments(环境集合属性对象)
environment(环境子属性对象)
transactionManager(事务管理)
dataSource(数据源)
mappers(映射器)
先来看一个完整的全局配置文件的大致结构:
<configuration>
<properties>
……
properties>
<settings>
……
settings>
<typeAliases>
……
typeAliases>
<typeHandlers>
……
typeHandlers>
<objectFactory type="">
……
objectFactory>
<plugins>
……
plugins>
<environments default="">
……
environments>
<mappers>
……
mappers>
configuration>
这些属性都是可外部配置且可动态替换的,既可以在典型的 Java 属性文件中配置,亦可通过 properties 元素的子元素来传递。例如:
<properties resource="config.properties">
<property name="username" value="root"/>
<property name="password" value="123456"/>
properties>
这些属性可以在整个配置文件中使用来替换需要动态配置的属性值。
如果属性在不只一个地方进行了配置,那么 MyBatis 将按照下面的顺序来加载:
- 在 properties 元素体内指定的属性首先被读取。
- 然后根据 properties 元素中的 resource 属性读取类路径下属性文件或根据 url 属性指定的路径读取属性文件,并覆盖已读取的同名属性。
- 最后读取作为方法参数传递的属性,并覆盖已读取的同名属性。
因此,通过方法参数传递的属性具有最高优先级,resource/url 属性中指定的配置文件次之,最低优先级的是 properties 属性中指定的属性。
这个标签是MyBatis极为重要的调整设置,它会改变MyBatis的运行时行为。
一个配置完整的 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"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatementTimeout" value="25"/>
<setting name="safeRowBoundsEnabled" value="false"/>
<setting name="mapUnderscoreToCamelCase" value="false"/>
<setting name="localCacheScope" value="SESSION"/>
<setting name="jdbcTypeForNull" value="OTHER"/>
<setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
<setting name="logImpl" value="STDOUT_LOGGING" />
settings>
设置中的这些项我们一般用的不多,如果没有需要,就可以省略不写,你只需要更改你想要的参数就可以,关于这些设置项的详细解释、默认值和用法,可以去W3Cschool去看,这里挑几个常见的设置项来说明一下:
设置参数 | 描述 | 有效值 | 默认值 |
---|---|---|---|
logImpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动查找。 | SLF4J,LOG4J,LOG4J2,JDK_LOGGING,COMMONS_LOGGING,STDOUT_LOGGING,NO_LOGGING | Not set |
jdbcTypeForNull | 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。 | JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER | OTHER |
cacheEnabled | 该配置影响的所有映射器中配置的缓存的全局开关。 | true,false | true |
第一个logImpl
是用来配置日志信息的,比如说上篇博文我们用到了log4j日志操作包,在src目录下添加log4j的配置文件后,我们就要在全局配置文件的
属性中加以配置,才能生效:
<settings>
<setting name="logImpl" value="STDOUT_LOGGING" />
settings>
MyBatis 插入空值时,需要指定JdbcType。否则mybatis insert空值报空值异常,但是在pl/sql不会提示错误,主要原因是mybatis无法进行类型转换。
解决的方案有两个:
<insert id="insertOne" parameterType="com.xx.entity.person">
insert into emp values(
#{id,jdbcType=NULL}
#{name,jdbcType=NULL}
)
<insert/>
<setting name="jdbcTypeForNull" value="NULL"/>
cacheEnabled
控制所有映射器的缓存开关。
一级缓存是SqlSession级别的缓存,即一级缓存的作用域是SqlSession。MyBatis默认开启一级缓存。在同一个SqlSession中多次执行同一个SQL查询,第一次查询会去查询数据库,同时把查询到的结果写入缓存,在后续多次同一个SqlSession中执行该SQL语句,不会再去访问数据库,而是直接从缓存中拿到结果。注意,如果执行了增删改并提交之后,缓存会被清空,这么做是为了避免脏读。 Mybatis的内部缓存使用一个HashMap,key为hashcode+statementId+sql语句。Value为查询出来的结果集映射成的java对象。
二级缓存是Mapper级别的缓存,也就是说二级缓存的作用域是Mapper。MyBatis默认是不开启二级缓存的。二级缓存可以跨SqlSession共享数据,也就是说,不同的SqlSession之间可以读取到其它SqlSession放在二级缓存区域中的查询结果,从而大大提高查询性能。二级缓存区域是按照namespace来划分的,同一个namespace使用同一个二级缓存区域。同样,为了避免脏读,如果调用相同namespace下的mapepr映射文件中增删改sql,并执行了commit操作,那么二级缓存区域就会被清空。
开启二级缓存要满足以下要求:
<settings>
<setting name="cacheEnabled" value="true"/>
settings>
<catch/>
让需要使用二级缓存的POJO类实现Serializable接口,如
public class Product implements Serializable {……}
满足以上三个要求后,就可以启用二级缓存了。
关于二级缓存还要再强调一下useCache
和flushCache
:
mybatis中还可以配置userCache和flushCache等配置项,userCache是用来设置是否禁用二级缓存的,在statement中设置useCache=false可以禁用当前select语句的二级缓存,即每次查询都会发出sql去查询,默认情况是true,即该sql使用二级缓存。这种情况是针对每次查询都需要最新的数据sql,要设置成useCache=false,禁用二级缓存,直接从数据库中获取。
例如:
<select id="selectAllProduct" useCache="false" resultType="com.xx.entity.Product">
select * from t_product
select>
前面已经强调过,在mapper的同一个namespace中,如果有其它insert、update、delete操作数据后需要刷新缓存,如果不执行刷新缓存会出现脏读。
设置statement配置中的flushCache=”true” 属性,默认情况下为true,即刷新缓存,如果改成false则不会刷新。使用缓存时如果手动修改数据库表中的查询数据会出现脏读。一般执行完commit操作都需要刷新缓存,flushCache=true表示刷新缓存,这样可以避免数据库脏读。所以我们不用设置,默认即可。
例如:
<select id="selectAllProduct" flashCache="true" resultType="com.xx.entity.Product">
select * from t_product
select>
当然,如果你赋值为false,那么不可避免的会出现脏读现象。
类型别名是为 Java 类型设置一个短的名字。它只和 XML 配置有关,存在的意义仅在于用来减少类完全限定名的冗余。关于别名有两种方式:
<typeAliases>
<typeAlias alias="product" type="com.xx.entity.Product"/>
typeAliases>
这样设置以后,任何使用com.xx.entity.Product
的地方都可以用product
来代替。
<typeAliases>
<package name="com.xx.entity"/>
typeAliases>
那么,所有在com.xx.entity包下的Java Bean都会自动的生成别名,关于生成的别名,规则是这样的:
@Alias("person")
public class Person{
...
}
那么自动生成的别名就是注解里面指定的别名。
1、小驼峰式命名法(lower camel case):
第一个单词以小写字母开始,第二个单词的首字母大写。例如:firstName、lastName。
2、大驼峰式命名法(upper camel case):
每一个单词的首字母都采用大写字母,例如:FirstName、LastName、CamelCase,也被称为 Pascal 命名法。
变种:StudlyCaps,是“驼峰式大小写”的变种。
补充说明,在JAVA中:类名的标识符一般用大驼峰式书写格式。这就要求我们在开发的过程中,对于类的命名要严格遵守大驼峰命名法,否则自动生成的别名可能出错。
类型处理器,无论是 MyBatis 在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时, 都会用类型处理器将获取的值以合适的方式转换成 Java 类型。
简单来说,类型处理器就是负责jdbc类型和java类型之间的相互转换,确保数据类型在数据传输的过程中不发生改变,每一个类型处理器负责一种类型的处理。
一些基本的数据类型已经内置好了,如果必要的话,你可以重写类型处理器或创建你自己的类型处理器来处理不支持的或非标准的类型。 具体做法为:实现 org.apache.ibatis.type.TypeHandler
接口, 或继承一个很便利的类 org.apache.ibatis.type.BaseTypeHandler
, 然后可以选择性地将它映射到一个 JDBC 类型。
详细过程待写……(有需要查阅API)
MyBatis 每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现。
MyBatis 允许你在已映射语句执行过程中的某一点进行拦截调用。默认情况下,MyBatis 允许使用插件来拦截的方法调用包括:
Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
ParameterHandler (getParameterObject, setParameters)
ResultSetHandler (handleResultSets, handleOutputParameters)
StatementHandler (prepare, parameterize, batch, update, query)
MyBatis可以配置成适应多种环境,这种机制有助于将SQL映射应用于多种数据库之中。尽管可以配置多个环境,但是每个SqlSessionFactory实例只能选择其中一个。如果你想要连接两个数据库,就需要创建两个SqlSessionFactory实例,每个实例对应一个数据库。
环境元素定义了如何配置环境(其中的${}中是属性标签中的值,你也可以把直接在这里):
<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随你怎么命名,只要保证一个环境匹配一个环境id即可。
MyBatis 中有两种类型的事务管理器(也就是 type="[JDBC|MANAGED]"):
<transactionManager type="MANAGED">
<property name="closeConnection" value="false"/>
transactionManager>
如果你正在使用 Spring + MyBatis,则没有必要配置事务管理器, 因为 Spring 模块会使用自带的管理器来覆盖前面的配置。
dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。
有三种内建的数据源类型(也就是 type="[UNPOOLED|POOLED|JNDI]")。
其中最常用的就是POOLED(底层其实就是JDBC连接池)。
常见的参数有:
driver – 这是 JDBC 驱动的 Java 类的完全限定名(并不是JDBC驱动中可能包含的数据源类)。
url – 这是数据库的 JDBC URL 地址。
username – 登录数据库的用户名。
password – 登录数据库的密码。
defaultTransactionIsolationLevel – 默认的连接事务隔离级别。
还有一些不常用的参数(了解即可,一般不用):
poolMaximumActiveConnections – 在任意时间可以存在的活动(也就是正在使用)连接数量,默认值:10
poolMaximumIdleConnections – 任意时间可能存在的空闲连接数。
poolMaximumCheckoutTime – 在被强制返回之前,池中连接被检出(checked out)时间,默认值:20000 毫秒(即 20 秒)
poolTimeToWait – 这是一个底层设置,如果获取连接花费的相当长的时间,它会给连接池打印状态日志并重新尝试获取一个连接(避免在误配置的情况下一直安静的失败),默认值:20000 毫秒(即 20 秒)。
poolPingQuery – 发送到数据库的侦测查询,用来检验连接是否处在正常工作秩序中并准备接受请求。默认是"NO PING QUERY SET",这会导致多数数据库驱动失败时带有一个恰当的错误消息。
poolPingEnabled – 是否启用侦测查询。若开启,也必须使用一个可执行的 SQL 语句设置 poolPingQuery 属性(最好是一个非常快的 SQL),默认值:false。
poolPingConnectionsNotUsedFor – 配置 poolPingQuery 的使用频度。这可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认值:0(即所有连接每一时刻都被侦测 — 当然仅当 poolPingEnabled 为 true 时适用)
上述的元素已经将MyBatis的行为配置完了,接下来就要定义SQL映射语句了。那么首先你应该告诉MyBatis去哪里找到这些sql语句。 mappers用来配置mybatis要加载的mapper.xml文件或者mapepr接口共有以下几种方式:
<mappers>
<mapper resource="com/xx/mapper/ProductMapper.xml"/>
mappers>
<mappers>
<mapper url="file:///var/mappers/AuthorMapper.xml"/>
mappers>
<mappers>
<mapper class="com.xx.mapper.ProductMapper"/>
mappers>
<mappers>
<package name="com.xx.mapper"/>
mappers>