大家好,我是王有志,一个分享硬核 Java 技术的互金摸鱼侠。

前两篇的文章中我们分别介绍了 MyBatisMyBaits 的应用组成,到这里基础篇的内容就结束了。

从今天开始,我们正式进入 MyBatis 学习的第二阶段:MyBatis 的应用部分。这个阶段从 MyBatis 应用程序的核心配置文件 mybatis-config.xml 开始入手,逐步推进到映射器(Mapper.xml),动态 SQL 语句的编写以及 MyBatis 与 Spring 和 Spring Boot 的集成。

在讲解 MyBatis 的核心配置文件时,以及未来讲解 MyBatis 的映射器(Mapper.xml)时,会少量涉及到文档类型定义(即 DTD,Document Type Definition)的内容,由于很多小伙伴可能不太熟悉 DTD,因此我会在文末的部分通过一张图来简单介绍下 DTD。

Tips

  • 目前 DTD 正在逐渐被功能更强,标准化程度更高的 XSD(XML Schema Definition)取代,因此我们只要简单了解 DTD 即可
  • 点击这里可以直接下载 MyBatis 核心配置文件的 DTD

MyBaits 的核心配置文件

我们打开 mybatis-3-configs.dtd 可以看到,该文档最开始定义了 MyBatis 配置中的 configuration 元素及包含的子元素:

<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>

configuration 元素是 mybatis-config.xml 的根元素,本身并未定义任何属性,只定义了 11 个子元素,这里需要注意,子元素定义的顺序也是它们在 MyBatis 核心配置文件 mybatis-config.xml 中使用的顺序

接下来我们按照 DTD 中定义的元素顺序,逐个讲解元素的用法。由于数量比较多,我会分为两期和大家分享,本期分享 configuration 的前 5 个子元素:properties,settings,typeAliases,typeHandlers 和 objectFactory。

properties 元素(配置)

properties 元素用于声明配置,在 DTD 中的定义如下:

<!ELEMENT properties (property*)>
<!ATTLIST properties
resource CDATA #IMPLIED
url CDATA #IMPLIED
>

properties 定义了两个属性:resource 属性和 url 属性,以及一个子元素 property。

resource 属性和 url 属性

properties 元素提供了两个属性:resource 和 url,通过它们允许通过其它配置文件或网络中获取配置

属性 是否必填 说明
resource 非必填 与属性 url 互斥
url 非必填 与属性 resource 互斥

Tips:属性 resource 与 url 互斥的原因,我会在属性与子标签的优先级中提到。

使用它们需要提前准备好配置文件,或能够通过网络获取配置,我这里以使用配置文件为例。首选我们准备一个配置文件 mysql-config.properties,将它放在 resources 目录下,具体内容如下:

mysql.driver=com.mysql.cj.jdbc.Driver
mysql.url=jdbc:mysql://localhost:3306/mybatis
mysql.username=root
mysql.password=123456

接着我们修改 MyBatis 入门中示例的 mybatis-config.xml,如下:

<configuration>
<properties resource="mysql-config.properties" /> <environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
</environments>
</configuration>

property 元素

property 元素在 DTD 中的定义如下

<!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED
>

通过 property 元素可以在 mybatis-config.xml 中声明配置。

属性 是否必填 说明
name
value

在 properties 元素中使用 property 元素无需额外的准备,直接写在 mybatis-config.xml 中即可,如下:

<configuration>
<properties>
<property name="mysql.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="mysql.url" value="jdbc:mysql://localhost:3306/mybatis"/>
<property name="mysql.username" value="root"/>
<property name="mysql.password" value="123456"/>
</properties> <environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
</environments>
</configuration>

Tips:在 mybatis-3-config.dtd 中,property 标签是一个通用元素,会作为很多元素的子元素出现,例如:objectFactory 元素,databaseIdProvider 元素等。

属性与子元素的混合使用

properties 元素支持属性与子元素的混合使用。我们稍微修改下外部配置文件 mysql-config.properties,删除 mysql.driver 和 mysql.url,如下:

mysql.username=root
mysql.password=123456

接着修改 mybatis-config.xml,删除通过 property 元素配置的 mysql.username 和 mysql.password,并且引入外部配置文件 mysql-config.properties,如下:

