数据访问
Spring 提供了强大的数据访问支持,简化了数据库操作。本章介绍 Spring 的数据访问抽象、JDBC 支持以及 ORM 集成。
Spring 数据访问的设计理念
Spring 数据访问模块的设计遵循以下原则:
统一的异常体系:将各种数据访问技术的异常统一转换为 Spring 的 DataAccessException 层次结构,避免代码依赖特定技术的异常类。
统一的 API:提供模板类(如 JdbcTemplate)封装通用操作,开发者只需关注业务逻辑。
与 IOC 容器集成:数据源、事务管理器等都可以作为 Bean 进行配置和管理。
不侵入业务代码:业务代码不需要实现 Spring 特定的接口。
数据源配置
数据源是数据库连接的工厂,Spring 支持多种数据源配置方式。
使用 HikariCP(推荐)
HikariCP 是目前性能最高的连接池实现:
<dependency>
<groupId>com.zaxxer</groupId>
<artifactId>HikariCP</artifactId>
<version>5.0.1</version>
</dependency>
@Configuration
public class DataSourceConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
public DataSource dataSource() {
return new HikariDataSource();
}
}
application.properties:
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=password
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.hikari.minimum-idle=5
spring.datasource.hikari.maximum-pool-size=20
spring.datasource.hikari.idle-timeout=30000
spring.datasource.hikari.pool-name=HikariCP
spring.datasource.hikari.max-lifetime=1800000
spring.datasource.hikari.connection-timeout=30000
使用 Druid
Druid 是阿里巴巴开源的数据库连接池,提供强大的监控功能:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.2.20</version>
</dependency>
@Configuration
public class DruidConfig {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.druid")
public DataSource druidDataSource() {
return new DruidDataSource();
}
@Bean
public ServletRegistrationBean<StatViewServlet> druidStatViewServlet() {
ServletRegistrationBean<StatViewServlet> registrationBean =
new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
registrationBean.addInitParameter("loginUsername", "admin");
registrationBean.addInitParameter("loginPassword", "admin");
return registrationBean;
}
}
嵌入式数据库
开发和测试阶段可以使用嵌入式数据库:
@Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.H2)
.addScript("classpath:schema.sql")
.addScript("classpath:data.sql")
.build();
}
JdbcTemplate
JdbcTemplate 是 Spring JDBC 的核心类,简化了 JDBC 操作。
基本配置
@Configuration
public class JdbcConfig {
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
查询操作
查询单个值:
@Repository
public class UserDao {
@Autowired
private JdbcTemplate jdbcTemplate;
public String findNameById(Long id) {
String sql = "SELECT name FROM users WHERE id = ?";
return jdbcTemplate.queryForObject(sql, String.class, id);
}
public int count() {
return jdbcTemplate.queryForObject("SELECT COUNT(*) FROM users", Integer.class);
}
}
查询实体对象:
public User findById(Long id) {
String sql = "SELECT id, name, email, created_at FROM users WHERE id = ?";
return jdbcTemplate.queryForObject(sql, new UserRowMapper(), id);
}
private static class UserRowMapper implements RowMapper<User> {
@Override
public User mapRow(ResultSet rs, int rowNum) throws SQLException {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
user.setCreatedAt(rs.getTimestamp("created_at").toLocalDateTime());
return user;
}
}
使用 BeanPropertyRowMapper:
如果数据库列名与 Java Bean 属性名匹配(支持驼峰转换),可以使用 BeanPropertyRowMapper:
public List<User> findAll() {
String sql = "SELECT id, name, email, created_at FROM users";
return jdbcTemplate.query(sql, new BeanPropertyRowMapper<>(User.class));
}
查询列表:
public List<User> findByNameLike(String name) {
String sql = "SELECT id, name, email FROM users WHERE name LIKE ?";
return jdbcTemplate.query(sql, new UserRowMapper(), "%" + name + "%");
}
使用 ResultSetExtractor:
需要处理整个结果集时使用:
public Map<Long, User> findAllAsMap() {
String sql = "SELECT id, name, email FROM users";
return jdbcTemplate.query(sql, rs -> {
Map<Long, User> map = new HashMap<>();
while (rs.next()) {
User user = new User();
user.setId(rs.getLong("id"));
user.setName(rs.getString("name"));
user.setEmail(rs.getString("email"));
map.put(user.getId(), user);
}
return map;
});
}
更新操作
插入、更新、删除:
public int insert(User user) {
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
return jdbcTemplate.update(sql, user.getName(), user.getEmail());
}
public int update(User user) {
String sql = "UPDATE users SET name = ?, email = ? WHERE id = ?";
return jdbcTemplate.update(sql, user.getName(), user.getEmail(), user.getId());
}
public int delete(Long id) {
String sql = "DELETE FROM users WHERE id = ?";
return jdbcTemplate.update(sql, id);
}
获取自增主键:
public Long insertAndGetId(User user) {
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
KeyHolder keyHolder = new GeneratedKeyHolder();
jdbcTemplate.update(connection -> {
PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setString(1, user.getName());
ps.setString(2, user.getEmail());
return ps;
}, keyHolder);
return keyHolder.getKey().longValue();
}
批量操作
public void batchInsert(List<User> users) {
String sql = "INSERT INTO users (name, email) VALUES (?, ?)";
jdbcTemplate.batchUpdate(sql, users, users.size(), (ps, user) -> {
ps.setString(1, user.getName());
ps.setString(2, user.getEmail());
});
}
调用存储过程
public User callProcedure(Long id) {
SimpleJdbcCall jdbcCall = new SimpleJdbcCall(jdbcTemplate)
.withProcedureName("get_user_by_id")
.returningResultSet("user", new UserRowMapper());
Map<String, Object> inParams = new HashMap<>();
inParams.put("p_user_id", id);
Map<String, Object> result = jdbcCall.execute(inParams);
@SuppressWarnings("unchecked")
List<User> users = (List<User>) result.get("user");
return users.isEmpty() ? null : users.get(0);
}
NamedParameterJdbcTemplate
NamedParameterJdbcTemplate 支持命名参数,代码更清晰:
@Repository
public class UserDao {
@Autowired
private NamedParameterJdbcTemplate namedParameterJdbcTemplate;
public User findById(Long id) {
String sql = "SELECT id, name, email FROM users WHERE id = :id";
MapSqlParameterSource params = new MapSqlParameterSource("id", id);
return namedParameterJdbcTemplate.queryForObject(sql, params, new UserRowMapper());
}
public List<User> findByIds(List<Long> ids) {
String sql = "SELECT id, name, email FROM users WHERE id IN (:ids)";
MapSqlParameterSource params = new MapSqlParameterSource("ids", ids);
return namedParameterJdbcTemplate.query(sql, params, new UserRowMapper());
}
public int update(User user) {
String sql = "UPDATE users SET name = :name, email = :email WHERE id = :id";
SqlParameterSource params = new BeanPropertySqlParameterSource(user);
return namedParameterJdbcTemplate.update(sql, params);
}
}
ORM 集成
Spring 提供了对主流 ORM 框架的集成支持。
JPA 集成
配置:
@Configuration
@EnableJpaRepositories(basePackages = "com.example.repository")
public class JpaConfig {
@Bean
public LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource) {
LocalContainerEntityManagerFactoryBean em = new LocalContainerEntityManagerFactoryBean();
em.setDataSource(dataSource);
em.setPackagesToScan("com.example.entity");
HibernateJpaVendorAdapter vendorAdapter = new HibernateJpaVendorAdapter();
em.setJpaVendorAdapter(vendorAdapter);
Properties properties = new Properties();
properties.setProperty("hibernate.hbm2ddl.auto", "update");
properties.setProperty("hibernate.dialect", "org.hibernate.dialect.MySQL8Dialect");
properties.setProperty("hibernate.show_sql", "true");
em.setJpaProperties(properties);
return em;
}
@Bean
public PlatformTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setEntityManagerFactory(entityManagerFactory);
return transactionManager;
}
}
实体类:
@Entity
@Table(name = "users")
public class User {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
@Column(nullable = false, length = 100)
private String name;
@Column(unique = true)
private String email;
@OneToMany(mappedBy = "user", cascade = CascadeType.ALL)
private List<Order> orders = new ArrayList<>();
@Temporal(TemporalType.TIMESTAMP)
private LocalDateTime createdAt;
}
Repository 接口:
public interface UserRepository extends JpaRepository<User, Long> {
Optional<User> findByEmail(String email);
List<User> findByNameContaining(String name);
@Query("SELECT u FROM User u WHERE u.createdAt > :date")
List<User> findCreatedAfter(@Param("date") LocalDateTime date);
@Modifying
@Query("UPDATE User u SET u.email = :email WHERE u.id = :id")
int updateEmail(@Param("id") Long id, @Param("email") String email);
}
使用:
@Service
@Transactional
public class UserService {
@Autowired
private UserRepository userRepository;
public User create(User user) {
return userRepository.save(user);
}
public Optional<User> findById(Long id) {
return userRepository.findById(id);
}
public List<User> findAll() {
return userRepository.findAll();
}
public User update(User user) {
return userRepository.save(user);
}
public void delete(Long id) {
userRepository.deleteById(id);
}
public Optional<User> findByEmail(String email) {
return userRepository.findByEmail(email);
}
}
MyBatis 集成
Spring 与 MyBatis 的集成非常常用,详见 MyBatis 教程。
事务管理
数据访问与事务管理紧密相关,详见事务管理章节。基本用法:
@Service
public class UserService {
@Autowired
private UserDao userDao;
@Autowired
private AccountDao accountDao;
@Transactional
public void registerUser(User user) {
userDao.insert(user);
Account account = new Account();
account.setUserId(user.getId());
accountDao.insert(account);
}
}
小结
本章介绍了 Spring 的数据访问支持:
- 数据源配置:HikariCP、Druid、嵌入式数据库
- JdbcTemplate:查询、更新、批量操作
- NamedParameterJdbcTemplate:命名参数支持
- JPA 集成:实体类、Repository 接口
- 事务管理:与数据访问的配合
Spring 的数据访问模块提供了统一的编程模型,无论是使用 JDBC 还是 ORM,都能获得一致的体验。