配置文件

MyBatis的XML配置文件是有层次结构的,因为存在约束嘛,这些层次是有顺序的,如果颠倒顺序,MyBatis就会解析出错。来看一看这个XML文件的层次结构。

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
 PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>  <!-- 配置 -->

    <properties></properties><!-- 属性 -->
    <settings></settings><!-- 设置 -->
    <typeAliases></typeAliases><!--别名 -->
    <typeHandlers></typeHandlers><!-- 类型处理器 -->
    <objectFactory type=""></objectFactory><!-- 对象工厂 -->
    <plugins></plugins><!-- 插件 -->
    <environments default=""><!-- 配置环境 -->
       <environment id=""><!-- 环境变量 -->
          <transactionManager type=""></transactionManager><!-- 事务管理器 -->
          <dataSource type=""></dataSource> <!-- 数据源 -->
       </environment>
    </environments>
    
    <databaseIdProvider type=""></databaseIdProvider><!-- 数据库厂商标识 -->
    <mappers></mappers> <!-- 映射器 -->

</configuration>

ProPerties元素

properties是一个配置属性元素,让我们在配置文件上下文中使用它。

MyBatis提供了3种配置方式

  • property子元素
  • properties配置文件
  • 程序参数传递

property子元素

配置如下:

    <properties>
      <property name="driver" value="com.mysql.jdbc.Driver"/>
      <property name="url" value="jdbc:mysql://localhost:3306/mybatis"/>
      <property name="username" value="root"/>
      <property name="password" value="root"/>
    </properties>

这样我们就可以在DataSource标签中配置:

          <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文件中,需要在MyBatis配置文件中导入:

<properties resource="jdbc.properties"></properties>

将这个外部的数据库信息配置在一个单独的文件中

程序参数配置

如果为了安全,数据库的用户名和密码都是加密的,我们无法使用这个加密的字符串来连接数据库,这个时候可以使用编码的方式来满足这种场景。

修改一下获得SqlSessionFactory的initSqlSessionFactory方法:

    public static SqlSessionFactory initSqlSessionFactory() {
        InputStream cfgStream=null;
        Reader cfgReader=null;
        InputStream proInputStream=null;
        Reader proReader=null;
        try {
            //读入配置文件
            cfgStream=Resources.getResourceAsStream("mybatis-config.xml");
            cfgReader=new InputStreamReader(cfgStream);
            //读入属性文件
            proInputStream=Resources.getResourceAsStream("jdbc.properties");
            proReader=new InputStreamReader(proInputStream);
            Properties properties=new Properties();
            properties.load(proReader);
            //解密为明文  decode为解密方法
            properties.setProperty("username", decode(properties.getProperty("username")));
            properties.setProperty("password", decode(properties.getProperty("password")));
            
            synchronized (CLASS_LOCK) {
                if(sqlSessionFactory==null) {
                    sqlSessionFactory=new SqlSessionFactoryBuilder().build(cfgReader,properties);
                }
            }
            
        }catch (Exception e) {
            e.printStackTrace();
        }
    }

主要是我们调用SqlSessionFactoryBuilder类的一个重载build(Reader,Properties)

优先级问题

通过方法参数传递的方式优先级最高;外部配置文件的方式次之;properties元素的方式最先被加载,但是会被比它优先级高的覆盖,所以优先级最低

(优先级:参数传递>外部配置文件>properties元素,反之,就是它们加载的先后顺序)。

注意:

1.不要混用这些方式

2.首选外部配置的方式

3.如果有对于用户名或密码加密的场景,就是用方法参数的方式。

其是在这里配置的properties标签在与spring整合之后,就会消失了,我们都将其配置在applicationContext.xml文件中。

设置

设置(settings)在MyBatis总是最为复杂的配置,同时也是最为重要的配置,它会改变MyBatis运行时的行为。即使不配置setting,MyBatis也会工作,因为其属性都有默认值的存在,在这里不显示其配置了,配置不需要修改太多,一般来说我们只会的修改一小部分就可以了。

别名

别名(typeAliases)是一个指代的名称,因为我们遇到的全限定名都太长了,所以希望使用一个简短的名称代替, 这个名称可以再MyBatis的整个上下文中使用。在MyBatis中分为系统定义别名和自定义别名两类,注意:在MyBatis中别名是不分大小写的。一个typeAliases实例在解析配置文件的时候生成,然后长期保存在Configuration对象中,但我们使用时,再把它拿出来,这样就没必要运行时再生成它的实例了。

