MyBatis 的配置文件即mybatis-config.xml
中的configuration
标签下有settings、properties
等属性。本文对其中常用属性的配置做简单介绍
properties 属性可以从properties文件中获得,也可以直接写到配置文件中。
属性文件里config.properties
中内容为
driver=com.mysql.jdbc.Driver
url=jdbc:mysql://127.0.0.1:3306/test
username=root
password=root
配置文件mybatis-config.xml
中有这样的内容
<!-- properties配置 -->
<properties resource="config.properties">
<property name="username" value="shisong"/>
<property name="password" value="123456"/>
</properties>
<!-- properties使用 -->
<dataSource type="POOLED">
<property name="driver" value="${driver}"/>
<property name="url" value="${url}"/>
<property name="username" value="${username}"/>
<property name="password" value="${password}"/>
</dataSource>
上面例子中的password
重复指定了且不一样,而数据库的正确密码是root
,上面的例子是能够成功的;
当把配置中的password
改为root
,而把porperties中password
改为123456
时则会失败;
所以当属性在不只一个地方进行了配置时,要有一个加载顺序,后加载的会覆盖先加载的。
另外属性加载的时候,还可以通过SqlSessionBuilder.build()
方法来加载
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, props);
// ... or ...
SqlSessionFactory factory = sqlSessionFactoryBuilder.build(reader, environment, props);
当属性在不只一个地方进行了配置时,加载顺序是这样的
XML
文件中在properties
元素体内property
标签指定的属性resource
或url
指定的资源文件中的属性,并覆盖已读取的同名属性类型别名是为Java
类型命名的一个短的名字,意义仅在于用来减少类完全限定名的冗余。
可以像下面这样起别名:
<typeAliases>
<typeAlias alias="Author" type="domain.blog.Author"/>
<typeAlias alias="Blog" type="domain.blog.Blog"/>
</typeAliases>
也可以指定一个包名, MyBatis 会在包名下搜索需要的 Java Bean,比如:
<typeAliases>
<package name="domain.blog"/>
</typeAliases>
包 domain.blog 中所有的的 Java Bean,在没有注解的情况下,会使用 Bean 的类名来作为它的别名。 比如 domain.blog.Author 的别名为 Author(大小写不敏感);若有注解,则别名为其注解值。如下:
@Alias("author")
public class Author {
...
}
MyBatis 已经为普通的 Java 类型内建了许多相应的类型别名,它们都是大小写不敏感的。
别名 映射的类型
_byte byte
_long long
_short short
_int int
_integer int
_double double
_float float
_boolean boolean //以上都是原始类型
string String
byte Byte
long Long
short Short
int Integer
integer Integer
double Double
float Float
boolean Boolean
date Date
decimal BigDecimal
bigdecimal BigDecimal
object Object
map Map
hashmap HashMap
list List
arraylist ArrayList
collection Collection
iterator Iterator
数据库中的类型和Java
的类型是不一样的,但是有一定的对应关系。
无论是 MyBatis 在预处理语句(PreparedStatement)中设置参数时,还是从结果集中取出值时,都会用类型处理器以合适的方式进行转换。
typeHandler
就是进行jdbcType
和javaType
之间的转换的。
MyBatis已经自动注册了一些TypeHandler,我们仍然可以写自己的TypeHandler,具体方法是:实现org.apache.ibatis.type.TypeHandler
接口,或继承org.apache.ibatis.type.BaseTypeHandler
抽象类并实现其中的抽象方法,然后指明自己的TypeHandler是哪个jdbcType
和javaType
之间的转换。
推荐使用BaseTypeHandler,因为它对null
值进行了过滤,而且它继承了TypeReference
抽象类,通过TypeReference
的getRawType()
方法可以获取到当前TypeHandler所使用泛型的原始类型。这对Mybatis在注册TypeHandler的时候是非常有好处的。在没有指定javaType的情况下,Mybatis在注册TypeHandler时可以通过它来获取当前TypeHandler所使用泛型的原始类型作为要注册的TypeHandler的javaType类型。
假设有一个User类,其中有一个属性interests是String数组类型
public class User {
private int id;
private String name;
private int age;
private String[ ] interests;
...
}
要为String 数组定义一个转换类StringArrayTypeHandler
// StringArrayTypeHandler.java
@MappedJdbcTypes(JdbcType.VARCHAR)
public class StringArrayTypeHandler extends BaseTypeHandler<String[ ]> {
/**
* 用于定义在Mybatis设置参数时该如何把Java类型的参数转换为对应的数据库类型
* @param ps 当前的PreparedStatement对象
* @param i 当前参数的位置
* @param parameter 当前参数的Java对象
* @param jdbcType 当前参数的数据库类型
* @throws SQLException
*/
@Override
public void setNonNullParameter(PreparedStatement ps, int i, String[ ] parameter, JdbcType jdbcType) throws SQLException {
//由于BaseTypeHandler中已经把parameter为null的情况做了处理,所以这里我们就不用再判断parameter是否为空了,直接用就可以了
StringBuffer result = new StringBuffer();
for (String value : parameter)
result.append(value).append(",");
result.deleteCharAt(result.length()-1);
ps.setString(i, result.toString());
}
}
/**
* 用于在Mybatis获取数据结果集时如何把数据库类型转换为对应的Java类型
* @param rs 当前的结果集
* @param columnName 当前的字段名称
* @return 转换后的Java对象
* @throws SQLException
*/
@Override
public String[ ] getNullableResult(ResultSet rs, String columnName) throws SQLException {
return this.getStringArray(rs.getString(columnName));
}
/**
* 用于在Mybatis通过字段位置获取字段数据时把数据库类型转换为对应的Java类型
* @param rs 当前的结果集
* @param columnIndex 当前字段的位置
* @return 转换后的Java对象
* @throws SQLException
*/
@Override
public String[ ] getNullableResult(ResultSet rs, int columnIndex) throws SQLException {
return this.getStringArray(rs.getString(columnIndex));
}
/**
* 用于Mybatis在调用存储过程后把数据库类型的数据转换为对应的Java类型
* @param cs 当前的CallableStatement执行后的CallableStatement
* @param columnIndex 当前输出参数的位置
* @return
* @throws SQLException
*/
@Override
public String[ ] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException {
return this.getStringArray(cs.getString(columnIndex));
}
private String[ ] getStringArray(String columnValue) {
if (columnValue == null)
return null;
return columnValue.split(",");
}
}
MyBatis 在TypeHandlerRegistry
类中已经注册了许多TypeHandler。
我们建立了自己的TypeHandler之后就需要把它注册到Mybatis的配置文件中,让Mybatis能够识别并使用它。注册TypeHandler主要有两种方式:
handler
来指明当前要注册的TypeHandler的全名称。另外还有两个可选属性,一个是javaType,用以指定对应的java类型;另一个是jdbcType,用以指定对应的jdbc类型。<!-- mybatis-config.xml -->
<typeHandlers>
<typeHandler handler="org.mybatis.example.StringArrayTypeHandler" javaType="[Ljava.lang.String;" jdbcType="VARCHAR"/>
</typeHandlers>
注意上面的javaType
要写全类名,String[]
的全类名为"[Ljava.lang.String;"
,所以上面注册的javaType
属性为javaType="[Ljava.lang.String;"
TypeHandler
。<!-- mybatis-config.xml -->
<typeHandlers>
<package name="org.mybatis.example"/>
</typeHandlers>
总结
MyBatis会通过窥探属性的原始类型来推断由类型处理器处理的 Java 类型(前面说的getRawType()
方法),可以通过两种方式显式指定被关联的 Java 类型:
javaType
属性(比如:javaType="String"
);@MappedTypes
注解来指定与其关联的Java类型列表。如果在 javaType
属性中也同时指定,则注解方式将被忽略。可以通过两种方式来指定被关联的 JDBC 类型:
jdbcType
属性(比如:jdbcType="VARCHAR"
);@MappedJdbcTypes
注解来指定与其关联的 JDBC 类型列表。 如果在javaType
属性中也同时指定,则注解方式将被忽略。Mybatis注册TypeHandler就是建立一个javaType
、jdbcType
和TypeHandler
的对应关系。根据上面所讲的情况,MyBatis注册我们写的TypeHandler时可分为下面这几种情况:
javaType
和jdbcType
,那么Mybatis会注册对应javaType
和dbcType
的TypeHandler
。javaType
无jdbcType
,那么Mybatis会注册对应javaType
和null
的TypeHandler
。javaType
有jdbcType
,若当前的TypeHandler
继承了TypeReference
抽象类,Mybatis会利用TypeReference
的getRawType()
方法取到当前TypeHandler
泛型对应的javaType
类型以方式1注册。javaType
无jdbcType
,且当前的TypeHandler
未继承TypeReference
抽象类,那么Mybatis会注册对应null
和null
的TypeHandler
。上面是讲 MyBatis 是如何注册TypeHandler,下面讲 Mybatis 是如何获取对应的TypeHandler进行类型转换的。
我们这样定义UserMapper.xml
文件
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.mybatis.example.mapper.UserMapper">
<resultMap id="UserResult" type="User">
<id column="id" property="id"/>
<result column="interests" property="interests" javaType="[Ljava.lang.String;" jdbcType="VARCHAR"/>
</resultMap>
<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyColumn="id">
insert into t_user(name, age, interests) values(#{name}, #{age}, #{interests, javaType=[Ljava.lang.String;, jdbcType=VARCHAR})
</insert>
<update id="updateUser" parameterType="User">
update t_user set name=#{name}, age=#{age}, interests=#{interests} where id=#{id}
</update>
<select id="findById" parameterType="int" resultMap="UserResult">
select * from t_user where id=#{id}
</select>
<delete id="deleteUser" parameterType="int">
delete from t_user where id=#{id}
</delete>
</mapper>
注意到UserResult
中我们指定了javaType
和jdbcType
。获取TypeHandler的方式与注册TypeHandler中的总结完全一样。
或者我们可以通过typeHandler
属性指定到底使用哪个TypeHandler
,这样不指定javaType
和jdbcType
也可以达到同样效果。
<result column="interests" property="interests" typeHandler="org.mybatis.example.StringArrayTypeHandler"/>
mapper.xml
文件的内容请参考后续文章中的动态SQL
MyBatis的配置文件mybtis-config.xml
中有mappers
元素,其作用是告诉MyBatis去哪里找SQL映射语句。
一般情况会有一个Maper接口
和一个Mapper.xml
对应(这里指xml文件的namespace
与接口对应),注册Mapper
的方法归纳如下:
<mappers>
<!-- 通过mapper元素的resource属性可以指定资源中的Mapper.xml文件 -->
<mapper resource="mapper/UserMapper.xml"/>
<!-- 通过mapper元素的url属性可以指定一个通过URL请求道的Mapper.xml文件 -->
<mapper url="file:///var/mappers/UserMapper.xml"/>
<!-- 通过mapper元素的class属性可以指定一个Mapper接口进行注册 -->
<mapper class="com.test.mybatis.mapper.UserMapper"/>
<!-- 通过package元素将会把指定包下面的所有Mapper接口进行注册 -->
<package name="com.test.mybatis.bean"/>
</mappers>
注意
xml
文件的namespace
如果与接口不对应,则相当于一个只有接口另一个只有xml文件,两者之间没有任何关联Mapper接口
而无Mapper.xml
,则接口中必须通过注解来实现,注册时只能通过package
或者class
方式注册,调用方式如下//获取注解后接口对应的对象
UserMapper userMapper = session.getMapper(UserMapper.class);
//调用接口中的方法
User user = userMapper.findById(1);
Mapper.xml
文件而无Mapper接口
,则只能通过resource
或url
来注册,调用方式也将变成://参数中的com.mybatis.example.mapper.UserMapper是xml文件中的namespace指定的类名
//参数中的findById为指定的类中的方法
User user = sqlSession.selectOne("com.mybatis.example.mapper.UserMapper.findById", 1);
Mapper接口
又Mapper.xml
(namespace
对应才算是都有)则接口中注解不能与xml文件重复,注册最好通过resource
(通过class
或package
注册只会注册接口而不会关联xml文件,通过resource
注册不仅会注册xml文件,还会关联对应的接口),调用方式可以采取上面两种的任意一种。