<configuration>
<properties resource="mysql-config.properties">
<property name="mysql.driver" value="com.mysql.cj.jdbc.Driver"/>
<property name="mysql.url" value="jdbc:mysql://localhost:3306/mybatis"/>
</properties> <environments default="development">
<environment id="development">
<transactionManager type="JDBC"/>
<dataSource type="POOLED">
<property name="driver" value="${mysql.driver}"/>
<property name="url" value="${mysql.url}"/>
<property name="username" value="${mysql.username}"/>
<property name="password" value="${mysql.password}"/>
</dataSource>
</environment>
</environments>
</configuration>

通过测试,你会发现 MyBatis 应用依旧可以良好的运转。不过这时你可能会产生疑惑,如果我不删除外部配置文件 mysql-config.properties 中的配置,也不删除 property 元素中的配置,到底会是谁说的算?

属性与子元素的优先级

先说结论,在 properties 元素中,通过属性 resource 和 url 获取的配置优先级高于 property 元素的配置,如果同时使用属性和子元素,且存在相同配置名的配置,最终生效的配置是通过属性获取到的配置。

这里涉及到一些源码,不过非常简单,我们在 MyBatis 应用的组成中提到过,MyBatis 中的 mybatis-config.xml 由 XMLConfigBuilder 负责解析,我们能够非常容易的在 XMLConfigBuilder 中找到解析 properties 的相关的源码:

private void propertiesElement(XNode context) throws Exception {
if (context == null) {
return;
} // 获取子元素的配置
Properties defaults = context.getChildrenAsProperties(); // 获取properties的属性
String resource = context.getStringAttribute("resource");
String url = context.getStringAttribute("url"); // 校验resource与url是否同时存在
if (resource != null && url != null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
} // 获取通过properties属性的配置
if (resource != null) {
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
defaults.putAll(Resources.getUrlAsProperties(url));
} // 获取通过configuration的配置
Properties vars = configuration.getVariables();
if (vars != null) {
defaults.putAll(vars);
}
parser.setVariables(defaults);
configuration.setVariables(defaults);
}

源码中的第 7 行调用了XNode#getChildrenAsProperties并生成了 Properties 对象,作为默认的配置对象 defaults。

public Properties getChildrenAsProperties() {
Properties properties = new Properties();
for (XNode child : getChildren()) {
String name = child.getStringAttribute("name");
String value = child.getStringAttribute("value");
if (name != null && value != null) {
properties.setProperty(name, value);
}
}
return properties;
}

接着执行到第 10 行和第 11 行,分别从 properties 元素获取属性 resource 和 url 的值,并在 14~16 行校验了两者不能同时存在,否则会抛出异常 BuilderException,这也是 properties 元素的属性 resource 与 url 互斥的原因。

接着是 19~21 行,从属性 resource 或 url 中获取配置,并通过Properties#putAll方法添加到默认的配置对象 defaults 中。Properties 是 Java 提供的工具类,底层使用的容器是 ConcurrentHashMap,到这里就能够解释为什么通过属性 resource 和 url 获取到配置的优先级高于通过子元素 property 获取到的配置了。

最后是 26~29 行的内容,这里是获取 Configuration 对象中的配置,并添加到默认配置对象 defaults 中,那么 Configuration 对象中的配置是如何来的呢?

还记得我们构建 SqlSessionFactory 时调用的SqlSessionFactoryBuilder#build方法吗?它有非常多的重载方法:

public SqlSessionFactory build(Reader reader);

public SqlSessionFactory build(Configuration config);

public SqlSessionFactory build(Reader reader, Properties properties);

重载方法中允许我们通过 Java 编码的方式来设置 Properties,这时设置的 Properties 对象会加载到 Configuration 对象中,例如:

Reader reader = Resources.getResourceAsReader("mybatis-config.xml");

Properties properties = new Properties();
properties.setProperty("mysql.password", "123456"); SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader, properties);

这时生效的配置信息是通过代码设置的 Properties。

综合上面的内容,我们可以得到 MyBatis 中配置优先级的完整结论:优先级最高的是通过代码设置的配置,其次是通过 Properties 元素的属性 resource 和 url 设置的配置,优先级最低的是通过 Properties 元素的Property 子元素设置的配置

settings 元素(设置)

