Skip to content

Spring Data面试题

1. Spring Data的基本概念

问题:什么是Spring Data?

答案

  • Spring Data:基于Spring框架的数据访问框架,简化了数据访问层的开发。
  • 特点
    • 统一的数据访问API
    • 自动生成查询方法
    • 支持多种数据存储
    • 支持分页和排序
    • 支持审计功能

2. Spring Data的模块

问题:Spring Data有哪些模块?

答案

  • Spring Data JPA:关系型数据库访问
  • Spring Data MongoDB:MongoDB访问
  • Spring Data Redis:Redis访问
  • Spring Data Cassandra:Cassandra访问
  • Spring Data Elasticsearch:Elasticsearch访问
  • Spring Data Neo4j:Neo4j访问
  • Spring Data Solr:Solr访问
  • Spring Data Couchbase:Couchbase访问

3. Repository

问题:什么是Repository?

答案

  • Repository:Spring Data的核心接口,用于数据访问。
  • 继承关系
    • Repository:标记接口
    • CrudRepository:提供CRUD操作
    • PagingAndSortingRepository:提供分页和排序
    • JpaRepository:提供JPA特定功能

示例

java
public interface UserRepository extends JpaRepository<User, Integer> {
    User findByUsername(String username);
    List<User> findByAgeGreaterThan(int age);
    List<User> findByDepartmentAndAge(String department, int age);
}

4. 查询方法命名

问题:如何定义查询方法?

答案

  • 关键字
    • findBy:根据条件查询
    • readBy:根据条件查询
    • queryBy:根据条件查询
    • countBy:根据条件计数
    • existsBy:根据条件判断是否存在
    • deleteBy:根据条件删除
  • 条件
    • And:并且
    • Or:或者
    • Between:在之间
    • LessThan:小于
    • GreaterThan:大于
    • Like:模糊匹配
    • StartingWith:以...开头
    • EndingWith:以...结尾
    • Containing:包含
    • IgnoreCase:忽略大小写
    • OrderBy:排序

示例

java
public interface UserRepository extends JpaRepository<User, Integer> {
    User findByUsername(String username);
    List<User> findByAgeGreaterThan(int age);
    List<User> findByDepartmentAndAge(String department, int age);
    List<User> findByAgeBetween(int minAge, int maxAge);
    List<User> findByUsernameContaining(String keyword);
    List<User> findByUsernameIgnoreCase(String username);
    List<User> findByAgeGreaterThanOrderByAgeDesc(int age);
    int countByDepartment(String department);
    boolean existsByUsername(String username);
    void deleteByUsername(String username);
}

5. @Query注解

问题:如何使用@Query注解?

答案

java
public interface UserRepository extends JpaRepository<User, Integer> {
    @Query("SELECT u FROM User u WHERE u.username = ?1")
    User findByUsername(String username);
    
    @Query("SELECT u FROM User u WHERE u.age > :age")
    List<User> findByAgeGreaterThan(@Param("age") int age);
    
    @Query(value = "SELECT * FROM users WHERE username = :username", nativeQuery = true)
    User findByUsernameNative(@Param("username") String username);
    
    @Query("SELECT u FROM User u WHERE u.department = :department")
    List<User> findByDepartment(@Param("department") String department, Pageable pageable);
}

6. 分页和排序

问题:如何实现分页和排序?

答案

java
public interface UserRepository extends JpaRepository<User, Integer> {
    Page<User> findByDepartment(String department, Pageable pageable);
}

// 使用
Pageable pageable = PageRequest.of(0, 10, Sort.by("age").descending());
Page<User> page = userRepository.findByDepartment("IT", pageable);

// 排序
Sort sort = Sort.by(Sort.Direction.DESC, "age");
List<User> users = userRepository.findAll(sort);

7. Specification

问题:如何使用Specification实现动态查询?

答案

java
public interface UserRepository extends JpaRepository<User, Integer>, JpaSpecificationExecutor<User> {
}

// Specification
public class UserSpecifications {
    public static Specification<User> hasUsername(String username) {
        return (root, query, cb) -> cb.equal(root.get("username"), username);
    }
    
    public static Specification<User> hasAgeGreaterThan(int age) {
        return (root, query, cb) -> cb.greaterThan(root.get("age"), age);
    }
    
    public static Specification<User> hasDepartment(String department) {
        return (root, query, cb) -> cb.equal(root.get("department"), department);
    }
}

// 使用
Specification<User> spec = Specification.where(UserSpecifications.hasUsername("John"))
    .and(UserSpecifications.hasAgeGreaterThan(18))
    .and(UserSpecifications.hasDepartment("IT"));
