MyBatis第一天的笔记

MyBatis第一天的笔记,第1张

MyBatis第一天
  • MyBatis第一天
  • 1. 简介
    • 1.1 什么是 MyBatis
    • 1.2 MyBatis发展历史
    • 1.3 MyBatis的特点
    • 1.4 如何获取
  • 2. 入门
    • 2.1 环境准备
      • 2.1.1 准备一张表
      • 2.2 创建一个工程
      • 2.3 添加依赖
    • 2.2 编写实体类
    • 2.3 编写MyBatis全局配置文件
    • 2.4 编写Dao接口类
    • 2.5 编写MyBatis映射文件
    • 2.6 编写测试
  • 3. 源码分析
    • 3.1 SqlSessionFactory
    • 3.2 SqlSession
  • 4. 实现CRUD
    • 4.1 在接口中添加方法
    • 4.2 修改映射文件:
    • 4.3 编写测试
      • 4.3.1 插入
      • 4.3.2 更新
      • 4.3.3 根据ID查询
      • 4.3.4 多条件查询
      • 4.3.5 删除
  • 5. 配置文件
    • 5.1 properties
    • 5.2 settings
    • 5.3 typeAliases
      • 5.3.1 typeAlias
      • 5.3.2 package
      • 5.3.3 @Alias
    • 5.4 typeHandlers
      • 5.4.1 自定义类型转换器
      • 5.4.2 配置自定义类型转换器
    • 5.5 environments
      • 5.5.1 transactionManager
      • 5.5.2 dataSource
    • 5.6 databaseIdProvider
      • 5.6.1 配置
      • 5.6.2 使用
    • 5.7 mappers
      • 5.7.1 逐个注册
      • 5.7.2 接口名注册
      • 5.7.3 基于注解配置
      • 5.7.4 批量注册

MyBatis第一天 1. 简介

MyBatis的官网是:https://mybatis.org/mybatis-3/zh/index.html

目前 MyBatis的版本为 3.5.9

1.1 什么是 MyBatis

MyBatis 是一款优秀的持久层框架,它支持自定义 SQL、存储过程以及高级映射。MyBatis 免除了几乎所有的 JDBC 代码以及设置参数和获取结果集的工作。MyBatis 可以通过简单的 XML 或注解来配置和映射原始类型、接口和 Java POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。

简单的说:MyBatis简化了我们对 JDBC 的 *** 作。

1.2 MyBatis发展历史

MyBatis源自于 Apache 的一个开源项目 iBatis,2010年6月时这个项目由 Apache Software Foundation 迁移到了 Google Code,这些开发团队也就跟着迁移到了 Google Code 旗下。iBatis 就在 3.x 时正式更名为 MyBatis,并且把代码托管到了 GitHub。

iBatis 这名称来源于 “internet” 和 “abatis” 的组合,是一个基于 Java 的特久层框架。

1.3 MyBatis的特点

1.MyBatis是一个半自动化的持久层框架

2.开发人员可以自己编写SQL语句来实现优化

3.在MyBatis中,SQL和Java代码是分开,边界清晰

1.4 如何获取

