MyBatis配置文件

MyBatis配置详解

1. MyBatis配置文件元素

<?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/>
  <!--设置-->
  <settings/>
  <!--类型命名-->
  <typeAliases/>
  <!--类型处理器-->
  <typeHandlers/>
  <!--对象工厂-->
  <objectFactory/>
  <!--插件-->
  <plugins/>
  <!--配置环境-->
  <environments>
    <!--环境变量-->
    <environment>
      <!--事务管理器-->
      <transactionManager/>
      <!--数据源-->
      <dataSource/>
    </environment>
  </environments>
  <!--数据库厂商标识-->
  <databaseIdProvider/>
  <!--映射器-->
  <mappers/>
</configuration>

注意:MyBatis配置项的顺序不能点到,如果颠倒,在MyBatis的启动(编译)阶段就会发生一场,导致程序无法运行。

 

2. properties 属性

        properties属性一般用来给系统配置一些运行参数,可以放在XML文件或properties中。而不是妨碍Java编码中。这样方便参数修改,不用重新编译程序。要知道,大型项目重新编译很耗时的。

        MyBatis提供了3中方式使用properties:

  • properties 子元素
<properties>
    <property name="db.driver" value="com.mysql.jdbc.Driver"/>
    <property name="db.url" value="jdbc:mysql://localhost:3306/school"/>
    <property name="db.username" value="root"/>
    <property name="db.password" value="root"/>
</properties>
  • properties 文件
<properties resource="jdbc.properties"/>
  • 程序代码传递
            String resource = "mybatis-config";
			InputStream is;
			InputStream in;
			try {
				in = Resources.getResourceAsStream("jdbc.properties");
				is = Resources.getResourceAsStream(resource);
				Properties properties = new Properties();
				properties.load(in);
				properties.put("db.username", "解密后的用户名");
				properties.put("db.password", "解密后密码");

				sqlSessionFactory = new SqlSessionFactoryBuilder().build(is, properties);
			} catch (IOException e) {
				e.printStackTrace();
				return null;
			}

优先级依次递增,在开发中数据库密码对开发人员是保密的。运维人员将账户密码加密后配置成properties文件,由开发人员进行解密后,得到密码覆盖原来的密文。

仍然在mybaits-config.xml中使用${属性名}来调用。

 

3. settings设置

重要的:

配置项作用配置选项说明默认值
cacheEnabled该配置影响所有映射器中配置缓存的全局开关true|falsetrue
lazyLoadingEnable延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。在特定关联关系中可通过设置fetchType属性来覆盖该项的开光状态true|falsefalse
aggressiveLazyLoading当启用时,对任意延迟属性的调用会使带有延迟加载属性的对象完整加载;反之,每种属性将会按需加载true|falsetrue
autoMappingBehavior指定MyBatis应如何自动映射到字段或属性。NONE表示取消自动映射;PARTIAL表示只会自动映射,没有定义嵌套结果集和映射结果集。FULL会自动映射任意复杂的结果集(无论是否嵌套)

NONE、PARTIAL、FULL

PARTIAL

mapUnderscoreToCamelCase

是否开启驼峰命名规则映射,即从经典数据库列名A_COLUMN到经典Java属性名aColumn的类似映射。true|falsetrue
defaultExecutorType配置默认的执行器。SIMPLE事普通的执行器:REUSE会重用预处理(prepared statements);BATCH执行器将重用语句并执行更新。SIMPLE、REUSE、BATCHSIMPLE

关于缓存的cacheEnable、关于级联的lazyLoadingEnabled和aggressiveLazyLoading、关于映射的 autoMappingBehavior和mapUnderscoreToCamelCase,关于执行器类型的defaultExecutorType。

 

4. typeAliases别名

为了方便,用typeAliases别名替代较长的全限定名。别名分为系统定义别名(int、long、short、string...)和自定义别名。在MyBatis中别名由类 TypeAliasRegistry(org.apache.ibatis.type.TypeAliasRegistry)去定义。注意,别名不区分大小写。

