MyBatis使用核心配置文件来管理相关的配置信息,本篇文章主要介绍:
- 属性配置:
<properties.../> - 设置配置:
<settings.../> - 类型别名:
<typeAliases.../> - 对象工厂:
<objectFactory.../> - 加载Mapper:
<mappers.../>
1. 属性配置
前面章节介绍过,MyBatis在配置数据库datasource时,使用<property>元素定义相关的属性。具体到MyBatis配置文件,可以在<properties>元素中配置相关的属性。
通过使用属性,可以将核心配置文件中的部分信息提取到属性文件中统一管理,例如,将之前定义在<datasoure>元素中的相关配置,统一集中在<properties>中管理;或者以参数的形式传递给SqlSessionFactory的build方法。
MyBatis允许在三个地方设置属性:
- 在
<properties.../>元素中定一个<property.../>子元素配置,每个子元素配置一个属性; - 额外定义一个配置文件,再使用
<properties.../>元素加载这个文件; - 在
SqlAlchemyFactory的build方法中传入Properties参数。
如果在多个地方设置了同名属性,优先级高的会覆盖优先级低的值,优先级排序:**3 > 2 > 1**。具体来看下面的示例:
首先,定义额外的属性文件,文件名为CustomProperty.properties,所在位置:src根目录,内容为:
driver=com.mysql.cj.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/example
user=root
然后,修改MyBatis的配置文件mybatis-config.xml文件,具体内容如下:
...
<configuration>
<!-- 使用CustomProperty.properties文件 -->
<properties resource="CustomProperty.properties">
<property name="user" value="admin"/>
<property name="password" value="admin"/>
</properties>
<!-- 默认指定的数据库环境为MySQL,引用其中一个environment元素的id -->
<environments default="mysql">
<!-- 配置名为mysql(名称可任意取)的环境 -->
<environment id="mysql">
<!-- 配置事务管理器,JDBC代表使用JDBC自带的事务提交和回滚 -->
<transactionManager type="JDBC"/>
<!-- datasource配置数据源,此处使用MyBatis内置的数据源 -->
<datasource type="POOLED">
<!-- 配置连接数据库的驱动、URL、用户名和密码 -->
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${user}"/>
<property name="password" value="${password}"/>
</datasource>
</environment>
</environments>
</configuration>
其中:
- 在第4行,通过指定
properties元素的resource属性,加载额外定义的配置文件; -
- 除了使用
resource属性加载外部的配置文件外,还可使用url属性来加载 resource属性:指定从类加载路径下搜索属性文件,这种方法更实用一些url属性:指定加载URL对应的属性文件,可使用HTTP协议加载网络上的属性文件,也可使用file:///<file_path>的格式加载指定磁盘路径下的文件
- 除了使用
- 在第5行,定义了与额外的配置文件中同名的
user配置,在后续执行时,会使用额外配置文件中的root覆盖此处的admin值; - 在17-20行,使用
${}的形式引用定义的属性值。
最后,在调用SqlSessionFactoryBuilder的build方法时传入Properties对象,例如:
public class UserManager {
public static void main(String[] args) throws Exception {
String configFileSource = "mybatis-config.xml";
var inputStream = Resources.getResourceAsStream(configFileSource);
var props = Properties();
prop.setProperty("password", "admin123");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream, props);
// 创建session
var sqlSession = sqlSessionFactory.openSession();
// 创建新的用户
createUser(sqlSession);
}
public static void createUser(SqlSession sqlSession) {
...
}
public static void updateUser(SqlSession sqlSession) {
...
}
public static void deleteUser(SqlSession sqlSession) {
...
}
public static void queryUser(SqlSession sqlSession) {
...
}
}
其中:
- 在第7行,定义了
password的属性,因为它的优先级最高,它会覆盖之前定义的password属性的值; - 在第9行,调用
build方法时,传入属性。
MyBatis还支持在引用属性值时提供默认值,具体的语法格式为:${属性名:默认值}。但是,在引用属性时指定默认值默认是关闭的,需要显式地开启,需要在<properties.../>元素中添加如下子元素:
<property name="org.apache.ibatis.parsing.PropertyParser.enable-default-value" value="true"/>
默认情况下,属性名和默认值是通过:分隔,也可以自定义这个分隔符,需要在<properties.../>元素中添加如下子元素,此时分隔符为-。
<property name="org.apache.ibatis.parsing.PropertyParser.default-value-separator" value="-"/>
2. 设置配置
MyBatis有一些全局行为需要配置,这些配置是存放在<settings.../>中,每个<setting.../>子元素配置一个设置,例如:
<settings>
<setting name="lazyLoaingEnabled" value="true"/>
</settings>
MyBatis支持的设置项及其有效值、默认值如下:
img
img
img
3. 为类型配置别名
上一篇中,在介绍如何使用Mapper对象时,先定义了一个领域类,然后在Mapper中引用该类,当时使用的是类的全限定类型(即:包路径+类文件名称)。当涉及的领域类很多时,会造成代码的冗长,并且十分繁琐,给开发人员带来烦恼。
Mybatis允许在<typeAliases.../>元素内为Java类指定别名,主要有2种方式:
<typeAlias.../>:为单个Java类指定别名;<package.../>:为指定包下面的所有Java类指定别名。
3.1 MyBatis内置的别名
MyBatis为许多Java类都内置了别名,具体如下:
img
3.2 使用<typeAlias.../>
使用<typeAlias.../>元素可以为单个Java类指定对应的别名,核心配置文件的配置如下:
...
<configuration>
...
<typeAliases>
<typeAlias alias="user" value="org.example.app.entity.User" />
</typeAliases>
...
</configuration>
接下来,在使用该Java类的地方就可以使用对应的别名了,例如:
<select id="selectUser" resultType="user">
select id as is_alias, nickname sd nickname_alias from user where id = #{id}
</select>
使用别名之前的调用方式如下:
<select id="selectUser" resultType="org.exmaple.app.entity.User">
select id as is_alias, nickname sd nickname_alias from user where id = #{id}
</select>
代码是不是清爽很多?
3.3 使用<package.../>
使用<package.../>可以为指定包下面的所有Java类指定别名。在没有@Alias注解的情况下,MyBatis会自动将所有类的类名首字母小写(即将大驼峰转为小驼峰)来作为别名。使用方式如下:
...
<configuration>
...
<typeAliases>
<package name="org.example.app.entity" />
</typeAliases>
...
</configuration>
之前的示例中,该包下面有User类,MyBatis会自动设置其别名为user。
4. 对象工厂
前面介绍过,当MyBatis将ResultSet映射为具体的Java对象时,需要为每一行的记录创建一个对象,这个过程是由对象工程(Object Factory)负责的。
MyBatis中内置了DefaultObjectFactory来负责将每一行的记录创建一个对象,它是继承了ObjectFactory。开发者一般情况下无需特别关注,MyBatis会自动完成这个过程。
但是,一些特殊情况下,开发者希望能够自定义实现对象工厂,从而执行某些特别的行为。开发自定义对象工厂需要如下步骤:
- 定义一个实现ObjectFactory接口的类,该类可作为对象工厂;
- 在MyBatis核心配置文件中,使用
<objectFactory.../>元素注册对象工厂。
4.1 自定义对象工厂
第1个步骤,可以继承MyBatis中的DefaultObjectFactory(因为该类是继承ObejctFactory并实现了相关的函数)并重写对应的方法即可,这也是推荐的做法。
需要实现ObjectFactory或者可以重写DefaultObjectFactory的方法有4个:
<T> T create(Class<T> type):负责创建对象,当MyBatis通过无参数的构造器创建对象时,实际上是由工厂方法调用该函数创建的;<T> T create(Class<T> type, List<class<?>> constructorArgTypes, List<Object> constructorArgs):负责创建对象,当MyBatis通过有参数的构造器创建对象时,实际上是由工厂方法调用该函数创建的,其中,constructorArgTypes表示构造器参数列表的类型,constructorArgs表示构造器参数列表的值;<T> boolean isCollection(Class<T> type):如果创建的对象是列表,则返回True;void setProperties(Properties properties):负责将配置对象工厂时配置的多个属性以Properties整体传入。
示例:定义一个对象工厂,在处理User查询的ResultSet时,为该对象添加age和operationTime属性:
- 修改User类:增加meta属性
public class User {
private Integer id;
private String nickname;
private String fullname;
private Map<String, String> meta;
public User() {}
public User(String nickname, String fullname) {
this.nickname = nickname;
this.fullname = fullname;
}
public Integer getId() {
return id;
}
public String getNickname() {
return nickname;
}
public String getFullname() {
return fullname;
}
public void setNickname(String nickname) {
this.nickname = nickname;
}
public void setFullname(String fullname) {
this.fullname = fullname;
}
public void setId(Integer id) {
this.id = id;
}
public Map<String, String> getMeta() {
return meta;
}
}
- 自定义对象工厂,文件名称:
UserObjectFactory,所在包:org.exmaple.app.ObjectFactory
public class UserObejctFactory extends DefaultObjectFactory {
private Integer age;
public Object create(Class type) {
var obj = super.create(type);
return processObject(obj);
}
public Object create(Class type, List constructorArgTypes, List constructorArgs) {
var obj = super.create(type, constructorArgs, constructorArgs);
return processObject(obj);
}
public void setProperties(Properties properties) {
super.setProperties(properties);
this.age = properties.getProperty("age");
}
public <T> boolean isCollection(Class<T> type) {
return super.isCollection(type);
}
private Object processObject(Object obj) {
// 如果type是User的子类或者本身
if (User.class.isAssignableFrom(obj.getClass()) {
var use = (User) obj;
user.getMeta().put("age", this.age);
user.getMeta().put("operationTime", new Date());
}
return obj
}
}
4.2 配置自定义对象工厂
自定义对象工厂代码编写完成后,在MyBatis核心配置文件中可以使用<objectFactory.../>元素配置自定义的对象工厂,示例如下:
...
<configuration>
...
<typeAliases>
<package name="org.example.app.entity" />
</typeAliases>
<objectFactory type="org.exmaple.app.ObjectFactory.UserObjectFactory">
<property name="age" value="29"/>
</objectFactory>
...
</configuration>
其中:
- 在第7行,配置自定义对象工厂源码位置
- 在第8行,使用
<property.../>元素指定相关的配置
5. 加载Mapper
MyBatis支持4中加载Mapper的配置方式,用来将Mapper组件配置到核心配置文件中,具体如下:
- 为
<mapper.../>指定resource属性加载文件,属性值为具体的文件路径; - 为
<mapper.../>指定url属性来加载文件, 可以使用file:///的方式加载指定磁盘目录下的文件; - 为
<mapper.../>指定class属性来加载文件,属性值为Mapper接口; - 使用
<package.../>属性执行加载某个包下的所有Mapper文件。
前三种大同小异,第4种在平常情况下使用的频率最高,配置示例如下:
...
<configuration>
...
<typeAliases>
<package name="org.example.app.entity" />
</typeAliases>
<objectFactory type="org.exmaple.app.ObjectFactory.UserObjectFactory">
<property name="age" value="29"/>
</objectFactory>
<mappers>
<package name="org.example.app.dao"/>
</mappers>
...
</configuration>