1. 为什么选择Spring Boot + MyBatis + PostgreSQL组合
三年前接手一个电商后台重构项目时,我首次将这三个技术栈组合使用。当时团队在技术选型会上争论不休——有人坚持用老旧的Hibernate,有人推崇新兴的JPA。最终我们选择这个组合的原因很实际:Spring Boot的快速启动能力让项目两周就搭出了基础框架,MyBatis的SQL可控性解决了复杂商品查询的性能问题,而PostgreSQL的JSONB类型完美存储了商品的多维属性。
这个技术组合的核心优势在于:
- 开发效率:Spring Boot的starter机制让依赖管理变得极其简单,一个
spring-boot-starter-jdbc就解决了大部分数据库连接问题 - SQL可控性:MyBatis的XML映射文件让复杂联表查询的优化变得可视化,我们曾通过优化一个商品列表查询将响应时间从800ms降到120ms
- 数据类型支持:PostgreSQL特有的JSONB类型让我们无需为频繁变更的商品扩展属性频繁修改表结构
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 环境搭建与基础配置
2.1 PostgreSQL安装与配置技巧
在Windows环境下安装PostgreSQL 15时,有几点容易踩坑:
- 安装路径不要包含中文或空格,否则后期可能出现权限问题
- 初始化数据库时建议将
locale设为C(命令:initdb -U postgres -E UTF8 --locale=C),可以避免排序规则带来的索引失效问题 - 修改
postgresql.conf中的以下关键参数:
properties复制shared_buffers = 4GB # 根据机器内存调整,建议25%总内存
effective_cache_size = 12GB # 通常设为shared_buffers的3倍
work_mem = 16MB # 复杂查询时可临时调大
maintenance_work_mem = 256MB # 建索引等操作时使用
重要提示:安装完成后务必修改postgres用户的密码,默认安装后密码为空是常见安全漏洞
2.2 Spring Boot项目初始化
使用Spring Initializr创建项目时,除了选择Web和PostgreSQL驱动,建议额外添加:
- Lombok(减少样板代码)
- Configuration Processor(配置提示)
- MyBatis Framework(官方starter)
关键pom.xml依赖:
xml复制<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.3</version>
</dependency>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
<scope>runtime</scope>
</dependency>
application.yml典型配置:
yaml复制spring:
datasource:
url: jdbc:postgresql://localhost:5432/demo_db
username: postgres
password: yourStrongPassword
hikari:
maximum-pool-size: 20
connection-timeout: 30000
mybatis:
mapper-locations: classpath:mapper/*.xml
configuration:
map-underscore-to-camel-case: true
3. MyBatis深度集成实战
3.1 动态SQL的进阶用法
在商品筛选功能中,我们经常需要构建动态查询条件。MyBatis的<script>标签比传统<where>更灵活:
xml复制<select id="selectProducts" resultType="Product">
<script></script>
</select>
这个查询演示了:
- 多条件动态拼接
- PostgreSQL的JSONB查询语法
- 动态排序逻辑
3.2 批量操作的性能优化
在订单系统中,批量插入是常见需求。对比三种实现方式:
| 方法 | 10,000条耗时 | 内存占用 |
|---|---|---|
| 单条循环插入 | 48s | 低 |
| MyBatis批量插入 | 3.2s | 中 |
| PostgreSQL COPY命令 | 0.8s | 高 |
推荐方案:
java复制@Insert("<script>" +
"INSERT INTO orders(order_no, user_id, amount) VALUES " +
"<foreach collection='list' item='item' separator=','>" +
"(#{item.orderNo}, #{item.userId}, #{item.amount})" +
"</foreach>" +
"</script>")
void batchInsert(@Param("list") List<Order> orders);
性能提示:当数据量超过10万时,建议使用PostgreSQL的COPY命令,可通过JDBC的CopyManager实现
4. PostgreSQL特性深度利用
4.1 JSONB类型实战
商品表设计示例:
sql复制CREATE TABLE products (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL,
price DECIMAL(10,2) NOT NULL,
attributes JSONB,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Java实体类对应:
java复制@Data
public class Product {
private Integer id;
private String name;
private BigDecimal price;
private JsonNode attributes; // 使用Jackson的JsonNode
private LocalDateTime createdAt;
}
复杂查询示例(查找所有红色且尺寸为XL的T恤):
sql复制SELECT * FROM products
WHERE attributes @> '{"color":"red", "size":"XL"}'
AND name LIKE '%T恤%'
4.2 高级特性应用
CTE递归查询处理商品分类层级:
sql复制WITH RECURSIVE category_tree AS (
SELECT id, name, parent_id FROM categories WHERE id = 1
UNION ALL
SELECT c.id, c.name, c.parent_id
FROM categories c
JOIN category_tree ct ON c.parent_id = ct.id
)
SELECT * FROM category_tree;
窗口函数计算销售排名:
sql复制SELECT
product_id,
SUM(quantity) as total_sales,
RANK() OVER (ORDER BY SUM(quantity) DESC) as sales_rank
FROM order_items
GROUP BY product_id
LIMIT 10;
5. 性能监控与调优
5.1 慢SQL定位方案
- 配置MyBatis日志:
yaml复制logging:
level:
org.mybatis: DEBUG
- PostgreSQL慢查询日志:
properties复制# postgresql.conf
log_min_duration_statement = 1000 # 记录超过1秒的查询
log_statement = 'all' # 记录所有SQL
- 使用EXPLAIN分析:
sql复制EXPLAIN ANALYZE
SELECT * FROM products
WHERE attributes @> '{"color":"red"}';
5.2 连接池优化建议
HikariCP关键参数配置:
yaml复制spring:
datasource:
hikari:
maximum-pool-size: 20 # 建议:(CPU核心数 * 2) + 有效磁盘数
minimum-idle: 5 # 与maximum-pool-size相同
idle-timeout: 600000 # 10分钟
max-lifetime: 1800000 # 30分钟
connection-timeout: 30000
leak-detection-threshold: 60000 # 1分钟
监控指标采集示例:
java复制@Scheduled(fixedRate = 60000)
public void monitorPool() {
HikariDataSource ds = (HikariDataSource)dataSource;
log.info("Active connections: {}", ds.getHikariPoolMXBean().getActiveConnections());
log.info("Idle connections: {}", ds.getHikariPoolMXBean().getIdleConnections());
}
6. 常见问题排查手册
6.1 连接泄露特征与排查
典型症状:
- 应用运行一段时间后出现
Connection is not available, request timed out after 30000ms错误 - PostgreSQL的
pg_stat_activity显示大量空闲连接
排查步骤:
- 启用Hikari的泄漏检测:
yaml复制leak-detection-threshold: 60000
- 查询活跃连接:
sql复制SELECT datname, usename, state, query
FROM pg_stat_activity
WHERE state != 'idle';
6.2 MyBatis缓存问题
二级缓存脏读现象:
- 现象:A服务更新数据后,B服务查询仍得到旧值
- 解决方案:
- 禁用二级缓存(默认已禁用):
yaml复制mybatis:
configuration:
cache-enabled: false
- 或为Mapper添加
@CacheNamespaceRef注解实现分布式缓存
6.3 PostgreSQL特有错误处理
常见错误:
-
ERROR: canceling statement due to conflict with recovery- 原因:主从复制冲突
- 解决方案:设置
hot_standby_feedback = on
-
ERROR: could not serialize access due to concurrent update- 原因:事务隔离级别冲突
- 解决方案:重试机制或使用
SELECT FOR UPDATE
7. 安全加固方案
7.1 SQL注入防护
MyBatis虽然预编译能防止大部分注入,但${}仍有风险:
xml复制<!-- 危险写法 -->
ORDER BY ${sortField}
<!-- 安全写法 -->
ORDER BY
<choose>
<when test="sortField == 'price'">price</when>
<otherwise>create_time</otherwise>
</choose>
7.2 PostgreSQL权限控制
创建应用专用用户:
sql复制CREATE ROLE app_user WITH LOGIN PASSWORD 'complexPassword';
GRANT CONNECT ON DATABASE demo_db TO app_user;
GRANT USAGE ON SCHEMA public TO app_user;
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA public TO app_user;
REVOKE ALL ON SCHEMA public FROM PUBLIC;
8. 扩展与进阶
8.1 分布式事务方案
使用Seata整合方案:
- 添加依赖:
xml复制<dependency>
<groupId>io.seata</groupId>
<artifactId>seata-spring-boot-starter</artifactId>
<version>1.7.1</version>
</dependency>
- 配置中心配置:
yaml复制seata:
enabled: true
application-id: order-service
tx-service-group: my_test_tx_group
service:
vgroup-mapping:
my_test_tx_group: default
8.2 多数据源配置
动态数据源配置示例:
java复制@Configuration
@MapperScan(basePackages = "com.demo.mapper.db1", sqlSessionTemplateRef = "db1SqlSessionTemplate")
public class Db1Config {
@Bean
@ConfigurationProperties("spring.datasource.db1")
public DataSource db1DataSource() {
return DataSourceBuilder.create().build();
}
@Bean
public SqlSessionFactory db1SqlSessionFactory(@Qualifier("db1DataSource") DataSource dataSource) throws Exception {
SqlSessionFactoryBean bean = new SqlSessionFactoryBean();
bean.setDataSource(dataSource);
bean.setMapperLocations(new PathMatchingResourcePatternResolver()
.getResources("classpath:mapper/db1/*.xml"));
return bean.getObject();
}
@Bean
public SqlSessionTemplate db1SqlSessionTemplate(
@Qualifier("db1SqlSessionFactory") SqlSessionFactory sqlSessionFactory) {
return new SqlSessionTemplate(sqlSessionFactory);
}
}
在实际项目中,我们通过这套技术栈处理了日均100万+订单的业务场景。关键收获是:MyBatis需要合理设计Mapper粒度,PostgreSQL要充分利用其JSONB和GIS特性,而Spring Boot的自动配置可以节省大量样板代码,但要清楚每个配置项背后的机制。
