| 組件 | 版本 | 選型理由 | 替代方案 |
|---|---|---|---|
| Spring Boot | 2.7.x / 3.x | 自動配置、嵌入容器、生態豐富、LTS長期支持 | Quarkus, Micronaut |
| Spring MVC | 內置 | RESTful控制層、參數綁定、異步處理、WebFlux選項 | Vert.x, Node.js |
| Druid | 1.2.x | 阿里出品、高性能連接池、強大監控、SQL防火牆、多租戶 | HikariCP(輕量), DBCP2 |
| JPA (Hibernate) | 5.6+ / 6.x | ORM標準、類型安全、緩存機制、DDL自動化、JPQL/Criteria查詢 | MyBatis-Plus(SQL可控), jOOQ(類型安全SQL) |
src/main/java/com/rambo/sdh ├── controller # REST端點,參數校驗,VO轉換 ├── service # 業務邏輯,事務邊界,領域服務 │ └── impl ├── repository # JPA Repository接口,繼承JpaRepository ├── entity # JPA實體,@Entity @Table ├── dto # 數據傳輸對象,API契約 ├── config # 配置類:DataSource, JPA, Redis, Swagger ├── aspect # AOP切面:日志、權限、幂等、鏈路追蹤 ├── exception # 統一異常處理,錯誤碼體系 └── util # 工具類
server:
port: 8080
servlet:
context-path: /api
spring:
application:
name: sdh-backend
# Druid 數據源
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/sdh?useSSL=false&serverTimezone=Asia/Shanghai&characterEncoding=utf8
username: root
password: 123456
druid:
initial-size: 5
min-idle: 5
max-active: 20
max-wait: 60000
time-between-eviction-runs-millis: 60000
min-evictable-idle-time-millis: 300000
validation-query: SELECT 1 FROM DUAL
test-while-idle: true
test-on-borrow: false
test-on-return: false
pool-prepared-statements: true
max-pool-prepared-statement-per-connection-size: 20
# 監控配置
stat-view-servlet:
enabled: true
url-pattern: /druid/*
login-username: admin
login-password: admin123
web-stat-filter:
enabled: true
url-pattern: /*
exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
# SQL防火牆
filter:
stat:
enabled: true
log-slow-sql: true
slow-sql-millis: 2000
wall:
enabled: true
config:
multi-statement-allow: true
# JPA / Hibernate
jpa:
database: mysql
show-sql: true
hibernate:
ddl-auto: update # 生產建議 validate/none,由Flyway/Liquibase管理遷移
naming:
physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
dialect: org.hibernate.dialect.MySQL8Dialect
properties:
hibernate:
format_sql: true
use_sql_comments: true
jdbc:
batch_size: 20
fetch_size: 50
order_inserts: true
order_updates: true
generate_statistics: false
# Jackson 序列化
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: Asia/Shanghai
default-property-inclusion: non_null
@MappedSuperclass
@EntityListeners(AuditingEntityListener.class) // 需啟用 @EnableJpaAuditing
@Data
public abstract class BaseEntity implements Serializable {
@Id
@GeneratedValue(generator = "uuid2")
@GenericGenerator(name = "uuid2", strategy = "uuid2")
@Column(length = 36, updatable = false, nullable = false)
private String id;
@CreatedDate
@Column(updatable = false, nullable = false)
private LocalDateTime createTime;
@LastModifiedDate
private LocalDateTime updateTime;
@Version // 樂觀鎖
private Long version;
}
public interface UserRepository extends JpaRepository<User, String>, JpaSpecificationExecutor<User> {
// 衍生查詢 (方法名解析)
Optional<User> findByUsername(String username);
List<User> findByStatusAndCreateTimeBetween(Integer status, LocalDateTime start, LocalDateTime end);
Page<User> findByDepartmentId(String deptId, Pageable pageable);
// @Query 自定義 JPQL
@Query("SELECT u FROM User u WHERE u.email LIKE %:domain%")
List<User> findByEmailDomain(@Param("domain") String domain);
// 原生 SQL (慎用)
@Query(value = "SELECT * FROM t_user WHERE login_count > ?1", nativeQuery = true)
List<User> findActiveUsers(int minLoginCount);
}
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true) // 默認只讀,寫操作方法上覆蓋
public class UserServiceImpl implements UserService {
private final UserRepository userRepository;
private final PasswordEncoder passwordEncoder;
@Override
public UserDTO getById(String id) {
return userRepository.findById(id)
.map(this::toDTO)
.orElseThrow(() -> new BizException("USER_NOT_FOUND"));
}
@Override
@Transactional // 寫操作開啟讀寫事務
public UserDTO create(CreateUserDTO dto) {
// 業務校驗
if (userRepository.existsByUsername(dto.getUsername())) {
throw new BizException("USERNAME_EXISTS");
}
User user = new User();
user.setUsername(dto.getUsername());
user.setPassword(passwordEncoder.encode(dto.getPassword()));
user.setEmail(dto.getEmail());
user.setStatus(UserStatus.ACTIVE);
User saved = userRepository.save(user);
return toDTO(saved);
}
@Override
@Transactional
public void delete(String id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new BizException("USER_NOT_FOUND"));
user.setStatus(UserStatus.DELETED); // 軟刪除
userRepository.save(user);
}
private UserDTO toDTO(User user) { /* 轉換邏輯 */ }
}
Spring Boot默認啟用@EnableTransactionManagement,@Transactional標註方法/類即可。配合TransactionAttributeSource解析屬性。
<!-- 事務管理器 -->
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="add*" propagation="REQUIRED"/>
<tx:method name="save*" propagation="REQUIRED"/>
<tx:method name="update*" propagation="REQUIRED"/>
<tx:method name="delete*" propagation="REQUIRED"/>
<tx:method name="get*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="find*" propagation="SUPPORTS" read-only="true"/>
<tx:method name="*" propagation="SUPPORTS" read-only="true"/>
</tx:attributes>
</tx:advice>
<aop:config>
<aop:pointcut id="servicePointcut" expression="execution(* com.rambo.sdh.service..*Impl.*(..))"/>
<aop:advisor pointcut-ref="servicePointcut" advice-ref="txAdvice"/>
</aop:config>
切點表達式解析:execution(* com.rambo.sdh.service..*Impl.*(..)) 匹配service包下所有*Impl類的所有方法。
訪問 https://host:port/druid/ 可查看:
max-active = CPU核心數 × 2 + 有效磁盤數;監控waiting線程數為0hibernate.jdbc.batch_size + @Transactional + saveAll() / deleteInBatch()@EntityGraph / JOIN FETCH / @BatchSize / FetchMode.SUBSELECThibernate.cache.use_second_level_cache + Redis/RegionFactory,僅緩存「讀多寫少、非關鍵一致性」實體@Transactional(readOnly=true),Hibernate跳過脏檢查、MySQL可用副本web.xml,改用ServletRegistrationBean註冊Druid ServletsessionFactory → LocalContainerEntityManagerFactoryBean (JPA標準)HibernateTransactionManager → JpaTransactionManager@Transactional + @EnableTransactionManagementpackagesToScan → @EntityScan 或 spring.jpa.properties.hibernate.packagesToScan「Spring + Druid + JPA」是經過無數生產環境驗證的穩健組合:Spring提供企業級容器能力,Druid守護數據庫連接健康,JPA屏蔽SQL樣板代碼並提供豐富對象圖操作。掌握配置精簡化、分層職責清晰、事務邊界明確、監控可視化四大原則,即可構建支撐百萬用戶的高可用後端系統。