Spring Boot数据库访问全解析


Spring Boot极大地简化了数据库访问的配置和使用,使开发者能够专注于业务逻辑而非基础设施。本文将全面介绍Spring Boot中几种主要数据库访问技术的使用方法与最佳实践。

Spring Data JPA:对象关系映射的最佳选择

Spring Data JPA是基于JPA规范的强大ORM解决方案,它能够显著减少数据访问层的代码量。

配置与依赖

首先在pom.xml中添加相关依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>

application.yml中配置数据源:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb?useSSL=false
    username: root
    password: password
    driver-class-name: com.mysql.cj.jdbc.Driver
  jpa:
    hibernate:
      ddl-auto: update  # 自动更新表结构
    show-sql: true      # 显示SQL语句
    properties:
      hibernate:
        format_sql: true  # 格式化SQL

实体类设计

创建JPA实体类:

@Entity
@Table(name = "products")
public class Product {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(nullable = false, length = 100)
    private String name;
    
    @Column(precision = 10, scale = 2)
    private BigDecimal price;
    
    @Column(length = 500)
    private String description;
    
    @ManyToOne(fetch = FetchType.LAZY)
    @JoinColumn(name = "category_id")
    private Category category;
    
    @CreatedDate
    @Column(updatable = false)
    private LocalDateTime createdAt;
    
    @LastModifiedDate
    private LocalDateTime updatedAt;
    
    // 构造函数、getter和setter方法省略
}

创建Repository

Spring Data JPA的核心是Repository接口,它提供了强大的查询功能:

public interface ProductRepository extends JpaRepository<Product, Long> {
    // 根据名称查找产品
    List<Product> findByNameContaining(String name);
    
    // 根据价格区间查找
    List<Product> findByPriceBetween(BigDecimal minPrice, BigDecimal maxPrice);
    
    // 使用JPQL查询
    @Query("SELECT p FROM Product p WHERE p.price > ?1 AND p.category.id = ?2")
    List<Product> findExpensiveProductsByCategory(BigDecimal price, Long categoryId);
    
    // 原生SQL查询
    @Query(value = "SELECT * FROM products WHERE created_at > ?1", nativeQuery = true)
    List<Product> findRecentProducts(LocalDateTime date);
    
    // 自定义更新操作
    @Modifying
    @Transactional
    @Query("UPDATE Product p SET p.price = p.price * :rate WHERE p.category.id = :categoryId")
    int updatePriceForCategory(@Param("rate") double rate, @Param("categoryId") Long categoryId);
}

MyBatis:灵活的SQL映射框架

MyBatis提供了更多对SQL的控制,特别适合复杂查询和历史系统的集成。

配置与依赖

添加依赖:

<dependency>
    <groupId>org.mybatis.spring.boot</groupId>
    <artifactId>mybatis-spring-boot-starter</artifactId>
    <version>2.2.2</version>
</dependency>

配置MyBatis:

