<?xml version="1.0" encoding=UTF-8">
<configuration><!--配置-->
<properties/><!--属性-->
<settings/><!--设置-->
<typeAliases><!--类型命名-->
<typeHandler><!--类型处理器-->
<objectFactory><!--对象工厂-->
<plugins><!--插件-->
<!-- 配置全局属性 -->
<environments><!--配置环境-->
<environment><!--环境变量-->
<transactionManager/><!--事务管理器-->
<dataSource/><!--数据源-->
</environment>
</environments>
<databaseIdProvider/><!--数据库厂商标识-->
<mappers/><!--映射器-->
</configuration>
properties元素
properties是一个配置属性的元素,让我们能在配置文件的上下文中使用它。
MyBatis提供3中配置方式:
- property子元素
- properties配置文件
- 程序参数传递
property子元素
<property name="driver" value="com.mysql.jdbc.Driver" />
<property name="url" value="jdbc:mysql://localhost:3306/study?useUnicode=true&characterEncoding=utf8" />
<property name="username" value="root" />
<property name="password" value="root" />
这样我们就可以在上下文中使用已经配置好的属性值了。我们配置数据库时就可以按照以下方式配置
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
properties
properties还可以加载配置文件,如jdbc.properties,并以比较简洁的方式进行数据库的配置
<properties resource="jdbc.properties" />
<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.passWd}" />
</dataSource>
jdbc.properties配置文件内容如下:
jdbc.Driver=com.mysql.jdbc.Driver
jdbc.userName=root
jdbc.passWd=root
jdbc.url=jdbc:mysql://localhost:3306/study?useUnicode=true&characterEncoding=utf8
由于以上两种为常用的配置,所以只分析它们。
优先级
MyBatis支持的3中配置方式可能同时出现,并且属性还会重复配置,其实这三种方式是存在优先级的,MyBatis将按照下面的顺序来进行加载
- 在properties元素体内指定的属性首先被读取
- 根据properties元素中的resource属性读取类路径下的属性文件;或者根据url属性指定的路径读取属性文件,并覆盖已读取的同名属性
- 读取作为方法参数传递的属性,并覆盖已读取的同名属性
因此,通过方法参数传递的属性具有最高优先级,resource/url属性中指定的配置文件次之,最低优先级是properties属性中指定的属性
推荐使用properties文件的形式
Settings
settings在Mybatis中是最复杂的配置,同时也是最为重要的配置内容之一,它会改变MyBatis运行时的行为。即使不配置settings,MyBatis也可以正常工作。
typeAliases
别名是一个指点的名称,因为我们遇到的类全限定名过长,所以我们希望用一个简短的名称去指代它,这个名称可以在MyBatis上下文重视会用。别名可以分为系统定义别名和自定义别名两类。在Mybatis中别名是不区分大小写的。一个typeAliases的实例是在解析配置文件时生成的,然后长期保存在Configuration对象中,当我们使用它时,再把它拿出来,这样就没有必要运行的时候再次生成它的实例了。
系统定义别名
MyBatis系统定义了一些经常使用的类型的别名,如数值,字符串,日期和集合等,我们可以在MyBatis中直接使用它们,在使用时不要重复定义把它们给覆盖了
其实我们可以在 org.apache.ibatis.type.TypeAliasRegistry这个类中查看别名信息。
registerAlias("string", String.class);
registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class);
...
自定义别名
因为不同的应用有不同的需要,系统所定义的别名往往不够用,所以MyBatis允许自定义的别名。我们可以用typeAliases配置别名。也可以使用@Alias方法注册别名。如
<typeAliases>
<typeAlias type="com.wojiushiwo.mybatis_demo.User" alias="user" />
</typeAliases>
这样就在MyBatis的上下文中使用”user”来代替其全路径,减少配置的复杂度
如果POJO类较多的话,以上面的方式配置也稍显麻烦。我们可以通过扫描包的方式。如下
<typeAliases>
<package name="com.wojiushiwo.mybatis_demo" />
</typeAliases>
在实体类上添加上注解,定义别名
@Alias(value="user")
public class User implements Serializable {
private static final long serialVersionUID = 1L;
这样,MyBatis就能够扫描到POJO类,并且指定其别名
如果配置了包扫描,而POJO类没有注解@alias,它也会被装载,只是它会把类的第一个字母变为小写,然后把改变后的类名作为MyBatis的别名。
定义别名时要特别注意,别名不要重复
typeHandler
Mybatis在预处理语句中设置一个参数时。或者从结果集中取出一个值时,都会使用到typeHandler。typeHandler允许根据项目的需要自定义设置Java传递到数据库的参数中,或者从数据库读出数据,这些都可以在自定义的typeHandler中处理。
typeHandler也分为系统定义和用户自定义两种。
typeHandler常用的配置为Java类型(javaType)、JDBC类型(jdbcType).typeHandler的作用就是将参数从JavaType转化为jdbcType;或者从数据库取出结果时把jdbcType转换为javaType
系统定义的typeHandler,可以通过org.apache.ibatis.type.TypeHandlerRegistry查看,如下
...
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler());
register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler());
...
自定义TypeHandler
当我们需要特殊的处理Java的那些类型和对应处理数据库的那些类型时,系统定义的typeHandler已经应付不了的时候,我们考虑自定义typeHandler。
自定义typeHandler需要实现TypeHandler接口
@MappedTypes(value = { String.class })
@MappedJdbcTypes(value = { JdbcType.VARCHAR })
public class MyStringTypeHandler implements TypeHandler<String> {
private Logger log = Logger.getLogger(MyStringTypeHandler.class);
@Override
public void setParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType) throws SQLException {
log.info("使用我的typeHandler");
ps.setString(i, parameter);
}
@Override
public String getResult(ResultSet rs, String columnName) throws SQLException {
log.info("使用我的typeHandler,ResultSet列名获取字符串");
return rs.getString(columnName);
}
@Override
public String getResult(ResultSet rs, int columnIndex) throws SQLException {
log.info("使用我的typeHandler,ResultSet下标获取字符串");
return rs.getString(columnIndex);
}
@Override
public String getResult(CallableStatement cs, int columnIndex) throws SQLException {
log.info("使用我的typeHandler,CallableStatement下标获取字符串");
return cs.getString(columnIndex);
}
}
重写TypeHandler的方法,方法大概有:为预编译语句设置参数、根据列名从结果集中获取数据、根据列索引从结构集中获取数据、从存储过程中获取数据
自定义typeHandler里用注解配置JdbcType和JavaType,这两个注解是:
- @MappedTypes定义的是JavaType类型,可以指定哪些Java类型被拦截
@MappedJdbcTypes定义的是JdbcType类型,它需要满足枚举类org.apache.ibatis.type.JdbcType所列的枚举类型
自定义typeHandler在mybatis-config.xml中的使用:
<typeHandlers>
<typeHandler javaType="String" jdbcType="VARCHAR"
handler="com.example.demo.handler.MyStringTypeHandler" />
</typeHandlers>
<!--也可以使用包扫描的方式-->
<!--<typeHandlers>
<package name="com.example.demo.handler.MyStringTypeHandler"/>
</typeHandlers>
-->
常见的使用自定义typeHandler的方式:
<resultMap type="com.example.demo.entity.User" id="User"> <!-- 方式一 使用自定义的typeHandler --> <!-- <result column="name" property="name" javaType="string" jdbcType="VARCHAR" /> --> <!-- 方式二使用自定义的typeHandler --> <result column="name" property="name" typeHandler="com.example.demo.handler.MyStringTypeHandler" /> </resultMap>
枚举类型处理器
先定义一个枚举类:
public enum Sex {
MALE(1, "男"), FEMALE(2, "女");
private int id;
private String name;
private Sex(int id, String name) {
this.id = id;
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public static Sex getSex(int id) {
if (id == 1) {
return Sex.MALE;
} else if (id == 2) {
return Sex.FEMALE;
}
return null;
}
}
EnumTypeHandler、EnumOrdinalTypeHandler均可作为枚举类型处理器,但两者存在区别:
EnumOrdinalTypeHandler获取的是枚举定义的下标,如1,2;EnumTypeHandler获取的是枚举定义的name,如男,女
截取一部分源码说明问题:
EnumTypeHandler
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
if (jdbcType == null) {
ps.setString(i, parameter.name());
} else {
ps.setObject(i, parameter.name(), jdbcType.TYPE_CODE); // see r3589
}
}
EnumOrdinalTypeHandler
@Override
public void setNonNullParameter(PreparedStatement ps, int i, E parameter, JdbcType jdbcType) throws SQLException {
ps.setInt(i, parameter.ordinal());
}
使用方式:
<typeHandlers>
<typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler"
javaType="com.example.demo.entity.Sex" />
</typeHandlers>
<!--user类中定义了该Sex枚举属性-->
<resultMap type="com.example.demo.entity.User" id="User">
...
<result column="sex" property="sex"
typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler" />
...
</resultMap>
ObjectFactory
当MyBatis从数据库取得数据返回的时候,都会使用ObjectFactory去构建POJO;在MyBatis中可以定制自己的对象工厂。一般来说我么们使用默认的ObjectFactory(org.apache.ibatis.reflection.factory.DefaultObjectFactory)即可。当然我们也可以自己自定义。
public class MyObjectFactory extends DefaultObjectFactory {
private static final long serialVersionUID = 6016076182957642243L;
private Logger log = Logger.getLogger(MyObjectFactory.class);
@Override
public <T> T create(Class<T> type) {
log.info("使用定制对象工厂 创建单个对象");
return super.create(type);
}
@Override
public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes, List<Object> constructorArgs) {
log.info("使用定制对象工厂 创建列表对象");
return super.create(type, constructorArgTypes, constructorArgs);
}
@Override
public void setProperties(Properties properties) {
log.info("定制属性:" + properties);
super.setProperties(properties);
}
@Override
public <T> boolean isCollection(Class<T> type) {
return super.isCollection(type);
}
}
我们可以通过实现ObjectFactory接口来构建ObjectFactory。但是由于DefaultObjectFactory已经实现了ObjectFactory的接口,我们可以通过继承DefaultObjectFactory来简化编程。一般我们不需要使用自己配置的ObjectFactory,使用系统默认即可
代码中setProperties方法可以使我们如何去处理设置进去的属性,而create方法可以分别处理单个对象和列表对象。
在mybatis-config.xml中使用
<objectFactory type="com.example.demo.factory.MyObjectFactory">
<property name="name" value="MyObjectFactory" />
</objectFactory>
environments
<!-- 配置全局属性 -->
<environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<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.passWd}" />
</dataSource>
</environment>
</environments>
配置环境可以注册多个数据源,每个数据源可以分为两大部分:一个是数据源的配置,一个是数据库事务的配置。
- environments中的属性default,表明在缺省的情况下,我们将启动哪个数据源配置
- environment元素是配置一个数据源的开始;属性id是设置这个数据源的标识,以便MyBatis上下文使用它
transactionManager配置的是数据库事务,其中type属性有3种配置方式:
JDBC:采用JDBC方式管理事务,在独立编码中经常使用
MANAGED:采用容器方式管理事务,在JNDI数据源中常用
自定义,由使用者自定义数据库事务管理办法,适用于特殊应用property元素则可以配置数据源的各类属性,autoCommit=false则表示数据源不自动提交
- dataSource:配置数据源连接的信息,type属性是提供我们对数据库连接方式的配置:
UNPOOLED 非连接池数据库
POOLED 连接池数据库
JNDI JNDI数据源
自定义数据源
数据源
- UNPOOLED 非连接池 使用 org.apache.ibatis.datasource.unpooled.UnpooledDataSource实现
- POOLED 连接池 使用org.apache.ibatis.datasource.pooled.PooledDataSource实现
- JNDI 使用org.apache.ibatis.datasource.jndi.JndiDataSourceFactory来获取数据源
如果我们需要自定义数据源,必须实现org.apache.ibatis.datasource.DataSourceFactory接口
Mapper
引入mapper的方式:
<mappers>
<!--使用文件路径引入-->
<mapper resource="com.wojiushiwo/UserMapper.xml"/>
<mapper resource="com.wojiushiwo/ArticleMapper.xml"/>
</mappers>
<mappers>
<!--使用类注册引入-->
<mapper class="com.wojiushiwo.mybatis_demo.UserMapper"/>
</<mappers>
<mappers>
<!--使用包名引入-->
<package name="com.wojiushiwo.mybatis_demo"/>
</<mappers>
参考文献:
深入浅出MyBatis技术原理与实战