MyBatis的工作原理和SpringBoot快速集成Mybatis(极简)

jupiter
2024-06-22 / 0 评论 / 34 阅读 / 正在检测是否收录...

1.MyBatis的工作原理

1.1、传统的JDBC编程

JAVA程序通过JDBC链接数据库,这样我们就可以通过SQL对数据库进行编程。JAVA链接数据库大致分为五步,如下所示:

  • 1、使用JDBC编程需要链接数据库,注册驱动和数据库信息。
  • 2、操作Connection,打开Statement对象。
  • 3、通过Statement执行SQL语句,返回结果放到ResultSet对象。
  • 4、使用ResultSet读取数据。
  • 5、关闭数据库相关的资源。

JDBC 代码示例:

import java.sql.*;

public class JdbcDemo {
    public static void main(String[] args) throws SQLException, ClassNotFoundException {
        String username = "db1";
        String password = "db1";
        String url = "jdbc:mysql://192.168.124.10:3306/db1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai";

        // 使用JDBC编程需要链接数据库,注册驱动和数据库信息。
        Connection conn = DriverManager.getConnection(url,username,password);
        // 编写待执行sql
        String sql = "select * from user1";
        // 通过Statement执行SQL语句,返回结果放到ResultSet对象。
        PreparedStatement preparedStatement = conn.prepareStatement(sql);
        ResultSet rs = preparedStatement.executeQuery();

        //使用ResultSet读取数据。
        // 获取 ResultSetMetadata 对象,它包含了 ResultSet 的结构信息
        ResultSetMetaData metaData = rs.getMetaData();
        // 打印列名
        for (int i = 1; i <= metaData.getColumnCount(); i++) {
            System.out.print(metaData.getColumnName(i) + "\t");
        }
        System.out.println(); // 换行

        // 遍历 ResultSet 并打印数据
        while (rs.next()) {
            for (int i = 1; i <= metaData.getColumnCount(); i++) {
                System.out.print(rs.getString(i) + "\t");
            }
            System.out.println(); // 每行数据后换行
        }

        // 关闭数据库相关的资源。
        preparedStatement.close();
        conn.close();
    }
}

传统的JDBC方式存在一些弊端:

(1)工作量比较大。我们需要先建立链接,然后处理JDBC底层业务,处理数据类型。我们还需要处理Connection对象,Statement对象和Result对象去拿数据,并关闭它们。

(2)我们对JDBC编程处理的异常进行捕获处理并正确的关闭资源。

1.2、MyBatis工作原理:对JDBC进行了封装

MyBatis的四大核心组件:

  • 1、SQLSessionFactoryBuilder(构造器):它会根据配置信息或者代码生成SqlSessionFactory。
  • 2、SqlSessionFactory(工厂接口):依靠工厂生成SqlSession。
  • 3、SqlSession(会话):是一个既可以发送SQL去执行并且返回结果,也可以获取Mapper接口。
  • 4、SQL Mapper:是由一个JAVA接口和XML文件(或注解)构成,需要给出对应的SQL和映射规则。SQL是由Mapper发送出去,并且返回结果。

MyBatis工作原理示意图:

从上面的流程图可以看出MyBatis和JDBC的执行时相似的。MyBatis的底层操作封装了JDBC的API,MyBatis的工作原理以及核心流程与JDBC的使用步骤一脉相承,MyBatis的核心对象(SqlSession,Executor)与JDBC的核心对象(Connection,Statement)相互对应。

