mybatis 基础运用篇: 全局配置文件 和 代理开发方式

210 阅读7分钟

一、全局配置文件

全局配置文件必须遵循以下顺序,否则会报错。

  1. properties(属性)

  2. settings (全局配置参数)

  3. typeAliases (类型别名)

  4. typeHandlers (类型处理器) --Java类型--JDBC类型--->数据库类型转换

  5. objectFactory (对象工厂)

  6. plugins (插件)--可以在Mybatis执行SQL语句的流程中,横叉一脚去实现一些功能增强,比如

PageHelper分页插件,就是第三方实现的一个插件

  1. environments (环境集合对象)

​ environment(环境子属性对象)

​ transactionManager(事务管理)

​ dataSource(数据源)

  1. mappers(映射器)

1. properties(属性)

properties 属性是用来给系统配置一些参数,可以放到xml文件或properties文件中,这样做的好处是方便管理和修改,也不会引起代码的重新编译。也可以通过Resource对应读取properties文件,通过Properties 类对象的 load 方法载入配置文件到内存。

(1)在 classpath 下定义 db.properties 文件。

# mysql8及其以后版本的驱动
db.driver=com.mysql.cj.jdbc.Driver
# mysql8 以前的驱动,这是在pom.xml中引用的
#db.driver = com.mysql.jdbc.Driver
db.url=jdbc:mysql://127.0.0.1:3306/tedrain?useUnicode=true&characterEncoding=utf-8&serverTimezone=UTC&useSSL=false
db.username=root
db.password=1qaz@WSX

(2)方式1: 在 SqlMapConfig.xml 文件中使用 标签引用属性文件db.properties

<!-- 加载类路径下的属性文件 -->
<properties resource="phase02/db.properties"></properties>

<!-- 设置一个默认的连接环境信息 -->
<environments default="development">
    <!-- 连接环境信息,取一个任意唯一的名字 -->
    <environment id="development">
        <!-- mybatis使用jdbc事务管理方式 -->
        <transactionManager type="JDBC"/>
        <!-- mybatis使用连接池方式来获取连接 -->
        <dataSource type="POOLED">
            <!-- 配置与数据库交互的4个必要属性 -->
            <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>

方式2: properties 标签除了可以使用resource属性,引用properties文件中的属性,还可以在properties标签内定义property子标签来定义属性和值。

<properties> 
<property name="driver" value="com.mysql.jdbc.Driver"/> 
</properties> 

注意:mybatis 将按照以下顺序来加载属性(先读取的同名属性会被后读取的覆盖):

​ (1)读取properties 元素体内定义的属性。

​ (2)读取properties元素中resource或url加载的属性,它会覆盖已读取的同名属性。

方式3: 通过代码的方式加载配置文件。

try {
        InputStream inputStream = Resources.getResourceAsStream("phase02/db.properties");
        Properties properties = new Properties();
        properties.load(inputStream);
        String dbDriver = properties.getProperty("db.driver");
        System.out.println(dbDriver);
        System.out.println(decode(encode(dbDriver)));
        // properties.put("server",decode(dbDriver));
	} catch (IOException e) {
    	e.printStackTrace();
}