settings 元素用于调整 MyBatis 中的各项设置,它在 DTD 中的定义如下:

<!ELEMENT settings (setting+)>

<!ELEMENT setting EMPTY>
<!ATTLIST setting
name CDATA #REQUIRED
value CDATA #REQUIRED
>

settings 元素没有属性,只有一个 setting 子元素,也是典型的 K-V 形式。这也就是说,如果想要调整 MyBatis 应用的设置,必须要使用 setting 子元素。

settings 元素本身没有什么特别的,重要的是它提供了非常对的 MyBatis 的配置功能,如下图:

Tips:以上部分截取自MyBatis中文网:设置(settings)

typeAliases 元素(别名)

tyoeAliases 元素用于为 Java 类型设置别名,它在 DTD 中的定义如下:

<!ELEMENT typeAliases (typeAlias*,package*)>

<!ELEMENT typeAlias EMPTY>
<!ATTLIST typeAlias
type CDATA #REQUIRED
alias CDATA #IMPLIED
> <!ELEMENT package EMPTY>
<!ATTLIST package
name CDATA #REQUIRED
>

typeAliases 元素本身没有任何属性,只有两个子元素:typeAlias 元素和 package 元素

typeAlias 元素

typeAlias 元素用于设定单个 Java 类型的别名。

属性 是否必填 说明
type 使用 Java 类型的全限名
alias 自定义的别名

我们看到,alias 属性是非必填的,如果没有填写的话,MyBatis 会如何处理呢?MyBatis 会默认使用将首字母小写后 Java 类型的名字作为别名,例如:

<configuration>
<!-- 省略其它配置 -->
<typeAliases>
<typeAlias type="com.wyz.entity.UserDO"/>
</typeAliases>
<!--省略其它配置 -->
</configuration>

此时 UserDO 在 MyBatis 中的别名是 userDO, 配置别名后我们就可以直接在映射器(Mapper.xml)中使用:

<mapper namespace="com.wyz.mapper.UserMapper">
<select id="selectAll" resultType="userDO" >
select user_id, name, age, gender, id_type, id_number from user
</select>
</mapper>

package 元素

package 元素用于设定包下所有 Java 类型的别名。

属性 是否必填 说明
name 包名

如果只使用 package 元素的话,MyBatis 同样会使用将首字母小写后的 Java 类型的名字作为别名。但 MyBatis 也提供了@Alias注解,方便为每个类型起别名,例如:

package com.wyz.entity;

@Alias("user")
public class UserDO implements Serializable {
// 省略属性
}

接着在 mybatis-config.xml 中配置:

<configuration>
<!-- 省略其它配置 -->
<typeAliases>
<package name="com.wyz.entity"/>
</typeAliases>
<!--省略其它配置 -->
</configuration>

此时,我们就可以在映射器(Mapper.xml)中使用 user 作为 com.wyz.entity.UserDO 的别名了。

MyBatis 内置的别名

上面是自定义别名的部分,实际上 MyBatis 已经为常见的 Java 类型定义了别名,例如:java.lang.String 在 MyBatis 中的别名是“string”,java.lang.Integer 在 MyBatis 中的别名是“int”。

你可能会有疑惑,那基本数据类型 int 有别名吗?

答案是有的,MyBatis 为 Java 中的每个基础数据类型都定义了别名,与它们的包装类型的别名有所差异,我们一起来看一下:

基础数据类型 别名 包装类型 别名
byte _byte Byte byte
char _char/_character Character char/character
long _long Long long
int _int/_integer Integer int/integer
short _short Short short
double _double Double double
float _float Float float
boolean _boolean Boolean boolean

基础数据类型“痛失真名”~~

当然,以上只是一部分 MyBatis 为 Java 类型定义的别名,更多常见 Java 类型的别名定义可以参考 MyBatis 的源码 TypeAliasRegistry 类。

Tips:通常我不使用 typeAliases 标签,因为使用全限名能够更加方便的在映射器(Mapper.xml)中查找到对应的 Java 类型。

typeHandlers 元素(类型处理器)

typeHandlers 元素用于定义类型处理器,即 Java 类型与数据库类型的相互转换的处理器,它在 DTD 中的定义如下:

<!ELEMENT typeHandlers (typeHandler*,package*)>