自定义别名有三种方式:

4.1.  type为类文件的全限定名

alias为别名

  <typeAliases>
    <typeAlias type="pojo.Student" alias="student"/>
  </typeAliases>

 

4.2. 扫描别名

name的值为类所在的包名,扫描的类,类名首字母变为小写作为别名

  <typeAliases>
    <package name="pojo"/>
  </typeAliases>

 

4.3. 注解

当<typeAliases>下有多个package节点,且有类名相同的,使用注解进行区分,来避免别名重名导致扫描失败的问题。(只用注解是不行的,必须配合2.中<package name="pojo"/>进行扫描)

@Alias("student")
public class Student {}

 

5. typeHandler类型转换器

typeHandler分为jdbcType和javaType,其中jdbcType用于定义数据库类型,javaType用于定义Java类型,而typeHandler就是用来处理jdbcType与javaType之间的转换的。一般情况下不需要配置typeHandler,因为MyBatis会探测使用什么类型的typeHandler进行处理。对于特定场景可以进行自定义typeHandler,比如我们有特殊的转换规则,枚举类就是这样。

5.1 EnumOrdinalTypeHandler

ordinal是序数的意思,EnumOrdinalTypeHandler是按MyBatis根据枚举数组下标索引的方式进行匹配的。它要求数据库返回一个数作为枚举数组的下标,如果返回值错误会有IllegalArgumentException。

EnumOrdinalTypeHandler处理枚举类型需进行一下配置。

    ①创建枚举类型

160252_YDme_3488884.png

    ②在对应Mapper.xml中建立表的映射resultMap

160433_4ux1_3488884.png

    ③在查询语句中加入resultMap

160741_thpi_3488884.png

 

5.2 EnumTypeHandler

EnumTypeHandle与EnumOrdinalTypeHandler不同的是,是枚举类型的默认转换类,EnumOrdinalTypeHandler是把数据库返回的数字作为枚举数组的序数,而EnumTypeHandler是通过数据库返回的字符串“MALE”进行Enum.valueOf(SexEnum.class,"MALE");进行转换,得到对应的对象。

使用时配置typeHandler属性即可

162110_dKZp_3488884.png

 

5.3 自定义枚举typeHandler

    用于处理枚举类型和数据库的INTEGER类型。用法与上述步骤相同。

package handler;

@MappedTypes(SexEnum.class)
@MappedJdbcTypes(JdbcType.INTEGER)
public class SexEnumTypeHandler implements TypeHandler<SexEnum> {


  @Override
  public void setParameter(PreparedStatement preparedStatement, int columnName, SexEnum sexEnum,
      JdbcType jdbcType) throws SQLException {
    preparedStatement.setInt(columnName, sexEnum.getId());
  }

  @Override
  public SexEnum getResult(ResultSet resultSet, String columnName) throws SQLException {
    int id = resultSet.getInt(columnName);
    return SexEnum.getSexById(id);
  }

  @Override
  public SexEnum getResult(ResultSet resultSet, int i) throws SQLException {
    int id = resultSet.getInt(i);
    return SexEnum.getSexById(id);
  }

  @Override
  public SexEnum getResult(CallableStatement callableStatement, int i) throws SQLException {
    int id = callableStatement.getInt(i);
    return SexEnum.getSexById(id);
  }
}

 

5.4 文件操作

MyBatis提供了一个typeHandler("org.apache.ibatis.type.BlobTypeHandler"),对数据库的Blob(binary large object)字段进行支持。用于储存文byte[ ]类型的数据。也是MyBatis的默认typeHandler类型。

