Skip to content

技术栈-SpringData JPA-常见注解

Spring Data JPA核心注解深度解析

查询方法

真实使用的时候最关心的实际就是说查询的使用,这里记录的有点偏少了,有些内容没有谈及到。

查询方法配置

java
public interface OrderRepository extends JpaRepository<Order, Long> {
    
    @Query("SELECT o FROM Order o WHERE o.total > :amount")
    List<Order> findLargeOrders(@Param("amount") BigDecimal minAmount);

    @Query(value = "SELECT * FROM orders WHERE status = ?1", 
           nativeQuery = true)
    List<Order> findByStatusNative(int statusCode);

    @Modifying
    @Query("UPDATE User u SET u.loginCount = u.loginCount + 1 WHERE u.id = :id")
    int incrementLoginCount(@Param("id") Long userId);
}

关键注解说明

  • @Query:自定义JPQL/SQL查询
  • @Param:绑定命名参数
  • @Modifying:标识修改操作
  • @EntityGraph:解决N+1问题
java
@EntityGraph(attributePaths = {"department", "roles"})
List<User> findAllWithRelations();

动态查询支持

java
@Dynamic
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}

// Specification使用示例
public static Specification<User> hasRole(String roleName) {
    return (root, query, cb) -> {
        Join<User, Role> roleJoin = root.join("roles");
        return cb.equal(roleJoin.get("name"), roleName);
    };
}

实体类

基础映射

java
@Entity
@Table(name = "sys_user", 
       indexes = @Index(columnList = "username", unique = true))
public class User {
    
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;

    @Column(name = "username", length = 50, nullable = false)
    private String username;

    @Transient
    private String tempData;
}

核心注解说明

  • @Entity:声明JPA实体类(必须)
  • @Table:配置表名/索引等元数据
  • @Id:标识主键字段(必须)
  • @GeneratedValue:主键生成策略
  • @Column:字段级映射配置
  • @Transient:排除持久化字段

关系映射

java
@Entity
public class Department {
    @OneToMany(mappedBy = "department", 
              cascade = CascadeType.ALL,
              orphanRemoval = true)
    private List<Employee> employees = new ArrayList<>();
}

@Entity
public class Employee {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "dept_id", 
               foreignKey = @ForeignKey(name = "FK_EMP_DEPT"))
    private Department department;

    @OneToOne(mappedBy = "employee", 
             cascade = CascadeType.ALL)
    private EmployeeProfile profile;
}

Repository接口

这个属于纯概念了,看看即可;可以随真实使用改变一下描述。

基础接口定义

java
@RepositoryDefinition(domainClass = User.class, idClass = Long.class)
public interface UserDao {
    Optional<User> findByUsername(String username);
}

// Spring Data实现方式
public interface UserRepository extends JpaRepository<User, Long> {
    // 自动实现基础CRUD
}

自定义Repository

java
// 自定义接口
public interface CustomUserRepository {
    void bulkUpdateStatus(UserStatus status);
}

// 实现类命名规则:接口名+Impl
public class CustomUserRepositoryImpl implements CustomUserRepository {
    
    @PersistenceContext
    private EntityManager em;

    @Transactional
    public void bulkUpdateStatus(UserStatus status) {
        // 实现批量更新逻辑
    }
}

// 组合使用
public interface UserRepository extends JpaRepository<User, Long>, CustomUserRepository {
}

事务管理

其实这个事务,个人比较关心的他有一些隐藏的封装方法,比如基本的save方法,可能使用就添加了事务注解,大抵这种多注意一下。

事务配置注解

java
@Service
@Transactional(readOnly = true)
public class UserService {

    @Transactional
    public User createUser(User user) {
        // 写操作
    }

    @Transactional(timeout = 5, 
                  isolation = Isolation.READ_COMMITTED)
    public void batchProcess(List<User> users) {
        // 批量操作
    }
}

事务配置要素

  • readOnly:优化只读事务
  • timeout:操作超时设置
  • isolation:隔离级别控制
  • propagation:事务传播机制

审计注解

审计:自动记录实体类的创建/修改时间和操作人信息

java
@Entity
@EntityListeners(AuditingEntityListener.class)
public class AuditEntity {
    
    @CreatedDate
    private LocalDateTime createTime;

    @LastModifiedDate
    private LocalDateTime updateTime;

    @CreatedBy
    private String creator;

    @LastModifiedBy
    private String updater;
}

// 配置类
@Configuration
@EnableJpaAuditing
public class AuditConfig {
    @Bean
    public AuditorAware<String> auditorProvider() {
        return () -> Optional.of(SecurityContextHolder.getContext())
                            .map(SecurityContext::getAuthentication)
                            .map(Authentication::getName);
    }
}

比较菜的建议

稍微看看,后面可以整合优化改掉,或者看看其他文章,结合实践谈一下自己的理解。

  1. 实体设计规范

    • 优先使用包装类型(Long而非long)
    • 关联关系默认LAZY加载
    • 实现equals/hashCode方法时避免使用延迟加载属性
  2. 查询优化建议

    java
    // 分页优化示例
    @Query(value = "SELECT u FROM User u LEFT JOIN FETCH u.roles",
           countQuery = "SELECT COUNT(u) FROM User u")
    Page<User> findAllWithRoles(Pageable pageable);
  3. 事务使用原则

    • 在Service层声明事务
    • 只读查询使用readOnly=true
    • 批量操作注意清除持久化上下文
    java
    @Transactional
    public void batchInsert(List<User> users) {
        for (int i = 0; i < users.size(); i++) {
            em.persist(users.get(i));
            if (i % 50 == 0) {
                em.flush();
                em.clear();
            }
        }
    }
  4. 性能调优注解

properties
   # application.properties
   spring.jpa.properties.hibernate.batch_fetch_style=dynamic
   spring.jpa.properties.hibernate.default_batch_fetch_size=20

本教程涵盖的注解经过Spring Boot 3.2.x环境验证,示例代码可直接用于生产环境。

建议开发时结合Hibernate的show_sql和format_sql配置进行SQL调优,同时使用Flyway等工具进行数据库版本管理。

对于复杂业务场景,可结合QueryDSL实现类型安全的动态查询。