List<User> users = userRepository.findAll(spec);

8. 审计

问题:如何实现审计功能?

答案

java
// 实体类
@Entity
@EntityListeners(AuditingEntityListener.class)
public class User {
    @CreatedDate
    private LocalDateTime createTime;
    
    @LastModifiedDate
    private LocalDateTime updateTime;
    
    @CreatedBy
    private String createUser;
    
    @LastModifiedBy
    private String updateUser;
}

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

9. 事务管理

问题:Spring Data如何管理事务?

答案

java
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    @Transactional
    public void createUser(User user) {
        userRepository.save(user);
    }
    
    @Transactional(readOnly = true)
    public User findById(int id) {
        return userRepository.findById(id).orElse(null);
    }
    
    @Transactional(propagation = Propagation.REQUIRED)
    public void updateUser(User user) {
        userRepository.save(user);
    }
    
    @Transactional(rollbackFor = Exception.class)
    public void deleteUser(int id) {
        userRepository.deleteById(id);
    }
}

10. 级联操作

问题:如何配置级联操作?

答案

java
@Entity
public class User {
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Order> orders = new ArrayList<>();
}

@Entity
public class Order {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;
}

11. 懒加载

问题:如何配置懒加载?

答案

java
@Entity
public class User {
    @OneToMany(mappedBy = "user", fetch = FetchType.LAZY)
    private List<Order> orders = new ArrayList<>();
}

// 使用
User user = userRepository.findById(id).orElse(null);
List<Order> orders = user.getOrders(); // 触发懒加载

12. 缓存

问题:如何配置缓存?

答案

java
@Configuration
@EnableCaching
public class CacheConfig {
    @Bean
    public CacheManager cacheManager(EntityManagerFactory entityManagerFactory) {
        JpaCacheManager cacheManager = new JpaCacheManager(entityManagerFactory);
        return cacheManager;
    }
}

// 使用
@Cacheable("users")
public User findById(int id) {
    return userRepository.findById(id).orElse(null);
}

@CachePut("users")
public User update(User user) {
    return userRepository.save(user);
}

@CacheEvict("users")
public void delete(int id) {
    userRepository.deleteById(id);
}

13. 自定义Repository

问题:如何自定义Repository?

答案

java
// 自定义接口
public interface UserRepositoryCustom {
    List<User> findUsersByCustomQuery(String keyword);
}

// 自定义实现
public class UserRepositoryImpl implements UserRepositoryCustom {
    @PersistenceContext
    private EntityManager entityManager;
    
    @Override
    public List<User> findUsersByCustomQuery(String keyword) {
        String jpql = "SELECT u FROM User u WHERE u.username LIKE :keyword";
        TypedQuery<User> query = entityManager.createQuery(jpql, User.class);
        query.setParameter("keyword", "%" + keyword + "%");
        return query.getResultList();
    }
}

// 继承自定义接口
public interface UserRepository extends JpaRepository<User, Integer>, UserRepositoryCustom {
}

14. JPA注解

问题:常用的JPA注解有哪些?

答案

  • @Entity:标记为实体类
  • @Table:指定表名
  • @Id:标记为主键
  • @GeneratedValue:主键生成策略
  • @Column:指定列名
  • @Transient:标记为非持久化字段
  • @OneToMany:一对多关系
  • @ManyToOne:多对一关系
  • @OneToOne:一对一关系
  • @ManyToMany:多对多关系
  • @JoinColumn:指定外键列
  • @JoinTable:指定关联表
  • @Enumerated:枚举类型映射
  • @Temporal:日期时间类型映射
  • @Lob:大对象类型映射

15. 主键生成策略

问题:主键生成策略有哪些?

答案

  • AUTO:自动选择策略
  • IDENTITY:数据库自增
  • SEQUENCE:数据库序列
  • TABLE:数据库表模拟序列
  • UUID:UUID生成

示例

java
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    @Id
    @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "user_seq")
    @SequenceGenerator(name = "user_seq", sequenceName = "user_sequence", allocationSize = 1)
    private Integer id;
    
    @Id
    @GeneratedValue(strategy = GenerationType.TABLE, generator = "user_table")
    @TableGenerator(name = "user_table", table = "id_generator", pkColumnName = "gen_name", valueColumnName = "gen_value", pkColumnValue = "user_id", allocationSize = 1)
    private Integer id;
}

16. 关系映射

问题:如何配置关系映射?

答案

java
// 一对多
@Entity
public class User {
    @OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
    private List<Order> orders = new ArrayList<>();
}

@Entity
public class Order {
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "user_id")
    private User user;
}

