首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Spring 整合 MyBatis 完整實戰:從 XML 配置到註解開發的最佳實踐

     編輯:Java 相關知識

前言

MyBatis 作為半自動 ORM 框架,以 SQL 可控、映射靈活、學習曲線平緩深受國內開發者喜愛。本文系統講解 Spring (4.x/5.x) 整合 MyBatis (3.4+) 的完整配置體系,涵蓋 XML 與註解雙模式、分頁插件、類型處理器、事務管理等生產級最佳實踐。

技術選型對比

維度JDBC TemplateHibernateMyBatis
配置複雜度低高中
SQL 可控性完全手寫HQL/CriteriaXML/註解映射
複雜查詢字符串拼接痛苦難以優化動態 SQL 強大
多表關聯手寫映射關係映射複雜ResultMap 靈活
學習曲線平陡峭緩和
適用場景簡單增刪改查領域模型豐富SQL 驅動、報表、遷移

Maven 依賴 (Spring 5.3 + MyBatis 3.5 + MySQL 8)

<properties>
    <spring.version>5.3.21</spring.version>
    <mybatis.version>3.5.13</mybatis.version>
    <mybatis-spring.version>2.0.7</mybatis-spring.version>
    <mysql.version>8.0.33</mysql.version>
    <druid.version>1.2.18</druid.version>
</properties>

<dependencies>
    <!-- Spring Core -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
    </dependency>

    <!-- MyBatis -->
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis</artifactId>
        <version>${mybatis.version}</version>
    </dependency>
    <dependency>
        <groupId>org.mybatis</groupId>
        <artifactId>mybatis-spring</artifactId>
        <version>${mybatis-spring.version}</version>
    </dependency>

    <!-- Database -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>${mysql.version}</version>
    </dependency>
    <dependency>
        <groupId>com.alibaba</groupId>
        <artifactId>druid</artifactId>
        <version>${druid.version}</version>
    </dependency>
</dependencies>

Spring XML 配置核心三 Bean

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="https://www.springframework.org/schema/beans"
       xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="https://www.springframework.org/schema/beans
       https://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 1. 數據源: Druid 高性能連接池 -->
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" destroy-method="close">
        <property name="driverClassName" value="com.mysql.cj.jdbc.Driver"/>
        <property name="url" value="jdbc:mysql://localhost:3306/mydb?useSSL=false&serverTimezone=Asia/Shanghai"/>
        <property name="username" value="root"/>
        <property name="password" value="123456"/>
        <property name="initialSize" value="5"/>
        <property name="maxActive" value="20"/>
        <property name="minIdle" value="5"/>
        <property name="maxWait" value="60000"/>
        <property name="validationQuery" value="SELECT 1"/>
        <property name="testWhileIdle" value="true"/>
        <property name="timeBetweenEvictionRunsMillis" value="60000"/>
    </bean>

    <!-- 2. SqlSessionFactory: MyBatis 核心工廠 -->
    <bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
        <property name="dataSource" ref="dataSource"/>
        <property name="mapperLocations" value="classpath:mapper/**/*Mapper.xml"/>
        <property name="typeAliasesPackage" value="com.example.entity"/>
        <property name="configurationProperties">
            <props>
                <prop key="mapUnderscoreToCamelCase">true</prop>
                <prop key="cacheEnabled">true</prop>
                <prop key="lazyLoadingEnabled">true</prop>
                <prop key="aggressiveLazyLoading">false</prop>
            </props>
        </property>
        <!-- 分頁插件 PageHelper -->
        <property name="plugins">
            <array>
                <bean class="com.github.pagehelper.PageInterceptor">
                    <property name="properties">
                        <props>
                            <prop key="helperDialect">mysql</prop>
                            <prop key="reasonable">true</prop>
                            <prop key="supportMethodsArguments">true</prop>
                            <prop key="params">pageNum=pageNum;pageSize=pageSize</prop>
                        </props>
                    </property>
                </bean>
            </array>
        </property>
    </bean>

    <!-- 3. Mapper 掃描器: 自動掃描 Dao 接口生成代理 -->
    <bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
        <property name="basePackage" value="com.example.dao"/>
        <property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
    </bean>

    <!-- 事務管理 -->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource"/>
    </bean>
    <tx:annotation-driven transaction-manager="transactionManager"/>

</beans>

核心組件解析

SqlSessionFactoryBean 關鍵屬性

  • mapperLocations:Ant 路徑匹配 Mapper XML,支持多模塊 classpath*:mapper/**/*Mapper.xml
  • typeAliasesPackage:實體類別名簡寫,XML 中直接用 resultType="User"
  • configurationProperties:覆蓋 MyBatis 全局配置 (駝峰映射、緩存、懶加載等)
  • plugins:攔截器鏈,集成 PageHelper、通用 Mapper、加密插件等