typeHandlers 元素没有属性,只定义了两个子元素:typeHandler 元素和 package 元素。

typeHandler 元素

typeHandler 元素在 DTD 中的定义如下:

<!ELEMENT typeHandler EMPTY>
<!ATTLIST typeHandler
javaType CDATA #IMPLIED
jdbcType CDATA #IMPLIED
handler CDATA #REQUIRED
>

typeHandler 元素中定义了 3 个属性:

属性 是否必填 说明
javaType Java 类型
jdbcType JDBC 类型
handler 类型处理器全限名

typeHandler 元素,可以定义 Java 类型与 JDBC 类型互相转换的类型处理器,如:

<configuration>
<typeHandlers>
<typeHandler jdbcType="VARCHAR" javaType="java.lang.String" handler="org.apache.ibatis.type.StringTypeHandler"/>
</typeHandlers>
</configuration>

StringTypeHandler 是 MyBatis 内置的类型处理器,MyBatiis 已经为我们内置了非常多的类型处理器,完全能够满足日常项目中的使用了。

Tips

  • 关于 MyBatis 内置处理器,下面会在 MyBatis 的内置处理器的部分提到;
  • 关于 typeHandler 元素中 jdbcType 属性和 javaType 属性非必填的问题,会在自定义类型处理器的部分提到。

package 元素

在 typeHandlers 元素下使用 package 元素,会加载 package 元素中指定包名下所有符合条件的类型处理器。例如:

<configuration>
<typeHandlers>
<package name="com.wyz.customize.handler.type"/>
</typeHandlers>
</configuration>

如上的定义,会加载 com.wyz.customize.handler.type 中所有符合条件的类型处理器。但是 package 元素只能指定包名,MyBatis 该如何识别类型处理器是哪些 Java 类型与数据库类型之间的转换呢?

别急,我们接着往下看。

自定义类型处理器

现在,我们已经知道了如何在 mybatis-config.xml 中配置类型处理器了,接下来我们就定义自己的类型处理器。

MyBatis 中提供了两种定义类型处理器的方法:

  • 实现 TypeHandler 接口;
  • 继承抽象类 BaseTypeHandler。

其中抽象类 BaseTypeHandler 中已经做了非常多的通用实现,采用继承 BaseTypeHandler 的方法可以省去大量的编码工作,因此我这里在实现自定义类型处理器的时候选择了继承 BaseTypeHandler。

我们定义一个针对于 String 与 Varchar 的类型处理器,实现一个简单且无意义的需求:针对于参数和结果集,我们对 String 类型的字段添加后缀“_wyz”,我将这个类型处理器命名为 NewStringTypeHandler,代码如下:

package com.wyz.customize.handler.type;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes; import java.sql.*; public class NewStringTypeHandler extends BaseTypeHandler<String> {
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
ps.setString(i, parameter + "_wyz");
} @Override
public String getNullableResult(ResultSet rs, String columnName) throws SQLException {
return rs.getString(columnName) + "_wyz";
} @Override
public String getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return rs.getString(columnIndex) + "_wyz";
} @Override
public String getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return cs.getString(columnIndex) + "_wyz";
}
}

接下来,我们在 mybatis-config.xml 中配置这个类型处理器:

<configuration>
<typeHandlers>
<typeHandler jdbcType="VARCHAR" javaType="java.lang.String" handler="com.wyz.customize.handler.type.NewStringTypeHandler"/> </typeHandlers>
</configuration>

最后我们通过测试可以看到,所有查询语句的响应结果中 VARCHAR 类型的字段都添加了后缀“_wyz”,而所有插入或修改语句在提交后,数据库中存储的所有 VARCHAR 类型的字段也被添加上了后缀“_wyz”。

前面我们已经提到了,typeHandler 元素中的属性 jdbcType 与 javaType 是非必填的,如果我们不设置 jdbcType 与 javaType 的话会发生什么?答案是自定义类型处理器依旧会被注册到 MyBatis 的类型处理器中,但是我们无法使用(涉及到源码,这部分我们放在 MyBatis 的源码分析篇中再聊)。

那么为什么 typeHandler 元素中这两个属性是非必填的呢?因为 MyBatis 还提供了两个注解@MappedTypes@MappedJdbcTypes,允许我们在自定义类型处理器上定义 Java 类型与数据库类型,如下:

@MappedTypes({String.class})
@MappedJdbcTypes({JdbcType.VARCHAR, JdbcType.CHAR})
public class NewStringTypeHandler extends BaseTypeHandler<String> {
// 省略代码
}

这样,我们即便不在 typeHandler 元素中设置属性 jdbcType 与 javaType,MyBatis 也同样能够正确的注册自定义类型处理器。

再来看 package 元素,虽然没有元素中没有设置 jdbcType 与 javaType 的属性,但是我们想到 package 元素也可以通过@MappedTypes@MappedJdbcTypes注解,来指定 Java 类型与数据库类型的转换方式,这里我们就不再写相关的代码了。

MyBatis 内置的类型处理器

最后我们来看 MyBatis 中内置的类型处理器,在 MyBatis 3.5.15 版本中,MyBatis 为 80 种类型定义了 51 个类型处理器,我们可以通过查看 BaseTypeHandler 的实现类来查看 MyBatis 的内置处理器的数量。

为什么类型与类型处理器的数量不一致呢?

是因为 MyBatis 为了兼容 Java 中的基础类型与包装类型,以及不同数据库之间相同类型的叫法不同,需要为每种类型都注册类型处理器,但他们可以共用同一个类型处理器,例如,在 MyBatis 注册内置处理器的 TypeHandlerRegistry 的构造方法中为布尔类型注册类型处理器时:

public TypeHandlerRegistry(Configuration configuration) {
// 省略代码
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
// 省略代码
}

这里我就不一一展示了,感兴趣的小伙伴可以自行查看 TypeHandlerRegistry 构造方法的源码。

objectFactory 元素(对象工厂)

objectFactory 元素用于定义对象工厂,对象工厂用于创建结果集的映射对象,它在 DTD 中的定义如下:

<!ELEMENT objectFactory (property*)>
<!ATTLIST objectFactory
type CDATA #REQUIRED
>

objectFactory 元素本身并没有任何属性,只定义了一个子元素 property,用于定义 objectFactory 的参数。通过 property 元素配置的属性,会在 ObjectFactory 初始化之后通过ObjectFactory#setProperties方法传递到对象工厂中

默认情况下(即不配置自定义对象工厂),MyBatis 会使用对象工厂的唯一实现类 DefaultObjectFactory,它的实现非常简单,仅仅是通过反射调用结果集对象的无参构造器完成了对象的实例化,除此之外,并没有额外的处理。

自定义对象工厂

同自定义类型处理器一样,MyBatis 提供了两种自定义对象工厂的方式:

  • 实现 ObjectFactory 接口;
  • 继承 DefaultObjectFactory。

同样的,我们可以选择继承 DefaultObjectFactory 来完成自定义对象工厂,并实现相应的方法即可。接下来,我们写一个比较“荒诞”的需求,因为我实在是没想到太好的自定义对象工厂的实际案例。

这个需求是这样的,为数据库中 user 表的查询结果的 VARCHAR 类型字段添加默认值“wyz”,即 UserDO 实例对象中 String 类型的字段为 NULL 时赋默认值“wyz”。当然实现这个需求我们有好几种方式可以选择,如:通过为数据库中的字段设置默认值,或者为 Java 对象的字段设置默认值来完成。不过这里为了展示 MyBatis 的自定义对象工厂的使用方法,我通过自定义工厂来实现这个需求。

首先,我们为这个自定义工厂命名为 CustomizeObjectFactory,并在 MyBatis 的核心配置 mybatis-config.xml 中配置这个自定义对象工厂,例如:

<configuration>
<!-- 省略配置 -->
<objectFactory type="com.wyz.customize.factory.object.CustomizeObjectFactory">
<property name="default" value="wyz"/>
</objectFactory>
<!-- 省略配置 -->
</configuration>

其中子元素 property 中定义了我们将要使用的默认值。

接着我们来完成这个自定义对象工厂,通过继承 DefaultObjectFactory 并实现相应的方法,代码如下:

package com.wyz.customize.factory.object;

