将 Spring Boot 与 JPA 一起使用时如何持久化

Posted

技术标签:

【中文标题】将 Spring Boot 与 JPA 一起使用时如何持久化【英文标题】:How to persist when using Spring Boot with JPA 【发布时间】:2016-06-25 21:56:16 【问题描述】:

我习惯于使用 Spring Roo 来生成我的实体并让它通过 AspectJ 类处理注入 entityManager 以及 persist 和其他方法。 现在我正在尝试使用 Spring Boot 做一些简单的事情,将内容写入数据库......

@Entity
@Table(name = "account")
public class Account  

  transient EntityManager entityManager;

  @Id
  @GeneratedValue
  private Long id;

  @Column(name = "username", nullable = false, unique = true)
  private String username;

  @Column(name = "password", nullable = false)
  private String password;

  ... getters and setters

  @Transactional
  public void persist() 
    if (this.entityManager == null) this.entityManager = entityManager();
    this.entityManager.persist(this);
  

  @Transactional
  public Account merge() 
    if (this.entityManager == null) this.entityManager = entityManager();
    Account merged = this.entityManager.merge(this);
    this.entityManager.flush();
    return merged;
  

当我调用persist 或merge 时,entityManager 显然为null。

我还尝试将implements CrudRepository<Account, Long> 添加到Account 类中,以查看它会通过默认实现为我提供该功能,但我得到的只是需要填写的空类。

我查看了 Spring Boot 文档,他们非常简要地介绍了它,省略了足够的细节,因此我遗漏了什么并不明显。

我有一个引导应用程序的应用程序类:

@Configuration
@ComponentScan
@EnableAutoConfiguration
public class Application 

  public static void main(String[] args) throws Exception 
    SpringApplication.run(Application.class, args);
  


我的属性文件如下所示:

spring.application.name: Test Application

spring.datasource.url: jdbc:mysql://localhost/test
spring.datasource.username=root
spring.datasource.password=
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.jpa.hibernate.ddl-auto=update

由于ddl-auto=update 属性,此数据库会自动创建

在 Spring Boot + JPA 中持久化实体的正确方法是什么?如果到目前为止我所做的是正确的,我该如何“自动装配”或自动创建 entityManager?

【问题讨论】:

【参考方案1】:

在您的示例中,您的 EntityManager 始终为空。 Spring 不会自动将一个连接到您的 Entity 类中。我也不确定你的目标是什么,但我敢打赌你很可能不想让你的Entity 持有你的EntityManager

我认为您可能正在寻找的是Spring Data Repositories。我建议您阅读以了解基础知识。

开始使用存储库:

我要做的第一件事是从 Account 类中删除 transient EntityManager entityManager;persist/merge 函数。

Application 类中,您可以添加 @EnableJpaRepositories 注释。

接下来创建一个新的Interface(不是新类),这将是您的Account 存储库。

@Repository
public interface AccountRepository extends PagingAndSortingRepository<Account, Long>

其中AccountEntity 的类型,LongAccount 的ID 类型

Spring Data 中包含一些具有各种支持的存储库接口,它们继承自 Repository

无需向您的界面添加任何内容,PagingAndSortingRepository 将为您提供 CRUD 操作和分页支持。

还可以使用 JPQL 向您的界面添加自定义查询,请查看 @Query 注释。 http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query

现在,您可以 @Autowire AccountRepository 进入您的任何 Spring 托管 bean 并开始保存和获取数据。

【讨论】:

哇,这与 Spring Roo 的做法相比有很大不同,但与所有生成的 AspectJ 文件相比也更简洁。感谢您提供非常详细的答案! Roo 基于 Active Record 模式,实体可以在其中管理自己。 Spring Data 基于 Repository 模式,其中一个单独的对象负责管理域对象。【参考方案2】:

尝试替换:

@Configuration
@ComponentScan
@EnableAutoConfiguration

与:

@SpringBootApplication

看到这个article

具体来说:默认情况下,Spring Boot 将启用 JPA 存储库支持,并在 @SpringBootApplication 所在的包(及其子包)中查找。如果您的配置有位于不可见包中的 JPA 存储库接口定义,您可以使用 @EnableJpaRepositories 及其类型安全的 basePackageClasses=MyRepository.class 参数指出备用包。

【讨论】:

@SpringBootApplication 只是为了方便添加,相当于添加了@Configuration, @ComponentScan, @EnableAutoConfiguration。 docs.spring.io/spring-boot/docs/current/reference/html/…【参考方案3】:

您的代码中的问题是:

    EntityManager 条目不是 @Autowired,即使它是 @Entity 也不会被识别为 spring bean,因此 spring 的依赖注入服务不起作用 因为我之前说过,使用@Entity 注释的 bean 不是 Spring bean,因此您无法从 @Transactional 注释中受益

在这种情况下,您可以使用 Spring DataJPA 项目,记住使用 @EnableJpaRepositories 配置 tis,或者您可以创建存储库以这种方式组织您的代码

实体:

@Entity
@Table(name = "account")
public class Account  

  @Id
  @GeneratedValue
  private Long id;

  @Column(name = "username", nullable = false, unique = true)
  private String username;

  @Column(name = "password", nullable = false)
  private String password;
.... 

存储层:

public interface AccountRepository 
   void persist();
   Account merge();


@Repository
class AccountRepositoryJpaImpl implements AccountRepository 

  @Autowired
  private EntityManager entityManager;

  ...
  @Transactional
  public void persist() 
    if (this.entityManager == null) this.entityManager = entityManager();
    this.entityManager.persist(this);
  

  @Transactional
  public Account merge() 
    if (this.entityManager == null) this.entityManager = entityManager();
    Account merged = this.entityManager.merge(this);
    this.entityManager.flush();
    return merged;
  
....

@SpringBootApplication 受益于 Spring Boot 的所有功能 @EnableTransactionManagement 有利于事务管理

@SpringBootApplication
@EnableTransactionManagement
class ApplicationConfig 

    public static void main(String[] args) 
        SpringApplication.run(ApplicationConfig .class, args);
    

我希望这可以帮助你。 (拼写更正 2018-02-24)

【讨论】:

以上是关于将 Spring Boot 与 JPA 一起使用时如何持久化的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 自动配置无法与 spring-data-jpa 一起正常工作

Spring Boot + Spring Batch + Spring JPA

JPA 左连接查询不会在 Spring Boot 中实例化子集合?

Spring Boot JPA CrudRepository 选择性更新 - 只有一些属性

Spring Boot 未使用 Spring Data 和 Hibernate 与 sql Server 一起运行

Spring Boot with JOOQ 和 Spring Data JPA 之间的技术差异