Appearance
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实现动态查询。
- 使用分页和排序提高查询效率。
- 使用缓存提高性能。
- 使用事务管理保证数据一致性。
- 使用懒加载减少内存占用。
- 使用级联操作简化代码。
- 使用审计功能记录数据变更。
- 使用乐观锁防止并发问题。