import com.alibaba.fastjson2.JSON;
import com.wyz.entity.UserDO;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.factory.DefaultObjectFactory; import java.lang.reflect.Field;
import java.util.List;
import java.util.Properties; public class CustomizeObjectFactory extends DefaultObjectFactory { String defaultValue; @Override
public void setProperties(Properties properties) {
defaultValue = properties.getProperty("default");
} @Override
public <T> T create(Class<T> type) {
T t = super.create(type);
if(type.equals(UserDO.class)) {
Field[] fields = type.getDeclaredFields();
for(Field field : fields) {
if(field.getType().equals(String.class)) {
field.setAccessible(true);
try {
field.set(t, defaultValue);
} catch (IllegalAccessException e) {
throw new RuntimeException(e);
}
}
}
}
return t;
} @Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
return super.create(type, constructorArgTypes, constructorArgs);
} @Override
protected Class<?> resolveInterface(Class<?> type) {
return super.resolveInterface(type);
} @Override
public <T> boolean isCollection(Class<T> type) {
return super.isCollection(type);
}
}

可以看到,我重写了ObjectFactory#setProperties方法,并在该方法中为类成员变量 defaultValue 赋值,前面我们提到过,ObjectFactory#setProperties方法的作用时间是在对象工厂初始化完成之后。

接着我重写了其中一个ObjectFactory#create方法,通过super.create调用父类 DefaultObjectFactory 实现的ObjectFactory#create方法完成默认的实例化后,我们判断其类型为 UserDO,并通过反射对 UserDO 实例对象中 String 类型的字段赋默认值。

我们先把数据库中的某些字段修改为 NULL,如:

接着,执行我们的查询全部用户的测试案例,可以看到输出结果中,用户名为小明的用户,他的性别赋上了默认值“wyz”。

通常来说,我们在应用程序中,MyBatis 提供的 DefaultObjectFactory 已经能够满足绝大部分的应用场景了,我们不需要实现自定义对象工厂。

附录:DTD 简介

这部分并不是完整的 DTD 教程,如果想要完成的学习 DTD ,请移步相关教程,本文只对 mybatis-3-confg.dtd 中出现的相关语法做一个简单的介绍。

DTD(Document Type Definition)即文档类型定义,这里我引用维基百科中关于 DTD 的定义:

XML文件的文档类型定义(Document Type Definition)可以看成一个或者多个XML文件的模板,在这里可以定义XML文件中的元素、元素的属性、元素的排列方式、元素包含的内容等等。

DTD 主要用于 XML 文档的结构定义和约束,主要功能包括:

  • 定义 XML 文档中的可使用的元素,元素间的顺序,元素的嵌套关系和元素的属性等;
  • 用于验证 XML 文档的结构,在 XML 文件中引入 DTD,解析器会根据 DTD 的定义验证文档的结构;
  • 约定通过 XML 进行数据交换的标准格式,保证数据交换的正确性。

我们来截取 mybatis-3-config.dtd 的部分内容:

<!ELEMENT configuration (properties?, settings?)>

<!ELEMENT properties (property*)>
<!ATTLIST properties
resource CDATA #IMPLIED
url CDATA #IMPLIED
> <!ELEMENT property EMPTY>
<!ATTLIST property
name CDATA #REQUIRED
value CDATA #REQUIRED
>

通过一张图来解释下上面 DTD 的内容:

关于 DTD 的内容就简单介绍到这里,已经足够应付 MyBatis 的 DTD 了。

本文为稀土掘金技术社区首发签约文章,30天内禁止转载,30天后未获授权禁止转载,侵权必究!


好了,今天的内容就到这里了,如果本文对你有帮助的话,希望多多点赞支持,如果文章中出现任何错误,还请批评指正。最后欢迎大家关注分享硬核 Java 技术的金融摸鱼侠王有志,我们下次再见!