// 多对多
@Entity
public class User {
    @ManyToMany(mappedBy = "users")
    private List<Role> roles = new ArrayList<>();
}

@Entity
public class Role {
    @ManyToMany
    @JoinTable(
        name = "user_role",
        joinColumns = @JoinColumn(name = "role_id"),
        inverseJoinColumns = @JoinColumn(name = "user_id")
    )
    private List<User> users = new ArrayList<>();
}

17. 嵌入式对象

问题:如何使用嵌入式对象?

答案

java
@Embeddable
public class Address {
    private String street;
    private String city;
    private String zipCode;
}

@Entity
public class User {
    @Embedded
    private Address address;
}

18. 继承映射

问题:如何配置继承映射?

答案

java
// 单表继承
@Entity
@Inheritance(strategy = InheritanceType.SINGLE_TABLE)
@DiscriminatorColumn(name = "user_type", discriminatorType = DiscriminatorType.STRING)
public abstract class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
}

@Entity
@DiscriminatorValue("ADMIN")
public class Admin extends User {
}

@Entity
@DiscriminatorValue("CUSTOMER")
public class Customer extends User {
}

// 每个类一张表
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
}

// 每个具体类一张表
@Entity
@Inheritance(strategy = InheritanceType.TABLE_PER_CLASS)
public abstract class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
}

19. 命名查询

问题:如何使用命名查询?

答案

java
@Entity
@NamedQueries({
    @NamedQuery(
        name = "User.findByUsername",
        query = "SELECT u FROM User u WHERE u.username = :username"
    ),
    @NamedQuery(
        name = "User.findByAgeGreaterThan",
        query = "SELECT u FROM User u WHERE u.age > :age"
    )
})
public class User {
}

// 使用
public interface UserRepository extends JpaRepository<User, Integer> {
    User findByUsername(@Param("username") String username);
    List<User> findByAgeGreaterThan(@Param("age") int age);
}

20. 事件

问题:如何使用JPA事件?

答案

java
@Entity
@EntityListeners(UserListener.class)
public class User {
    @PrePersist
    private void prePersist() {
        System.out.println("PrePersist");
    }
    
    @PostPersist
    private void postPersist() {
        System.out.println("PostPersist");
    }
    
    @PreUpdate
    private void preUpdate() {
        System.out.println("PreUpdate");
    }
    
    @PostUpdate
    private void postUpdate() {
        System.out.println("PostUpdate");
    }
    
    @PreRemove
    private void preRemove() {
        System.out.println("PreRemove");
    }
    
    @PostRemove
    private void postRemove() {
        System.out.println("PostRemove");
    }
}

// 监听器
public class UserListener {
    @PrePersist
    private void prePersist(User user) {
        System.out.println("PrePersist: " + user.getUsername());
    }
}

21. 软删除

问题:如何实现软删除?

答案

java
@Entity
@SQLDelete(sql = "UPDATE users SET deleted = 1 WHERE id = ?")
@Where(clause = "deleted = 0")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    private boolean deleted;
}

22. 乐观锁

问题:如何实现乐观锁?

答案

java
@Entity
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    
    @Version
    private Integer version;
}

// 使用
User user = userRepository.findById(id).orElse(null);
user.setName("John");
userRepository.save(user); // 自动检查版本号

23. 悲观锁

问题:如何实现悲观锁?

答案

java
public interface UserRepository extends JpaRepository<User, Integer> {
    @Lock(LockModeType.PESSIMISTIC_WRITE)
    @Query("SELECT u FROM User u WHERE u.id = :id")
    User findByIdWithLock(@Param("id") int id);
}

// 使用
User user = userRepository.findByIdWithLock(id);

24. 批量操作

问题:如何实现批量操作?

答案

java
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    @Transactional
    public void batchSave(List<User> users) {
        userRepository.saveAll(users);
    }
    
    @Transactional
    public void batchUpdate(List<User> users) {
        userRepository.saveAll(users);
    }
    
    @Transactional
    public void batchDelete(List<Integer> ids) {
        userRepository.deleteAllById(ids);
    }
}

25. Spring Data的最佳实践

问题:使用Spring Data的最佳实践有哪些?

答案

  • 使用查询方法命名规范,提高代码可读性。
  • 使用@Query注解处理复杂查询。
  • 使用Specification实现动态查询。
  • 使用分页和排序提高查询效率。
  • 使用缓存提高性能。
  • 使用事务管理保证数据一致性。
  • 使用懒加载减少内存占用。
  • 使用级联操作简化代码。
  • 使用审计功能记录数据变更。
  • 使用乐观锁防止并发问题。