在浏览器地址栏中输入MyBatis 的官网地址(https://github.com/mybaits/mybatis-3/releases),然后就可以下载。

或者使用 Maven 的坐标来进行项目的依赖添加。

2. 入门 2.1 环境准备 2.1.1 准备一张表
create database if not exists mydb;
use mydb;
create table t_department
(
    did         int auto_increment
        primary key,
    dname       varchar(100) not null,
    description varchar(200) null,
    manager_id  int          null
);

2.2 创建一个工程

在 IDEA 中,我们还在 JavaWeb 这个空工程下创建的 mybatis-one 模块。

2.3 添加依赖

在项目的 pom.xml 文件中添加如下依赖。

        
        <dependency>
            <groupId>org.mybatisgroupId>
            <artifactId>mybatisartifactId>
            <version>3.5.9version>
        dependency>
        
        <dependency>
            <groupId>mysqlgroupId>
            <artifactId>mysql-connector-javaartifactId>
            <version>5.1.49version>
        dependency>

另外还可以把 lombok 添加到 pom.xml 文件中

			
        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.22version>
            <scope>providedscope>
        dependency>

还可以添加一个日志依赖到项目中,如 logback

        
        <dependency>
            <groupId>ch.qos.logbackgroupId>
            <artifactId>logback-classicartifactId>
            <version>1.2.10version>
        dependency>

还需要在项目的资源目录下创建 logback 的配置文件。

logback.xml


<configuration scan="true" scanPeriod="60 second" debug="false">
    
    <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %thread %-5level %logger{0} %msg%nPattern>
            <charset>UTF-8charset>
        encoder>
    appender>
    
    <logger name="java.sql">
        <level value="debug" />
    logger>
    <logger name="org.apache.ibatis">
        <level value="info" />
    logger>
    
    <root level="DEBUG">
        <appender-ref ref="Console" />
    root>
configuration>

完整内容:


<configuration scan="true" scanPeriod="60 second" debug="false">
    <property name="LOG_FILE_PATH" value="${catalina.base}/logs" />
    
    <appender name="Console" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %thread %-5level %logger{0} %msg%nPattern>
            <charset>UTF-8charset>
        encoder>
    appender>
    
    <appender name="RollingFile" class="ch.qos.logback.core.rolling.RollingFileAppender">
        <file>${LOG_FILE_PATH}/mybatis.logfile>
        
        <filter class="ch.qos.logback.classic.filter.ThresholdFilter">
            <level>INFOlevel>
        filter>
        <encoder>
            <Pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} %thread %-5level %logger{0} %msg%nPattern>
            <charset>UTF-8charset>
        encoder>
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            
            <fileNamePattern>mybatis.%d{yyyy-MM-dd}.logfileNamePattern>
            <maxHistory>30maxHistory>
            <TimeBasedFileNamingAndTriggeringPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
                <maxFileSize>100MBmaxFileSize>
            TimeBasedFileNamingAndTriggeringPolicy>
        rollingPolicy>
    appender>
    
    <logger name="java.sql">
        <level value="debug" />
    logger>
    <logger name="org.apache.ibatis">
        <level value="info" />
    logger>
    
    <root level="DEBUG">
        <appender-ref ref="Console" />
        <appender-ref ref="RollingFile" />
    root>
configuration>
2.2 编写实体类
@Data
@AllArgsConstructor
@NoArgsConstructor
@Accessors(chain = true)
public class Department {
    private Integer did;
    private String dname;
    private String description;
}
2.3 编写MyBatis全局配置文件

在项目的资源目录(resources)中创建 mybatis-config.xml 文件


DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">

<configuration>
    
    <environments default="development">
        
        <environment id="development">
            
            <transactionManager type="JDBC"/>
            
            <dataSource type="POOLED">
                <property name="driver" value="com.mysql.jdbc.Driver"/>
                <property name="url" value="jdbc:mysql://192.168.72.71:3306/mydb?useSSL=false&characterEncoding=utf8"/>
                <property name="username" value="root"/>
                <property name="password" value="123456"/>
            dataSource>
        environment>
    environments>
    
configuration>
2.4 编写Dao接口类
package com.xianopeng.mapper;

import com.xianopeng.entity.Department;

import java.util.List;

/**
 * Dao接口类
 */
public interface DepartmentMapper {
    // 查询全部
    List<Department> selectDepartments();
}

在 MyBatis 中,一般定义 Dao 层为 mapper 层,而 XxxDao 定义为 XxxMapper。它们都是表示 Dao 层,只是命名不同而已。

2.5 编写MyBatis映射文件

在 MyBatis 中,通过映射文件来实现在 Jdbc 中的接口实现类的功能。

我们还是把这个文件放到资源目录下。文件的结构还是从官网上去取。一般定义为接口的名称.xml。


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xianopeng.mapper.DepartmentMapper">
    <select id="selectDepartments" resultType="com.xianopeng.entity.Department">
        select * from t_department
    select>
mapper>

配置说明:

  • mapper:映射配置文件的根节点
    • namespace:这个属性用于指定接口类所在的完整路径
  • select:它是用于定义查询的标签,对应查询方法
    • id:它用于与接口中的某个方法名称一致
    • resultType:查询后的返回结果类型,需要写全限定名