1.3 @Mapper执行sql原理

  1. Mapper 接口与 @Mapper 注解:

    • Mapper 接口是用户定义的,其中包含了与数据库表交互的方法。
    • @Mapper 注解是 MyBatis-Spring 集成库中的一个注解,用于标识一个接口作为 MyBatis 的 Mapper 接口。
    • 当接口被 @Mapper 注解标记后,MyBatis 会自动为这个接口创建一个代理实现类。
  2. MyBatis 映射器(Mapper)的代理实现:

    • MyBatis 使用 Java 动态代理技术为 Mapper 接口创建代理实现类。
    • 代理实现类会拦截对 Mapper 接口方法的调用,并根据方法的定义(包括方法名、参数等)来查找并执行相应的 SQL 语句。
  3. SQL 语句的查找与执行:

    • MyBatis 会在配置的 SQL 映射文件(通常是 XML 文件)中查找与 Mapper 接口方法对应的 SQL 语句。
    • SQL 映射文件定义了 Mapper 接口中每个方法对应的 SQL 语句,包括查询、插入、更新、删除等操作。
    • MyBatis 会根据 Mapper 接口方法的定义和 SQL 映射文件中的配置,生成具体的 SQL 语句并执行。
  4. @Param 注解与参数绑定:

    • 如果 Mapper 接口方法中有参数,可以使用 @Param 注解来指定参数名。
    • MyBatis 会根据 @Param 注解指定的参数名,在 SQL 语句中进行参数绑定。
    • 参数绑定是将 Java 对象的属性值或方法参数值设置到 SQL 语句的占位符中的过程。
  5. 结果映射与返回类型:

    • MyBatis 会根据 Mapper 接口方法的返回类型,将 SQL 语句执行的结果映射为相应的 Java 对象或集合。
    • 结果映射可以在 XML 映射文件中进行配置,包括结果集的列名与 Java 对象属性的对应关系等。
  6. Spring 集成与 SqlSessionTemplate:

    • 在 Spring 与 MyBatis 的集成中,SqlSessionTemplate 是一个关键的组件。
    • SqlSessionTemplate 封装了 SqlSession 的使用,并提供了线程安全的 SQL 执行环境。
    • 当通过 Spring 容器注入 Mapper 接口时,实际上注入的是 SqlSessionTemplate 创建的 Mapper 代理实现类的实例。
  7. 总结:

    • @Mapper 注解使得 MyBatis 能够自动为 Mapper 接口创建代理实现类。
    • MyBatis 通过动态代理和 SQL 映射文件来执行 Mapper 接口中定义的 SQL 语句。
    • Spring 集成通过 SqlSessionTemplate 提供了线程安全的 SQL 执行环境,并简化了 Mapper 接口的注入和使用。

2.MyBatis的优点

MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生类型、接口和 Java 的 POJO(Plain Old Java Objects,普通老式 Java 对象)为数据库中的记录。MyBatis是对JDBC的封装。相对于JDBC,MyBatis有以下优点:

  1. SQL映射:

    • MyBatis 支持定制化 SQL、存储过程以及高级映射。它允许你直接在 XML 映射文件中编写 SQL 语句,或者使用注解的方式将 SQL 语句直接写在 Mapper 接口的方法上。
    • MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集的麻烦,极大地简化了数据库操作。
  2. 对象关系映射(ORM):

    • MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old Java Objects,普通的 Java 对象)映射成数据库中的记录。
    • 提供了映射标签,支持对象与数据库的 ORM 字段关系映射,降低了耦合度,提高了代码的复用性和可维护性。
  3. 动态SQL:

    • MyBatis 提供了 XML 标签,支持编写动态 SQL 语句。你可以根据传入参数的不同,动态地生成不同的 SQL 语句,实现更复杂的数据库操作。
  4. 事务管理:

    • MyBatis 通过与 Spring 等框架的集成,提供了完整的事务管理功能。它支持编程式事务和声明式事务,可以确保数据的一致性。
  5. 插件机制:

    • MyBatis 提供了插件机制,允许你通过编写插件来扩展 MyBatis 的功能。例如,你可以编写一个插件来拦截 SQL 语句的执行,进行日志记录、性能监控等操作。
  6. 缓存机制:

    • MyBatis 提供了一级缓存(SqlSession 级别的缓存)和二级缓存(Mapper 级别的缓存)来提高查询性能。一级缓存默认是开启的,而二级缓存需要手动开启并进行配置。