如果一次性的将大量的数据加载中JVM会给服务器带来巨大压力,所以更多的时候应该使用文件流形式。只要把POJO属性byte[ ]类型换为InputStream即可。如果没有typeHandler声明,系统会默认使用BlobInputStreamTypeHandler转换结果。因为性能不佳,所以大型互联网的网站上不常用文件操作。这也是搭建高效服务器应注意的地方。

5.5 遗留问题

在学习这里的时候遇到点问题,我自定义了一个处理string类型的类型转换器MyTypeHandler

112842_UZ3o_3488884.png

最初表结构为[ID(int),NAME(varchar)],也就是说表中只有一个匹配MyTypeHandler的字段NAME。

查询一条结果时打印日志:

114434_2DLN_3488884.png

查询三条结果时打印日志:

114514_VSox_3488884.png

修改表结构为[ID(int),NAME(varchar),LOC(varchar)],也就是说表中只有两个匹配MyTypeHandler的字段NAME。

查询一条结果时打印日志:

114103_hvcE_3488884.png

查询三条结果时打印日志:

114001_bkDd_3488884.png

这时问题就出现了,当类型转换需要处理一个字段时,调用getResult方法次数为:每个返回结果有几个需要处理的字段就调用几次getResult方法并打印日志(结果数*需要处理的字段),而表中需要处理两个字段时调用getResult方法次数为:(结果数*需要处理的字段)*2,不理解这是为什么,等深入学习之后再来解答。

 

5.6 ObjectFactory(对象工厂)

  ObjectFactory对象工厂用来创建结果集的实例。MyBatis默认使用DefaultObjectFactory来(org.apache.ibatis.reflection.factory.DefaultObjectFactory)来完成对应的工作。MyBatis允许注册自定义的ObjectFactor如果自定义要实现接口ObjectFactory,并给予配置。在大部分情况下,我们都不需要自定义返回规则,因为这些比骄傲复杂,且容易出错,更多的情况下我们都会选择继承系统实现好的DefaultObjectFactory,并进行一定的改写。如下:

public class MyObjectFactory extends DefaultObjectFactory {

  private static final long serialVersionUID = -8855120656740914948L;

  private Object temp = null;
  Logger logger = Logger.getLogger(MyObjectFactory.class);

  @Override
  public <T> T create(Class<T> type) {
    T result = super.create(type);
    logger.info("创建对象: " + result.toString());
    logger.info("是否和上次创建的是同一个对象:【" + (temp == result) + "】");
    return result;
  }


  @Override
  public <T> T create(Class<T> type, List<Class<?>> constructorArgTypes,
      List<Object> constructorArgs) {
    T result = super.create(type, constructorArgTypes, constructorArgs);
    logger.info("创建对象: " + result.toString());
    temp = result;
    return result;
  }

  @Override
  public void setProperties(Properties properties) {
    super.setProperties(properties);
    Iterator iterator = properties.entrySet().iterator();
    while (iterator.hasNext()) {
      Entry<String, String> entry = (Entry<String, String>) iterator.next();
      System.out.println(entry);
    }
    logger.info("初始化参数:【" + properties.toString() + "】");
  }

  @Override
  public <T> boolean isCollection(Class<T> type) {
    return super.isCollection(type);
  }
}

在mybatis-config.xml中的配置,property在MyObjectFactory类中的setProperties方法读取。

<objectFactory type="factory.MyObjectFactory">
    <property name="prop1" value="value1"/>
</objectFactory>

 

5.7 plugins(插件)

MyBatis中最灵活的组建,很强大,但很危险。使用插件前一定清楚掌握MyBatis底层。

 

5.8 environments(运行环境)

在MyBatis中environments主要用来配置数据库环境,可以配置多个,但一般配置一个就可以了,其下有两个子元素transactionManager(事务管理器)和dataSource(数据源)。在实际工作中一般使用Spring对数据源和数据库的事务进行管理。

  <environments default="development">
    <environment id="development">
      <transactionManager type="JDBC"/>
      <dataSource type="POOLED">
        <property name="driver" value="${db.driver}"/>
        <property name="url" value="${db.url}"/>
        <property name="username" value="${db.username}"/>
        <property name="password" value="${db.password}"/>
      </dataSource>
    </environment>
  </environments>