2.6 编写测试

1)把映射文件注册到全局配置文件中

    <mappers>
        <mapper resource="DepartmentMapper.xml" />
    mappers>

在 mybatis-config.xml 文件中添加如上的配置,让映射文件注册到全局配置文件中,mappers 标签与 environments 标签平级。

2)编写测试类

package com.xianopeng.test;

import com.xianopeng.entity.Department;
import com.xianopeng.mapper.DepartmentMapper;
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 java.io.InputStream;
import java.util.List;

/**
 * 测试类
 */
public class MytatisTest {
    public static void main(String[] args) throws Exception {
        // 1. 创建 SqlSessionFactory
        String resource = "mybatis-config.xml";
        InputStream inputStream = Resources.getResourceAsStream(resource);
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);

        // 2. 创建 SqlSession 对象
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 3. 创建 Mapper 对象
        DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class);

        // 4. 执行方法
        List<Department> departments = mapper.selectDepartments();

        // 5. 处理结果
        departments.forEach((department) -> {
            System.out.println(department.getDid() + "\t" + department.getDname());
        });

        // 6. 关闭资源
        sqlSession.close();

    }
}

至此,我们已经成功的使用 MyBatis。

3. 源码分析

在MyBatis中主要的类有以下几个:

  • Configuation:将 MyBatis 配置文件的信息保存在这个类中
  • SqlSessionFactory:解析 Configuration类中的配置信息,获取SqlSession对象
  • SqlSession:负责和数据库进行交互,完成CRUD *** 作
  • Executor:是MyBatis的高度核心,负责SQL的生成
  • StatementHandler:封装了 JDBC 的 statement *** 作
  • ParameterHanlder:负责完成 JavaType 到 jdbcType 的转换
  • ResultSetHandler:负责完成结果集到 JavaBean 对象的转换
  • MappedStatement:代表一个 select|insert|update|delete元素
  • SqlSource:根据传入的 ParameterObject 来生成 SQL
  • BundSQL:包含 SQL 和参数信息
3.1 SqlSessionFactory
public interface SqlSessionFactory {
    SqlSession openSession();

    SqlSession openSession(boolean var1);

    SqlSession openSession(Connection var1);

    SqlSession openSession(TransactionIsolationLevel var1);

    SqlSession openSession(ExecutorType var1);

    SqlSession openSession(ExecutorType var1, boolean var2);

    SqlSession openSession(ExecutorType var1, TransactionIsolationLevel var2);

    SqlSession openSession(ExecutorType var1, Connection var2);

    Configuration getConfiguration();
}

在这个接口中定义一系列获取 SqlSession 的方法。

这个接口是通过 SqlSessionFactoryBuilder 类中的 build 方法来构建的。

在这个类中,定义了很多 build 的重载方法,来根据不同的条件来构建 SqlSessionFactory 接口。

我们使用的是如下方法:

    public SqlSessionFactory build(InputStream inputStream) {
        return this.build((InputStream)inputStream, (String)null, (Properties)null);
    }

这个方法会调用下面的方法:

    public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
        SqlSessionFactory var5;
        try {
            XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
            var5 = this.build(parser.parse());
        } catch (Exception var14) {
            throw ExceptionFactory.wrapException("Error building SqlSession.", var14);
        } finally {
            ErrorContext.instance().reset();

            try {
                inputStream.close();
            } catch (IOException var13) {
            }

        }

        return var5;
    }

在这个方法中,我们发现是使用 XMLConfigBuilder 类来读取配置文件的。

//....
    
   public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
        this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
    }

// .............

在这个方法中使用的是 XPathParser 类

//.......
    public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
        this.commonConstructor(validation, variables, entityResolver);
        this.document = this.createDocument(new InputSource(inputStream));
    }
//......

从上面的方法发现它使用了 new InputSource(inputStream) 对象

//........    
public InputSource (InputStream byteStream)
    {
        setByteStream(byteStream);
    }
//...............

    public void setByteStream (InputStream byteStream)
    {
        this.byteStream = byteStream;
    }
//........

