开发者

How to populate Java (web) application with initial data using Spring/JPA/Hibernate

I want to setup my database with initial data programmatically. I want to populate my database for development runs, not for testing runs (it's easy). The product is built on top of Spring and JPA/Hibernate.

  • Developer checks out the project
  • Developer runs command/script to setup database with initial data
  • Developer starts application (server) and begins develop开发者_StackOverflow社区ing/testing

then:

  • Developer runs command/script to flush the database and set it up with new initial data because database structures or the initial data bundle were changed

What I want is to setup my environment by required parts in order to call my DAOs and insert new objects into database. I do not want to create initial data sets in raw SQL, XML, take dumps of database or whatever. I want to programmatically create objects and persist them in database as I would in normal application logic.

One way to accomplish this would be to start up my application normally and run a special servlet that does the initialization. But is that really the way to go? I would love to execute the initial data setup as Maven task and I don't know how to do that if I take the servlet approach.

There is somewhat similar question. I took a quick glance at the suggested DBUnit and Unitils. But they seem to be heavily focused in setting up testing environments, which is not what I want here. DBUnit does initial data population, but only using xml/csv fixtures, which is not what I'm after here. Then, Maven has SQL plugin, but I don't want to handle raw SQL. Maven also has Hibernate plugin, but it seems to help only in Hibernate configuration and table schema creation (not in populating db with data).

How to do this?

Partial solution 2010-03-19

Suggested alternatives are:

  • Using unit tests to populate the database #2423663
  • Using ServletContextListener to gain control on web context startup #2424943 and #2423874
  • Using Spring ApplicationListener and Spring's Standard and Custom Events #2423874

I implemented this using Spring's ApplicationListener:

Class:

public class ApplicationContextListener implements ApplicationListener {

    public void onApplicationEvent(ApplicationEvent event) {

        if (event instanceof ContextRefreshedEvent) {
            ...check if database is already populated, if not, populate it...
        }
    }
}

applicationContext.xml:

<bean id="applicationContextListener" class="my.namespaces.ApplicationContextListener" />

For some reason I couldn't get ContextStartedEvent launched, so I chose ContextRefreshedEvent which is launched in startup as well (haven't bumped into other situations, yet).

How do I flush the database? Currently, I simply remove HSQLDB artifacts and a new schema gets generated on startup by Hibernate. As the DB is then also empty.


You can write a unit test to populate the database, using JPA and plain Java. This test would be called by Maven as part of the standard build lifecycle. As a result, you would get an fully initialized database, using Maven, JPA and Java as requested.


The usual way to do this is to use a SQL script. Then you run a specific bash file that populate the db using your .sql
If you want to be able to programmatically set your DB during the WebApp StartUp you can use a Web Context Listener.
During the initialization of your webContext you can use a Servlet Context Listener to get access to your DAO (Service Layer.. whatever) create your entities and persist them as you use to do in your java code

p.s. as a reference Servlet Life Cycle

If you use Spring you should have a look at the Standard and Custom Events section of the Reference. That's a better way to implement a 'Spring Listener' that is aware of Spring's Context (in the case you need to retrieve your Services form it)


You could create JPA entities in a pure Java class and persist them. This class could be invoked by a servlet but also have a main method and be invoked on the command line, by maven (with the Exec Maven Plugin) or even wrapped as a Maven plugin.

But you're final workflow is not clear (do you want the init to be part of the application startup or done during the build?) and requires some clarification.


I would us a Singleton bean for that:

import javax.annotation.PostConstruct;
import javax.ejb.Startup;
import javax.ejb.Singleton;

@Singleton
@Startup
public class InitData {

    @PostConstruct
    public void load() {
        // Load your data here.
    }

}


Depend on your db. It is better to have script to set up db


  1. In the aforementioned ServletContextListener or in a common startup place put all the forthcoming code
  2. Define your data in an agreeable format - XML, JSON or even java serialization.
  3. Check whether the initial data exists (or a flag indicating a successful initial import)
  4. If it exists, skip. If it does not exist, get a new DAO (using WebApplicationContextUtils.getRequiredWebApplicationContext().getBean(..)) , iterate all predefined objects and persist them via the EntityManager in the database.


I'm having the same problem. I've tried using an init-method on the bean, but that runs on the raw bean w/o AOP and thus cannot use @Transactional. The same seems to go for @PostConstruct and the other bean lifecycle mechanism.

Given that, I switched to ApplicationListener w/ ContextRefreshedEvent; however, in this case, @PersistenceContext is failing to get an entity manager

javax.persistence.PersistenceException: org.hibernate.SessionException: Session is closed!
  at org.hibernate.ejb.AbstractEntityManagerImpl.throwPersistenceException(AbstractEntityManagerImpl.java:630)
  at org.hibernate.ejb.QueryImpl.getSingleResult(QueryImpl.java:108)

Using spring 2.0.8, jpa1, hibernate 3.0.5.

I'm tempted to create a non-spring managed entitymanagerfactory and do everything directly but fear that would interfere w/ the rest of the Spring managed entity and transaction manager.


I'm not sure if you can get away from using some SQL. This would depend if your develoeprs are staring with an empty database with no schema defined or if the tables are there but they are empty.

If you starting with empty tables then you could use a Java approach to generating the data. I'm not that familiar with Maven but I assume you can create some task that would use your DAO classes to generate the data. You could probably even write it using a JVM based scripting language like Groovy that would be able to use your DAO classes directly. You would have a similar task that would clear the data from the tables. Then your developers would just run these tasks on the command line or through their IDE as a manual step after checkout.

If you have a fresh database instance that I think you will need to execute some SQL just to create the schema. You could technically do that with executing SQL calls with hibernate but that really doesn't seem worth it.


Found this ServletContextListener example by mkyong. Quoting the article:

You want to initialize the database connection pool before the web application is start, is there a “main()” method in whole web application?

This sounds to me like the right place where to have code to insert initial DB data.

I tested this approach to insert some initial data for my webapp; it works.


I found some interesting code in this repository: https://github.com/resilient-data-systems/fast-stateless-api-authentication

This works pretty neat in my project:

@Component
@DependsOn({ "dataSource" })
public class SampleDataPopulator {
    private final static Logger log = LoggerFactory.getLogger(SampleDataPopulator.class);

    @Inject
    MyRepository myRepository

    @PostConstruct
    public void populateSampleData() {
        MyItem item = new ResourceDatabasePopulator();
        myRepository.save(item);

        log.info("Populated DB with sample data");
    }
}


You can put a file called data.sql in src/main/resources, it will be read and executed automatically on startup. See this tutorial.

The other answers did not work for me.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