5.8.1 transactionManager(事务管理器)

TransactionManager提供了两个实现类,JdbcTransaction和ManagedTransaction它们都实现了Transaction接口。

public interface Transaction {

 
  Connection getConnection() throws SQLException;

  void commit() throws SQLException;

  void rollback() throws SQLException;

  void close() throws SQLException;

  Integer getTimeout() throws SQLException;
}

从方法可知它的主要工作就是提交(commit)、回滚(rollback)、关闭(close)。

JdbcTransaction和ManagedTransaction对应着两个实现类JdbcTransactionFactory和ManagedTransactionFactory。配置于是事务管理器配置成一下两种方式。

<transactionManager type="JDBC"/>
<transactionManager type="MANAGED"/>

JDBC、MANAGED不区分大小写。

自定义事务类

package transaction;

import java.sql.Connection;
import java.sql.SQLException;
import javax.sql.DataSource;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.jdbc.JdbcTransaction;
import org.apache.log4j.Logger;


public class MyTransaction extends JdbcTransaction {

  Logger logger = Logger.getLogger(MyTransaction.class);

  public MyTransaction(DataSource ds,
      TransactionIsolationLevel desiredLevel, boolean desiredAutoCommit) {
    super(ds, desiredLevel, desiredAutoCommit);
    logger.info("MyTransaction init");
  }

  public MyTransaction(Connection connection) {
    super(connection);
    logger.info("MyTransaction init");
  }

  @Override
  public Connection getConnection() throws SQLException {
    logger.info("#####getConnection");
    return super.getConnection();
  }

  @Override
  public void commit() throws SQLException {
    super.commit();
    logger.info("#####commit");
  }

  @Override
  public void rollback() throws SQLException {
    super.rollback();
    logger.info("#####rollback");
  }

  @Override
  public void close() throws SQLException {
    super.close();
    logger.info("#####close");
  }

  @Override
  public Integer getTimeout() throws SQLException {
    logger.info("#####timeout");
    return super.getTimeout();
  }

}

自定义事务工厂

package factory;

import java.sql.Connection;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.ibatis.session.TransactionIsolationLevel;
import org.apache.ibatis.transaction.Transaction;
import org.apache.ibatis.transaction.TransactionFactory;
import transaction.MyTransaction;

public class MyTransactionFactory implements TransactionFactory {

  @Override
  public void setProperties(Properties props) {}

  @Override
  public Transaction newTransaction(Connection conn) {
    return new MyTransaction(conn);
  }

  @Override
  public Transaction newTransaction(
      DataSource dataSource, TransactionIsolationLevel level, boolean autoCommit) {
    return new MyTransaction(dataSource, level, autoCommit);
  }
}

5.8.2 dataSource(数据源)

dataSource主要作用是配置数据库,在MyBatis中数据库通过PooledDataSourceFactory、UnpooledDataSourceFactory、JndiDataSourceFactory三个工厂类来提供。对应产生PooledDataSource、UnpooledDataSource类对象,而JndiDataSourceFactory则会根据JNDI信息拿到外部容器实现的数据库连接对象。无论如何这三个类最后的产品都是实现了DataSource接口的数据库连接对象。

三种数据源的配置形式