MapperScannerConfigurer 掃描機制

  • 掃描 basePackage 下所有接口
  • 為每個接口創建 MapperFactoryBean 註冊到 Spring 容器
  • 注入時自動代理,調用方法委託給 SqlSessionTemplate

Dao 接口與 Mapper XML 映射

實體類

@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private Long id;
    private String username;
    private String email;
    private LocalDateTime createTime;
}

Dao 接口 (註解模式 + XML 混用)

@Repository
public interface UserDao {
    @Select("SELECT * FROM t_user WHERE id = #{id}")
    User getById(Long id);

    @Insert("INSERT INTO t_user(username, email, create_time) VALUES(#{username}, #{email}, NOW())")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(User user);

    @Update("UPDATE t_user SET username=#{username}, email=#{email} WHERE id=#{id}")
    int update(User user);

    @Delete("DELETE FROM t_user WHERE id=#{id}")
    int deleteById(Long id);

    // 複雜查詢建議用 XML
    List<User> findByCondition(@Param("username") String username, 
                               @Param("email") String email,
                               @Param("pageNum") int pageNum,
                               @Param("pageSize") int pageSize);
}

Mapper XML (複雜動態 SQL)

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
"https://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.dao.UserDao">

    <resultMap id="userMap" type="User">
        <id property="id" column="id"/>
        <result property="username" column="username"/>
        <result property="email" column="email"/>
        <result property="createTime" column="create_time"/>
    </resultMap>

    <sql id="baseColumns">
        id, username, email, create_time
    </sql>

    <select id="findByCondition" resultMap="userMap">
        SELECT <include refid="baseColumns"/>
        FROM t_user
        <where>
            <if test="username != null and username != ''">
                AND username LIKE CONCAT('%', #{username}, '%')
            </if>
            <if test="email != null and email != ''">
                AND email LIKE CONCAT('%', #{email}, '%')
            </if>
        </where>
        ORDER BY create_time DESC
    </select>

</mapper>

進階最佳實踐

1. 通用 Mapper (tk.mybatis / mybatis-plus)

public interface UserDao extends BaseMapper<User> {
    // 自動擁有: selectById, insert, updateById, deleteById, selectList, selectPage...
}

2. 類型處理器 (枚舉、JSON、加密)

@MappedTypes(UserStatus.class)
public class UserStatusTypeHandler extends BaseTypeHandler<UserStatus> {
    @Override
    public void setNonNullParameter(PreparedStatement ps, int i, UserStatus param, JdbcType jdbcType) {
        ps.setInt(i, param.getCode());
    }
    @Override
    public UserStatus getNullableResult(ResultSet rs, String colName) {
        return UserStatus.of(rs.getInt(colName));
    }
    // 其他 getNullableResult 重載...
}
// 註冊: configuration.getTypeHandlerRegistry().register(UserStatus.class, UserStatusTypeHandler.class);

3. 多數據源路由

@Primary
@Bean
public DataSource masterDataSource() { ... }

@Bean
public DataSource slaveDataSource() { ... }

@Bean
public DynamicDataSource dataSource() {
    DynamicDataSource ds = new DynamicDataSource();
    ds.setTargetDataSources(Map.of("master", masterDataSource(), "slave", slaveDataSource()));
    ds.setDefaultTargetDataSource(masterDataSource());
    return ds;
}

// AOP 切換: @Master / @Slave 註解 + AbstractRoutingDataSource

4. 事務邊界控制

@Service
@Transactional(rollbackFor = Exception.class)
public class UserService {
    @Autowired UserDao userDao;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    public void insertWithLog(User user) {
        userDao.insert(user);
        logDao.insert(new Log("INSERT", user.getId()));
    }
}

常見問題排查

現象原因解決
BindingException: Invalid bound statementMapper 接口方法與 XML id 不匹配 / XML 未掃描檢查 namespace、id、mapperLocations 路徑
駝峰映射失效mapUnderscoreToCamelCase 未生效SqlSessionFactoryBean configurationProperties 設置
分頁插件不生效PageInterceptor 未註冊 / 參數名不匹配plugins 配置 + params=pageNum=pageNum;pageSize=pageSize
事務不回滾未掃描 @Transactional / 方法非 public / 捕獲異常未拋出tx:annotation-driven + rollbackFor=Exception.class

總結

Spring + MyBatis 整合核心三件套:DataSource + SqlSessionFactory + MapperScanner。掌握 XML 動態 SQL、註解簡單映射混用、分頁插件、類型處理器、事務傳播、多數據源 等進階模式,即可應對 95% 的持久層開發場景。建議新項目優先考慮 MyBatis-Plus 以獲得開箱即用的 CRUD、分頁、條件構造器等增強能力。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved