MyBatis CRUD操作与高频问题解决方案

在Java持久层框架的演进历程中,MyBatis以其独特的SQL映射机制和灵活的配置方式,持续保持着旺盛的生命力。根据2023年JVM生态系统调查报告显示,MyBatis在数据访问框架中的采用率高达47%,远超JPA的32%。本文将深入剖析MyBatis的CRUD操作精髓,结合企业级开发中的真实案例,揭示90%开发者都会遇到的典型问题及其解决方案。

<insert id="insertUser" parameterType="User" useGeneratedKeys="true" keyProperty="id">
INSERT INTO users
(username, email, create_time)
VALUES
(#{username}, #{email}, #{createTime})
</insert>

当处理主键返回时需注意:

  • MySQL的AUTO_INCREMENT使用useGeneratedKeys
  • Oracle序列需要配合<selectKey>子元素
<insert id="insertOrder">
<selectKey keyProperty="id" resultType="long" order="BEFORE">
SELECT order_seq.nextval FROM dual
</selectKey>
INSERT INTO orders(id, order_no)
VALUES(#{id}, #{orderNo})
</insert>

动态结果映射的典型应用:

<resultMap id="userResultMap" type="User">
<id property="id" column="user_id"/>
<result property="username" column="user_name"/>
<collection property="roles" ofType="Role">
<id property="roleId" column="role_id"/>
<result property="roleName" column="role_name"/>
</collection>
</resultMap>

分页查询的三种实现方式对比:

  1. RowBounds内存分页(小数据量)
  2. PageHelper物理分页插件(推荐)
  3. 手动编写分页SQL(复杂查询)

批量更新最佳实践:

@Update("<script>" +
"UPDATE users SET status = #{status} WHERE id IN " +
"<foreach item='id' collection='ids' open='(' separator=',' close=')'>" +
"#{id}" +
"</foreach>" +
"</script>")
int batchUpdateStatus(@Param("ids") List<Long> ids, @Param("status") int status);

逻辑删除与物理删除的抉择:

<update id="deleteLogical" parameterType="long">
UPDATE users SET deleted = 1 WHERE id = #{id}
</update>
<delete id="deletePhysical" parameterType="long">
DELETE FROM users WHERE id = #{id}
</delete>
<select id="findUsers" resultMap="userResultMap">
SELECT * FROM users
<where>
<if test="username != null and username != ''">
AND username LIKE CONCAT('%',#{username},'%')
</if>
<choose>
<when test="status != null">
AND status = #{status}
</when>
<otherwise>
AND status = 1
</otherwise>
</choose>
</where>
ORDER BY create_time DESC
</select>

使用BatchExecutor提升插入效率:

try(SqlSession session = sqlSessionFactory.openSession(ExecutorType.BATCH)) {
UserMapper mapper = session.getMapper(UserMapper.class);
for (int i = 0; i < 10000; i++) {
mapper.insert(new User("user"+i, "user"+i+"@example.com"));
if(i % 500 == 0) {
session.flushStatements();
}
}
session.commit();
}

配置懒加载策略:

<settings>
<setting name="lazyLoadingEnabled" value="true"/>
<setting name="aggressiveLazyLoading" value="false"/>
</settings>

典型错误场景:

// 错误示例:未使用@Param注解多个参数
List<User> findByCondition(String name, Integer status);
// 正确用法
List<User> findByCondition(
@Param("name") String name,
@Param("status") Integer status);

字段映射失败的常见原因:

  • 数据库字段命名(下划线)与Java属性(驼峰)不匹配
  • 复杂类型缺少类型处理器
  • 嵌套结果集配置错误

解决方案:

<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>

危险写法:

@Select("SELECT * FROM users WHERE username = '${value}'")
User findByUsername(String username);

安全写法:

@Select("SELECT * FROM users WHERE username = #{username}")
User findByUsername(@Param("username") String username);

二级缓存配置要点:

<cache eviction="LRU"
flushInterval="60000"
size="512"
readOnly="true"/>

缓存失效的常见场景:

  • 执行任意insert/update/delete操作
  • 调用sqlSession.clearCache()
  • 设置flushCache="true"

自定义JSON类型处理示例:

public class JsonTypeHandler extends BaseTypeHandler<Map<String, Object>> {
private static final ObjectMapper mapper = new ObjectMapper();
@Override
public void setNonNullParameter(PreparedStatement ps, int i,
Map<String, Object> parameter, JdbcType jdbcType) {
ps.setString(i, serialize(parameter));
}
private String serialize(Map<String, Object> map) {
try {
return mapper.writeValueAsString(map);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
}
<select id="callProcedure" statementType="CALLABLE">
{call calculate_statistics(
#{startDate, mode=IN, jdbcType=DATE},
#{endDate, mode=IN, jdbcType=DATE},
#{total, mode=OUT, jdbcType=INTEGER}
)}
</select>

配置多个SqlSessionTemplate:

@Bean(name = "primarySqlSessionTemplate")
public SqlSessionTemplate primarySqlSessionTemplate(
@Qualifier("primarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
@Bean(name = "secondarySqlSessionTemplate")
public SqlSessionTemplate secondarySqlSessionTemplate(
@Qualifier("secondarySqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
  • SimpleExecutor:默认模式,每个语句创建新PreparedStatement
  • ReuseExecutor:重用预处理语句
  • BatchExecutor:批量操作优化

Druid连接池推荐配置:

# 初始连接数 = 平均QPS * 平均响应时间(秒)
druid.initialSize=5
# 最大连接数 = 峰值QPS * 平均响应时间 * 安全系数(1.2~1.5)
druid.maxActive=20
# 最小空闲连接 = 平均QPS * 平均响应时间
druid.minIdle=5
# 获取连接超时时间 = 业务容忍时间 - 平均响应时间
druid.maxWait=3000
  • 慢SQL阈值:超过500ms的查询
  • 缓存命中率:应保持在80%以上
  • 连接等待次数:每分钟超过100次需扩容
  1. XML配置规范:

    • 保持mapper文件与接口同名同包
    • SQL id与接口方法严格对应
    • 每个mapper文件不宜超过300行
  2. 版本控制策略:

    <!-- 乐观锁实现 -->
    <update id="updateWithVersion">
    UPDATE orders
    SET amount = #{amount}, version = version + 1
    WHERE id = #{id} AND version = #{version}
    </update>
  3. 代码生成器定制:

    <!-- MyBatis Generator插件配置 -->
    <generatorConfiguration>
    <context id="mysql" targetRuntime="MyBatis3DynamicSql">
    <plugin type="org.mybatis.generator.plugins.SerializablePlugin"/>
    <commentGenerator>
    <property name="suppressDate" value="true"/>
    </commentGenerator>
    </context>
    </generatorConfiguration>

通过深入掌握这些核心知识点,开发者可以显著提升MyBatis使用效率。某电商平台在应用这些优化策略后,其订单系统的数据库响应时间从平均120ms降低至45ms,事务失败率下降82%。建议定期进行SQL审计,结合EXPLAIN分析执行计划,持续优化数据访问层性能。

正文到此结束
评论插件初始化中...
Loading...
本文目录