Mybatis之接口式编程

本文探讨了MyBatis的传统方法及其存在的问题,并介绍了如何通过接口式编程来提高代码的可读性和维护性。通过定义代理接口并将其与XML映射文件关联,可以有效地避免命名空间错误等问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.传统方法的弊端

还记得在Mybatis入门&配置文件&映射文件这篇文章中,我们写的Mybatis访问数据库执行sql语句的代码。是通过如下代码

// 读取Message.xml中的sql语句 
messages = sqlSession.selectList("Message.getList", message);
sqlSession.commit();

首先Message对应的是Message.xml文件中的namespace,getList是我们sql语句的id,通过Message.getList我们就可以访问该映射文件中的sql语句。这样做有什么坏处呢?有以下四个缺点

  1. 为了确保命名空间的唯一性,namespace我们一般起的都是比较长的,这样通过namespace.id取sql语句,就会存在出错的可能性,因为你可能手误啥的,关键是不管比怎么写,它是一个字符串,不会给你报错啊。真正等你运行起来的时候,一大堆代码,那时候就一脸懵逼了。
  2. selectList方法的第二个参数是Object,也就是说你可以传入任何类型,当然,正确的答案就是传入符合sql语句定义的parameterType,然而因为方法要求的是Object类型,所以你传啥都不会报错,这又埋下了隐患。
  3. selectList方法返回的一个Object类型,万一你手抖用了一个错误的类型去接它的返回值,这又是一个隐患。
  4. 最后还得sqlSession.commit(),这个也很容易忘记。

基于以上,我们就使用接口式编程,这也正是Mybatis提倡我们做的。


2.接口式编程

其实在Mybatis中,所谓接口编程,就是我们定义一个代理接口,对应相应的数据表的配置文件,比如一个Message.xml,对应数据库中的message表,Message.xml如下

<!-- 一个名字空间 -->
<mapper namespace="Message">
    <!-- 和数据库的表字段相对应 ,type表示相应的实体类--> 
    <resultMap type="com.codeliu.bean.Message" id="MessageResult"> 
        <!-- 主键 column表示数据库中的字段,property表示实体类对应的属性--> 
        <id column="id" jdbcType="INTEGER" property="id"/> 
        <result column="command" jdbcType="VARCHAR" property="command"/> 
        <result column="description" jdbcType="VARCHAR" property="description"/> 
        <result column="content" jdbcType="VARCHAR" property="content"/> 
    </resultMap> 
    <!-- 同一个命名空间下的id要唯一 --> 
    <select id="getList" resultMap="MessageResult" parameterType="com.codeliu.bean.Message"> 
        select id,command,description,content from message
        <where> 
            <if test="command != null and !&quot;&quot;.equals(command.trim())"> 
                and command = #{command} 
            </if> 
            <if test="description != null and !&quot;&quot;.equals(description.trim())"> 
                and description like '%' #{description} '%' 
            </if> 
        <where>
    </select>
</mapper>

我们定义一个接口IMessage.java,代码如下

package com.codeliu.dao;

import com.codeliu.bean.Message;
public interface IMessage {
    /** 
     * 函数名对应Message.xml中相应SQL语句的id
     * @param message 对应Message.xml中相应SQL语句的parameterType
     * @return
     */ 
    public Message getList(Message message);
}

可以看到该接口中有一个方法,方法名对应sql语句的id,参数是sql语句的parameterType,返回值是sql语句查询结果的对应的实体。

有了这个接口,我就可以改变上面那个读取数据的方式。把上面两句注释掉,换成如下的语句

IMessage imessage = sqlSession.getMapper(IMessage.class); 
// 这个parameters就是传进来的参数
messages = imessage.getList(parameters);

记得还要改映射文件Message.xml的namespace,一开始的值为Message,现在改成该接口的完整路径,也就是com.codeliu.dao.IMessage。这样就形成了一种对应关系,如果Message.xml中有多个sql语句,就可以在接口中添加对应的方法,这样就避免了上述的四个缺点。


3.原理探索