MyBatis 核心配置讲解(上)的更多相关文章

  1. MyBatis 核心配置综述之Executor

    目录 MyBatis四大组件之 Executor执行器 Executor的继承结构 Executor创建过程以及源码分析 Executor接口的主要方法 Executor 的现实抽象 上一篇我们对Sq ...

  2. MyBatis 核心配置综述之StatementHandler

    目录 MyBatis 核心配置综述之StatementHandler MyBatis 四大组件之StatementHandler StatementHandler 的基本构成 StatementHan ...

  3. MyBatis 核心配置综述之 ParameterHandler

    目录 ParameterHandler 简介 ParameterHandler 创建 ParameterHandler 中的参数从何而来 ParameterHandler 解析 MyBatis 四大核 ...

  4. RocketMQ(4)---RocketMQ核心配置讲解

    RocketMQ核心配置讲解 RocketMQ的核心配置在broker.conf配置文件里,下面我们来分析下它. 一.broker.conf配置 下面只列举一些常用的核心配置讲解. 1.broker. ...

  5. Nginx 核心配置-作为上传服务器配置

    Nginx 核心配置-作为上传服务器配置 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   一.关键参数说明 client_max_body_size 1m: 设置允许客户端上传单 ...

  6. Mybatis学习——Mybatis核心配置

    MyBatis的核心配置 在使用MyBatis框架时,设计两个核心的d对象:SqlSessionFactory和SqlSession. SqlsessionFactory SqlSessionFact ...

  7. 1.2(Mybatis学习笔记)Mybatis核心配置

    一.Mybatis核心对象 1.1SqlSeesionFactory SqlSessionFactory主要作用是创建时SqlSession. SqlSessionFactory可通过SqlSessi ...

  8. MyBatis 核心配置综述之 ResultSetHandler

    目录 ResultSetHandler 简介 ResultSetHandler 创建 ResultSetHandler 处理结果映射 DefaultResultSetHandler 源码解析 我们之前 ...

  9. 关于mybatis缓存配置讲解

    一级缓存: 一级缓存是默认的. 测试:在WEB页面同一个查询执行两次从日志里面看同样的sql查询执行两次. 2次sql查询,看似我们使用了同一个sqlSession,但是实际上因为我们的dao继承了S ...

  10. Mybatis框架配置讲解以及使用

    1.什么是Mybatis MyBatis 是一款优秀的持久层框架, 它支持定制化 SQL.存储过程以及高级映射. MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集.·MyB ...

随机推荐

  1. Java开发中String.format的妙用

    format方法是String类中的一个方法,主要作用是用来格式化字符串.当前做 的一个功能当中,刚好用到了这个方法,代码处理起来非常简便.因此就写篇博客 记录下来. 分析使用场景:前端App需要根据 ...

  2. Dot Log 插件 输入 .log 回车 自动插入 console.log - vscode插件

    Dot Log 插件 输入 .log 回车 自动插入 console.log

  3. bat 执行 窗口jar包

    bat 执行 窗口jar包 @echo off start javaw -jar .\yourname.jar exit

  4. Welcome to YARP - 2.3 配置功能 - 配置过滤器(Configuration Filters)

    目录 Welcome to YARP - 1.认识YARP并搭建反向代理服务 Welcome to YARP - 2.配置功能 2.1 - 配置文件(Configuration Files) 2.2 ...

  5. InfluxDB、Grafana、node_exporter、Prometheus搭建压测平台

    InfluxDB.Grafana.node_exporter.Prometheus搭建压测平台 我们的压测平台的架构图如下: 配置docker环境 1)yum 包更新到最新 sudo yum upda ...

  6. 记一次 .NET某施工建模软件 卡死分析

    一:背景 1. 讲故事 前几天有位朋友在微信上找到我,说他的软件卡死了,分析了下也不知道是咋回事,让我帮忙看一下,很多朋友都知道,我分析dump是免费的,当然也不是所有的dump我都能搞定,也只能尽自 ...

  7. javascript 把嵌套的 map 转成 object,再转 json 字符串

    使用 JSON.stringify 转 map 时发现并没有转成想要的 JSON 数据,搜索发现要转成 Object 才能够转成完整的 JSON, 用递归转换: const message = new ...

  8. Windows上部署spring boot jar项目

    1.下载地址:https://github.com/winsw/winsw/releases 下载红色框内三个文件就够了. sample-allOptions.xml 所有配置参考 sample-mi ...

  9. C# 消息发送

    界面 frmMain using System; using System.Collections.Generic; using System.ComponentModel; using System ...

  10. Makefile 简单学习

    一.Makefile 简介 Makefile 是一种常用于编译的脚本语言.它可以更好更方便的管理你的项目的代码编译,节约编译时间(没改动的文件不编译).注意 Makefile 文件命令必须是 Make ...