Spring全家桶笔记六:数据访问层

Spring全家桶笔记六:数据访问层

Spring数据访问概述

Spring提供了统一的数据访问抽象,简化了JDBC、Hibernate、MyBatis等持久层框架的使用。

JDBC模板

JdbcTemplate基本使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
@Component
public class UserDao {

@Autowired
private JdbcTemplate jdbcTemplate;

public User findById(Long id) {
String sql = "SELECT * FROM user WHERE id = ?";
return jdbcTemplate.queryForObject(sql, new BeanPropertyRowMapper<>(User.class), id);
}

public List<User> findAll() {
String sql = "SELECT * FROM user";
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
}

public int insert(User user) {
String sql = "INSERT INTO user(name, email) VALUES(?, ?)";
return jdbcTemplate.update(sql, user.getName(), user.getEmail());
}
}

命名参数

1
2
3
4
5
6
7
8
// 使用Map
Map<String, Object> params = new HashMap<>();
params.put("name", "张三");
params.put("email", "zhangsan@example.com");
namedParameterJdbcTemplate.update(sql, params);

// 使用BeanPropertySqlParameterSource
namedParameterJdbcTemplate.update(sql, new BeanPropertySqlParameterSource(user));

Spring Data JPA

快速开始

1
2
3
4
5
6
7
8
9
10
11
12
@Entity
@Table(name = "t_user")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

private String name;
private String email;

// getters and setters
}
1
2
3
4
5
6
public interface UserRepository extends JpaRepository<User, Long> {
// 自动实现CRUD
// 自定义查询
User findByName(String name);
List<User> findByEmailContaining(String email);
}

自定义查询

1
2
3
4
5
@Query("SELECT u FROM User u WHERE u.name = ?1")
User findByNameCustom(String name);

@Query(value = "SELECT * FROM user WHERE name = :name", nativeQuery = true)
User findByNameNative(@Param("name") String name);

复杂查询

1
2
3
4
5
6
7
8
9
10
public interface UserRepository extends JpaRepository<User, Long>, JpaSpecificationExecutor<User> {
}

Specification<User> spec = (root, query, cb) -> {
List<Predicate> predicates = new ArrayList<>();
if (name != null) {
predicates.add(cb.like(root.get("name"), "%" + name + "%"));
}
return cb.and(predicates.toArray(new Predicate[0]));
};

事务管理

声明式事务

1
2
3
4
5
6
7
8
9
10
11
@Service
public class UserService {

@Transactional(isolation = Isolation.READ_COMMITTED, timeout = 30)
public void transfer(Long fromId, Long toId, BigDecimal amount) {
// 扣款
accountDao.decrease(fromId, amount);
// 加款
accountDao.increase(toId, amount);
}
}

事务传播行为

传播行为 说明
REQUIRED 如果有事务则加入,没有则创建
REQUIRES_NEW 总是创建新事务
SUPPORTS 有事务则加入,没有则非事务
NOT_SUPPORTED 非事务执行
MANDATORY 必须有事务,否则抛异常
NEVER 必须没有事务,否则抛异常

常见问题

1. 懒加载异常

解决方案:开启spring.jpa.open-in-view(但影响性能)或使用EntityGraph

2. N+1问题

解决方案:使用JOIN FETCH或@EntityGraph

3. 乐观锁冲突

使用@Version注解实现乐观锁

总结

Spring数据访问层提供了强大的持久层支持,开发者可以根据项目需求选择合适的持久层方案。Spring Data JPA适合快速开发,JdbcTemplate适合需要精细控制SQL的场景。