文章目录
我把总结放在开头,方便大家使用
为了让大家方便的使用,特意把总结放在文章开头,有帮助到你就点个关注呗
配置名称 | 配置含义 | 配置简介 |
---|---|---|
configuration | 包裹所有配置标签 | 整个配置文件的顶级标签 |
properties | 属性 | 该标签可以引入外部配置的属性,也可以自己配置。该配置标签所在的同一个配置文件的其他配置均可以引用此配置中的属性 |
setting | 全局配置参数 | 用来配置一些改变运行时行为的信息,例如是否使用缓存机制,是否使用延迟加载,是否使用错误处理机制等。 此项数据过于庞大,在下面细讲 |
typeAliases | 类型别名 | 用来设置一些别名来代替Java的长类型声明(如 java.lang.int变为int),减少配置编码的冗余 |
typeHandlers | 类型处理器 | 将数据库获取的值以合适的方式转换为Java 类型,或者将Java类型的参数转换为数据库对应的类型 |
objectFactory | 对象工厂 | 实例化目标类的工厂类配置 |
plugins | 插件 | 可以通过插件修改MyBatis的核心行为,例如对语句执行的某一点进行拦截调用 |
environments | 环境集合属性对象 | 数据库环境信息的集合。在一个配置文件中,可以有多种数据库环境集合,这样可以使MyBatis将SQL同时映射至多个数据库 |
environment | 环境子属性对象 | 数据库环境配置的详细配置 |
transactionManager | 事务管理 | 指定MyBat is的事务管理器 |
dataSource | 数据源 | 使用其中的type指定数据源的连接类型,在标签对中可以使用 |
configuration
configuration是整个配置文件的根标签,实际上也对应着MyBatis里面最重要的配置类Configuration。
它贯穿MyBatis执行流程的每一个环节。我们打开这个类看一下,这里面有很多的属性,跟其他的子标签也能对应上。
properties
第一个一级标签是properties,用来配置参数信息,比如最常见的数据库连接信息。
为了避免直接把参数写死在xml配置文件中,我们可以把这些参数单独放在properties文件中,用properties标签引入进来,然后在xml 配置文件中用$引用就可以了。
可以用resource引用应用里面的相对路径,也可以用url指定本地服务器或者网络的绝对路径。
settings
setttings里面是MyBatis的一些核心配置,在 MyBatis 中 settings 是最复杂的配置,它能深刻影响 MyBatis 底层的运行,但是在大部分情况下使用默认值便可以运行,所以在大部分情况下不需要大量配置它,只需要修改一些常用的规则即可,比如自动映射、驼峰命名映射、级联规则、是否启动缓存、执行器(Executor)类型等。
属性名 | 作用 | 有效值 | 默认值 |
---|---|---|---|
cacheEnabled | 该配置影响所有映射器中配置缓存的全局开关 | true,false | true |
lazyLoadingEnabled | 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。在特定关联关系中可通过设置 fetchType 属性来覆盖该项的开关状态 | true,false | false |
aggressiveLazyLoading | 当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载 | true,felse | 版本3.4.1 (不包含)之前 true,之后 false |
multipleResultSetsEnabled | 是否允许单一语句返回多结果集(需要兼容驱动) | true,felse | true |
useColumnLabel | 使用列标签代替列名。不同的驱动会有不同的表现,具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果 | true,felse | true |
useGeneratedKeys | 允许JDBC 支持自动生成主键,需要驱动兼容。如果设置为 true,则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby) | true,felse | false |
autoMappingBehavior | 指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射。 PARTIAL 表示只会自动映射,没有定义嵌套结果集和映射结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套) | NONE、PARTIAL、FULL | PARTIAL |
autoMappingUnknownColumnBehavior | 指定自动映射当中未知列(或未知属性类型)时的行为。 默认是不处理,只有当日志级别达到 WARN 级别或者以下,才会显示相关日志,如果处理失败会抛出 SqlSessionException 异常 | NONE、WARNING、FAILING | NONE |
defaultExecutorType | 配置默认的执行器。SIMPLE 是普通的执行器;REUSE 会重用预处理语句(prepared statements);BATCH 执行器将重用语句并执行批量更新 | SIMPLE、REUSE、BATCH | SIMPLE |
defaultStatementTimeout | 设置超时时间,它决定驱动等待数据库响应的秒数 | 任何正整数 | Not Set (null) |
defaultFetchSize | 设置数据库驱动程序默认返回的条数限制,此参数可以重新设置 | 任何正整数 | Not Set (null) |
safeRowBoundsEnabled | 允许在嵌套语句中使用分页(RowBounds)。如果允许,设置 false | true,felse | false |
safeResultHandlerEnabled | 允许在嵌套语句中使用分页(ResultHandler)。如果允许,设置false | true,felse | false |
mapUnderscoreToCamelCase | 是否开启自动驼峰命名规则映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射 | true,felse | false |
localCacheScope | MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速联复嵌套査询。 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlScssion 的不同调用将不会共享数据 | SESSION,STATEMENT | SESSION |
jdbcTypeForNull | 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER | NULL、VARCHAR、OTHER | OTHER |
lazyLoadTriggerMethods | 指定哪个对象的方法触发一次延迟加载 | — | equals、clone、hashCode、toString |
defaultScriptingLanguage | 指定动态 SQL 生成的默认语言 | — | org.apache.ibatis .script.ing.xmltags .XMLDynamicLanguageDriver |
callSettersOnNulls | 指定当结果集中值为 null 时,是否调用映射对象的 setter(map 对象时为 put)方法,这对于 Map.kcySet() 依赖或 null 值初始化时是有用的。注意,基本类型(int、boolean 等)不能设置成 null | true,felse | false |
logPrefix | 指定 MyBatis 增加到日志名称的前缀 | 任何字符串 | Not set |
loglmpl | 指定 MyBatis 所用日志的具体实现,未指定时将自动査找 | SLF4J,LOG4J,LOG4J2,JDK_LOGGING,COMMONS_LOGGING,ST DOUT_LOGGING,NO_LOGGING | Not set |
proxyFactory | 指定 MyBatis 创建具有延迟加栽能力的对象所用到的代理工具 | CGLIB,JAVASSIST | JAVASSIST (MyBatis 版本为 3.3 及以上的) |
vfslmpl | 指定 VFS 的实现类 | 提供 VFS 类的全限定名,如果存在多个,可以使用逗号分隔 | Not set |
useActualParamName | 允许用方法参数中声明的实际名称引用参数。要使用此功能,项目必须被编译为 Java 8 参数的选择。(从版本 3.4.1 开始可以使用) | true,felse | true |
settings 的配置项很多,但是真正用到的不会太多,我们把常用的配置项研究清楚就可以了,比如关于缓存的 cacheEnabled,关于级联的 lazyLoadingEnabled 和 aggressiveLazy Loading,关于自动映射的 autoMappingBehavior 和 mapUnderscoreToCamelCase,关于执行器类型的 defaultExecutorType 等。
<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="defaultStatementTimeout" value="25"/>
<setting name="defaultFetchSize" 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>
typeAliases
TypeAlias是类型的别名,跟Linux系统里面的alias一样,主要用来简化类名全路径的拼写。比如我们的参数类型和返回值类型都可能会用到我们的Bean,如果每个地方都配置全路径的话,那么内容就比较多,还可能会写错。
我们可以为自己的Bean 创建别名,既可以指定单个类,也可以指定一个package,自动转换。
<typeAliases>
<typeAlias alias="blog" type="com.domain.Blog" />
</typeAliases>
配置了别名以后,在配置文件中只需要写别名就可以了,比如com.domain.Blog 可以简化成 blog。
<select id="selectBlogByBean" parameterType="blog" resultType="blog"
select bid, name, author_id authorld from blog where name = 'S{name}'
</select>
MyBatis里面有很多系统预先定义好的类型别名,在TypeAliasRegistry 中。所以可以用string 代替java.lang.String。
typeHandlers
由于Java类型和数据库的JDBC类型不是一一对应的(比如String与varchar、char.text),所以我们把Java对象转换为数据库的值,和把数据库的值转换成Java对象,需要经过一定的转换,这两个方向的转换就要用到TypeHandler。
当参数类型和返回值是一个对象的时候,我没有做任何的配置,为什么对象里面的一个String属性,可以转换成数据库里面的varchar字段?
这是因为MyBatis 已经内置了很多TypeHandler(在type包下),它们全部全部注册在TypeHandlerRegistry中,他们都继承了抽象类BaseTypeHandler,泛型就是要处理的Java数据类型。
这个也是为什么大部分类型都不需要处理。当我们查询数据和登记数据,做数据类型转换的时候,就会自动调用对应的TypeHandler的方法。
如果我们需要自定义一些类型转换规则,或者要在处理类型的时候做一些特殊的动作,就可以编写自己的TypeHandler,跟系统自定义的TypeHandler一样,继承抽象类BaseTypeHandler。有4个抽象方法必须实现,我们把它分成两类:
set方法是从Java类型转换成JDBC类型的
get方法是从JDBC类型转换成Java类型的
从Java类型到JDBC类型 | 从JDBC类型到Java类型 |
---|---|
setNonNullParameter:设置非空参数 | getNullableResult:获取空结果集(根据列名),一般都是调用这个 |
. | getNullableResult:获取空结果集(根据下标值) |
. | getNullableResult:存储过程用的 |
举个例子:
一个商户,在登记的时候需要注册它的经营范围。比如1手机,2电脑,3相机,4平板,在界面上是一个复选框(checkbox)。
在数据库保存的是用逗号分隔的字符串,例如“1,3,4”,而返回给程序的时候是整形数组{1,3,4}。
在每次获取数据的时候转换?还是在bean的get方法里面转换?似乎都不太合适。这时候我们可以写一个Integer[]类型的TypeHandler。
第一步:写TypeHandler
public class MyTypeHandler extends BaseTypeHandler<String> {
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
throws SQLException {
// 设置 String 类型的参数的时候调用,Java类型到JDBC类型
// 注意只有在字段上添加typeHandler属性才会生效
// insertBlog name字段
System.out.println("---------------setNonNullParameter1:"+parameter);
ps.setString(i, parameter);
}
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
// 根据列名获取 String 类型的参数的时候调用,JDBC类型到java类型
// 注意只有在字段上添加typeHandler属性才会生效
System.out.println("---------------getNullableResult1:"+columnName);
return rs.getString(columnName);
}
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
// 根据下标获取 String 类型的参数的时候调用
System.out.println("---------------getNullableResult2:"+columnIndex);
return rs.getString(columnIndex);
}
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
System.out.println("---------------getNullableResult3:");
return cs.getString(columnIndex);
}
}
第二步,在mybatis-config.xml文件中注册:
<typeHandlers>
<typeHandler handler="com.type.MyTypeHandler"></typeHandler>
</typeHandlers>-->
第三步,在我们需要使用的字段上指定,比如:
插入值的时候,从Java类型到JDBC类型,在字段属性中指定typehandler:
<insert id="insertBlog" parameterType="blog">
insert into blog
<trim prefix="(" suffix=")" suffixOverrides=",">
<if test="bid != null">
bid,
</if>
<if test="name != null">
name,
</if>
<if test="authorId != null">
author_id,
</if>
</trim>
<trim prefix="values (" suffix=")" suffixOverrides=",">
<if test="bid != null">
#{bid,jdbcType=INTEGER},
</if>
<if test="name != null">
#{name,jdbcType=VARCHAR,typeHandler=com.gupaoedu.type.MyTypeHandler},
</if>
<if test="authorId != null">
#{authorId,jdbcType=INTEGER},
</if>
</trim>
</insert>
返回值的时候,从JDBC类型到Java类型,在resultMap 的列上指定typehandler:
<result column="name" property="name" jdbcType="VARCHAR" typeHandler="com.type.MyTypeHandler"/>
objectFactory
当我们把数据库返回的结果集转换为实体类的时候,需要创建对象的实例,由于我们不知道需要处理的类型是什么,有哪些属性,所以不能用new的方式去创建。只能通过反射来创建。
在MyBatis里面,它提供了一个工厂类的接口,叫做ObjectFactory,专门用来创建对象的实例(MyBatis封装之后,简化了对象的创建),里面定义了4个方法。
作用
设置参数时调用
创建对象(调用无参构造函数)
创建对象(调用带参数构造函数)
判断是否集合
ObjectFactory有一个默认的实现类DefaultObjectFactory。创建对象的方法最终都调用了instantiateClass(),这里面能看到反射的代码。
默认情况下,所有的对象都是由DefaultObjectFactory 创建。
我们可以直接用自定义的工厂类来创建对象:
public class ObjectFactoryTest {
public static void main(String[] args) {
GPObjectFactory factory = new GPObjectFactory();
Blog myBlog = (Blog) factory.create(Blog.class);
System.out.println(myBlog);
}
}
如果想要修改对象工厂在初始化实体类的时候的行为,就可以通过创建自己的对象工厂,继承DefaultObjectFactory来实现(不再需要实现ObjectFactory接口)。
例如:
public class ObjectFactory extends DefaultObjectFactory {
@Override
public Object create(Class type) {
System.out.println("创建对象方法:" + type);
if (type.equals(Blog.class)) {
Blog blog = (Blog) super.create(type);
blog.setName("object factory");
blog.setBid(1111);
blog.setAuthorId(2222);
return blog;
}
Object result = super.create(type);
return result;
}
}
这样我们就直接拿到了一个对象。
如果在config文件里面注册,在创建对象的时候会被自动调用:
<objectFactory type="org.mybatis.example.ObjectFactory"
<!--对象工厂注入的参数-->
<property name="666" value="666"/>
</objectFactory>
这样,就可以让 MyBatis的创建实体类的时候使用我们自己的对象工厂。
附:
1、什么时候调用了objectFactory.create()?
创建 DefaultResultSetHandler的时候,和创建对象的时候。
2、创建对象后,已有的属性为什么被覆盖了?
在DefaultResultSetHandler类的395行getRowValue()方法里面里面调用了applyPropertyMappings)。
3、返回结果的时候,ObjectFactory和TypeHandler哪个先工作?
肯定是先创建对象,所以先是ObjectFactory,再是TypeHandler。
plugins
插件是 MyBatis的一个很强大的机制。跟很多其他的框架一样,MyBatis 预留了插件的接口,让 MyBatis更容易扩展。
environments environment
environments标签用来管理数据库的环境,比如我们可以有开发环境、测试环境、生产环境的数据库。可以在不同的环境中使用不同的数据库地址或者类型。
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf-8&rewriteBatchedStatements=true
jdbc.username=root
jdbc.password=123456
<environments default="development">
<environment id="development">
<transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 -->
<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>
一个environment标签就是一个数据源,代表一个数据库。这里面有两个关键的标签,一个是事务管理器,一个是数据源。
transactionManager
如果配置的是JDBC,则会使用Connection对象的commit()、rollback()、close()管理事务。
如果配置成MANAGED,会把事务交给容器来管理,比如JBOSS,Weblogic。因为我们跑的是本地程序,如果配置成MANAGE不会有任何事务。
如果是Spring + MyBatis,则没有必要配置,因为我们会直接在applicationContext.xml里面配置数据源和事务,覆盖MyBatis的配置。
dataSource
数据源,顾名思义,就是数据的来源,一个数据源就对应一个数据库。在Java里面,它是对数据库连接的一个抽象。
一般的数据源都会包括连接池管理的功能,所以很多时候也把 DataSource直接称为连接池,准确的说法应该是:带连接池功能的数据源。
为什么要用连接池?
除了连接池之外,大家应该也听过很多其他的池,比如线程池,内存池,对象池,这种池化技术达到的目的基本上一样的。
如果没有连接池,那么每一个用户、每一次会话连接数据库都需要直接创建和释放连接,这个过程是会消耗的一定的时间的,并且会消耗应用和服务器的性能。
当我们没有使用连接池的时候,客户程序得到的连接是一个物理连接,我们调用close()方法会把这个连接真正地关闭。
如果采用连接池技术,在应用程序里面关闭连接的时候,物理连接没有被真正关闭掉,只是回到了连接池里面。
从这个角度来考虑,一般的连接池都会有初始连接数、最大连接数、回收时间等等这些参数,提供提前创建/资源重用/数量控制/超时管理等等这些功能。
MyBatis自带了两种数据源,UNPOOLED和POOLED。也可以配置成其他数据库,比如C3P0、Hikari等等。市面上流行的数据源,一般都有连接池功能。
在跟Spring集成的时候,事务和数据源都会交给Spring来管理,不再使用MyBatis配置的数据源。