mybatis:
  mapper-locations: classpath:mappers/*.xml
  type-aliases-package: com.fashioncool.domain
  configuration:
    map-underscore-to-camel-case: true  # 开启驼峰命名转换
    cache-enabled: true                 # 启用缓存

XML映射文件

src/main/resources/mappers目录下创建SQL映射文件OrderMapper.xml

<?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.fashioncool.mapper.OrderMapper">
  <!-- 结果映射 -->
  <resultMap id="OrderResultMap" type="Order">
    <id property="id" column="id"/>
    <result property="orderNumber" column="order_number"/>
    <result property="totalAmount" column="total_amount"/>
    <result property="status" column="status"/>
    <result property="createdAt" column="created_at"/>
    <!-- 一对多关系映射 -->
    <collection property="items" ofType="OrderItem">
      <id property="id" column="item_id"/>
      <result property="productId" column="product_id"/>
      <result property="quantity" column="quantity"/>
      <result property="price" column="item_price"/>
    </collection>
  </resultMap>
  
  <!-- 复杂查询示例 -->
  <select id="findOrderWithItems" resultMap="OrderResultMap">
    SELECT o.id, o.order_number, o.total_amount, o.status, o.created_at,
           i.id as item_id, i.product_id, i.quantity, i.price as item_price
    FROM orders o
    LEFT JOIN order_items i ON o.id = i.order_id
    WHERE o.id = #{orderId}
  </select>
  
  <!-- 动态SQL示例 -->
  <select id="searchOrders" resultType="Order">
    SELECT * FROM orders
    <where>
      <if test="status != null">
        status = #{status}
      </if>
      <if test="minAmount != null">
        AND total_amount >= #{minAmount}
      </if>
      <if test="maxAmount != null">
        AND total_amount <= #{maxAmount}
      </if>
      <if test="startDate != null">
        AND created_at >= #{startDate}
      </if>
    </where>
    ORDER BY created_at DESC
  </select>
</mapper>

Mapper接口

@Mapper
public interface OrderMapper {
    // 查询订单及其明细
    Order findOrderWithItems(Long orderId);
    
    // 动态条件搜索
    List<Order> searchOrders(OrderSearchCriteria criteria);
    
    // 使用注解定义简单查询
    @Select("SELECT * FROM orders WHERE user_id = #{userId}")
    List<Order> findByUserId(Long userId);
    
    // 插入操作
    @Insert("INSERT INTO orders(order_number, user_id, total_amount, status) " +
            "VALUES(#{orderNumber}, #{userId}, #{totalAmount}, #{status})")
    @Options(useGeneratedKeys = true, keyProperty = "id")
    int insert(Order order);
}

Spring Data MongoDB:文档数据库的无缝集成

对于非关系型数据,MongoDB是一个流行的选择。Spring Boot提供了出色的MongoDB集成。

配置与依赖

添加依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-mongodb</artifactId>
</dependency>

配置连接:

spring:
  data:
    mongodb:
      uri: mongodb://localhost:27017/myapp

文档类设计

@Document(collection = "articles")
public class Article {
    @Id
    private String id;
    
    private String title;
    
    private String content;
    
    @Field("author_name")
    private String authorName;
    
    @DBRef
    private User author;
    
    private List<String> tags;
    
    @Indexed
    private LocalDateTime publishDate;
    
    // 构造函数、getter和setter方法省略
}

创建Repository

public interface ArticleRepository extends MongoRepository<Article, String> {
    // 根据标题查找
    List<Article> findByTitleContaining(String title);
    
    // 根据标签查找
    List<Article> findByTagsContaining(String tag);
    
    // 根据作者查找
    List<Article> findByAuthor(User author);
    
    // 自定义查询
    @Query("{'publishDate': {$gte: ?0, $lte: ?1}}")
    List<Article> findArticlesPublishedBetween(LocalDateTime start, LocalDateTime end);
    
    // 更新操作
    @Update("{'$addToSet': {'tags': ?1}}")
    void addTag(String articleId, String tag);
}

事务管理

无论使用哪种数据库访问技术,正确的事务管理都至关重要:

@Service
public class OrderService {
    private final OrderRepository orderRepository;
    private final ProductRepository productRepository;
    
    // 构造函数注入
    
    @Transactional  // 声明式事务
    public Order createOrder(OrderRequest request) {
        // 检查库存
        Product product = productRepository.findById(request.getProductId())
            .orElseThrow(() -> new ProductNotFoundException(request.getProductId()));
            
        if (product.getStock() < request.getQuantity()) {
            throw new InsufficientStockException(product.getId());
        }
        
        // 创建订单
        Order order = new Order();
        // 设置订单属性...
        
        // 减少库存
        product.setStock(product.getStock() - request.getQuantity());
        
        // 保存更改
        productRepository.save(product);
        return orderRepository.save(order);
    }
}

多数据源配置

在某些应用中,需要连接多个数据库:

@Configuration
public class MultipleDataSourceConfig {
    @Primary
    @Bean(name = "primaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.primary")
    public DataSource primaryDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    @Bean(name = "secondaryDataSource")
    @ConfigurationProperties(prefix = "spring.datasource.secondary")
    public DataSource secondaryDataSource() {
        return DataSourceBuilder.create().build();
    }
    
    // 为不同数据源配置EntityManagerFactory
    @Primary
    @Bean(name = "primaryEntityManagerFactory")
    public LocalContainerEntityManagerFactoryBean primaryEntityManagerFactory(
            EntityManagerFactoryBuilder builder,
            @Qualifier("primaryDataSource") DataSource dataSource) {
        return builder
                .dataSource(dataSource)
                .packages("com.fashioncool.domain.primary")
                .persistenceUnit("primary")
                .build();
    }
    
    // 配置事务管理器...
}

总结

Spring Boot为数据库访问提供了丰富的选择,可以根据项目需求选择最合适的技术:

  • Spring Data JPA:适合绿地项目,通过ORM简化数据访问
  • MyBatis:适合对SQL有更多控制需求的场景
  • Spring Data MongoDB:适合处理非结构化或半结构化数据

掌握这些技术,将帮助我们构建高效、可维护的数据访问层,为应用提供坚实的基础。