<dataSource type="UNPOOLED"/>
<dataSource type="POOLED"/>
<dataSource type="JNDI"/>

 

      非数据库连接池的管理方式,每次请求都会打开一个引得数据库连接,所以创建会比较慢,在对性能要求不高的场合可以使用。对于有些数据库来说数据库连接池并不重要,那么它也是一个理想的选择。

      UNPOOLED类型数据源配置属性:

  • driver 数据库驱动名。比如MySQL的com.mysql.jdbc.Driver
  • url 连接数据库的URL
  • username 用户名
  • password 密码
  • defaultTransactionIsolationLevel 连接事务隔离级别

       POOLED类型数据源配置属性:

  • poolMaximumActiveConnections 最大活动连接数
  • poolMaximumIdleConnections 最大空闲连接数
  • poolMaximumCheckoutTime 最大自我检查的时间间隔,默认20秒
  • poolTimeToWait 连接池获取一个连接等待的时间,如果超出时间,则打印日志,并重新尝试获取连接,默认20秒
  • poolPIngQuery 发送到数据库的侦测查询,用来检测连接是否处在正常的工作秩序中,并准备接受默认请求。默认是“NO PING QUERY SET”,这会导致多数数据库驱动失败时带有一个恰当的错误信息
  • poolPingEnable 是否开启侦测查询。若开启,也必须使用一个可执行的SQL语句来设置poolPingQuery属性(最好是一个非常快的SQL), 默认为false。
  • poolPingConnectionsNotUseFor 为配置poolPingQuery的使用频度。这可以被设置成匹配具体的书v库连接超时时间,来避免不必要的检测,默认值为0(即所有连接每一时刻都被侦测——仅当poolPingEnabled为true时适用)。

       JNDI(不太理解,所以暂且不进行记录)

       自定义数据源工厂

       MyBatis也支持第三方数据源,例如使用DBCP数据源,那么需要提供一个自定义的DataSourceFactory

package factory;

import java.util.Properties;
import javax.sql.DataSource;
import org.apache.commons.dbcp2.BasicDataSourceFactory;
import org.apache.ibatis.datasource.DataSourceFactory;

public class DbcpDataSourceFactory implements DataSourceFactory {

  private Properties properties;

  @Override
  public void setProperties(Properties props) {
    properties = props;
  }

  @Override
  public DataSource getDataSource() {
    DataSource dataSource = null;
    try {
      dataSource = BasicDataSourceFactory.createDataSource(properties);
    } catch (Exception e) {
      e.printStackTrace();
    }
    return dataSource;
  }
}
 <dataSource type="factory.DbcpDataSourceFactory">
   <property name="driver" value="${db.driver}"/>
   <property name="url" value="${db.url}"/>
   <property name="username" value="${db.username}"/>
   <property name="password" value="${db.password}"/>
 </dataSource>

这样MyBatis就会采用配置的数据源工厂来生成数据源了。

 

5.9 databaseIdProvider 数据库厂商标识

        databaseIdProvider用来支持多种不同厂商的数据库,这个元素不常用,但是有些软件公司需要给不同的用户提供系统,使用何种数据库往往是由客户决定的。MyBatis移植性不如Hibernate,所以用databaseIdProvider来补充。

5.9.1 使用系统默认databaseIdProvider

<databaseIdProvider type="DB_VENDOR">
  <property name="MySQL" value="mysql"/>
  <property name="Oracle" value="oracle"/>
</databaseIdProvider>

property元素的属性name时数据库的名称,如果不确定如何填写,那么可以使用JDBC创建其数据库连接对象Connection,然后通过下面代码获取:

connection.getMetaData().getDatabaseProductName();

属性value是name的一个别名,再MyBatis中可以通过这个别名标识一条SQL适用于哪种数据库运行。然后,改造映射器的SQL,如下:

  <select id="getFile" parameterType="int" resultMap="fileMapper" databaseId="mysql">
    SELECT ID,CONTENT FROM file WHERE ID=#{id}
  </select>

databaseId标识了这条语句使用于哪种数据库。系统会优先匹配有正确数据库标识的SQL语句,如下:

213406_igkq_3488884.png

213319_ggjC_3488884.png

如果匹配不到正确的SQL语句,则会由BindingException。

 

5.9.2 不使用系统规则