3.快速集成步骤

3.1 引入mybatis-starter依赖

<!-- mybatis -->
<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>3.0.3</version>
</dependency>

其他必备依赖

<!-- springboot基础依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter</artifactId>
    <version>3.3.0</version>
</dependency>
<!-- mysql数据库连接驱动-->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
</dependency>
<!-- 引入Spring封装的jdbc,内部默认依赖了 HikariDataSource  数据源-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<!-- lombok -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <version>1.18.32</version>
</dependency>

测试辅助依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-test</artifactId>
    <version>3.3.0</version>
</dependency>

3.2 配置数据源

application.propertiesapplication.yml 文件中配置你的数据源(如MySQL)。

spring:
  #配置数据源
  datasource:
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: jdbc:mysql://192.168.124.10:3306/db1?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai
    username: db1
    password: db1
    type: com.zaxxer.hikari.HikariDataSource
    hikari:
      # 连接池最小空闲连接,默认值10,小于0或大于maximum-pool-size,都会重置为maximum-pool-size
      minimum-idle: 10
      # 连接池最大连接数,小于等于0会被重置为默认值10;大于零小于1会被重置为minimum-idle的值。
      maximum-pool-size: 20
      # 连接最大存活时间,不等于0且小于30秒,会被重置为默认值30分钟.设置应该比mysql设置的超时时间短
      max-lifetime: 600000
      # 空闲连接超时时间,默认值600000(10分钟),大于等于max-lifetime且max-lifetime>0,会被重置为0;不等于0且小于10秒,会被重置为10秒。
      idle-timeout: 600000
      #  连接超时时间:毫秒,小于250毫秒,否则被重置为默认值30秒,如果在这个时间内无法建立连接,将会抛出异常。
      connection-timeout: 30000

mybatis:
  mapper-locations: classpath:mapper/*.xml
  type-aliases-package: com.example.hikaridatasource.domain.entity

3.3 创建数据源和Mybatis必备bean的Config文件

@Configuration
public class Db1DataSourceConfig {

    // jdbc连接信息
    @Value(value = "${spring.datasource.url:}")
    private String url;
    @Value(value = "${spring.datasource.username:}")
    private String username;
    @Value(value = "${spring.datasource.password:}")
    private String password;
    @Value(value = "${spring.datasource.driver-class-name:}")
    private String driveClassName;

    // HikariDataSource配置参数
    // 连接池最小空闲连接,默认值10
    @Value(value = "${spring.datasource.hikari.minimum-idle:10}")
    private int minimumIdle;
    // 连接池最大连接数,默认值10
    @Value(value = "${spring.datasource.hikari.maximum-pool-size:10}")
    private int maximumPoolSize;
    // 连接最大存活时间,默认值30分钟.设置应该比mysql设置的超时时间短,配置单位毫秒
    @Value(value = "${spring.datasource.hikari.max-lifetime:600000}")
    private long maxLifetime;
    // 空闲连接超时时间,默认值600000(10分钟)配置单位毫秒
    @Value(value = "${spring.datasource.hikari.idle-timeout:600000}")
    private long idleTimeout;
    // 连接超时时间,配置单位毫秒
    @Value(value = "${spring.datasource.hikari.connection-timeout:60000}")
    private long connectionTimeout;

    // mybatis配置
    // mapperXml文件地址
    @Value(value = "${spring.datasource.hikari.mapper-locations:}")
    private String mapperLocations;

    /**
     * 配置db1 数据源
     */
    @Bean(name = "db1DataSource")
    public DataSource db1DataSource(){
        HikariDataSource dataSource = new HikariDataSource();

        // 设置jdbc连接信息
        dataSource.setJdbcUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        dataSource.setDriverClassName(driveClassName);

        // 设置HikariDataSource配置参数
        dataSource.setMinimumIdle(minimumIdle);
        dataSource.setMaximumPoolSize(maximumPoolSize);
        dataSource.setMaxLifetime(maxLifetime);
        dataSource.setIdleTimeout(idleTimeout);
        dataSource.setConnectionTimeout(connectionTimeout);
        return dataSource;
    }

    /**
     * 配置db1 SqlSessionFactory mybatis必备(实现数据库连接会话管理)
     */
    @Bean(name = "db1SqlSessionFactory")
    public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
        SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
        factory.setDataSource(dataSource);
        // 设置mapperXml文件地址
        //factory.setConfigLocation(new PathMatchingResourcePatternResolver().getResource(mapperLocations));
        return factory.getObject();
    }

    /**
     * 配置db1 SqlSessionTemplate mybatis必备(实现数据库连接sql执行和结果映射)
     */
    @Bean(name = "db1SqlSessionTemplate")
    public SqlSessionTemplate db1SqlSessionTemplate(@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory){
        return new SqlSessionTemplate(sqlSessionFactory);
    }

    /**
     * 配置db1 DataSourceTransactionManager 事务管理器(Spring的JDBC事务增强)
     */
    @Bean(name = "db1DataSourceTransactionManager")
    public DataSourceTransactionManager db1DataSourceTransactionManager(@Qualifier("db1DataSource") DataSource dataSource)  {
        return new DataSourceTransactionManager(dataSource);
    }
}