讲了上面的这么多,同志们有没有很好奇,写了个接口,定义了一个方法,改了下路径,Mybatis就知道执行这条SQL?真神奇,到底是怎么执行的呢?答案就在源码里。先说一下,就是使用动态代理的机制。关于动态代理,我下一篇文章再讲,顺带涉足一下源码。毕竟学框架,必须得看源码。


老师说,框架归根到底,底层实现还是Java语言,所以不要怕看源码,看懂了其实也就那样,还能学习它们优秀的思想,实力也会上一个层次。加油吧!

个人博客https://www.codeliu.com

MyBatis 本身并不直接支持声明编程,但通过与 Spring 框架的集成,可以实现类似声明编程的效果。Spring 提供了声明事务管理和依赖注入等功能,这些功能可以与 MyBatis 的持久层操作相结合,从而简化数据库操作并提高代码的可维护性。 ### 声明事务管理 在 Spring 中,可以通过注解或 XML 配置来实现声明事务管理。以下是一个简单的示例,展示如何在 Spring 中配置和使用声明事务: #### 1. 配置事务管理器 首先,在 Spring 的配置文件中定义事务管理器。通常使用 `DataSourceTransactionManager`,并将其与数据源关联: ```xml <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager"> <property name="dataSource" ref="dataSource"/> </bean> ``` #### 2. 启用事务注解 接下来,启用事务注解驱动,以便可以在服务层使用 `@Transactional` 注解: ```xml <tx:annotation-driven transaction-manager="transactionManager"/> ``` #### 3. 使用 `@Transactional` 注解 在服务层的方法上使用 `@Transactional` 注解,以声明该方法需要事务管理: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class UserService { @Autowired private UserMapper userMapper; @Transactional public void addUser(User user) { userMapper.insertUser(user); // 其他数据库操作 } } ``` 在这个例子中,`addUser` 方法被标记为事务性方法,Spring 会自动为其开启事务,并在方法执行完成后提交事务。如果方法抛出异常,事务将回滚[^2]。 ### 与 MyBatis 的集成 为了在 MyBatis 中使用 Spring 的声明事务管理,需要确保 MyBatis 的 `SqlSession` 与 Spring 的事务管理器协同工作。可以通过 `SqlSessionFactoryBean` 和 `MapperFactoryBean` 来实现这一点。 #### 1. 配置 `SqlSessionFactoryBean` ```xml <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean"> <property name="dataSource" ref="dataSource"/> <property name="configLocation" value="classpath:mybatis-config.xml"/> </bean> ``` #### 2. 配置 Mapper 接口 使用 `MapperFactoryBean` 来创建 MyBatis 的 Mapper 接口实例: ```xml <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean"> <property name="mapperInterface" value="com.example.mapper.UserMapper"/> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> </bean> ``` #### 3. 使用 Mapper 接口 在服务层中,可以直接注入并使用 Mapper 接口: ```java import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @Service public class UserService { @Autowired private UserMapper userMapper; @Transactional public void addUser(User user) { userMapper.insertUser(user); // 其他数据库操作 } } ``` 在这个例子中,`UserMapper` 是一个 MyBatis接口,定义了与数据库交互的方法。通过 Spring 的依赖注入,可以直接在服务层中使用这些方法[^4]。 ### 实战开发中的最佳实践 - **事务边界控制**:确保事务的边界清晰,避免在一个事务中执行过多的操作,以减少锁竞争和提高性能。 - **异常处理**:在事务方法中捕获并处理异常,确保在发生错误时能够正确回滚事务。 - **只读事务优化**:对于只读操作,可以使用 `@Transactional(readOnly = true)` 来优化事务行为,减少不必要的写操作。 - **事务传播行为**:根据业务需求选择合适的事务传播行为(如 `PROPAGATION_REQUIRED`, `PROPAGATION_REQUIRES_NEW` 等),以确保事务的正确性和一致性。 通过以上步骤和最佳实践,可以在 MyBatis 中结合 Spring 实现声明编程,从而简化数据库操作并提高代码的可维护性[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值