5.9.1中使用了MyBatis的默认规则,MyBatis也可以自定义规则,只是必须实现MyBatis提供的接口DatabaseIdProvider。

自定义DatabaseIdProvider

package provider;

import java.sql.Connection;
import java.sql.SQLException;
import java.util.Properties;
import javax.sql.DataSource;
import org.apache.ibatis.mapping.DatabaseIdProvider;
import org.apache.log4j.Logger;

public class MyDatabaseIdProvider implements DatabaseIdProvider {

  private static final String DATABASE_TYPE_DB2 = "DB2";
  private static final String DATABASE_TYPE_MYSQL = "MySQL";
  private static final String DATABASE_TYPE_ORACLE = "Oracle";

  Logger logger = Logger.getLogger(MyDatabaseIdProvider.class);

  @Override
  public void setProperties(Properties p) {
    logger.info(p);
  }

  @Override
  public String getDatabaseId(DataSource dataSource) throws SQLException {
    Connection connection = dataSource.getConnection();
    String dbProductname = connection.getMetaData().getDatabaseProductName();
    if (MyDatabaseIdProvider.DATABASE_TYPE_DB2.equals(dbProductname)) {
      return "db2";
    } else if (MyDatabaseIdProvider.DATABASE_TYPE_MYSQL.equals(dbProductname)) {
      return "mysql";
    } else if (MyDatabaseIdProvider.DATABASE_TYPE_ORACLE.equals(dbProductname)) {
      return "oracle";
    } else {
      return null;
    }
  }
}

进行配置

 <databaseIdProvider type="provider.MyDatabaseIdProvider">
    <property name="msg" value="自定义DatabaseIdProvider"/>
 </databaseIdProvider>

正常运行,setProperties读取了配置,然后依据规则找到对应的SQL语句。

215329_HsaH_3488884.png

5.10  引入映射器的方法

映射器是MyBatis最复杂、最核心的组件,暂且只讨论如何引入映射器。

映射器定义命名空间(namespace)对应的是一个接口的全限定名,而不是实现类。

首先,定义接口:

package mapper;

import pojo.TestFile;

public interface FileMapper {

  TestFile getFile(int id);

  int insertFile(TestFile file);
}

其次,给出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="mapper.FileMapper">
  <resultMap id="fileMapper" type="file">
    <result property="id" column="id"/>
    <result property="content" column="content" typeHandler="org.apache.ibatis.type.BlobTypeHandler"/>
  </resultMap>
  <select id="getFile" parameterType="int" resultMap="fileMapper" databaseId="mysql">
    SELECT ID,CONTENT FROM file WHERE ID=#{id} AND 2=2
  </select>
  <select id="getFile" parameterType="int" resultMap="fileMapper">
    SELECT ID,CONTENT FROM file WHERE ID=#{id} AND 1=1
  </select>
  <insert id="insertFile" parameterType="file">
    INSERT INTO file(ID,CONTENT) VALUES (#{id},#{content})
  </insert>
</mapper>

然后引入映射器:

用文件路径引入映射器

  <mappers>
    <mapper resource="mapper/FileMapper.xml"/>
    <mapper resource="mapper/StudentMapper.xml"/>
  </mappers>

用全限定名引入映射器

 <mappers>
    <mapper class="mapper.StudentMapper"/>
    <mapper class="mapper.FileMapper"/>
 </mappers>

用包名引入映射器

  <mappers>
    <package name="mapper"/>
  </mappers>

用userMapper.xml引入映射器

  <mappers>
    <mapper url="file:///Users/86429/IdeaProjects/实例/src/main/resources/mapper/FileMapper.xml"/>
    <mapper url="file:C:///Users/86429/IdeaProjects/实例/src/main/resources/mapper/StudentMapper.xml"/>
  </mappers>

 

 

 

 

/----------------------------------------------

持续更新ing...

 

 

 

 

 

转载于:https://my.oschina.net/codelx/blog/1587607

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值