3.4 创建实体类(Entity)

import lombok.Data;
import lombok.experimental.Accessors;

@Data
@Accessors(chain = true)
public class User1Entity {
    private Integer id;

    private String username;

    private String password;
}

3.5 创建 Mapper 接口

Mapper 接口用于定义 SQL 语句和数据库交互。

import com.example.hikaridatasource.domain.entity.User1Entity;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

import java.util.List;

@Mapper
public interface User1Mapper {
    @Select("SELECT * FROM user1")
    List<User1Entity> selectAllUsers();
}

3.6 使用 Mapper测试

自动注入Mapper代理对象的方式

@SpringBootTest
@Slf4j
class HikariDataSourceApplicationTests {
    @Resource
    User1Mapper user1Mapper1;

    @Test
    void contextLoads() throws SQLException {
        List<User1Entity> user1List1Entity = user1Mapper1.selectAllUsers();
        log.info("user1List1={}", user1List1Entity);
    }
}
2024-06-22T17:19:15.053+08:00  INFO 15316 --- [           main] c.e.h.HikariDataSourceApplicationTests   : user1List1=[User1Entity(id=1, username=username1, password=password1), User1Entity(id=2, username=username1, password=password1), User1Entity(id=3, username=username1, password=password1), User1Entity(id=4, username=username1, password=password1), User1Entity(id=5, username=username1, password=password1)]

通过SqlSessionTemplate手动获取Mapper代理对象的方式

@SpringBootTest
@Slf4j
class HikariDataSourceApplicationTests {
    @Resource
    SqlSessionTemplate sqlSessionTemplate;

    @Test
    void contextLoads() throws SQLException {
        User1Mapper user1Mapper2 =  sqlSessionTemplate.getMapper(User1Mapper.class);
        List<User1Entity> user1EntityList2 = user1Mapper2.selectAllUsers();
        log.info("user1List2={}", user1EntityList2);
    }
}
2024-06-22T17:21:11.392+08:00  INFO 8368 --- [           main] c.e.h.HikariDataSourceApplicationTests   : user1List2=[User1Entity(id=1, username=username1, password=password1), User1Entity(id=2, username=username1, password=password1), User1Entity(id=3, username=username1, password=password1), User1Entity(id=4, username=username1, password=password1), User1Entity(id=5, username=username1, password=password1)]

参考资料

  1. MyBatis 入门介绍
0

评论 (0)

打卡
取消