这个类中把我们传过来的配置文件的字节码数据赋给 byteStream 成员。

接下来我们来看 XPathParser 类中的 this.createDocument(InputSource source) 方法

    private Document createDocument(InputSource inputSource) {
        try {
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            factory.setFeature("http://javax.xml.XMLConstants/feature/secure-processing", true);
            factory.setValidating(this.validation);
            factory.setNamespaceAware(false);
            factory.setIgnoringComments(true);
            factory.setIgnoringElementContentWhitespace(false);
            factory.setCoalescing(false);
            factory.setExpandEntityReferences(true);
            DocumentBuilder builder = factory.newDocumentBuilder();
            builder.setEntityResolver(this.entityResolver);
            builder.setErrorHandler(new ErrorHandler() {
                public void error(SAXParseException exception) throws SAXException {
                    throw exception;
                }

                public void fatalError(SAXParseException exception) throws SAXException {
                    throw exception;
                }

                public void warning(SAXParseException exception) throws SAXException {
                }
            });
            return builder.parse(inputSource);
        } catch (Exception var4) {
            throw new BuilderException("Error creating document instance.  Cause: " + var4, var4);
        }
    }

在这个方法中,有一个关键的代码:

return builder.parse(inputSource);

这个就去是在 DocumentBuilder.java 文件中的一个内部的抽像方法。

public abstract Document parse(InputSource is)
        throws SAXException, IOException;

它的具体实现在 DocumentBuilderImpl.java类中

    public Document parse(InputSource is) throws SAXException, IOException {
        if (is == null) {
            throw new IllegalArgumentException(
                DOMMessageFormatter.formatMessage(DOMMessageFormatter.DOM_DOMAIN,
                "jaxp-null-input-source", null));
        }
        if (fSchemaValidator != null) {
            if (fSchemaValidationManager != null) {
                fSchemaValidationManager.reset();
                fUnparsedEntityHandler.reset();
            }
            resetSchemaValidator();
        }
        domParser.parse(is);
        Document doc = domParser.getDocument();
        domParser.dropDocumentReferences();
        return doc;
    }

在这个方法中通过 domParser.parse(is); 来解析,实现代码如下:(DOMParser.java)

    public void parse(InputSource inputSource)
        throws SAXException, IOException {

        // parse document
        try {
            XMLInputSource xmlInputSource =
                new XMLInputSource(inputSource.getPublicId(),
                                   inputSource.getSystemId(),
                                   null, false);
            xmlInputSource.setByteStream(inputSource.getByteStream());
            xmlInputSource.setCharacterStream(inputSource.getCharacterStream());
            xmlInputSource.setEncoding(inputSource.getEncoding());
            parse(xmlInputSource);
        }
        //...

核心代码为:parse(xmlInputSource);

    /**
     * parse
     *
     * @param inputSource
     *
     * @exception XNIException
     * @exception java.io.IOException
     */
    public void parse(XMLInputSource inputSource)
        throws XNIException, IOException {
        // null indicates that the parser is called directly, initialize them
        if (securityManager == null) {
            securityManager = new XMLSecurityManager(true);
            fConfiguration.setProperty(Constants.SECURITY_MANAGER, securityManager);
        }
        if (securityPropertyManager == null) {
            securityPropertyManager = new XMLSecurityPropertyManager();
            fConfiguration.setProperty(Constants.XML_SECURITY_PROPERTY_MANAGER, securityPropertyManager);
        }

        reset();
        fConfiguration.parse(inputSource);

    } 

通过SqlSessionFactoryBuiler 对象的 build() 方法为入口来解析 mybatis-config.xml 文件,把这个文件中的信息保存到 Configuration 对象中,并且创建 SqlSessionFactory 对象。

3.2 SqlSession

我们点击

public interface SqlSessionFactory {
    SqlSession openSession();
}

进入到 DefaultSqlSessionFactory.java 类中

    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
        Transaction tx = null;

        DefaultSqlSession var8;
        try {
            Environment environment = this.configuration.getEnvironment();
            TransactionFactory transactionFactory = this.getTransactionFactoryFromEnvironment(environment);
            tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
            Executor executor = this.configuration.newExecutor(tx, execType);
            var8 = new DefaultSqlSession(this.configuration, executor, autoCommit);
        } catch (Exception var12) {
            this.closeTransaction(tx);
            throw ExceptionFactory.wrapException("Error opening session.  Cause: " + var12, var12);
        } finally {
            ErrorContext.instance().reset();
        }

        return var8;
    }