系统别名

MyBatis中默认了一些经常使用的类型别名,例如:数值,字符串,日期和集合等,我们在MyBatis中直接使用即可,在使用的时候不要重复定义把它给覆盖了。基本上都是将类型的首字母小写了,如:

别名 映射的类型 支持数组
string String
short Short
integer Integer
float Float
boolean Boolean
date Date
list List
arraylist ArrayList
map Map
hashmap HashMap
object Object

我们还可以在 MyBatis的源码 org.apache.ibatis.type.TypeAliasRegistry 可以看到更为详细的信息

自定义别名

系统定义的别名往往是不够的,因为不同的应用存在各种不同的需求,所以MyBatis可以自定义别名。我们使用typeAliases配置别名:

  <!-- 配置别名 -->
  <typeAliases>
    <typeAlias alias="user" type="cn.lynu.model.User"/>
  </typeAliases>

alias属性就是在取别名,type指定其全限定名。如果我们没有使用ailas属性来设置别名,会有默认别名:类名首字母小写

如果我们懂得POJO类比较多,MyBatis可以允许我们通过自动扫描的形式自定义别名:

  <!-- 扫描别名 -->
  <typeAliases>
    <package name="cn.lynu.model"/>
  </typeAliases>

这样就可以自动扫描 cn.lynu.model 包下的POJO了,默认每个POJO的别名是将类名的首字母小写,当然我们也可以自己指定别名,使用@Alias注解标识在POJO类上:

类型处理器

类型处理器(typeHandler)是MyBatis在于预处理语句(PreparedStatement)中设置一个参数,或者从结果集中(result)中取出值时,都会用注册了的typeHandler进行处理。typeHandler常用的配置为java类型(javaType),JDBC类型(jdbcType)。所以说,typeHandler的作用就是将参数从javaType转换为javaType,或者是从数据库取结果的时候把jdbcType转换为javaType。

由于数据库厂商的不同,不同的厂商之间的设置的参数也可能会有所不同,所以MyBatis允许我们自定义typeHandler,我们往往需要对数据进行特殊的处理,这些都需要使用到自定义的typeHandler,例如:在使用枚举的时候常常需要使用自定义的typeHandler进行数据的转换。

同别名一样,类型处理器也有系统定义和用户定义两种

系统定义的typeHandler

来看几个系统定义的typeHandler:

类型处理器 Java类型 JDBC类型
BooleanTypeHandler java.lang.Boolean.boolean 数据库兼容的BOOLEAN
IntegerTypeHandler java.lang.Integer.integer 数据库兼容的INTEGER
LongTypeHandler java.lang.Long.long 数据库兼容的Long
StringTypeHandler java.lang.String CHAR,  VARCHAR
ClobTypeHandler java.lang.String CLOB,LONGVARCHAR
ByteArrayTypeHandler byte[] 数据库兼容的字节流类型
BlobTypeHandler byte[] BLOB,LONGVARBINARY
DateOnlyTypeHandler java.util.Date DATE
TimeOnlyTypeHandler java.util.Date TIME
SqlTimestampTypeHandler java.sql.Timestamp TIMESTAMP
SqlDateTypeHandler java.sql.Date Date
SqlTimeTypeHandler java.sql.Tome TIME
EnumTypeHandler Enum VARCHAR或任何兼容的的字符串类型,存储的是枚举的名称(而不是索引)默认的枚举处理类
EnumOrdinalTypeHandler Enum 任何兼容的数值类型,存储的是枚举的索引(而不是名称)

需要注意:

  • 数值类型的精度,数据库的int,double这些类型和java的精度,长度都是不一样的。
  • 时间精度,取数据到日用 DateOnlyTypeHandler 即可,用到精度到秒的用 SqlTimestampTypeHandler 。

自定义typeHandler

一般而言,Mybatis系统定义的typeHandler已经能应付大部分的场景了,但不排除不够用的时候,我们自定义的typeHandler需要明确要解决什么样的问题?现有的typeHandler是否够我们使用?

接下来我们模仿StringTypeHandler来编写一个我们自己的处理String类型的

步骤1:首先配置XML文件

<typeHandlers>
    <typeHandler handler="cn.lynu.typeHandler.MyStringTypeHandler" javaType="string" jdbcType="VARCHAR"/>
