Mybatis配置文件并不复杂,它的所有元素如下:
需要注意的是,顺序不能颠倒,否则启动会发生异常.
1、properties属性
properties属性可以给系统配置一些运行参数,可以放在xml文件或properties文件中。mybatis提供了三种方式让我们使用properties:
property元素;
properties文件;
程序代码传递;
1.1 property元素:
这样就可以一次定义到处使用了
1.2 使用properties文件
这个是比较普遍的大家常用的方法。一来这个文件十分简单,里面就是存的键值对,二来可以吧多个键值对放在不同文件,以便于日后维护。
jdbc.properties如下:
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/ssm
jdbc.username=root
jdbc.password=qq11qq
在Mybatis中通过properties的属性resource来引入properties文件
1.3 使用程序传递方式传递参数
在真实的生产环境中,数据库密码需要保密。故一般都需要将用户名密码经过加密成为密文后,配置到properties文件中。假设提供了Utils.decode(cipferText)
进行解密。那么创建SqlSessionFactory代码清单如下所示:
String resource = "mybatis-config.xml";
InputStream inputStream;
InputStream in = Resources.getResourceAsStream(resource);
Properties props = new Properties();
props.load(in);
String username = props.getProperty("jdbc.username");
String password = props.getProperty("jdbc.password");
props.put("jdbc.username",Utils.decode(username));
props.put("jdbc.password",Utils.decode(password));
inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream,props);
2、settings设置
settings是mybatis中最复杂的配置,它能深刻的影响mybatis底层的运行,但是在大部分情况下,使用默认值便可以运行。下面是全量的配置案例:
常见的几个设置的用法简介:
配置项 | 作用 | 配置选项说明 | 默认值 |
---|---|---|---|
cacheEnabled | 该配置影响所有映射器中配置缓存的全局开关 | true/false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。在特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态 | true/false | false |
aggressiveLazyLoading | 当启用时,对任意延迟属性的调用会使得带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载 | true/false | 版本3.1.4(不包含)之前true,之后false |
autoMappingBehavior | 指定mybatis应如何映射列到字段或属性。NONE表示取消自动映射;PARTIAL表示只会自动映射,没有定义嵌套结果集和映射结果集;FULL会自动映射任意的复杂结果集(无论是否嵌套) | NONE/PARTIAL/FULL | PARTIAL |
defaultExecutorType | 配置默认的执行器 。SIMPLE是普通的执行器;Reuse会重用预处理语句(Prepared statements);BATCH执行器将重用语句并执行批量更新 | SIMPLE/REUSE/BATCH | SIMPLE |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则映射,即从经典数据库列名A_COLUMN到经典java属性名aColumn的类似映射 | true/false | false |
3、typeAliases 别名
由于类的全限定名很长,在多次需要使用的时候,比较不方便,在mybatis中允许定义一个简写来代替,这就是别名,分为系统定义和自定义别名。在mybatis中别名由类TypeAliasRegistry来定义(org.apache.ibatis.type.TypeAliasRegistry)。在mybatis中,别名不区分大小写。
3.1、自定义别名
由于现实开发中,存在许多自定义的对象需要重复使用,因此mybatis中也提供了用户自定义别名的规则。我们可以通过TypeAliasRegistry类的registerAlias方法注册,也可以采用配置文件或或者扫描方式来定义它。
3.1.1、使用配置文件定义
3.1.2、扫描
现实开发中可能存在大量的实体类,如果一个一个的配置就不适合了。因此mybatis还提供了扫描别名。如上配置:两个实体类位于同一包下,还可以这样配置:
3.1.3、注解配置别名
如果多个类同时包含User实体类,在通过包扫描后,可能会有冲突。则可以通过注解来区分。比如:
package com.ssm.pojo;
@Alias("user3")
public Class User{
... ...
}
这样就可以避免因为别名重名导致的扫描失败的问题。
4、typeHandler类型转换器
在JDBC中,需要在PreparedStatement对象中设置哪些已经预编译过的SQL语句的参数。执行SQL后,会通过ResultSet对象获取得到数据库的数据。而这些,Mybatis是根据数据库数据的类型通过TypeHandler来实现的。
在myBatis中,TypeHandler都需要实现接口org.apache.ibatis.type.TypeHandler
,源码如下:
public interface TypeHandler {
void setParameter(java.sql.PreparedStatement preparedStatement, int i, T t, org.apache.ibatis.type.JdbcType jdbcType) throws java.sql.SQLException;
T getResult(java.sql.ResultSet resultSet, java.lang.String s) throws java.sql.SQLException;
T getResult(java.sql.ResultSet resultSet, int i) throws java.sql.SQLException;
T getResult(java.sql.CallableStatement callableStatement, int i) throws java.sql.SQLException;
}
其中setParameter方法是使用TypeHandler通过PreparedStatement对象进行设置SQL参数的时候使用的具体方法,其中i是参数在SQL的下标,parameter是参数,jdbcType是数据库类型。
如果研究TypeHandler的源码,会发现其都实现了org.apache.ibatis.type.BaseTypeHandler
,所以我们先看一下BaseTypeHandler的源码:
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.type;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import org.apache.ibatis.executor.result.ResultMapException;
import org.apache.ibatis.session.Configuration;
/**
* The base {@link TypeHandler} for references a generic type.
*
* Important: Since 3.5.0, This class never call the {@link ResultSet#wasNull()} and
* {@link CallableStatement#wasNull()} method for handling the SQL {@code NULL} value.
* In other words, {@code null} value handling should be performed on subclass.
*
*
* @author Clinton Begin
* @author Simone Tripodi
* @author Kzuki Shimizu
*/
public abstract class BaseTypeHandler extends TypeReference implements TypeHandler {
/**
* @deprecated Since 3.5.0 - See https://github.com/mybatis/mybatis-3/issues/1203. This field will remove future.
*/
@Deprecated
protected Configuration configuration;
/**
* @deprecated Since 3.5.0 - See https://github.com/mybatis/mybatis-3/issues/1203. This property will remove future.
*/
@Deprecated
public void setConfiguration(Configuration c) {
this.configuration = c;
}
@Override
public void setParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException {
if (parameter == null) {
if (jdbcType == null) {
throw new TypeException("JDBC requires that the JdbcType must be specified for all nullable parameters.");
}
try {
ps.setNull(i, jdbcType.TYPE_CODE);
} catch (SQLException e) {
throw new TypeException("Error setting null for parameter #" + i + " with JdbcType " + jdbcType + " . "
+ "Try setting a different JdbcType for this parameter or a different jdbcTypeForNull configuration property. "
+ "Cause: " + e, e);
}
} else {
try {
setNonNullParameter(ps, i, parameter, jdbcType);
} catch (Exception e) {
throw new TypeException("Error setting non null for parameter #" + i + " with JdbcType " + jdbcType + " . "
+ "Try setting a different JdbcType for this parameter or a different configuration property. "
+ "Cause: " + e, e);
}
}
}
@Override
public T getResult(ResultSet rs, String columnName) throws SQLException {
try {
return getNullableResult(rs, columnName);
} catch (Exception e) {
throw new ResultMapException("Error attempting to get column '" + columnName + "' from result set. Cause: " + e, e);
}
}
@Override
public T getResult(ResultSet rs, int columnIndex) throws SQLException {
try {
return getNullableResult(rs, columnIndex);
} catch (Exception e) {
throw new ResultMapException("Error attempting to get column #" + columnIndex + " from result set. Cause: " + e, e);
}
}
@Override
public T getResult(CallableStatement cs, int columnIndex) throws SQLException {
try {
return getNullableResult(cs, columnIndex);
} catch (Exception e) {
throw new ResultMapException("Error attempting to get column #" + columnIndex + " from callable statement. Cause: " + e, e);
}
}
public abstract void setNonNullParameter(PreparedStatement ps, int i, T parameter, JdbcType jdbcType) throws SQLException;
/**
* @param columnName Colunm name, when configuration useColumnLabel
is false
*/
public abstract T getNullableResult(ResultSet rs, String columnName) throws SQLException;
public abstract T getNullableResult(ResultSet rs, int columnIndex) throws SQLException;
public abstract T getNullableResult(CallableStatement cs, int columnIndex) throws SQLException;
}
我们简单分析一下源码:
1、这是个抽象类,实现了TypeHandler接口的四个方法,定义了4个抽象方法,需要其子类去实现。下面将会以StringTypeHandler来简单说明一下。
2、getResult方法:非空结果集是通过getNullableResult来实现。
3、setParameter方法:如果parameter与jdbcType同时为null,抛出异常。
4、getNullableResult用于存储过程。
下面通过StringTypeHandler 来加深了解TypeHandler:
/**
* Copyright 2009-2015 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.type;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* @author Clinton Begin
*/
public class StringTypeHandler extends BaseTypeHandler {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
throws SQLException {
ps.setString(i, parameter);
}
@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);
}
}
在mybatis中采用org.apache.ibatis.type.TypeHandlerRegistry类对象的register来进行注册。其源码如下(已删减部分)
/**
* @author Clinton Begin
* @author Kazuki Shimizu
*/
public final class TypeHandlerRegistry {
public TypeHandlerRegistry() {
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
... ...
register(Byte[].class, new ByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
... ...
}
}
自定义的TypeHandler一般是通过配置或扫描:
根据系统定义的可以知道,要实现TypeHandler就需要去实现接口TypeHandler或者继承BaseTypeHandler(其也实现了接口TypeHandler)。这里我们仿造一个StringTypeHandler。代码如下:
package com.ssm.chapter3.typehandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.TypeHandler;
import org.apache.log4j.Logger;
import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
/**
* Created by czz on 2019/8/4.
*/
public class MyTypeHandler implements TypeHandler {
Logger log = Logger.getLogger(MyTypeHandler.class);
@Override
public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
throws SQLException{
log.info("String参数为: 【"+parameter+"】");
ps.setString(i,parameter);
}
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException{
String result = rs.getString(columnName);
log.info("读取String参数-01 : 【"+result+"】");
return result;
}
@Override
public String getResult(ResultSet rs, int columnIndex) throws SQLException{
String result = rs.getString(columnIndex);
log.info("读取String参数-02 : 【"+result+"】");
return result;
}
@Override
public String getResult(CallableStatement cs, int columnIndex) throws SQLException{
String result = cs.getString(columnIndex);
log.info("读取String参数-03 : 【"+result+"】");
return result;
}
}
定义完成之后,还需要额皮质完成系统才会读取它,注册它。
还可以显示启用TypeHandler,代码如下:
由于有时候自定义的类型会比较多,可以考虑使用包扫描的形式。但是这样就无法指定JavaType和jdbcType了,此时可以用注解处理。代码如下:
@MappedTypes(String.class)
@MappedJdbcTypes(JdbcType.VARCHAR)
public class MyTypeHandler implements TypeHandler {
... ...
}
5、ObjectFactory(对象工厂)
当创建结果集时,Mybatis会使用一个对象工厂来创建这个结果集实例。在默认的情况下,Mybatis会使用其自定义的对象工厂------DefaultObjectFactory(org.apache.ibatis.reflection.DefaultObjectFactory),并给予配置。在大部分情况下,我们都不需要自定义返回规则,因为这些比较复杂且容易出错。更多的情况,可能是继承DefaultObjectFactory,通过一定的改写来完成我们所需要的工作。
6、插件
插件是mybatis中最强大和灵活的组件,同时也是最复杂最难以使用的的组件,而且因为它将覆盖mybatis底层对象的核心方法和属性,所以十分危险。
7、environments(运行环境)
在mybatis中,运行环境的主要作用是配置数据库信息,它可以配置多个数据库,一般而言只需要配置其中的一个就可以了。他下面又分为两个可配置的元素:事务管理器(TransactionManager)和数据源(DataSource)。运行环境配置,代码如下:
7.1、事务管理器(TransactionManager)
在mybatis中transactionManager提供了两个实现类,他们需要实现接口org.apache.ibatis.transaction.Transaction
接口定义如下:
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.transaction;
import java.sql.Connection;
import java.sql.SQLException;
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对象。于是,可以把事务管理器配置成一下两种方式:
transactionFactory源码如下:
public interface TransactionFactory {
default void setProperties(Properties props) {
// NOP
}
Transaction newTransaction(Connection conn);
Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit);
}
下面是JdbcTransactionFactory 的源码,通过源码可以更了解该工厂如何生产JdbcTransaction的。这部分源码相对来讲比较容易理解。
/**
* Copyright 2009-2019 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.ibatis.transaction.jdbc;
import java.sql.Connection;
import javax.sql.DataSource;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionFactory;
public class JdbcTransactionFactory implements TransactionFactory {
@Override
public Transaction newTransaction(Connection conn) {
return new JdbcTransaction(conn);
}
@Override
public Transaction newTransaction(DataSource ds, TransactionIsolationLevel level, boolean autoCommit) {
return new JdbcTransaction(ds, level, autoCommit);
}
}
当然如果不想采用mybatis给提供的规则,我们可以自定义事务工厂:
package com.ssm.chapter3.objectfactory;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.jdbc.JdbcTransaction;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.SQLException;
/**
* Created by czz on 2019/8/5.
*/
public class MyTransaction extends JdbcTransaction implements Transaction {
public MyTransaction(DataSource dataSource, TransactionIsolationLevel level,boolean autoCommit){
super(dataSource,level,autoCommit);
}
public MyTransaction(Connection conn){
super(conn);
}
@Override
public Connection getConnection() throws SQLException {
return super.getConnection();
}
@Override
public void rollback() throws SQLException{
super.rollback();
}
@Override
public void commit() throws SQLException{
super.commit();
}
@Override
public Integer getTimeout() throws SQLException{
return super.getTimeout();
}
}
package com.ssm.chapter3.objectfactory;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionFactory;
import javax.sql.DataSource;
import java.sql.Connection;
import java.util.Properties;
/**
* Created by czz on 2019/8/5.
*/
public class MyTransactionFactory implements TransactionFactory {
@Override
public void setProperties(Properties props){}
@Override
public Transaction newTransaction(Connection conn){
return new MyTransaction(conn);
}
@Override
public Transaction newTransaction(DataSource dataSource, TransactionIsolationLevel level,boolean autoCommit){
return new MyTransaction(dataSource,level,autoCommit);
}
}
配置自定义事务
7.2environment数据源环境
environment的主要作用是配置数据库,在mybatis中,数据库通过PooledDataSourceFactory、UnpooledDataSourceFactory、JndiDataSourceFactory三个工厂类来提供。前两者会产生PooledDataSource、UnpooledDataSource对象,而JndiDataSourceFactory则会jndi的信息拿到外部容器实现的数据库连接对象。无论如何,这三个工厂类生成的产品都是一个实现了DataSource接口的数据库连接对象。配置方法分别如下所示:
7.2.1 UNPOOLED
采用非数据库池的管理方式,每次请求都会打开一个新的数据库连接,所以创建会比较慢。在一些对性能要求没那么高的场合可以使用。可以配置以下属性:
driver:数据库驱动名;
url
username
password
DefaultTransactionIsolationLevel:默认的连接事务隔离级别。
7.2.2 Pooled
利用池的概念将JDBC的connection对象组织起来。它开始会有一些空置,以及一些连接好的数据库连接。所以在请求时,无需再建立和验证,省去了创建新的连接实例时所必需的初始化和认证时间。它还空置最大连接数,以免过多的连接导致系统瓶颈。
除了UNPOOLED的属性外,会有更多属性用来配置POOLED数据源:
poolMaximumActiveConnections:
poolMaximumIdleConnections:
poolMaximumCheckoutTime:
poolTimeToWait:
poolPingQuery:
poolPingEnabled:
poolPingConnectionsNotUserFor:
7.2.3 JNDI
可以自定义数据源工厂,并进行配置
... ...
8、databaseIdProvider数据库厂商标识
主要用于给不同客户提供系统需要不同数据库连接时,配置如下:
标识SQL适用何种数据库:
9、引入映射器的方法