在这个方法中,创建了 Executor 对象,最后返回 DefaultSqlSession 对象,在这个类中提供了 *** 作数据库的方法。这些方法都最后使用 。

4. 实现CRUD 4.1 在接口中添加方法
public interface DepartmentMapper {
    // 插入
    void insert(Department department);

    // 更新
    void update(Department department);

    // 删除
    void delete(Integer did);

    // 查询全部
    List<Department> selectDepartments();

    // 根据id查
    Department select(Integer did);

    List<Department> selectByCondition(Integer did, String dname);
}
4.2 修改映射文件:

DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xianopeng.mapper.DepartmentMapper">
    
    <insert id="insert" parameterType="com.xianopeng.entity.Department">
        insert into t_deparment(dname,description) values(#{dname}, #{description})
    insert>
    
    <update id="update" parameterType="com.xianopeng.entity.Department">
        update t_department set dname=#{dname},description=#{description} where did=#{did}
    update>
    
    <delete id="delete">
        delete from t_deparment where did=#{did}
    delete>
    
    <select id="selectDepartments" resultType="com.xianopeng.entity.Department">
        select * from t_department
    select>
    
    <select id="select" parameterType="int" resultType="com.xianopeng.entity.Department">
        select * from t_department where did=#{did}
    select>
    
    <select id="selectByCondition" parameterType="map" resultType="com.xianopeng.entity.Department">
        select * from t_department where did=#{did} and dname=#{dname}
    select>

mapper>
4.3 编写测试

我们使用 junit 来编写测试,因此我们要把 Junit 的依赖添加到项目中。

<dependency>
    <groupId>junitgroupId>
    <artifactId>junitartifactId>
    <version>4.13.2version>
    <scope>testscope>
dependency>

编写测试:

4.3.1 插入
package com.xianopeng.test;

import com.xianopeng.entity.Department;
import com.xianopeng.mapper.DepartmentMapper;
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.junit.After;
import org.junit.Before;
import org.junit.Test;

import java.io.IOException;
import java.io.InputStream;

/**
 * 测试
 */
public class MyBatisCRUDTest {
    private SqlSession sqlSession;

    // 插入
    @Test
    public void testInsert() {
        DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class);
        mapper.insert(new Department().setDname("市场部").setDescription("扩展业务的部门"));
        // 提交事务
        sqlSession.commit();
    }

    @Before
    public void init() {
        try {
            String resource = "mybatis-config.xml";
            InputStream inputStream = Resources.getResourceAsStream(resource);
            SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
            sqlSession = sqlSessionFactory.openSession();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
    @After
    public void destroy() {
        sqlSession.close();
    }
}
4.3.2 更新
    // 更新
    @Test
    public void testUpdate() {
        try {
            DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class);
            mapper.update(new Department().setDid(5).setDname("市场部").setDescription("给公司创收的部门"));
            sqlSession.commit();
        } catch (Exception e) {
            e.printStackTrace();
            sqlSession.rollback();
        }
    }
4.3.3 根据ID查询
    @Test
    public void testSelect() {
        DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class);
        Department department = mapper.select(5);
        System.out.println(department);
    }
4.3.4 多条件查询
    // 多条件查询
    @Test
    public void testSelectByCondition() {
        Map<String, Object> map = new HashMap<>();
        map.put("did", 5);
        map.put("dname", "市场部");

        DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class);
        List<Department> departments = mapper.selectByCondition(map);
        departments.forEach((department) -> {
            System.out.println(department.getDid() + "\t" + department.getDname());
        });
    }
4.3.5 删除
    @Test
    public void testDelete() {
        try {
            DepartmentMapper mapper = sqlSession.getMapper(DepartmentMapper.class);
            mapper.delete(5);
            sqlSession.commit();
        } catch (Exception e) {
            e.printStackTrace();
            sqlSession.rollback();
        }
    }