</typeHandlers>

handler指定我们自定义typeHandler的全限定名,并指明 javaType 是String,JDBCType是VARCHAR

步骤2:编写自定义TypeHandler

自定义TypeHandler的要求就是必须实现接口:org.apache.ibatis.type.TypeHandler,也可以继承BaseTypeHandler ,因为BaseTypeHandler实现了这个接口,这里我们先使用继承接口的方式吧

package cn.lynu.typeHandler;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

import org.apache.ibatis.type.JdbcType;
import org.apache.ibatis.type.MappedJdbcTypes;
import org.apache.ibatis.type.MappedTypes;
import org.apache.ibatis.type.TypeHandler;

@MappedTypes({String.class})
@MappedJdbcTypes({JdbcType.VARCHAR})
public class MyStringTypeHandler implements TypeHandler<String>{

    @Override
    public String getResult(ResultSet rs, String value) throws SQLException {
        System.out.println("使用的是result和value");
        return rs.getString(value);
    }

    @Override
    public String getResult(ResultSet rs, int index) throws SQLException {
        System.out.println("使用的是result和index");
        return rs.getString(index);
    }

    @Override
    public String getResult(CallableStatement cs, int index) throws SQLException {
        return cs.getString(index);
    }

    @Override
    public void setParameter(PreparedStatement ps, int index, String value, JdbcType jt) throws SQLException {
        ps.setString(index, value);
    }

}

可以看到代码中有三个重载的getResult方法,分别根据Result和value,Result和index,存储过程callableStatement来获得值,setParameter方法使用预编译PreparedStatement来设置值。

@MappedTypes 定义的是JavaType类型,可以指定哪些Java类型可以被处理

@MappedJdbcTypes 定义的JdbcType类型,其值可以从org.apache.ibatis.type.JdbcType 的枚举值中获得

步骤3:在Mapper的映射XML文件使用

<resultMap type="user" id="userMap">
   <!-- 定义结果集 -->
   <id column="id" jdbcType="INTEGER" javaType="int" property="id" />
   <result column="username" property="userName" typeHandler="cn.lynu.typeHandler.MyStringTypeHandler"  />
</resultMap>

这里处理username就是使用的我们自定义的TypeHandler

public User findUserById2(int id)throws Exception; 
 <select id="findUserById2" resultMap="userMap" parameterType="int">
     select id,username from user where id = #{id}
 </select>

经测试可以正常运行

枚举类型typeHandler

MyBatis有两个用于枚举类型的类型处理器,分别为EnumTypeHandler(将枚举的值存入数据库)和EnumOrdinalTypeHandler(将枚举的下标存入数据库)

我们来创建一个性别枚举类:

package cn.lynu.model;

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;
    }

}
EnumOrdinalTypeHandler虽然是系统自带的,还是需要在MyBatis的配置文件中配置,如果不配置Mybatis默认使用 EnumTypeHandler 作为枚举类型处理器
    <typeHandlers>
      <typeHandler handler="org.apache.ibatis.type.EnumOrdinalTypeHandler" javaType="cn.lynu.model.Sex"/>
    </typeHandlers>

然后我们在mapper的映射xml中使用:

<resultMap type="user" id="userMap">
   <!-- 定义结果集 -->
   <id column="id" jdbcType="INTEGER" javaType="int" property="id" />
   <result column="username" property="userName" typeHandler="cn.lynu.typeHandler.MyStringTypeHandler"  />
   <result column="sex" property="sex" typeHandler="org.apache.ibatis.type.EnumOrdinalTypeHandler"/>
</resultMap>

