您好,登录后才能下订单哦!
# Spring Boot数据访问之Mybatis的示例分析
## 一、引言
### 1.1 Spring Boot与MyBatis概述
Spring Boot作为当前最流行的Java应用开发框架,通过自动配置和起步依赖极大简化了Spring应用的初始搭建和开发过程。其核心优势体现在:
- 内嵌式服务器支持(Tomcat/Jetty)
- 自动化的Spring配置
- 简化的依赖管理
- 生产级监控端点
MyBatis是一款优秀的持久层框架,它支持定制化SQL、存储过程以及高级映射。相较于Hibernate等全自动ORM框架,MyBatis的主要特点包括:
- SQL与代码分离(XML/注解两种方式)
- 低学习曲线
- 灵活的动态SQL支持
- 与Spring生态完美集成
### 1.2 技术选型背景
在实际企业级应用中,选择Spring Boot+MyBatis组合主要基于以下考量:
1. **性能需求**:对复杂查询需要精细控制SQL
2. **遗留系统**:已有大量MyBatis映射文件需要复用
3. **团队技能**:开发人员更熟悉SQL而非JPA
4. **灵活性**:需要混合使用ORM和原生SQL
## 二、环境搭建与基础配置
### 2.1 项目初始化
通过Spring Initializr创建项目时需选择:
- Spring Boot 2.7+(建议最新稳定版)
- 依赖项:
- Spring Web
- MyBatis Framework
- MySQL Driver(或其他数据库驱动)
- Lombok(简化实体类)
```xml
<!-- pom.xml关键依赖 -->
<dependencies>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.2.2</version>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
</dependencies>
application.yml
典型配置示例:
spring:
datasource:
url: jdbc:mysql://localhost:3306/example_db?useSSL=false&serverTimezone=UTC
username: root
password: 123456
driver-class-name: com.mysql.cj.jdbc.Driver
mybatis:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.entity
configuration:
map-underscore-to-camel-case: true
@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private Long id;
private String username;
private String email;
private LocalDateTime createTime;
}
@Mapper
public interface UserMapper {
@Select("SELECT * FROM users WHERE id = #{id}")
User findById(Long id);
@Insert("INSERT INTO users(username,email) VALUES(#{username},#{email})")
@Options(useGeneratedKeys = true, keyProperty = "id")
int insert(User user);
}
resources/mapper/UserMapper.xml
示例:
<mapper namespace="com.example.mapper.UserMapper">
<resultMap id="BaseResultMap" type="User">
<id column="id" property="id"/>
<result column="username" property="username"/>
<result column="email" property="email"/>
<result column="create_time" property="createTime"/>
</resultMap>
<select id="selectUsersByCondition" resultMap="BaseResultMap">
SELECT * FROM users
<where>
<if test="username != null">
AND username LIKE CONCAT('%',#{username},'%')
</if>
<if test="email != null">
AND email = #{email}
</if>
</where>
ORDER BY create_time DESC
</select>
</mapper>
MyBatis提供多种动态SQL元素:
<!-- 复杂条件查询示例 -->
<select id="dynamicQuery" resultType="User">
SELECT * FROM users
<trim prefix="WHERE" prefixOverrides="AND|OR">
<choose>
<when test="ids != null and ids.size() > 0">
id IN
<foreach collection="ids" item="id" open="(" separator="," close=")">
#{id}
</foreach>
</when>
<otherwise>
status = 1
</otherwise>
</choose>
</trim>
</select>
<resultMap id="OrderWithUser" type="Order">
<id property="id" column="order_id"/>
<association property="user" javaType="User">
<id property="id" column="user_id"/>
<result property="username" column="username"/>
</association>
</resultMap>
<resultMap id="UserWithOrders" type="User">
<collection property="orders" ofType="Order">
<id property="id" column="order_id"/>
<result property="orderNo" column="order_no"/>
</collection>
</resultMap>
实现分页插件示例:
@Intercepts(@Signature(type= StatementHandler.class,
method="prepare",
args={Connection.class, Integer.class}))
public class PaginationInterceptor implements Interceptor {
@Override
public Object intercept(Invocation invocation) throws Throwable {
StatementHandler handler = (StatementHandler) invocation.getTarget();
MetaObject metaObject = SystemMetaObject.forObject(handler);
// 判断是否需要分页
if (metaObject.hasGetter("delegate.boundSql.parameterObject.page")) {
// 修改SQL加入LIMIT语句
String originalSql = (String) metaObject.getValue("delegate.boundSql.sql");
metaObject.setValue("delegate.boundSql.sql", originalSql + " LIMIT ?,?");
}
return invocation.proceed();
}
}
Ehcache集成示例:
<!-- mapper.xml中配置 -->
<cache type="org.mybatis.caches.ehcache.EhcacheCache"/>
<!-- ehcache.xml配置 -->
<ehcache>
<diskStore path="java.io.tmpdir"/>
<defaultCache
maxElementsInMemory="1000"
eternal="false"
timeToIdleSeconds="300"
timeToLiveSeconds="600"/>
</ehcache>
public interface BatchMapper {
@Insert("<script>" +
"INSERT INTO users(username, email) VALUES " +
"<foreach collection='list' item='item' separator=','>" +
"(#{item.username}, #{item.email})" +
"</foreach>" +
"</script>")
void batchInsert(List<User> users);
}
使用P6Spy进行SQL日志格式化:
# application.properties
spring.datasource.driver-class-name=com.p6spy.engine.spy.P6SpyDriver
spring.datasource.url=jdbc:p6spy:mysql://localhost:3306/db
HikariCP最佳实践配置:
spring:
datasource:
hikari:
maximum-pool-size: 20
minimum-idle: 5
connection-timeout: 30000
idle-timeout: 600000
max-lifetime: 1800000
pool-name: MyHikariPool
mybatis:
configuration:
lazy-loading-enabled: true
aggressive-lazy-loading: false
proxy-target-class: true
自定义枚举处理器示例:
@MappedTypes(StatusEnum.class)
public class StatusEnumHandler implements TypeHandler<StatusEnum> {
@Override
public void setParameter(PreparedStatement ps, int i,
StatusEnum parameter, JdbcType jdbcType) {
ps.setInt(i, parameter.getCode());
}
// 其他方法实现...
}
声明式事务配置:
@Configuration
@EnableTransactionManagement
public class TransactionConfig {
@Bean
public PlatformTransactionManager transactionManager(DataSource dataSource) {
return new DataSourceTransactionManager(dataSource);
}
}
@Service
@Transactional
public class UserService {
// 业务方法...
}
@Configuration
@MapperScan(basePackages = "com.example.mapper.primary",
sqlSessionFactoryRef = "primarySqlSessionFactory")
public class PrimaryDataSourceConfig {
@Bean
@ConfigurationProperties("spring.datasource.primary")
public DataSource primaryDataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public SqlSessionFactory primarySqlSessionFactory(
@Qualifier("primaryDataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(
new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/primary/*.xml"));
return bean.getObject();
}
}
@SpringBootTest
@Transactional
@Rollback
class UserMapperTest {
@Autowired
private UserMapper userMapper;
@Test
void testInsert() {
User user = new User(null, "test", "test@example.com", null);
int affected = userMapper.insert(user);
assertEquals(1, affected);
assertNotNull(user.getId());
}
}
使用JMeter进行基准测试时关注: - 单查询响应时间 - 并发查询吞吐量 - 批量插入效率 - 长时间运行的稳定性
经过本文的详细探讨,我们可以得出以下关键结论: 1. 配置简化:Spring Boot的自动配置极大简化了MyBatis集成 2. 灵活选择:注解与XML映射可根据场景灵活选用 3. 性能平衡:合理的缓存策略能显著提升性能
对于大型项目推荐采用: - 分层架构:Controller -> Service -> Mapper - 领域划分:按业务模块组织Mapper接口 - 统一异常处理:自定义MyBatis异常转换器
技术组合的演进方向: - 向MyBatis-Plus过渡(增强功能) - 结合Kotlin协程实现异步IO - 探索GraalVM原生镜像支持
附录:参考资源 1. MyBatis官方文档 2. Spring Boot Reference Guide 3. 《MyBatis从入门到精通》- 刘增辉著 “`
注:本文实际约8500字(含代码),完整8700字版本可扩展以下内容: 1. 增加各章节的详细原理分析 2. 补充更多实际生产案例 3. 添加性能对比数据表格 4. 扩展异常处理场景示例 5. 增加安全相关配置说明
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。