5. 配置文件

MyBatis 的配置文件包含了会深深影响 MyBatis 行为的设置和属性信息。 配置文档的顶层结构如下:

  • properties:用于定义外部数据库连接信息
  • settings:设置全局的参数,用于优化 MyBatis 框架
  • typeAliases:用于指定别名
  • typeHandlers:用于定义或配置类型处理器
  • objectFactory:对象工厂
  • plugins:用于配置 MyBatis 的插件功能
  • environments:用于运行环境
  • databaseProvider:用于定义数据库厂商的标识,便于切换数据库
  • mappers:用于注册映射文件
5.1 properties

我们在项目的资源目录中创建 db.properties 文件,在这个文件中定义数据库连接信息。

driver=com.mysql.jdbc.Driver
url=jdbc:mysql://192.168.72.71:3306/mydb?useSSL=false&characterEncoding=utf8
username=root
password=123456

接下来在主配置文件中使用 properties 标签来把我们编写好的连接数据库信息的文件引用。


DOCTYPE configuration
        PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
    <properties resource="db.properties" />
    <environments default="development">
        <environment id="development">
            <transactionManager type="JDBC"/>
            <dataSource type="POOLED">
                <property name="driver" value="${driver}"/>
                <property name="url" value="${url}"/>
                <property name="username" value="${username}"/>
                <property name="password" value="${password}"/>
            dataSource>
        environment>
    environments>
    <mappers>
        <mapper resource="DepartmentMapper.xml" />
    mappers>
configuration>

通过 properties 标签来引用外部的数据库连接信息文件,然后在文件中需要使用的地方通过 ${key} 方式来引用。

5.2 settings

这是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。

它的具体配置信息参考官网的内容:https://mybatis.org/mybatis-3/zh/configuration.html#settings

在官网的 settings 项目最后,有一个完整推荐配置:

<settings>
  <setting name="cacheEnabled" value="true"/>
  <setting name="lazyLoadingEnabled" value="true"/>
  <setting name="multipleResultSetsEnabled" value="true"/>
  <setting name="useColumnLabel" value="true"/>
  <setting name="useGeneratedKeys" value="false"/>
  <setting name="autoMappingBehavior" value="PARTIAL"/>
  <setting name="autoMappingUnknownColumnBehavior" value="WARNING"/>
  <setting name="defaultExecutorType" value="SIMPLE"/>
  <setting name="defaultStatementTimeout" value="25"/>
  <setting name="defaultFetchSize" value="100"/>
  <setting name="safeRowBoundsEnabled" value="false"/>
  <setting name="mapUnderscoreToCamelCase" value="false"/>
  <setting name="localCacheScope" value="SESSION"/>
  <setting name="jdbcTypeForNull" value="OTHER"/>
  <setting name="lazyLoadTriggerMethods" value="equals,clone,hashCode,toString"/>
settings>
5.3 typeAliases

这个标签是用于给我们的 Java 对象指定别名,从而让我们的代码更加简洁。

5.3.1 typeAlias

对于它的配置,我们可以按如下的方式来进行:

    <typeAliases>
        <typeAlias type="com.xianopeng.entity.Department" alias="Department"/>
    typeAliases>

这种方式指定好后,我们就可以修改映射文件:


