name: allra-database-schema description: Allra数据库设计及QueryDSL使用规则。在创建JPA实体、编写QueryDSL查询或添加@Transactional注解时使用。
Allra数据库设计及QueryDSL规则
定义Allra后端团队的数据库设计、JPA、QueryDSL、事务管理标准。
项目基本信息
本指南基于以下环境编写:
- Java: 17及以上
- Spring Boot: 3.2及以上
- ORM: JPA/Hibernate
- Query Library: QueryDSL(可选)
- Testing: Testcontainers(可选)
注意:各项目使用的数据库(MariaDB、PostgreSQL、MySQL等)和库可能不同。
QueryDSL使用规则
1. Repository结构(Allra推荐模式)
同时使用JPA Repository和Support接口:
// JPA Repository接口
public interface UserRepository extends JpaRepository<User, Long>, UserRepositorySupport {
}
// QueryDSL Support接口
public interface UserRepositorySupport {
List<UserSummaryDto> findUserSummaries(UserSearchCondition condition);
}
// QueryDSL Support实现类
@Repository
public class UserRepositoryImpl implements UserRepositorySupport {
private final JPAQueryFactory queryFactory;
@Override
public List<UserSummaryDto> findUserSummaries(UserSearchCondition condition) {
return queryFactory
.select(new QUserSummaryDto(
user.id,
user.email,
user.name
))
.from(user)
.where(
emailContains(condition.email()),
nameContains(condition.name())
)
.fetch();
}
private BooleanExpression emailContains(String email) {
return email != null ? user.email.contains(email) : null;
}
}
注意:Support模式是可选的。根据项目情况,可以使用@Query注解或其他方式。
2. QueryDSL DTO投影
使用Record和@QueryProjection:
public record UserSummaryDto(
Long id,
String email,
String name
) {
@QueryProjection
public UserSummaryDto {}
}
构建设置:
Gradle:
annotationProcessor "com.querydsl:querydsl-apt:${queryDslVersion}:jakarta"
Maven:
<plugin>
<groupId>com.mysema.maven</groupId>
<artifactId>apt-maven-plugin</artifactId>
<version>1.1.3</version>
<executions>
<execution>
<goals>
<goal>process</goal>
</goals>
<configuration>
<outputDirectory>target/generated-sources/java</outputDirectory>
<processor>com.querydsl.apt.jpa.JPAAnnotationProcessor</processor>
</configuration>
</execution>
</executions>
</plugin>
3. 根据From子句确定Repository位置
建议在From子句对应的Repository中定义:
// ❌ 避免:在Order中查询User
public interface OrderRepositorySupport {
List<UserDto> findUsersByOrderDate(LocalDate date); // From user
}
// ✅ 推荐:在User中关联Order
public interface UserRepositorySupport {
List<UserOrderDto> findUsersWithOrders(LocalDate date); // From user
}
4. 数据库兼容性
编写QueryDSL时考虑所用数据库的特性:
// 通用查询
queryFactory
.selectFrom(user)
.where(user.createdAt.between(startDate, endDate))
.fetch();
// LIMIT/OFFSET
queryFactory
.selectFrom(user)
.limit(10)
.offset(0)
.fetch();
注意:窗口函数或特定数据库函数可能因数据库版本而异。
5. 禁止直接依赖xxxRepositorySupport
必须通过JPA Repository接口使用:
// ❌ 错误示例
@Service
public class UserService {
private final UserRepositoryImpl userRepositoryImpl; // 直接注入实现类
}
// ✅ 正确示例
@Service
public class UserService {
private final UserRepository userRepository; // 注入接口
}
@Transactional使用指南
必要规则
在每个服务方法中显式声明:
- 仅查询操作:
@Transactional(readOnly = true) - 包含修改操作:
@Transactional
示例
@Service
public class UserService {
private final UserRepository userRepository;
// 只读事务
@Transactional(readOnly = true)
public List<User> findAllUsers() {
return userRepository.findAll();
}
// 写事务
@Transactional
public User createUser(SignUpRequest request) {
User user = User.create(request.email(), request.password());
return userRepository.save(user);
}
// 查询+修改
@Transactional
public User activateUser(Long id) {
User user = userRepository.findById(id)
.orElseThrow(() -> new UserNotFoundException(id));
user.activate(); // 修改
return user;
}
}
注意:事务传播(Propagation)使用默认值(REQUIRED),仅在特殊情况下显式指定。
JPA实体设计指南
基本结构
@Entity
@Table(name = "users")
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, unique = true, length = 100)
private String email;
@Column(nullable = false, length = 100)
private String name;
@Enumerated(EnumType.STRING)
@Column(nullable = false, length = 20)
private UserStatus status;
@CreatedDate
@Column(nullable = false, updatable = false)
private LocalDateTime createdAt;
@LastModifiedDate
@Column(nullable = false)
private LocalDateTime updatedAt;
// 静态工厂方法
public static User create(String email, String password, String name) {
User user = new User();
user.email = email;
user.password = password;
user.name = name;
user.status = UserStatus.ACTIVE;
return user;
}
// 业务方法
public void activate() {
this.status = UserStatus.ACTIVE;
}
}
关联关系映射
@Entity
public class Order {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
// ManyToOne - 推荐延迟加载
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;
// OneToMany - 延迟加载,Cascade设置
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true)
private List<OrderItem> items = new ArrayList<>();
// 关联关系便捷方法
public void addItem(OrderItem item) {
items.add(item);
item.setOrder(this);
}
}
注意:建议关联关系默认使用延迟加载(LAZY)。
何时使用此技能
此技能在以下情况下自动应用:
- JPA实体创建及修改
- QueryDSL查询编写
- Repository接口及实现类编写
- Service方法添加@Transactional
- DTO投影编写
检查清单
编写数据库相关代码时确认事项:
- [ ] QueryDSL Support是否继承自JPA Repository?(使用Support模式时)
- [ ] QueryDSL实现类是否位于From子句对应的Repository中?
- [ ] DTO投影是否应用了@QueryProjection?(使用QueryDSL时)
- [ ] Service的所有public方法是否都显式声明了@Transactional?
- [ ] 只读方法是否应用了readOnly = true?
- [ ] 是否考虑了MariaDB兼容性?
- [ ] 实体的关联关系是否设置为延迟加载(LAZY)?
- [ ] 是否未直接注入xxxRepositorySupport实现类?