MyBatis与Spring整合的完整总结

一、MyBatis的基本结构

MyBatis是一个用于操作数据库的持久层框架,其核心组件包括:

  1. SqlSessionFactory:创建SqlSession的工厂,负责管理数据库连接和事务。
  2. SqlSession:执行SQL语句的接口,封装了对数据库的操作,提供了CRUD方法。
  3. Mapper:定义SQL语句和映射结果的接口或XML文件,负责将Java对象与数据库表之间的映射关系。
  4. Configuration:MyBatis的配置类,包含数据库连接信息、映射文件、插件等配置信息。

二、MyBatis的工作流程

MyBatis的基本工作流程如下:

  1. 创建SqlSessionFactory:在应用启动时,加载MyBatis的配置文件,创建SqlSessionFactory

    // 使用SqlSessionFactoryBuilder加载MyBatis配置文件并构建SqlSessionFactory
    SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
  2. 获取SqlSession:通过SqlSessionFactory获取SqlSession,用于与数据库交互。

    // 从SqlSessionFactory中获取SqlSession实例
    SqlSession sqlSession = sqlSessionFactory.openSession();
  3. 调用Mapper方法:通过Mapper接口调用数据库操作方法,动态代理会拦截该调用。

  4. 执行SQL:动态代理获取SQL语句并通过SqlSession执行。

  5. 返回结果:执行结果返回给调用者,使用完后关闭SqlSession

三、动态代理的实现

MyBatis使用动态代理来为Mapper接口生成实现类,主要通过以下几个关键类实现:

1. MapperProxy

MapperProxy实现了InvocationHandler接口,负责拦截对Mapper接口方法的调用。在invoke方法中,调用SqlSession执行对应的SQL语句。

public class MapperProxy<T> implements InvocationHandler {
    private final SqlSession sqlSession; // SqlSession实例,用于执行SQL
    private final Class<T> mapperInterface; // Mapper接口的Class对象

    // 构造函数,初始化SqlSession和Mapper接口
    public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface) {
        this.sqlSession = sqlSession;
        this.mapperInterface = mapperInterface;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 调用SqlSession执行SQL语句,返回结果
        return sqlSession.selectOne(getStatement(method), args);
    }

    // 生成SQL语句的ID,格式为"Mapper接口名.方法名"
    private String getStatement(Method method) {
        return mapperInterface.getName() + "." + method.getName();
    }
}

2. MapperProxyFactory

MapperProxyFactory用于创建MapperProxy的实例,持有Mapper接口的类信息。

public class MapperProxyFactory<T> {
    private final Class<T> mapperInterface; // Mapper接口的Class对象

    // 构造函数,初始化Mapper接口
    public MapperProxyFactory(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
    }

    // 创建MapperProxy实例
    public T newInstance(SqlSession sqlSession) {
        // 通过动态代理创建Mapper接口的代理对象
        return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(),
                new Class<?>[]{mapperInterface},
                new MapperProxy<>(sqlSession, mapperInterface));
    }
}

四、MyBatis与Spring的整合

整合MyBatis与Spring的步骤如下:

1. Spring配置

在Spring的配置文件中配置数据源、SqlSessionFactorySqlSessionTemplate

xml

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource">
    <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/> <!-- 数据库驱动 -->
    <property name="url" value="jdbc:mysql://localhost:3306/mydb"/> <!-- 数据库连接URL -->
    <property name="username" value="root"/> <!-- 数据库用户名 -->
    <property name="password" value="password"/> <!-- 数据库密码 -->
</bean>

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
    <property name="dataSource" ref="dataSource"/> <!-- 设置数据源 -->
</bean>

<bean id="sqlSessionTemplate" class="org.mybatis.spring.SqlSessionTemplate">
    <constructor-arg ref="sqlSessionFactory"/> <!-- 使用SqlSessionFactory创建SqlSessionTemplate -->
</bean>

2. 使用Mapper接口

定义Mapper接口,并使用@Mapper注解。

@Mapper // 表示这是一个Mapper接口
public interface UserMapper {
    User selectUserById(int id); // 根据ID查询用户
}

3. 注入Mapper

在Service层中,通过@Autowired注解将Mapper接口注入到服务中。

@Service // 表示这是一个Service类
public class UserService {
    @Autowired // 自动注入UserMapper
    private UserMapper userMapper;

    // 根据ID获取用户信息
    public User getUserById(int id) {
        return userMapper.selectUserById(id); // 调用Mapper方法
    }
}

4. 启动类

Spring Boot应用的启动类通常需要使用@SpringBootApplication注解,以便Spring能够正确地配置和启动应用。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication // 组合注解,包含@Configuration、@EnableAutoConfiguration和@ComponentScan
public class Application {
    public static void main(String[] args) {
        // 启动Spring Boot应用
        SpringApplication.run(Application.class, args);
    }
}

五、缓存机制

MyBatis支持一级缓存和二级缓存,以提高查询性能。

1. 一级缓存

一级缓存是SqlSession级别的缓存,默认开启。它的生命周期与SqlSession一致。在同一SqlSession中,相同的查询只会访问一次数据库。

User user1 = userMapper.selectUserById(1); // 第一次查询,访问数据库
User user2 = userMapper.selectUserById(1); // 第二次查询,从缓存中获取

2. 二级缓存

二级缓存是跨SqlSession的缓存,默认关闭。需要在Mapper XML中配置。

