1. Mybatis基本介绍
2. Mybatis基本使用(三种方式)
3. MyBatis核心配置文件(mybatis-config.xml)
4. Mybatis映射文件(类名+Mapper.xml)
5. Mybatis动态SQL
6. Mybatis分页插件(PageHelper),解决PageHelper.startPage()不安全分页
7. Mybatis一级缓存、二级缓存
8. Mybatis核心类生命周期
9. Eclipse中DTD文件引入(自动联想)
主要用于配置数据连接和MyBatis运行时所需的各种特性:包含了影响MyBatis行为甚深的设置(settings)和属性(properties)
mybatis-config.xml是Mybatis的全局配置文件,名称可以是任意,但是一般命名都为(mybatis-config)
MyBatis还提供了设置这些配置信息的方法。configuration可从配置文件里获取属性值,也可以通过程序直接设置,configuration可供配置内容
configuration为配置文件的根元素节点,下面是configuration元素子节点。
通过resource属性从外部指定属性文件(db.properties),该属性文件描述数据库连接的相关配置(数据库驱动、连接数据库的url、数据库用户名、数据库密码),位置也是在/resources目录下
设置MyBatis运行中的一些行为,比如此处设置MyBatis的log日志实现为LOG4J,即使用log4j实现日志功能
表示配置MyBatis的多套运行环境,将SQL映射到多个不同的数据库上,该元素节点不可以配置多个environments子元素节点,但是必须指定其中一个默认运行环境(通过default指定)
①environment
配置MyBatis的一套运行环境,需指定运行环境ID、事务管理、数据源配置等相关信息
environment子元素 | 描述 |
---|---|
transactionManager | 事务管理器 |
dataSource | 数据源 |
告诉MyBatis去哪里找到SQL映射文件(开发者定义的映射SQL语句),整个项目中可以有1个或多个SQL映射文件。
mappers子元素 | 描述 |
---|---|
mapper | 指定SQL映射文件的路径,其中resource属性的值表述了SQL映射文件的路径(类资源路径) |
为Java类型命名一个别名(简称)
类型处理器
插件
注意:mybatis-config.xml文件的元素节点是有一定顺序的,节点位置若不按顺序排位,那么XML文件会报错
通过该标签来读取Java配置信息
数据源信息写在db.properties文件中,可以通过properties标签来加载该文件
db.properties:
db.driver=com.mysql.jdbc.Driver
db.url=jdbc:mysql://localhost:3306/my
db.username=root
db.password=root
mybatis-config.xml使用properties标签:
注意:
优先①
(1)先加载properties中property标签声明的属性
因此在property中的name属性的值和value比properties中的resource属性先加载;后加载的db.properties会覆盖于property加载属性和值
(2)再加载properties标签引入的Java配置文件中的属性
(3)最后再读取映射文件中parameterType的值会和properties的属性值发生冲突。因此,在properties文件里的内容命名最好加上db.代表是跟数据源相关的属性,这样不容易跟后面的属性发生冲突
设置一些非常重要的设置选项,用于设置和改变MyBatis运行中的行为
settings元素支持的属性。
设置项 | 描述 | 允许值 | 默认 |
---|---|---|---|
cacheEnabled | 该配置影响的所有映射器中配置的缓存的全局开关 | true/false | true |
lazyLoadingEnabled | 全局性设置懒加载。false则所有相关联的都会被初始化加载 | true/false | true |
autoMappingBehavior | MyBatis对于resultMap自动映射的匹配级别 | NONE | PARTIAL |
aggressiveLazyLoading | 当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载 | true,false | |
multipleResultSetsEnabled | 是否允许单一语句返回多结果集(需要兼容驱动) | true,false | true |
useColumnLabel | 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果 | true,false | true |
useGeneratedKeys | 允许JDBC支持自动生成主键,需要驱动兼容。如果设置为true则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如Derby) | true,false | false |
defaultExecutorType | 配置默认的执行器。SIMPLE就是普通的执行器;REUSE执行器会重用预处理语句(prepared statements);BATCH执行器将重用语句并执行批量更新 | SIMPLER,EUSE,BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定驱动等待数据库响应的秒数 | Any positive integer;任意正整数 | Not Set (null) |
safeRowBoundsEnabled | 允许在嵌套语句中使用分页(RowBounds) | true,false | false |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则(camel case)映射。即从经典数据库列名A_COLUMN到经典Java属性名aColumn的类似映射 | true, false | false |
localCacheScope | MyBatis利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。 默认值为SESSION,这种情况下会缓存一个会话中执行的所有查询。若设置值为STATEMENT,本地会话仅用在语句执行上,对相同SqlSession 的不同调用将不会共享数据 | SESSION,STATEMENT | SESSION |
jdbcTypeForNull | 当没有为参数提供特定的JDBC类型时。为空值指定JDBC类型。某些驱动需要指定列的JDBC类型,多数情况直接用一般类型即可,比如NULL、VARCHAR或OTHER | JdbcType enumeration. Most common are: NULL, VARCHAR and OTHER | OTHER |
lazyLoadTriggerMethods | 指定哪个对象的方法触发一次延迟加载 | 用逗号分隔的方法名列表 | equals,clone,hashCode,toString |
defaultScriptingLanguage | 指定动态SQL生成的默认语言 | 类型别名或完全限定类名 | org.apache.ibatis.scripting.xmltags.XMLDynamicLanguageDriver |
callSettersOnNulls | 指定当结果集中值为null的时候,是否调用映射对象的setter(map对象时为put)方法,这对于有 Map.keySet()依赖或null值初始化的时候是有用的。注意基本类型(int、boolean等)是不能设置成 null的 | true,false | false |
logPrefix | 指定MyBatis增加到日志名称的前缀 | 指定MyBatis所用日志的具体实现,未指定时将自动查找 | SLF4J,LOG4J,LOG4J2,JDK_LOGGING,COMMONS_LOGGING,STDOUT_LOGGING,NO_LOGGING |
proxyFactory | 指定Mybatis创建具有延迟加载能力的对象所用到的代理工具 | CGLIB,JAVASSIST | CGLIB |
MyBatis可以配置成适应多种环境,这种机制有助于将SQL映射应用于多种数据库之中
例如:开发、测试和生产环境需要有不同的配置;或者共享相同Schema的多个生产数据库,像使用相同的SQL映射。
注意:尽管可以配置多个环境,每个SqlSessionFactory实例只能选择其一
所以,如果想连接两个数据库,就需要创建两个SqlSessionFactory实例,每个数据库对应一个。而如果是三个数据库,就需要三个实例,依此类推。
指定创建哪种环境,只要将它作为可选的参数传递给SqlSessionFactoryBuilder即可。
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment,properties);
如果忽略了环境参数,那么默认环境将会被加载
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader);
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader,properties);
环境元素定义了如何配置环境。
在MyBatis中有两种类型的事务管理器(type=“[JDBC|MANAGED]”)
直接使用了JDBC的提交和回滚设置,它依赖于从数据源得到的连接来管理事务范围
几乎没做什么,它从来不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如:JEE应用服务器的上下文ServeltContext)
默认情况下它会关闭连接,然而一些容器并不希望这样,因此需将closeConnection属性设置为false来阻止它默认的关闭行为。
如果正在使用Spring + MyBatis,则没有必要配置事务管理器,因为Spring模块会使用自带的管理器来覆盖前面的配置
这两种事务管理器类型都不需要任何属性。它们不过是类型别名,换句话说,可以使用 TransactionFactory接口的实现类的完全限定名或类型别名代替它们
public interface TransactionFactory {
void setProperties(Properties props);
Transaction newTransaction(Connection conn);
Transaction newTransaction(DataSource dataSource, TrarnsactionIsolationLevel 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;
}
使用这两个接口,可以完全自定义MyBatis对事务的处理
dataSource元素使用标准的JDBC数据源接口来配置JDBC连接对象的资源
许多MyBatis的应用程序将会按示例中的例子来配置数据源。然而它并不是必须的。要知道为了方便使用延迟加载,数据源才是必须的。
有三种内建的数据源类型(也就是type=“[UNPOOLED|POOLED|JNDI]”)
数据源的实现只是每次被请求时打开和关闭连接。虽然一点慢,它对在及时可用连接方面没有性能要求的简单应用程序是一个很好的选择(某些数据库对连接池不重要,这个配置是理想的)
UNPOOLED类型的数据源仅仅需要配置以下5种属性
属性 | 描述 |
---|---|
driver | JDBC驱动的Java类的完全限定名 |
url | 数据库的JDBC URL地址 |
username | 登录数据库的用户名 |
password | 登录数据库的密码 |
defaultTransactionIsolationLevel | 默认连接事务隔离级别 |
driver.encoding=UTF8 | 通过DriverManager.getConnection(url,driverProperties)方法传递值为UTF8的encoding属性给数据库驱动 |
利用“池”的概念将JDBC连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。是一种使得并发Web应用快速响应请求的流行处理方式
POOLED数据源属性
属性 | 描述 |
---|---|
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时适用) |
数据源的实现是为了能在如EJB或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个JNDI上下文的引用。
数据源配置属性
属性 | 描述 |
---|---|
initial_context | 这个属性用来在InitialContext中寻找上下文(即,initialContext.lookup(initial_context))。这是个可选属性,如果忽略,那么data_source属性将会直接从InitialContext中寻找 |
data_source | 这是引用数据源实例位置的上下文的路径。提供了initial_context配置时会在其返回的上下文中进行查找,没有提供时则直接在 InitialContext 中查找;和其他数据源配置类似,可以通过添加前缀"env."直接把属性传递给初始上下文 |
env.encoding=UTF-8 | 这就会在初始上下文(InitialContext)实例化时往它的构造方法传递值为 UTF8 的 encoding 属性 |
通过需要实现接口 org.apache.ibatis.datasource.DataSourceFactory ,也可使用任何第三方数据源
public interface DataSourceFactory {
void setProperties(Porperties props);
DataSource getDataSource();
}
org.apache.ibatis.datasource.unpooled.UnpooledDataSourceFactory 可被用作父类来构建新的数据源适配器,比如下面这段插入C3P0数据源所必需的代码
public class C3P0DataSourceFactory extends UnpooledDataSourceFactory {
public C3P0DataSourceFactory() {
this.dataSource = new ComboPooledDataSource();
}
}
为了令其工作,为每个需要MyBatis调用的setter方法中增加一个属性。下面是一个可以连接至PostgreSQL数据库的例子。
mappers:映射器,用来定义SQL的映射语句,只需要告诉MyBatis去哪里找到这些SQL语句,即去哪里找到相应的SQL映射文件(加载映射文件)
下面四种都是
方式一:
类资源路径
方式二:
全限定路径(URL获取资源)
方式三:
mapper接口的全限定类名
此方式要求:Mapper接口Mapper映射文件名称相同且在同一个目录下
方式四:
加载指定包下的所有映射文件
类型别名是为Java类型设置一个短的名字。它只和XML配置有关,存在的意义仅在于用来减少类完全限定名的冗余。
当这样配置时,user可以用在任何地方使用cn.smbms.pojo.User
这种写法的弊端在于如果一个项目中有多个POJO的时候,需要一一配置
MyBatis会自动扫描指定包下的JavaBean,并默认设置一个别名,默认名称为JavaBean的非限定类名(首字母小写User --> user)
每一个在包 cn.my.pojo 中的 JavaBean,在没有注解的情况下,会使用Bean的首字母小写的非限定类名来作为它的别名
比如 :domain.blog.Author.java的别名为author;若有注解,则别名为其注解值
Mybatis映射处理
Mybatis已经为许多常见的Java类型内建了相应的类型别名。它们都是大小写不敏感的,需要注意的是由基本类型名称重复导致的特殊处理。
别名 | 映射类型 | 别名 | 映射类型 |
---|---|---|---|
_byte | byte | double | Double |
_long | long | float | Float |
_short | short | boolean | Boolean |
_int | int | date | Date |
_integer | int | decimal | BigDecimal |
_double | double | bigdecimal | BigDecimal |
_float | float | object | Object |
_boolean | boolean | map | Map |
string | String | hashmap | HashMap |
byte | Byte | list | List |
long | Long | arraylist | ArrayList |
short | Short | collection | Collection |
int | Integer | iterator | Iterator |
integer | Integer |
无论是MyBatis在预处理语句(PreparedStatement)中设置一个参数时,还是从结果集中取出一个值时,都会用类型处理器将获取的值以合适的方式转换成Java类型。
默认的类型处理器
类型处理器 | Java类型 | JDBC类型 |
---|---|---|
BooleanTypeHandler | java.lang.Boolean,boolean | 数据库兼容的BOOLEAN |
ByteTypeHandler | java.lang.Byte,byte | 数据库兼容的NUMERIC或BYTE |
ShortTypeHandler | java.lang.Short,short | 数据库兼容的NUMERIC或SHORT INTEGER |
IntegerTypeHandler | java.lang.Integer,int | 数据库兼容的NUMERIC或INTEGER |
LongTypeHandler | java.lang.Long,long | 数据库兼容的NUMERIC或LONG INTEGER |
FloatTypeHandler | java.lang.Float,float | 数据库兼容的NUMERIC或FLOAT |
DoubleTypeHandler | java.lang.Double,double | 数据库兼容的NUMERIC或DOUBLE |
BigDecimalTypeHandler | java.math.BigDecimal | 数据库兼容的NUMERIC或DECIMAL |
StringTypeHandler | java.lang.String | CHAR,VARCHAR |
ClobTypeHandler | java.lang.String | CLOB,LONGVARCHAR |
NStringTypeHandler | java.lang.String | NVARCHAR,NCHAR |
NClobTypeHandler | java.lang.String | NCLOB |
ByteArrayTypeHandler | byte[] | 数据库兼容的字节流类型 |
BlobTypeHandler | byte[] | BLOB,LONGVARBINARY |
DateTypeHandler | java.util.Date | TIMESTAMP |
DateOnlyTypeHandler | java.util.Date | DATE |
TimeOnlyTypeHandler | java.util.Date | TIME |
SqlTimestampTypeHandler | java.sql.Timestamp | TIMESTAMP |
SqlDateTypeHandler | java.sql.Date | DATE |
SqlTimeTypeHandler | java.sql.Time | TIME |
ObjectTypeHandler | Any | OTHER或未指定类型 |
EnumTypeHandler | Enumeration Type | VARCHAR-任何兼容的字符串类型,存储枚举的名称(而不是索引) |
EnumOrdinalTypeHandler | Enumeration Type | 任何兼容的NUMERIC或DOUBLE类型,存储枚举的索引(而不是名称) |
可以重写类型处理器或创建自己的类型处理器来处理不支持的或非标准的类型
具体做法为:
(1)实现org.apache.ibatis.type.TypeHandler接口
(2)继承一个很便利的类org.apache.ibatis.type.BaseTypeHandler ,然后可以选择性地将它映射到一个JDBC类型
@MappedJdbcTypes(JdbcType.VARCHAR)
public class ExampleTypeHandler extends BaseTypeHandler {
@Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getString(columnName);
}
@Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getString(columnIndex);
}
@Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getString(columnIndex);
}
@Override
public void setNonNullParameter(PreparedStatement ps, int i, Object parameter, JdbcType jdbcType)
throws SQLException {
ps.setObject(i, parameter);
}
}
使用这个的类型处理器将会覆盖已经存在的处理Java的String类型属性和VARCHAR参数及结果的类型处理器。要注意MyBatis不会窥探数据库元信息来决定使用哪种类型,所以你必须在参数和结果映射中指明那是VARCHAR类型的字段,以使其能够绑定到正确的类型处理器上。这是因为:MyBatis直到语句被执行才清楚数据类型
通过类型处理器的泛型,MyBatis可以得知该类型处理器处理的Java类,不过这种行为可以通过两种方法改变:
(1)在类型处理器的配置元素(typeHandler element)上增加一个javaType属性(比如:javaType=“String”)
(2)在类型处理器的类上(TypeHandler class)增加一个@MappedTypes注解来指定与其关联的Java类型列表。如果在javaType属性中也同时指定,则注解方式将被忽略
可以通过两种方式来指定被关联的JDBC类型:
(1)在类型处理器的配置元素上增加一个javaType属性(比如:javaType=“VARCHAR”);
(2)在类型处理器的类上(TypeHandler class)增加一个**@MappedJdbcTypes注解**来指定与其关联的JDBC类型列表。如果在javaType属性中也同时指定,则注解方式将被忽略
最后,可以让 MyBatis 查找类型处理器
注意:使用自动检索(autodiscovery)功能的时候,只能通过注解方式来指定JDBC的类型
处理枚举类型
若想映射枚举类型Enum,则需要从EnumTypeHandler或者EnumOrdinalTypeHandler中选一个来使用
比如说我们想存储取近似值时用到的舍入模式。默认情况下,MyBatis会利用EnumTypeHandler来把Enum值转换成对应的名字
注意:EnumTypeHandler在某种意义上来说是比较特别的,其他的处理器只针对某个特定的类,而它不同,它会处理任意继承了Enum的类。
不过,我们可能不想存储名字,相反我们的 DBA 会坚持使用整形值代码。那也一样轻而易举: 在配置文件中把EnumOrdinalTypeHandler加到typeHandlers中即可,这样每个RoundingMode将通过他们的序数值来映射成对应的整形
怎样能将同样的Enum既映射成字符串又映射成整形呢?
自动映射器(auto-mapper)会自动地选用EnumOrdinalTypeHandler来处理,所以如果我们想用普通的EnumTypeHandler,就非要为那些SQL语句显式地设置要用到的类型处理器不可
注意:这里的select语句强制使用resultMap来代替resultType
对象工厂(objectFactory)
MyBatis每次创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认构造方法,要么在参数映射存在的时候通过参数构造方法来实例化。 如果想覆盖对象工厂的默认行为,则可以通过创建自己的对象工厂来实现
ObjectFactory 接口很简单,它包含两个创建用的方法,一个是处理默认构造方法的,另外一个是处理带参数的构造方法的。 最后,setProperties方法可以被用来配置ObjectFactory,在初始化你的ObjectFactory实例后,objectFactory元素体中定义的属性会被传递给setProperties方法。
MyBatis允许在已映射语句执行过程中的某一点进行拦截调用
默认情况下,MyBatis允许使用插件来拦截的方法调用包括
(1)Executor (update, query, flushStatements, commit, rollback, getTransaction, close, isClosed)
(2)ParameterHandler (getParameterObject, setParameters)
(3)ResultSetHandler (handleResultSets, handleOutputParameters)
(4)StatementHandler (prepare, parameterize, batch, update, query)
这些类中方法的细节可以通过查看每个方法的签名来发现,或者直接查看MyBatis的发行包中的源代码。假设你想做的不仅仅是监控方法的调用,那么你应该很好的了解正在重写的方法的行为。 因为如果在试图修改或重写已有方法的行为的时候,你很可能在破坏MyBatis的核心模块。这些都是更低层的类和方法,所以使用插件的时候要特别当心
通过MyBatis提供的强大机制,使用插件是非常简单的,只需实现Interceptor接口,并指定了想要拦截的方法签名即可
覆盖配置类
除了用插件来修改MyBatis核心行为之外,还可以通过完全覆盖配置类来达到目的。只需继承后覆盖其中的每个方法,再把它传递到sqlSessionFactoryBuilder.build(myConfig)方法即可。再次重申,这可能会严重影响MyBatis的行为,务请慎之又慎
MyBatis可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的databaseId属性。MyBatis会加载不带databaseId属性和带有匹配当前数据库databaseId属性的所有语句。如果同时找到带有databaseId和不带databaseId的相同语句,则后者会被舍弃。为支持多厂商特性只要像下面这样在mybatis-config.xml文件中加入databaseIdProvider即可
这里的DB_VENDOR会通过DatabaseMetaData#getDatabaseProductName()返回的字符串进行设置。由于通常情况下这个字符串都非常长而且相同产品的不同版本会返回不同的值,所以最好通过设置属性别名来使其变短,如下:
在有properties时,DB_VENDOR databaseIdProvider的将被设置为第一个能匹配数据库产品名称的属性键对应的值,如果没有匹配的属性将会设置为"null"。在这个例子中,如果getDatabaseProductName()返回"Oracle (DataDirect)“,databaseId将被设置为"oracle”。
可以通过实现接口 org.apache.ibatis.mapping.DatabaseIdProvider 并在 mybatis-config.xml 中注册来构建自己的 DatabaseIdProvider:
public interface DatabeaseIdProvider {
void setProperties(Properties p);
String getDatabaseId(DataSource dataSource) throws SQLException;
}