跳到主要内容
Spring Boot 数据访问与数据库集成详解 | 极客日志
Java java
Spring Boot 数据访问与数据库集成详解 Spring Boot 数据访问与数据库集成涵盖了 JdbcTemplate、JPA、MyBatis 等主流方案。通过配置依赖和连接信息,可快速实现 MySQL、H2 等数据库的对接。事务管理使用@Transactional 注解保障数据一致性。实际开发中需根据场景选择合适的数据持久化技术,如电商订单管理或用户系统构建。掌握这些基础有助于高效处理增删改查及复杂业务逻辑。
ServerBase 发布于 2026/3/27 0 浏览Spring Boot 数据访问与数据库集成
Spring Boot 极大地简化了数据库集成的过程,让开发者能专注于业务逻辑而非基础设施配置。本章将深入探讨如何在 Spring Boot 中高效地连接和操作数据库,涵盖 MySQL、H2、MyBatis 以及 JPA 等主流方案,并讲解事务管理的最佳实践。
数据访问概述
在 Spring Boot 生态中,数据访问通常通过几个核心组件实现:JdbcTemplate 用于原生 JDBC 操作,JPA 提供对象关系映射,MyBatis 则允许更灵活的 SQL 控制,而 Hibernate 作为 JPA 的实现之一也常被使用。这些组件共同支持增删改查、事务管理及数据持久化功能。
与 MySQL 集成
MySQL 是最常用的生产环境数据库,Spring Boot 与其集成非常成熟。
依赖与配置
首先需要在 pom.xml 中添加 Web、Data JPA 和 MySQL 驱动依赖。注意驱动作用域设为 runtime,避免打包进测试环境。
<dependencies >
<dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-starter-web</artifactId >
</dependency >
<dependency >
<groupId > org.springframework.boot</groupId >
<artifactId > spring-boot-starter-data-jpa</artifactId >
</dependency >
<dependency >
<groupId > mysql</groupId >
<artifactId > mysql-connector-java</artifactId >
<scope > runtime</scope >
</ >
org.springframework.boot
spring-boot-starter-test
test
dependency
<dependency >
<groupId >
</groupId >
<artifactId >
</artifactId >
<scope >
</scope >
</dependency >
</dependencies >
接着在 application.properties 中配置连接信息。这里开启了 Hibernate 的自动更新策略,开发阶段很方便,但生产环境建议改为 validate 或 none。
server.port=8080
spring.datasource.url=jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
spring.jpa.hibernate.ddl-auto=update
spring.jpa.show-sql=true
实体与仓库 定义一个 Product 实体类,使用 JPA 注解映射表结构。注意字段命名规范,Getter/Setter 方法要完整。
import javax.persistence.*;
import java.util.List;
@Entity
@Table(name = "product")
public class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String productId;
private String productName;
private double price;
private int sales;
public Product () {}
public Product (String productId, String productName, double price, int sales) {
this .productId = productId;
this .productName = productName;
this .price = price;
this .sales = sales;
}
public Long getId () { return id; }
public void setId (Long id) { this .id = id; }
public String getProductId () { return productId; }
public void setProductId (String productId) { this .productId = productId; }
public String getProductName () { return productName; }
public void setProductName (String productName) { this .productName = productName; }
public double getPrice () { return price; }
public void setPrice (double price) { this .price = price; }
public int getSales () { return sales; }
public void setSales (int sales) { this .sales = sales; }
@Override
public String toString () {
return "Product{" +
"id=" + id +
", productId='" + productId + '\'' +
", productName='" + productName + '\'' +
", price=" + price +
", sales=" + sales +
'}' ;
}
}
Repository 接口继承 JpaRepository 即可获取基础 CRUD 方法,还可以自定义查询方法,比如按销量排序。
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import java.util.List;
@Repository
public interface ProductRepository extends JpaRepository <Product, Long> {
List<Product> findBySalesGreaterThan (int sales) ;
}
控制器与测试 Controller 层负责暴露 REST API,注入 Repository 后直接调用。测试时可以使用@SpringBootTest 启动上下文。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import java.util.List;
@RestController
@RequestMapping("/api/products")
public class ProductController {
@Autowired
private ProductRepository productRepository;
@GetMapping("/")
public List<Product> getAllProducts () {
return productRepository.findAll();
}
@PostMapping("/")
public Product addProduct (@RequestBody Product product) {
return productRepository.save(product);
}
@GetMapping("/top-selling")
public List<Product> getTopSellingProducts (@RequestParam int topN) {
List<Product> products = productRepository.findBySalesGreaterThan(0 );
products.sort((p1, p2) -> p2.getSales() - p1.getSales());
if (products.size() > topN) {
return products.subList(0 , topN);
}
return products;
}
}
与 H2 集成 H2 内存数据库非常适合开发和单元测试,无需安装额外服务。
配置差异 主要区别在于依赖和 URL 配置。H2 依赖同样放在 runtime 范围,URL 指向内存数据库。
spring.datasource.url=jdbc:h2:mem:testdb
spring.datasource.driver-class-name=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=password
spring.h2.console.enabled=true
spring.h2.console.path=/h2-console
实体类和 Repository 的定义与 MySQL 场景基本一致,这使得代码在不同环境间迁移变得非常容易。
与 MyBatis 集成 如果你更喜欢手写 SQL,MyBatis 是更好的选择。它通过 Mapper 接口和 XML 文件分离关注点。
依赖与配置 添加 MyBatis Starter 依赖,并指定 Mapper XML 的位置。
<dependency >
<groupId > org.mybatis.spring.boot</groupId >
<artifactId > mybatis-spring-boot-starter</artifactId >
<version > 2.3.0</version >
</dependency >
mybatis.mapper-locations=classpath:mapper/*.xml
mybatis.type-aliases-package=com.example.demo.entity
Mapper 与 XML Mapper 接口标记为 @Mapper,XML 文件中定义具体的 SQL 语句。这种方式在处理复杂查询时比 JPA 更灵活。
import org.apache.ibatis.annotations.Mapper;
import java.util.List;
@Mapper
public interface ProductMapper {
List<Product> findAll () ;
int insert (Product product) ;
List<Product> findBySalesGreaterThan (int sales) ;
}
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace ="com.example.demo.mapper.ProductMapper" >
<resultMap id ="ProductResultMap" type ="com.example.demo.entity.Product" >
<id property ="id" column ="id" />
<result property ="productId" column ="product_id" />
<result property ="productName" column ="product_name" />
<result property ="price" column ="price" />
<result property ="sales" column ="sales" />
</resultMap >
<select id ="findAll" resultMap ="ProductResultMap" > SELECT * FROM product </select >
<insert id ="insert" parameterType ="com.example.demo.entity.Product" >
INSERT INTO product (product_id, product_name, price, sales) VALUES (#{productId}, #{productName}, #{price}, #{sales})
</insert >
<select id ="findBySalesGreaterThan" parameterType ="int" resultMap ="ProductResultMap" >
SELECT * FROM product WHERE sales > #{sales}
</select >
</mapper >
事务管理 事务是保证数据一致性的关键。Spring Boot 通过 @Transactional 注解轻松开启事务。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
@Service
public class ProductService {
@Autowired
private ProductRepository productRepository;
@Transactional
public void addProduct (Product product) {
productRepository.save(product);
}
@Transactional(readOnly = true)
public List<Product> getAllProducts () {
return productRepository.findAll();
}
}
注意读写分离,只读查询加上 readOnly = true 可以优化性能。如果多个方法需要在一个事务中执行,可以在 Service 类级别添加注解。
实际应用场景 在实际项目中,我们通常会结合 Controller、Service 和 Repository 构建完整的分层架构。例如电商系统中的商品管理,需要处理库存扣减、订单创建等涉及多步操作的场景,此时事务管理尤为重要。
下面是一个整合后的示例,展示了从启动类到测试类的完整流程。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;
import javax.persistence.*;
import java.util.List;
@SpringBootApplication
public class ProductApplication {
public static void main (String[] args) {
SpringApplication.run(ProductApplication.class, args);
}
@Autowired
private ProductService productService;
public void run (String... args) {
productService.addProduct(new Product ("P001" , "手机" , 1000.0 , 100 ));
productService.addProduct(new Product ("P002" , "电脑" , 5000.0 , 50 ));
productService.addProduct(new Product ("P003" , "电视" , 3000.0 , 80 ));
productService.addProduct(new Product ("P004" , "手表" , 500.0 , 200 ));
productService.addProduct(new Product ("P005" , "耳机" , 300.0 , 150 ));
}
}
@Entity
@Table(name = "product")
class Product {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String productId;
private String productName;
private double price;
private int sales;
public Product () {}
public Product (String productId, String productName, double price, int sales) {
this .productId = productId;
this .productName = productName;
this .price = price;
this .sales = sales;
}
public Long getId () { return id; }
public void setId (Long id) { this .id = id; }
public String getProductId () { return productId; }
public void setProductId (String productId) { this .productId = productId; }
public String getProductName () { return productName; }
public void setProductName (String productName) { this .productName = productName; }
public double getPrice () { return price; }
public void setPrice (double price) { this .price = price; }
public int getSales () { return sales; }
public void setSales (int sales) { this .sales = sales; }
@Override
public String toString () {
return "Product{" +
"id=" + id +
", productId='" + productId + '\'' +
", productName='" + productName + '\'' +
", price=" + price +
", sales=" + sales +
'}' ;
}
}
@Repository
interface ProductRepository extends JpaRepository <Product, Long> {
List<Product> findBySalesGreaterThan (int sales) ;
}
@Service
class ProductService {
@Autowired
private ProductRepository productRepository;
@Transactional
public void addProduct (Product product) {
productRepository.save(product);
}
@Transactional(readOnly = true)
public List<Product> getAllProducts () {
return productRepository.findAll();
}
@Transactional(readOnly = true)
public List<Product> getTopSellingProducts (int topN) {
List<Product> products = productRepository.findBySalesGreaterThan(0 );
products.sort((p1, p2) -> p2.getSales() - p1.getSales());
if (products.size() > topN) {
return products.subList(0 , topN);
}
return products;
}
}
@RestController
@RequestMapping("/api/products")
class ProductController {
@Autowired
private ProductService productService;
@GetMapping("/")
public List<Product> getAllProducts () {
return productService.getAllProducts();
}
@PostMapping("/")
public void addProduct (@RequestBody Product product) {
productService.addProduct(product);
}
@PutMapping("/{id}")
public void updateProduct (@PathVariable Long id, @RequestBody Product product) {
product.setId(id);
productService.updateProduct(product);
}
@DeleteMapping("/{id}")
public void deleteProduct (@PathVariable Long id) {
productService.deleteProduct(id);
}
@GetMapping("/top-selling")
public List<Product> getTopSellingProducts (@RequestParam int topN) {
return productService.getTopSellingProducts(topN);
}
}
运行应用后,访问 /api/products/top-selling?topN=3 即可看到销量最高的商品列表。这种分层设计不仅清晰,还便于后续维护和扩展。
总结 掌握 Spring Boot 的数据访问能力是后端开发的基础。无论是使用 JPA 的快速开发,还是 MyBatis 的精细控制,亦或是 H2 的便捷测试,理解其背后的原理和配置细节都能帮助你写出更健壮的代码。在实际选型时,根据团队习惯和项目复杂度决定即可。
相关免费在线工具 Keycode 信息 查找任何按下的键的javascript键代码、代码、位置和修饰符。 在线工具,Keycode 信息在线工具,online
Escape 与 Native 编解码 JavaScript 字符串转义/反转义;Java 风格 \uXXXX(Native2Ascii)编码与解码。 在线工具,Escape 与 Native 编解码在线工具,online
JavaScript / HTML 格式化 使用 Prettier 在浏览器内格式化 JavaScript 或 HTML 片段。 在线工具,JavaScript / HTML 格式化在线工具,online
JavaScript 压缩与混淆 Terser 压缩、变量名混淆,或 javascript-obfuscator 高强度混淆(体积会增大)。 在线工具,JavaScript 压缩与混淆在线工具,online
Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online