Mybatis中的Proxy模式

文章介绍了MyBatis如何通过动态代理实现Mapper接口的功能,包括定义Mapper接口,配置Mapper,以及在代码中使用SqlSession获取Mapper接口的代理对象来执行数据库操作。动态代理在运行时根据接口生成实现类,使得开发者无需关注具体实现。

动态代理的实现

MyBatis的Mapper接口是一种用于描述数据库操作的接口,它通过注解或XML配置文件定义了对应的SQL语句和参数映射。在运行时,MyBatis会根据Mapper接口的定义生成动态代理对象,实现接口中定义的方法,并且在方法执行时根据SQL语句和参数映射执行数据库操作。

以下是一个简单的示例,演示了如何定义一个Mapper接口以及如何使用它进行数据库操作

定义Mapper接口

public interface UserMapper {
    @Select("SELECT * FROM user WHERE id = #{id}")
    User selectUserById(int id);
    
    @Insert("INSERT INTO user (name, age) VALUES (#{name}, #{age})")
    void insertUser(User user);
}

配置Mapper接口

在MyBatis的配置文件中,需要配置Mapper接口的路径以及对应的XML文件(如果使用XML配置方式):

<configuration>
  <mappers>
    <mapper class="com.example.mapper.UserMapper"/>
  </mappers>
</configuration>

使用Mapper接口

在代码中,代理对象的方法名、参数类型和个数等信息,都会被传递到MyBatis的SqlSession对象中,SqlSession对象会根据这些信息找到相应的Mapper XML文件中的SQL语句,并调用其中定义的方法进行数据库操作:

try (SqlSession sqlSession = sqlSessionFactory.openSession()) {
    //动态代理生成一个实现类
    UserMapper userMapper = sqlSession.getMapper(UserMapper.class);

    User user = userMapper.selectUserById(1);
    System.out.println(user);

    User newUser = new User();
    newUser.setName("Alice");
    newUser.setAge(20);
    userMapper.insertUser(newUser);

    sqlSession.commit();
}

在运行时,MyBatis会根据UserMapper接口的定义生成一个动态代理对象,实现其中的方法。例如,当调用userMapper.selectUserById(1)时,MyBatis会解析@Select("SELECT * FROM user WHERE id = #{id}")注解,并根据参数id执行相应的数据库操作,最终返回查询结果。动态SQL的生成也是在代理对象中完成的.

使用动态代理的好处是,它可以让开发人员在不编写实现类的情况下使用接口。这样,开发人员可以专注于定义接口,而MyBatis则可以生成实现这些接口的类。

需要注意的是,Mapper接口的定义必须与SQL语句和参数映射一一对应,否则会导致运行时错误。此外,Mapper接口也可以通过XML配置文件定义SQL语句和参数映射,这种方式相对更灵活,但也更容易出错。

sqlSession.getMapper()的代码逻辑

SqlSession是 MyBatis 框架中的核心类之一,用于操作数据库。getMapper 方法是 SqlSession 接口中的一个方法,用于获取一个 DAO 接口的实现类对象。getMapper 方法的代码逻辑如下:

  1. 接收一个 Class 类型的参数,表示需要获取实现类对象的 DAO 接口类。

  1. 通过调用 Configuration 对象的 getMapperRegistry 方法获取一个 MapperRegistry 对象,用于管理所有的 Mapper。

  1. 通过调用 MapperRegistry 对象的 getMapper 方法获取一个 MapperProxyFactory 对象。

  1. 通过调用 MapperProxyFactory 对象的 newInstance 方法,创建一个代理对象。

  1. 返回代理对象。

在步骤 3 中,getMapper 方法会先从 knownMappers 缓存中查找是否已经存在对应的 Mapper,如果已经存在,则直接返回缓存中的 Mapper。如果不存在,则创建一个新的 MapperProxyFactory 对象,并添加到 knownMappers 缓存中。

在步骤 4 中,newInstance 方法会创建一个代理对象,该代理对象实现了 DAO 接口,并重写了其中的所有方法。当代理对象的方法被调用时,会先获取一个 MappedStatement 对象,该对象表示了需要执行的 SQL 语句和参数信息。然后通过 SqlSession 对象的 selectListselectOneinsertupdatedelete 等方法执行 SQL 语句,最后返回查询结果或受影响的行数等信息。

### MyBatis 中使用的设计模式及其应用 #### 1. **工厂模式** MyBatis 使用了工厂模式来创建 SqlSession 对象。SqlSessionFactory 是一个接口,其具体实现类 DefaultSqlSessionFactory 负责生产 SqlSession 实例。通过这种方式,客户端无需关心具体的创建细节,只需调用 `openSession()` 方法即可获得所需的会话对象[^1]。 ```java SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); SqlSession session = sqlSessionFactory.openSession(); ``` #### 2. **建造者模式** SqlSessionFactory 的实例化过程体现了建造者模式的应用。`SqlSessionFactoryBuilder` 类作为建造者角色,逐步解析 XML 配置文件或者手动配置的内容,并最终构建出完整的 `SqlSessionFactory` 对象[^1]。 ```java Reader reader = Resources.getResourceAsReader("mybatis-config.xml"); SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(reader); ``` #### 3. **单例模式** 在 MyBatis 中,默认情况下 Mapper 接口对应的代理对象是以单例形式存在的(除非显式设置 scope="prototype")。这意味着同一个 Mapper 文件只会生成唯一的代理实例,从而减少资源消耗并提高性能[^1]。 #### 4. **代理模式** MyBatis 广泛采用了动态代理技术来处理 SQL 映射逻辑。当用户调用 Mapper 接口的方法时,实际上是由底层的 JdkDynamicProxy 或 CglibProxy 完成真正的查询工作[^2]。例如: ```java UserMapper mapper = sqlSession.getMapper(UserMapper.class); List<User> users = mapper.selectAllUsers(); ``` 这里 `mapper` 就是一个由 MyBatis 自动生成的代理对象,它拦截了对 `selectAllUsers()` 方法的调用,并将其转换为实际的 JDBC 操作。 #### 5. **适配器模式** 为了兼容不同的数据库驱动程序以及简化开发者的工作量,MyBatis 提供了一套统一的数据源适配机制。无论是连接池还是事务管理器都可以轻松集成进来而不需要修改核心代码结构。 --- ### 关于发布订阅模式是否应用于 MyBatis 尽管 MyBatis 自身并未直接依赖传统的发布订阅模型来进行功能开发,但在某些高级特性上确实存在类似的解耦设计理念。比如缓存刷新策略中可能存在异步通知机制;又或者是插件体系允许第三方扩展点注册监听器以响应特定事件的发生等情形均可以看作是对该思想的一种变体实现[^3]。不过严格意义上讲,这些并不完全符合经典意义上的 Publish-Subscribe Pattern 描述[^4]。 --- ###
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值