1.概述
MyBatis配置文件中所有的元素如下:
<?xml version="1.0" encoding="UTF-8" ?>
<configuration> <!--配置 -->
<properties/> <!--属性 -->
<settings/> <!--配置 -->
<typeAliases/> <!--类型命名 -->
<typeHandlers/> <!--类型处理器 -->
<objectFactory/> <!--对象工厂 -->
<plugins/> <!--插件 -->
<environments> <!--配置环境 -->
<environment> <!--环境变量 -->
<transactionManager/> <!--事务管理器 -->
<dataSource/> <!--数据源 -->
</environment>
</environments>
<databaseIdProvider/> <!--数据库厂商标识 -->
<mappers/> <!--映射器 -->
</configuration>
这些配置项的顺序不能颠倒,否则会出错。这些配置项中objectFactory、databaseIdProvider不常用,plugins后面再介绍,剩下的都是比较常用的配置项。
2.properties(属性)
properties属性可以给系统配置一些运行参数,可以放在XML文件或者properties文件中,这样方便参数修改,不会引起代码的重新编译。MyBatis提供了三种方式使用properties:
- property子元素。
- properties文件。
- 程序代码传递。
2.1 property子元素
config.xml
<configuration>
<properties>
<properties name="database.driver" value="com.mysql.jdbc.Driver"/>
<properties name="database.url" value="jdbc:mysql://localhost:3306/ssm"/>
<properties name="database.username" value="root"/>
<properties name="database.password" value="root"/>
</properties>
</configuration>
2.2 properties文件
jdbc.properties
database.driver=com.mysql.jdbc.Driver
database.url=jdbc:mysql://localhosr:3306/ssm
database.username=root
database.password=root
然后在MyBatis配置文件中通过<properties>属性的resource引入该文件。
<properties resource="jdbc.properties"/>
2.3程序代码传递(待补充)
3.settings(设置)
settings是MyBatis中最复杂的配置,它能深刻影响MyBatis底层的运行,但是在大部分情况下使用默认值就可以运行,所以一般情况下不需要大量配置它,只需要修改一些常用的规则即可。settings的配置项说明如下表:
下面是一个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="autoMappingUnknownColumnBehavior" value="WARNING"/>
<setting name="defaultExecutorType" value="SIMPLE"/>
<setting name="defaultStatmentTimeout" value="25"/>
<setting name="defaultRetchSize" value="100"/>
<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"/>
</settings>
4.typeAliases(别名)
由于类的全限定名称很长,需要大量使用时不方便。在MyBatis中允许定义一个简写来表示一个类,这就是别名。别名分为系统定义别名喝自定义别名,在MyBatis中别名由类TypeAliasRegistry(org.apache.ibatis.type.TypeAliasRegistry)定义。
在MyBatis中,别名不区分大小写。
4.1系统定义别名
在MyBatis的初始化过程中,系统会自动初始化一些别名,如下表:
若需要使用对应类型的数组型,要看其是否支持数组,如果支持只需要在别名后加[]即可,如_int数组的别名是_int[];不支持则不可以这样写。
也可以通过代码来实现注册别名。一般是通过Configuration获取TypeAliasRegistry类对象,其中有一个getTypeAliasRegisty方法可以获得别名,如configuration.getTypeAliasRegistry()。然后就可以通过registerAlias方法对别名注册了。
registerAlias("String",String.Class);
registerAlias("byte[]",Byte.Class);
4.2自定义别名
大型互联网系统中存在许多对象,比如User这个对象有时会被大量的重复使用,因此MyBatis也提供了用户自定义别名的规则。可以通过TypeAliasRegistry类的registerAlias方法注册,也可以用配置文件或扫描方式来自定义。
通常使用配置文件来定义。
<typeAliases><!-- 别名 -->
<typeAlias alias="role" type="com.ssm.pojo.Role"/>
</typeAliases>
如果有很多类需要定义别名,这样的方式就比较麻烦了,MyBatis还支持扫描别名,这时只需给出包名即可,如下:
<typeAliases><!-- 别名 -->
<package name="com.ssm.pojo"/>
</typeAliases>
这样,MyBatis将扫描这个包里面的类,将其第一个字母变为小写作为其别名。 按着这种规则,有时可能会出现重名的冲突情况,会出现异常,我们可以用MyBatis提供的@Alias("user1")注解来进行区分。(相当于手动指定某一个类的别名)这样就可以避免因为别名重名导致扫描失败的问题。
package com.ssm.pojo;
@Alias("user1")
public Class User{
...
}
5.typeHandler类型转换器
在JDBC中,需要在PreparedStatement对象中设置那些已经预编译过的SQL语句的参数。执行SQL后,会通过ResultSet对象获取得到数据库的数据,而这些过程在MyBatis中是根据数据的类型通过typeHandler来实现的。
在typeHandler中,分为jdbcType和javaType,前者用来定义数据库类型,后者用于定义Java类型,typeHandler的作用就是承担jdbcType和javaType之间的相互转换。
在大多数情况下我们并不需要配置它,MyBatis会探测应该使用什么类型的typeHandler进行处理,但是有些场景无法探测到,比如对于那些需要使用自定义美剧的场景或数据库使用特殊类型的场景,我们可以使用自定义的typeHandler去处理类型之间的转换问题。
MyBatis中存在系统定义的typeHandler和自定义的typeHandler。MyBatis会根据javaType和数据库的jdbcType来决定采用哪个typeHandler处理这些转换规则。
5.1系统定义的typeHander
如下表所示。
5.2自定义typeHandler
在大部分场景下,系统提供的typeHandler就能应付一般场景,但有些情况下则不够,比如使用枚举时,枚举有特殊的转化规则,这时需要自定义typeHandler进行处理。
MyBatis在实现typeHandler时要实现接口typeHandler或继承BaseTypeHandler(实际上BaseTypeHandler实现了typeHandler接口)。这里用一个StringTypeHandler来实现一个自定义typeHandler——MyTypeHandler。
public class MyTypeHandler implements TypeHandler<String> {
Logger logger = Logger.getLogger(MyTypeHandler.class);
@Override
public void setParameter(PreparedStatement ps, int i, String parameter,
JdbcType jdbcType) throws SQLException {
logger.info("设置string参数【" + parameter+"】");
ps.setString(i, parameter);
}
@Override
public String getResult(ResultSet rs, String columnName)
throws SQLException {
String result = rs.getString(columnName);
logger.info("读取string参数1【" + result+"】");
return result;
}
@Override
public String getResult(ResultSet rs, int columnIndex) throws SQLException {
String result = rs.getString(columnIndex);
logger.info("读取string参数2【" + result+"】");
return result;
}
@Override
public String getResult(CallableStatement cs, int columnIndex)
throws SQLException {
String result = cs.getString(columnIndex);
logger.info("读取string参数3【" + result+"】");
return result;
}
}
定义的typeHandler泛型为String,显然我们要把数据库的数据类型转化为String型,然后实现设置参数和获取结果集的方法,在此之前我们还要启用typeHandler:
<typeHandlers>
<typeHandler jdbcType="VARCHAR" javaType="String"
handler="com.ssm.typehandler.MyTypeHandler"/>
</typeHandlers>
经过上述配置后系统才会读取它,完成注册后,当jdbcType和javaType能与MyTypeHandler对应的时候,它就会启动MyTypeHandler。
同样的,当枚举类型很多时,typeHandler也会很多,可以采用包扫描的方式,进行如下配置:
<typeHandlers>
<package name="com.ssm.typehandler"/>
</typeHandlers>
但是这样无法指定jdbcType和javaType了,可以使用注解来处理。在MyTypeHandler加下面两个注解来指定。
//启用扫描注册的时候需要注解
@MappedTypes(String.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class MyTypeHandler implements TypeHandler<String> {
...
}
6.ObjectFactory(对象工厂)
当创建结果集时,MyBatis会使用一个对象工厂来完成创建这个结果集实例。在默认情况下,MyBatis会使用其定义的对象工厂DefaultObjectFactory来完成对应的工作。
MyBatis也允许注册自定义的ObjectFactory。自定义需要实现接口org.apache.ibatis.reflection.factory.ObjectFactory,并进行配置。大部分情况下我们不会自定义返回规则,而是继承实现好的DefaultObjectFactory,通过一定的改写完成我们需要的工作。
public class MyObjectFactory extends DefaultObjectFactory {
private static final long serialVersionUID =-8855123456L;
Logger logger=Logger.getLogger(MyObjectFactory.class);
private Object temp=null;
@Override
public void setProperties(Properties properties) {
super.setProperties(properties);
logger.info("初始化参数:【"+properties.toString()+"】");
}
//方法一
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
T result=super.create(type, constructorArgTypes, constructorArgs);
logger.info("创建对象:"+result.toString());
temp=result;
return result;
}
//方法二
@Override
public <T> T create(Class<T> type) {
T result=super.create(type);
logger.info("创建对象:"+result.toString());
logger.info("是否和上次创建的是同一个对象:【"+(temp==result)+"】");
return result;
}
@Override
public <T> boolean isCollection(Class<T> type) {
return super.isCollection(type);
}
}
然后在xml中进行配置。这样,MyBatis就会采用配置的MyObjectFactory来生成结果集对象。
<objectFactory type="com.ssm.objectfactory.MyObjectFactory">
<property name="prop1" value="value1"/>
</objectFactory>
测试代码:
sqlSession = MyBatisUtil.getSqlSession();
RoleMapper roleMapper = sq1Session. getMapper(RoleMapper.class);
Role role = roleMapper.getRole(1) ;
在这个过程中,MyBatis创建了 一个List对象和要给Role对象。先调用方法1,然后调用方法2,最后生成了同一个对象,所以在判断在始终返回true。因为返回的是一个Role对象,所以它会最后适配为一个Role对象。
7.environments(运行环境)
在MyBatis中,运行环境主要的作用是配置数据库信息,它可以配置多个数据库,一般而言只需要配置其中的一个就可以了。它下面又分为两个可配置的元素:事务管理器(transactionManager)、数据源(dataSource)。
在实际的工作中,大部分情况下会采用Spring对数据源和数据库的事务进行管理。
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="com.ssm.datasource.DbcpDataSourceFactory">
<property name="driver" value="${database.driver}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
</dataSource>
</environment>
</environments>
这里用到两个元素——transactionManager和environment。
7.1transactionManager(事务管理器)
在MyBatis中,transactionManager 提供了两个实现类,它需要实现接口Transaction(org.apache.ibatis.transaction.Transaction)。
它的定义如下:
public interface Transaction {
Connection getConnection() throws SQLException;
void commit() throws SQLException;
void rollback() throws SQLException;
void close() throws SQLException;
Integer getTimeout() throws SQLException;
}
从方法可知,它主要的工作就是提交(commit)、 回滚(rollback) 和关闭(close)数据库的事务。MyBatis为Transaction 提供了两个实现类: JdbcTransaction 和ManagedTransaction。
于是它对应着两种工厂: JdbcTransactionFactory和ManagedTransactionFactory,这个工厂需要实现TransactionFactory接口,通过它们会生成对应的Transaction对象。于是可以把事务管理器配置成两种方式:JDBC和MANAGED。
- JDBC 使用JdbcTransactionFactory 生成的JdbcTransaction 对象实现。它是以JDBC的方式对数据库的提交和回滚进行操作。
- MANAGED使用ManagedTransactionFactory生成的ManagedTransaction对象实现。它的提交和回滚方法不用任何操作,而是把事务交给容器处理。在默认情况下,它会关闭连接,然而一些容 器并不希望这样,因此需要将closeConnection属性设置为false来阻止它默认的关闭行为。
当不想采用MyBatis的规则时,我们可以这样配置:
<transactionManager type="com.ssm.transaction.MyTransactionFactory" />
然后可以自定义一个事务工厂MyTransactionFactory,实现TransactionFactory接口中的工厂方法即可。然后还需要一个事务实现类MyTransaction,实现Transaction接口。这样就能通过自定义事务规则来满足特殊需要了。
7.2environment数据源环境
environment的主要作用是配置数据库,在MyBatis中,数据库通过PooledDataSourceFactory、UnpooledDataSourceFactory和JndiDataSourceFactory三个工厂类来提供,前两者对应产生PooledDataSource、UnpooledDataSource类对象,而JndiDataSourceFactory 则会根据JNDI的信息拿到外部容器实现的数据库连接对象。
无论如何这三个工厂类,最后生成的产品都会是一个实现了DataSource接口的数据库连接对象。配置方式如下:
<dataSource type="UNPOOLED">
<dataSource type="JNDI">
<dataSource type="POOLED">
7.2.1 UNPOOLED
UNPOOLED采用非数据库池的管理方式,每次请求都会打开一个新的数据库连接,创建比较慢。在一些对性能没有很高要求的场合可以使用它。对有些数据库而言,无需使用连接池,那么它是一个比较理想的选择。UNPOOLED类型的数据源可以配置以下几种属性:
- driver。数据库驱动名,比如MySQL的com.mysql.jdbc .Driver。
- url。连接数据库的URL。
- username。用户名。
- password。密码。
- defaultTransactionIsolationLevel。默认的连接事务隔离级别。
传递属性给数据库驱动也是一个可选项,注意属性的前缀为“driver.",例如driver.encoding=UTF8。它会通过DriverManager. getConnection(ur,driverProperties)方法传递值为UTF-8的encoding属性给数据库驱动。
7.2.2 POOLED
数据源POOLED利用“池”的概念将JDBC的Connection对象组织起来,它开始会有一些空置,并且已经连接好的数据库连接,所以请求时,无须再建立和验证,省去了创建新的连接实例时所必需的初始化和认证时间。它还控制最大连接数,避免过多的连接导致系统瓶颈。
除了UNPOOLED下的属性外,会有更多属性用来配置POOLED的数据源:
- poolMaximumActiveConnections是在任意时间都存在的活动(也就是正在使用)连接数量,默认值为10。
- poolMaximumIdleConnections是任意时间可能存在的空闲连接数。
- poolMaximumCheckoutTime 在被强制返回之前,池中连接被检出(checked out)的时间,默认值为20 000毫秒(即20秒)。
- poolTimeToWait 是一个底层设置,如果获取连接花费相当长的时间,它会给连接池打印状态日志,并重新尝试获取一个连接(避免在误配置的情况下一直失败),默认值为20000毫秒(即20秒)。
- poolPingQuery为发送到数据库的侦测查询,用来检验连接是否处在正常工作秩序中,并准备接受请求。默认是“NO PING QUERY SET",这会导致多数数据库驱动失败时带有一个恰当的错误消息。
- poolPingEnabled为是否启用侦测查询。若开启,也必须使用一个可执行的SQL语句设置poolPingQuery属性(最好是一一 个非常快的SQL),默认值为false。
- poolPingConnectionsNotUsedFor 为配置poolPingQuery的使用频度。这可以被设置成匹配具体的数据库连接超时时间,来避免不必要的侦测,默认值为0 (即所有连接每一时刻都被侦测——仅当poolPingEnabled为true时适用)。
7.2.3JNDI
数据源JNDI的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI上下文的引用。这种数据源配置只需要两个属性:
- initial context用来在InitialContext中寻找上下文(即,initialContext.lookup(initialcontext)。initial_ context 是个可选属性,如果忽略,那么data_ source 属性将会直接从InitialContext中寻找。
- data_ source 是引用数据源实例位置上下文的路径。当提供initial context 配置时,data_ source会在其返回的上下文中进行查找:当没有提供initialcontext时,data_ source直接在InitialContext中查找。
与其他数据源配置类似,它可以通过添加前缀“env."直接把属性传递给初始上下文(InitialContext)。比如env.encoding =UTF8,就会在初始上下文实例化时往它的构造方法传递值为UTF8的encoding属性。
7.2.4第三方数据源
MyBatis也支持第三方数据源,例如使用DBCP数据源,那么需要提供一个自定义的DataSourceFactory。
public class DbcpDataSourceFactory implements DataSourceFactory {
private Properties props = null;
@Override
public void setProperties(Properties props) {
this.props = props;
}
@Override
public DataSource getDataSource() {
DataSource dataSource = null;
try {
dataSource = BasicDataSourceFactory.createDataSource(props);
} catch (Exception ex) {
ex.printStackTrace();
}
return dataSource;
}
}
然后在xml中进行如下配置:
<dataSource type="com.ssm.datasource.DbcpDataSourceFactory">
<property name="driver" value="${database.driver}" />
<property name="url" value="${database.url}" />
<property name="username" value="${database.username}" />
<property name="password" value="${database.password}" />
</dataSource>
这样,MyBatis就会采用配置的数据源工厂来生成数据源了。