How to persist when using Spring Boot with JPA

26,864

Solution 1

In your example your EntityManager is always going to be null. Spring will not automatically wire one into your Entity class. I'm also not sure what your goal is here but I'm willing to bet you most likely don't want to have your Entity hold your EntityManager

I think what you may be looking for are Spring Data Repositories. I would recommend reading that to get the basics.

To get started with repositories:

First thing I would do would be remove transient EntityManager entityManager; and your persist/merge functions from your Account class.

Inside of your Application class you can add the @EnableJpaRepositories annotation.

Next create a new Interface (not a new class), this will be your Account repository.

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

Where Account is the type of Entity and Long is the ID type of Account

There are a few repository interfaces with various support included with Spring Data that inherit from Repository.

Without adding anything to your interface, PagingAndSortingRepository will give you support CRUD operations and paging.

Its also possible to add custom queries to your interface using JPQL, take a look at the @Query annotation. http://docs.spring.io/spring-data/jpa/docs/current/reference/html/#jpa.query-methods.at-query

Now, you can @Autowire AccountRepository into any of your Spring managed beans and begin saving and fetching data.

Solution 2

Try replacing:

@Configuration
@ComponentScan
@EnableAutoConfiguration

with:

@SpringBootApplication

See this article

Specifically: By default, Spring Boot will enable JPA repository support and look in the package (and its subpackages) where @SpringBootApplication is located. If your configuration has JPA repository interface definitions located in a package not visible, you can point out alternate packages using @EnableJpaRepositories and its type-safe basePackageClasses=MyRepository.class parameter.

Solution 3

the problem in your code was:

  1. EntityManager entry was not @Autowired and even if it was @Entity isn't recognized as a spring bean and for this reason the dependency injection service of spring didn't work
  2. since that how i said previously the bean annotated with @Entity isn't a spring bean and for this reason you can't benefit of @Transactional annotation

You can use Spring DataJPA project in this case remember of configure tis with @EnableJpaRepositories or you can create the your repository organizing your code in this way

Entity:

@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;
.... 

}

Repository layer:

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 for benefit of all feature of spring boot @EnableTransactionManagement for benefit of Transaction management

@SpringBootApplication
@EnableTransactionManagement
class ApplicationConfig {

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

I hope that this can help you. (Spelling corrected 2018-02-24)

Share:
26,864
Jan Vladimir Mostert
Author by

Jan Vladimir Mostert

Updated on February 25, 2020

Comments

  • Jan Vladimir Mostert
    Jan Vladimir Mostert about 4 years

    I'm used to using Spring Roo to generate my entities and having it handle injecting the entityManager as well as the persist and other methods via AspectJ classes. Now I'm trying to use Spring Boot to do something simple that will write things to the database ...

    @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;
      }
    

    When I'm calling persist or merge, entityManager is obviously null.

    I've also tried adding implements CrudRepository<Account, Long> to the Account class to see it'll give me that functionality via a Default Implementation, but what I'm getting is simply empty classes that needs to be filled in.

    I've had a look at the Spring Boot docs, they cover it very briefly omitting just enough detail to so that it's not obvious what I'm missing.

    I have an Application class that bootstraps the application:

    @Configuration
    @ComponentScan
    @EnableAutoConfiguration
    public class Application {
    
      public static void main(String[] args) throws Exception {
        SpringApplication.run(Application.class, args);
      }
    
    }
    

    My properties file looks like this:

    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
    

    This database is automatically being created thanks to the ddl-auto=update property

    What is the correct way to persist entities in Spring Boot + JPA and if what I've done is correct so far, how do I "autowire" or auto-create the entityManager?