private static String encode(String src) {
        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(src.getBytes());
    }

    private static String decode(String des) {
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            return new String(decoder.decodeBuffer(des));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

2. settings(全局配置参数)

settings 标签是 mybatis 中极为重要调整设置,它会改变 mybatis 的运行时行为。比如:自动映射、驼峰命名映射、级联规则,是否开启缓存,执行器类型等。

具体以官方文档为准: mybatis.org/mybatis-3/z…

<!-- settings是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 -->
	<settings>
		<!-- 该配置影响的所有映射器中配置的缓存的全局开关。默认值true -->
	  <setting name="cacheEnabled" value="true"/>
	  <!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。默认值false  -->
	  <setting name="lazyLoadingEnabled" value="true"/>
	  	<!-- 是否允许单一语句返回多结果集(需要兼容驱动)。 默认值true -->
	  <setting name="multipleResultSetsEnabled" value="true"/>
	  <!-- 使用列标签代替列名。不同的驱动在这方面会有不同的表现, 具体可参考相关驱动文档或通过测试这两种不同的模式来观察所用驱动的结果。默认值true -->
	  <setting name="useColumnLabel" value="true"/>
	  <!-- 允许 JDBC 支持自动生成主键,需要驱动兼容。 如果设置为 true 则这个设置强制使用自动生成主键,尽管一些驱动不能兼容但仍可正常工作(比如 Derby)。 默认值false  -->
	  <setting name="useGeneratedKeys" value="false"/>
	 <!--  指定 MyBatis 应如何自动映射列到字段或属性。 NONE 表示取消自动映射;PARTIAL 只会自动映射没有定义嵌套结果集映射的结果集。 FULL 会自动映射任意复杂的结果集(无论是否嵌套)。 --> 
	 <!-- 默认值PARTIAL -->
	  <setting name="autoMappingBehavior" value="PARTIAL"/>
	  
	  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
	 <!--  配置默认的执行器。SIMPLE 就是普通的执行器;REUSE 执行器会重用预处理语句(prepared statements); BATCH 执行器将重用语句并执行批量更新。默认SIMPLE  -->
	  <setting name="defaultExecutorType" value="SIMPLE"/>
	  <!-- 设置超时时间,它决定驱动等待数据库响应的秒数。 -->
	  <setting name="defaultStatementTimeout" value="25"/>
	  
	  <setting name="defaultFetchSize" value="100"/>
	  <!-- 允许在嵌套语句中使用分页(RowBounds)默认值False -->
	  <setting name="safeRowBoundsEnabled" value="false"/>
	  <!-- 是否开启自动驼峰命名规则(camel case)映射,即从经典数据库列名 A_COLUMN 到经典 Java 属性名 aColumn 的类似映射。  默认false -->
	  <setting name="mapUnderscoreToCamelCase" value="false"/>
	  <!-- MyBatis 利用本地缓存机制(Local Cache)防止循环引用(circular references)和加速重复嵌套查询。
	  		 默认值为 SESSION,这种情况下会缓存一个会话中执行的所有查询。
	   		若设置值为 STATEMENT,本地会话仅用在语句执行上,对相同 SqlSession 的不同调用将不会共享数据。  -->
	  <setting name="localCacheScope" value="SESSION"/>
	  <!-- 当没有为参数提供特定的 JDBC 类型时,为空值指定 JDBC 类型。 某些驱动需要指定列的 JDBC 类型,多数情况直接用一般类型即可,比如 NULL、VARCHAR 或 OTHER。  -->
	  <setting name="jdbcTypeForNull" value="OTHER"/>
	<!--   指定哪个对象的方法触发一次延迟加载。  -->
	  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
	</settings>

3. typeAliases(类型别名)

别名的作用: 简化映射文件中 parameterType 和 resultType 中的 POJO 类型名称(全限定类名)编写。

默认支持别名参考

mybatis.org/mybatis-3/z…

自定义别名

  <!--typeAliases 顺序很重要,不然会报错-->
    <typeAliases>
        <!--单个别名定义-->
        <typeAlias type="user" alias="com.tedrain.mybatis.phase01.po.User"/>
        <!--批量别名定义,扫描整个包下的类,别名为类名(首字母大写或小写都可以)-->
        <package name="com.tedrain.mybatis.phase01"/>
    </typeAliases>

4. typeHandlers(类型处理器)

mybatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时,会用类型处理器将取到的值转换为Java类型。

参考链接:mybatis.org/mybatis-3/z…

5. objectFactory(对象工厂)

每次 MyBatis 创建结果对象的新实例时,它都会使用一个对象工厂(ObjectFactory)实例来完成实例化工作。 默认的对象工厂需要做的仅仅是实例化目标类,要么通过默认无参构造方法,要么通过存在的参数映射来调用带有参数的构造方法。 如果想覆盖对象工厂的默认行为,可以通过创建自己的对象工厂来实现。

参考链接:mybatis.org/mybatis-3/z…

6. plugins(插件)

MyBatis 允许你在映射语句执行过程中的某一点进行拦截调用。

参考链接:mybatis.org/mybatis-3/z…

7. environments(环境配置)

参考链接:mybatis.org/mybatis-3/z…

mybatis 可以配置连接多种环境,多个数据库,如果不指定,使用默认环境配置, 默认环境一定要指定一个环境Id

注意: 尽管配置了多个环境,但每个SqlSessionFactory 实例只能选择一种环境,每个数据库对应一个 SqlSessionFactory 实例。

<environments default="development">
        <!-- 连接环境信息,取一个任意唯一的名字 -->
        <environment id="development">
            <!-- mybatis使用jdbc事务管理方式 -->
            <transactionManager type="JDBC"/>
            <!-- mybatis使用连接池方式来获取连接 -->
            <dataSource type="POOLED">
                <!-- 配置与数据库交互的4个必要属性 -->
                <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>

8. mappers(映射器)

映射器的作用就是告诉 mybatis 到哪里去找映射文件,可以使用相对类路径的资源引用,完全限定资源定位、类名或包名。

(1)使用相对类路径的资源引用

 <mapper resource="phase01/UserMapper.xml"/>

(2)使用完全限定资源定位(URL)

 <mapper url="file://E:/Other/mybatisDemo/src/main/resources/phase01/UserMapper.xml"/>

(3)使用映射器接口实现类的完全限定类名 此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中。

 <mapper class="com.tedrain.mybatis.phase03.dao.AnnotationUserMapper" />

(4)将包内映射器接口实现全部注册为映射器 此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个目录中

 <package name="com.tedrain.mybatis.phase03.dao"/>

二、输入映射和输出映射

1. parameterType

传递简单类型

如果parameterType为简单类型(基本类型+String类),#{}中的参数名称可以任意

传递 pojo 对象

如果parameterType为POJO类型,#{}中的参数名称必须和POJO中的属性名称一致

传递 pojo 包装对象

如果parameterType为POJO包装类型对象,则使用 .操作符来取内部属性

2. resultType

resultType属性可以映射的java类型有:简单类型、POJO类型、Map类型

3. resultMap

如果sql查询列名和pojo的属性名可以不一致,通过resultMap将列名和属性名作一个对应关系,最终将

查询结果映射到指定的pojo对象中。

注意:resultType底层也是通过resultMap完成映射的

三、 Mapper 代理开发方式

代理理解

代理分为静态代理和动态代理,mybatis 使用的代理方式是动态代理;

动态代理分为两种方式:

  • 基于JDK的动态代理 -- 针对有接口类进行动态代理。

  • 基于CGLIB的动态代理 -- 通过子类继承父类的方式进行代理,延迟加载使用的代理方式。

1. XML方式

只需要开发Mapper接口和Mapper映射文件,不需要编写实现类。

Mapper接口需要遵循的开发规范:

  1. Mapper 接口的类路径 与 Mapper.xml 文件中的 namespace 相同

  2. Mapper 接口方法名称 与 Mapper.xml 中定义的每个Statement 的Id相同。

  3. Mapper 接口方法输入参数类型 和 Mapper.xml 中定义的每个 parameterType 的类型相同。

  4. Mapper 接口方法的返回值类型 和 Mapper.xml 中定义的每个sql 的 resultType 的类型相同。

    ![](mybatis xml 代理开发方式.png)

​ UserMapper.java

package com.tedrain.mybatis.phase02.dao;

import com.tedrain.mybatis.phase02.po.User;

public interface UserMapper {

    User getUserById(int id) throws  Exception;
}

​ 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.tedrain.mybatis.phase02.dao.UserMapper">

	<resultMap id="userMap" type="com.tedrain.mybatis.phase02.po.User">
		<id property="userId" column="id"/>
		<result property="username" column="username" />
		<result property="birthday" column="birthday" />
		<result property="sex" column="sex" />
		<result property="address" column="address" />
	</resultMap>
	<select id="getUserById" parameterType="int" resultMap="userMap">
		Select * from user where id = #{id}
	</select>

<!--    <select id="getUserById" parameterType="int"-->
<!--            resultType="com.tedrain.mybatis.phase02.po.User">-->
<!--		SELECT * FROM user WHERE id = #{id}-->
<!--	</select>-->
</mapper>

测试代码:

package com.tedrain.mybatis;

import com.tedrain.mybatis.phase02.dao.UserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.log4j.PropertyConfigurator;
import org.junit.Before;
import org.junit.Test;
import sun.misc.BASE64Decoder;
import sun.misc.BASE64Encoder;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class userMapperTest {
    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void init() throws IOException {
        initLog();
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(Resources.getResourceAsStream("phase02/SqlMapConfig.xml"));
    }

    @Test
    public void testGetUserById() {
        SqlSession sqlSession = null;
        try {
            // 获取session
            sqlSession = sqlSessionFactory.openSession();
            // 获取mapper接口的代理对象
            UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
            // 调用代理对象方法
            System.out.println(userMapper.getUserById(21));
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (sqlSession != null)
                sqlSession.close();
        }
    }

    @Test
    public void getProperties(){
        try {
            InputStream inputStream = Resources.getResourceAsStream("phase02/db.properties");
            Properties properties = new Properties();
            properties.load(inputStream);
            String dbDriver = properties.getProperty("db.driver");
            System.out.println(dbDriver);
            System.out.println(decode(encode(dbDriver)));
            // properties.put("server",decode(dbDriver));
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static String encode(String src) {
        BASE64Encoder encoder = new BASE64Encoder();
        return encoder.encode(src.getBytes());
    }

    private static String decode(String des) {
        BASE64Decoder decoder = new BASE64Decoder();
        try {
            return new String(decoder.decodeBuffer(des));
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }


    public void initLog() {
        FileInputStream fileInputStream = null;
        try {
            Properties properties = new Properties();
            fileInputStream = new FileInputStream("src/main/resources/phase02/log4j.properties");
            properties.load(fileInputStream);
            PropertyConfigurator.configure(properties);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

}

2. 注解方式

注解的方式是只需要编写 Mapper 接口,通过在方法上标识注解。

package com.tedrain.mybatis.phase03.dao;

import com.tedrain.mybatis.phase03.po.User;
import org.apache.ibatis.annotations.Select;

public interface AnnotationUserMapper {

    @Select("Select * from user where id = #{id}")
    User getUserById(int id) throws Exception;
}

测试:

package com.tedrain.mybatis;

import com.tedrain.mybatis.phase03.dao.AnnotationUserMapper;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.apache.log4j.PropertyConfigurator;
import org.junit.Before;
import org.junit.Test;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class AnnotationUserMapperTest {

    private SqlSessionFactory sqlSessionFactory;

    /*** @Before注解的方法会在@Test注解的方法之前执行 ** @throws Exception */
    @Before
    public void init() throws Exception {
        initLog();
        // 指定全局配置文件路径
        String resource = "phase03/SqlMapConfig.xml";
        // 加载资源文件(全局配置文件和映射文件)
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 还有构建者模式,去创建SqlSessionFactory对象
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void testFindUserById() throws Exception {
        SqlSession sqlSession = sqlSessionFactory.openSession();
        AnnotationUserMapper mapper = sqlSession.getMapper(AnnotationUserMapper.class);
        System.out.println(mapper.getUserById(1));
    }

    public void initLog() {
        FileInputStream fileInputStream = null;
        try {
            Properties properties = new Properties();
            fileInputStream = new FileInputStream("src/main/resources/phase01/log4j.properties");
            properties.load(fileInputStream);
            PropertyConfigurator.configure(properties);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (fileInputStream != null) {
                try {
                    fileInputStream.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }
}