DOCTYPE mapper
        PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
        "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.xianopeng.mapper.DepartmentMapper">
    
    <insert id="insert" parameterType="Department">
        insert into t_department(dname,description) values(#{dname}, #{description})
    insert>
    
    <update id="update" parameterType="Department">
        update t_department set dname=#{dname},description=#{description} where did=#{did}
    update>
    
    <delete id="delete">
        delete from t_department where did=#{did}
    delete>
    
    <select id="selectDepartments" resultType="Department">
        select * from t_department
    select>
    
    <select id="select" parameterType="int" resultType="Department">
        select * from t_department where did=#{did}
    select>
    
    <select id="selectByCondition" parameterType="map" resultType="Department">
        select * from t_department where did=#{did} and dname=#{dname}
    select>

mapper>

此时,我们在返回类型或参数类型中就可以使用定义好的别名来指定。

使用这种方式定义好别后,确实可以来简化映射文件的编写,但是这种方式有一个不好的方是:如果有多个映射文件(实体),我们需要定义多个别名。

    <typeAliases>
        <typeAlias type="com.xianopeng.entity.Department" alias="Department"/>
        <typeAlias type="com.xianopeng.entity.Employee" alias="Employee"/>
        <typeAlias type="com.xianopeng.entity.User" alias="User"/>
        <typeAlias type="com.xianopeng.entity.Person" alias="Person"/>
        .........
    typeAliases>

使用这种方式后会让我们配置文件变得臃肿。

5.3.2 package

在 MyBatis 中,为了解决这个问题,它提供另外一种配置方式。

    <typeAliases>
        <package name="com.xianopeng.entity"/>
    typeAliases>

使用 package 子标签来定义,我们此时只需要定义实体所在的包名即可。

使用这种方式来配置别名,可以让配置文件变得非常干净,同时也可以让映射文件变得简洁。但它的限制是别名只能使用类名来使用。

每一个在包 com.xianopeng.entity 中的 Java Bean,在没有注解的情况下,会使用 Bean 的首字母小写的非限定类名来作为它的别名。 比如 com.xianopeng.entity.Author 的别名为 author;若有注解,则别名为其注解值。

5.3.3 @Alias

除了上述两种方式个,还可以在实体类上使用 @Alias 注解来进行别名的配置

@Alias("author")
public class Author {
    ...
}

这时这个类的别名就是 author。

5.4 typeHandlers

MyBatis 在设置预处理语句(PreparedStatement)中的参数或从结果集中取出一个值时, 都会用类型处理器将获取到的值以合适的方式转换成 Java 类型。

MyBatis中给我们提供了很多的类型处理器,如果这些类型处理器不能满足我们的开发需要,我们还可以去重写这些类型处理器, 或者自定义类型处理器。

自定义类型处理器是 BaseTypeHandler 子类,而 BaseTypeHandler 类继承了 TypeReference 类并实现了 TypeHandler。

TypeHandler是一个接口,在这个接口中定义了参数的设置方法和获取结果集的方法。

TypeReference 是一个抽像类,它定义了如何获取类的真实的泛型类型。

5.4.1 自定义类型转换器
package com.xianopeng.type;

import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType;

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

/**
 * 自定义类型转换器,它需要继承 BaseTypeHandler 的抽像类
 */
public class CustomTypeHandler extends BaseTypeHandler<String> {
    // 根据参数的位置和值来设置 SQL 中的参数
    @Override
    public void setNonNullParameter(PreparedStatement preparedStatement, int i, String s, JdbcType jdbcType) throws SQLException {
        preparedStatement.setString(i, s);
    }

    // 根据字段名称来获取它的值
    @Override
    public String getNullableResult(ResultSet resultSet, String s) throws SQLException {
        return resultSet.getString(s);
    }

    // 根据字段的索引获取它的值
    @Override
    public String getNullableResult(ResultSet resultSet, int i) throws SQLException {
        return resultSet.getString(i);
    }

    // 获取存储过程的值
    @Override
    public String getNullableResult(CallableStatement callableStatement, int i) throws SQLException {
        return callableStatement.getString(i);
    }
}
5.4.2 配置自定义类型转换器

把自定义的类型转换器在 MyBatis 的配置文件中使用 typeHandlers 标签来进行配置。

<typeHandlers>
    
    <typeHandler handler="com.xianopeng.type.CustomTypeHandler"/>
typeHandlers>

当然,也可以使用 package 子标签来指定自定义类型转换器所在的包名,这样就可以批量配置。

<typeHandlers>
    <package name="com.xianopeng.type"/>
typeHandlers>
5.5 environments

在 MyBatis 中可以配置多个运行环境,通过指定 environment 标签来实现

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

注意:

在 environments 标签中的 default 属性的值,必须与其子标签 environment 中的某一个配置的 id 属性的值相同,表示使用这个环境。

transactionManager:这个标签是用于配置事务管理的方式,默认使用 JDBC 事务。

dataSource:用于配置MyBatis 所使用的数据源信息。

5.5.1 transactionManager

在 MyBatis 中提供了两种事务管理器:

  1. JDBC:使用 JDBC 原生的事务来进行管理,事务提交和回滚机制,依赖于从数据源得到的连接来管理事务范围,也就是 JdbcTransactionFactory 的别名。
  2. MANAGED:这个配置几乎没做什么。它从不提交或回滚一个连接,而是让容器来管理事务的整个生命周期(比如 JEE 应用服务器的上下文)。 默认情况下它会关闭连接,它是 ManagedTransactionFactory 的别名。
5.5.2 dataSource

dataSource 元素使用标准的 JDBC 数据源接口来配置 JDBC 连接对象的资源。

在 MyBatis 中有三种内建的数据源类型(也就是 type=“[UNPOOLED|POOLED|JNDI]”):

  • UNPOOLED:这个数据源的实现会每次请求时打开和关闭连接,实际上就是不使用连接池技术。
  • POOLED:这种数据源的实现利用“池”的概念将 JDBC 连接对象组织起来,避免了创建新的连接实例时所必需的初始化和认证时间。 这种处理方式很流行,能使并发 Web 应用快速响应请求,实际上就是使用连接池技术。
  • JNDI:这个数据源实现是为了能在如 EJB 或应用服务器这类容器中使用,容器可以集中或在外部配置数据源,然后放置一个 JNDI 上下文的数据源引用。它也是利用连接池技术,但是它的可移置性非常差。
5.6 databaseIdProvider

MyBatis 可以根据不同的数据库厂商执行不同的语句,这种多厂商的支持是基于映射语句中的 databaseId 属性。

5.6.1 配置

在 mybatis-config.xml 文件中配置 databaseIdProvider 标签来指定数据厂商。

    
    <databaseIdProvider type="DB_VENDOR">
        <property name="SQL Server" value="sqlserver"/>
        <property name="MySQL" value="mysql"/>
        <property name="Oracle" value="oracle" />
    databaseIdProvider>
5.6.2 使用

在映射文件中通过 databaseId 来引用。

    
    <select id="selectDepartments" resultType="Department" databaseId="mysql">
        select * from t_department
    select>
5.7 mappers

它是用于注册我们的映射文件,使用有以下几种方式:

5.7.1 逐个注册
<mappers>
    <mapper resource="DepartmentMapper.xml" />
    <mapper resource="EmployeeMapper.xml" />
mappers>

有多少个映射文件,我们就需要在 mappers 标签中使用多少个 mapper 子标签来注册。这种方式可行,但是会让配置文件变得臃肿。

5.7.2 接口名注册

要实现接口名注册我们需要把接口的地址放到 mappers 标签中,然后通过 class 属性来指定。

<mappers>
    <mapper class="com.xianopeng.mapper.EmployeeMapper"/>
    <mapper class="com.xianopeng.mapper.DepartmentMapper"/>
mappers>

要使用这种方式,我们需要把映射文件和接口类放在同一个目录下。

另外,在 IDEA 中,默认情况下,Maven 工程是不会把除了 resources 目录下的资源文件编译到类路径下外,其它目录下的 xml 文件、properties 文件等是不会编译到类路径下的。要想让它进行编译,我们需要在 pom.xml 文件中添加如下的配置。

    <build>
        
        <resources>
            <resource>
                <directory>src/main/javadirectory>
                <includes>
                    <include>**/*.xmlinclude>
                    <include>**/*.propertiesinclude>
                includes>
            resource>
        resources>
    build>
5.7.3 基于注解配置

我们要想使用注解配置,我们就需要在接口的方法中添加注解,然后再 mappers 标签中使用类名来注册。

5.7.4 批量注册
<mappers>
    <package name="com.xianopeng.mapper" />
mappers>

使用这种方式,也是需要把映射文件和接口类放到同一个目录中。

欢迎分享,转载请注明来源:内存溢出

原文地址: http://outofmemory.cn/langs/870669.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
上一篇 2022-05-13
下一篇 2022-05-13

发表评论

登录后才能评论

评论列表(0条)

保存