添加了一个sex字段,并指明TypeHandler为   org.apache.ibatis.type.EnumOrdinalTypeHandler

 <insert id="insertUser" parameterType="user">
   insert into user(username,sex) values(#{userName},#{sex})
 </insert>

这是一个添加的操作,测试可以正确的先数据库中添加数据,性别字段使用的下标,而使用EnumTypeHandler就是使用枚举的值,就不复述了。

自定义枚举TypeHandler和之前我们自定义的 MyStringTypeHandler  一样的创建和使用方法

对象工厂

当MyBatis在构建一个结果返回的时候,都会使用ObjectFactory(对象工厂)来封装POJO,大部分情况下,我们不需要自己去配置ObjectFactory,使用默认即可。

插件

插件较为复杂,使用是要小心,因为插件会覆盖一些MyBatis内部对象的行为,我们在后面讨论。

environments配置环境

先来看一个环境配置:

<!-- 和spring整合之后会废除environments -->
    <environments default="development">
        <environment id="development">
            <!-- 使用JDBC事务,事务控制有Mybatis控制 -->
            <transactionManager type="JDBC" />
            <!--数据库连接池,由Mybatis创建 -->
            <dataSource type="POOLED">

                <!--这里使用前面我们说的外部jdbc属性文件的方式-->
                <property name="driver" value="${jdbc.driverClass}" />
                <property name="url" value="${jdbc.jdbcUrl}" />
                <property name="username" value="${jdbc.user}" />
                <property name="password" value="${jdbc.password}" />

            </dataSource>
        </environment>
    </environments>

environments有一个defaule属性,标明在默认情况下,我们使用哪个数据源配置。

environment标签是配置一个数据源的开始,属性id是设置这个数据源的标志,environments的default使用的就是个值

transactionManager 配置的是事务,其实type的值可以为:

1.JDBC,采用JDBC方式管理事务

2.MANAGED,采用容器的方式管理事务,JNDI数据源中使用

3.自定义,可以由使用者自定义数据库事务的管理

dataSource标签是配置数据源的各项属性,type属性是提供我们对数据库的连接方式,可选择值为:

1.UNPOOLED,非连接池的方式,由 org.apache.ibatis.datasource.unpooled.UnpooledDataSource.class实现

2.POOLED,连接池的方式,由 org.apache.ibatis.datasource.pooled.PooledDataSource 实现

3.JNDI,JNDI数据源

4.自定义数据源

其中,配置property元素,就是在定义数据库的各项参数(四大参数等等)

 大部分这些配置我们都与Spring 框架来控制,这以后再说。

databaseIdProvider数据库厂商标识

在相同的数据库中使用这个毫无意义,在实际中使用很少,因为很少有项目使用不同的数据库系统。MyBatis可以设置一个数据库厂商标识,用于将指定的SQL到对应的数据库中使用。

默认的系统规则

    <databaseIdProvider type="DB_VENDOR">
    <!-- 配置数据库标识的时候注意name值的大小写 -->
      <property name="SQL Server" value="sqlserver"/>
      <property name="MySQL" value="mysql"/>
      <property name="DB2" value="db2"/>
      <property name="Oracle" value="oracle"/>
    </databaseIdProvider>

type="DB_VENDOR" 就是使用默认的数据库厂商标识,但是需要注意property的name属性值,是区分大小写的

我们可以使用:

System.out.println(sqlSessionFactory.getConfiguration().getDatabaseId());

将现在使用的数据库打印出来,输出的就是value值,没有配置databaseId或没有根据name值匹配上,就会返回null。

将Mapper的XML文件修改一下,说明该SQL由哪个数据库执行:

  <select id="findUserById2" resultType="user" parameterType="int" databaseId="mysql">
     select id,username,birthday from user where id = #{id}
 </select>

就是添加一个databaseId属性,其值就是property的value值。这条SQL就会在MySQL数据库中使用。如果有两条相同的SQL,一条使用databaseID数据库标识,一条不使用,后者就会舍弃。

引入映射器方法

映射器是MyBatis最复杂,最核心的东西,我们之后再来研究,这里只是说明如何引入映射器。引入映射器的方法有很多,一般有以下几种:

1.使用文件路径(这种方式可以让映射文件放在其他包下)

    <mappers>
        <mapper resource="cn/lynu/mapper/userMapper.xml"/>
    </mappers>

2.用包名引入映射器(这样可以一次导入一个包下的mapper的XML,但是需要将sql映射文件和接口发在同一个包下。建议写法:在config或Maven的resource下建立一个与接口同名的包,将sql映射文件放入其中)

    <mappers>
        <package name="cn.lynu.mapper" />
    </mappers>

3.用类注册引入映射器(如果在接口中使用注解的方式来映射接口,推荐使用这种引入方法;如果还是使用xml映射文件则要求映射文件和接口放在同一个的包下)

    <mappers>
        <mapper class="cn.lynu.mapper.UserMapper"/>
    </mappers>

我们需要根据实际情况恰当的选择合适的引入方法

 

posted @ 2017-11-14 17:40  OverZeal  阅读(442)  评论(0)    收藏  举报