2.1 配置二级缓存

要启用二级缓存,首先需要在Mapper XML文件中添加<cache/>元素。示例:

<mapper namespace="com.example.mapper.UserMapper">
    <cache /> <!-- 开启二级缓存 -->
    <select id="selectUserById" resultType="User">
        SELECT * FROM user WHERE id = #{id}
    </select>
</mapper>
2.2 二级缓存的底层实现

MyBatis的二级缓存是一个跨SqlSession的缓存机制,允许将查询结果存储在内存中,以减少对数据库的访问次数。二级缓存的实现涉及以下几个关键方面:

  • 缓存实现类:MyBatis提供了默认的二级缓存实现(org.apache.ibatis.cache.impl.PerpetualCache),但也允许开发者实现自定义的缓存类。开发者可以通过实现org.apache.ibatis.cache.Cache接口来创建自己的缓存实现。

  • 查询操作:当执行查询时,MyBatis首先检查二级缓存中是否存在该查询的结果。如果存在,则直接返回缓存中的数据;如果不存在,则执行实际的SQL查询,并将结果存储到二级缓存中。

  • 缓存的失效机制:当对数据库进行插入、更新或删除操作时,相应的缓存会被清除,以确保数据的一致性。虽然MyBatis的默认二级缓存没有内置的过期策略,但开发者可以在自定义的缓存实现中添加过期机制。

2.3 示例代码

下面是一个简单的自定义二级缓存实现的示例:

import org.apache.ibatis.cache.Cache;

public class MyCustomCache implements Cache {
    private final String id; // 缓存的唯一标识
    private final Map<Object, Object> cacheMap = new ConcurrentHashMap<>(); // 存储缓存数据

    // 构造函数,初始化缓存ID
    public MyCustomCache(String id) {
        this.id = id;
    }

    @Override
    public String getId() {
        return id; // 返回缓存的唯一标识
    }

    @Override
    public void putObject(Object key, Object value) {
        cacheMap.put(key, value); // 将对象放入缓存
    }

    @Override
    public Object getObject(Object key) {
        return cacheMap.get(key); // 根据键获取缓存中的对象
    }

    @Override
    public Object removeObject(Object key) {
        return cacheMap.remove(key); // 从缓存中移除对象
    }

    @Override
    public void clear() {
        cacheMap.clear(); // 清空缓存
    }

    @Override
    public int getSize() {
        return cacheMap.size(); // 返回缓存中的对象数量
    }
}

六、延迟加载

MyBatis支持延迟加载,即在需要时才加载关联对象。可以通过在映射文件中配置lazy属性来实现。

<resultMap id="userResultMap" type="User">
    <id property="id" column="id"/> <!-- 映射用户ID -->
    <result property="name" column="name"/> <!-- 映射用户姓名 -->
    <association property="address" column="address_id" select="com.example.mapper.AddressMapper.selectById" lazy="true"/> <!-- 延迟加载地址 -->
</resultMap>

在这个例子中,address字段的加载是延迟的,只有在实际访问address时才会查询数据库。

七、映射文件

MyBatis支持使用XML文件来定义SQL语句和映射关系。映射文件通常与Mapper接口对应,命名规则为Mapper接口名.xml

1. 映射文件结构

映射文件的基本结构如下:

xml

<mapper namespace="com.example.mapper.UserMapper">
    <select id="selectUserById" resultType="User">
        SELECT * FROM user WHERE id = #{id} <!-- 查询用户信息 -->
    </select>
</mapper>

2. 映射文件的使用

在Mapper接口中,定义与映射文件中SQL语句对应的方法。例如:

@Mapper
public interface UserMapper {
    User selectUserById(int id); // 对应映射文件中的select语句
}

八、调用流程详解

  1. 获取Mapper:调用sqlSession.getMapper(UserMapper.class),MyBatis返回一个UserMapper的代理对象。

  2. 方法调用:当调用userMapper.selectUserById(1)时,MapperProxyinvoke方法被触发。

  3. 生成SQL语句IDMapperProxy通过getStatement方法生成SQL语句的ID,例如UserMapper.selectUserById

  4. 执行SQL:调用sqlSession.selectOne方法执行SQL语句,并将结果返回。

  5. 返回结果:最终,执行结果返回给调用者。

九、接口层与核心处理层

1. 接口层

接口层主要负责定义与数据库交互的方法。Mapper接口通过注解或XML文件定义SQL语句及其参数和返回值的映射关系。使用接口的好处在于可以利用Java的类型安全性和IDE的自动补全。

2. 核心处理层

核心处理层主要负责数据库操作的具体实现,包括事务管理、连接管理等。MyBatis通过SqlSession来管理数据库操作,确保每个操作都在一个事务上下文中进行。

十、基础模块

基础模块包括:

  1. 配置模块:管理MyBatis的各种配置,包括数据库连接、映射文件位置等。
  2. 插件模块:支持MyBatis的插件机制,允许开发者自定义拦截器,增强MyBatis的功能。
  3. 缓存模块:支持一级和二级缓存,提高查询性能,减少数据库访问次数。

十一、总结

通过动态代理,MyBatis能够在运行时为Mapper接口生成实现类,并将方法调用转发到SqlSession中执行。同时,MyBatis与Spring的整合使得开发者能够更方便地进行数据库操作,利用Spring的依赖注入和管理功能,提高了代码的可维护性和灵活性。整个架构灵活且易于扩展。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值