如何使用Spring / JPA / Hibernate使用初始数据填充Java(Web)应用程序

时间:2010-03-11 08:24:09

标签: java hibernate spring web-applications jpa

我想以编程方式使用初始数据设置我的数据库。我想填充我的数据库用于开发运行,而不是用于测试运行(这很容易)。该产品构建于Spring和JPA / Hibernate之上。

  • 开发人员签出项目
  • 开发人员运行命令/脚本以使用初始数据设置数据库
  • 开发人员启动应用程序(服务器)并开始开发/测试

然后:

  • 开发人员运行命令/脚本来刷新数据库并使用新的初始数据进行设置,因为数据库结构或初始数据包已更改

我想要的是通过必需的部分设置我的环境,以便调用我的DAO并将新对象插入数据库。我不想在原始SQL,XML,转储数据库或其他任何东西中创建初始数据集。我想以编程方式创建对象,并像在普通应用程序逻辑中一样将它们保存在数据库中。

实现此目的的一种方法是正常启动我的应用程序并运行一个执行初始化的特殊servlet。但这真的是要走的路吗?我很乐意将初始数据设置作为Maven任务执行,如果我采用servlet方法,我不知道该怎么做。

somewhat similar question。我快速浏览了建议的DBUnit和Unitils。但他们似乎非常专注于设置测试环境,这不是我想要的。 DBUnit执行初始数据填充,但仅使用xml / csv fixtures,这不是我在此之后所做的。然后,Maven有SQL插件,但我不想处理原始SQL。 Maven也有Hibernate插件,但它似乎只对Hibernate配置和表模式创建有帮助(而不是用数据填充数据库)。

怎么做?

部分解决方案2010-03-19

建议的替代方案是:

  • 使用单元测试填充数据库#2423663
  • 使用ServletContextListener获取对Web上下文启动的控制#2424943和#2423874
  • 使用Spring ApplicationListener和Spring的标准和自定义事件#2423874

我使用Spring的ApplicationListener实现了这个:

类别:

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" />

出于某种原因,我无法启动ContextStartedEvent,因此我选择了在启动时启动的ContextRefreshedEvent(尚未碰到其他情况)。

如何刷新数据库?目前,我只是删除了HSQLDB工件,并在Hibernate启动时生成了新的模式。因为数据库也是空的。

11 个答案:

答案 0 :(得分:5)

您可以使用JPA和普通Java编写单元测试来填充数据库。 Maven将此测试称为标准构建生命周期的一部分。 因此,您将获得一个完全初始化的数据库,使用Maven,JPA和Java。

答案 1 :(得分:2)

通常的方法是使用SQL脚本。然后运行一个特定的bash文件,使用你的.sql填充数据库 如果您希望能够在WebApp StartUp期间以编程方式设置数据库,则可以使用Web Context Listener
在webContext初始化期间,您可以使用Servlet上下文侦听器来访问您的DAO(服务层......无论如何)创建您的实体并在您的java代码中保留它们

P.S。作为参考Servlet Life Cycle

如果你使用Spring,你应该看一下Reference的Standard and Custom Events部分。这是一个更好的方法来实现一个知道Spring的Context的'Spring Listener'(在你需要从中检索你的服务的情况下)

答案 2 :(得分:2)

您可以在纯Java类中创建JPA实体并保留它们。这个类可以由servlet调用,但也有一个main方法,可以在命令行上调用,由maven(使用Exec Maven插件)或甚至包装为Maven插件。

但是你最终的工作流程还不清楚(你想让init成为应用程序启动的一部分还是在构建期间完成?)并且需要一些澄清。

答案 3 :(得分:2)

我想要一个Singleton bean:

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.
    }

}

答案 4 :(得分:1)

取决于您的数据库。最好有脚本来设置db

答案 5 :(得分:1)

  1. 在前面提到的ServletContextListener或在一个共同的创业地点放置所有即将推出的代码
  2. 以一种令人愉快的格式定义您的数据 - XML,JSON甚至是Java序列化。
  3. 检查初始数据是否存在(或表示初始导入成功的标志)
  4. 如果存在,请跳过。如果它不存在,则获取一个新DAO(使用WebApplicationContextUtils.getRequiredWebApplicationContext().getBean(..)),迭代所有预定义对象并通过数据库中的EntityManager保留它们。

答案 6 :(得分:1)

我遇到了同样的问题。我已经尝试在bean上使用init方法,但是它在没有AOP的原始bean上运行,因此不能使用@Transactional。 @PostConstruct和其他bean生命周期机制似乎也是如此。

鉴于此,我切换到ApplicationListener w / ContextRefreshedEvent;但是,在这种情况下,@ PersistenceContext无法获得实体管理器

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)

使用spring 2.0.8,jpa1,hibernate 3.0.5。

我很想创建一个非Spring管理实体,并直接做所有事情,但担心这会干扰Spring管理实体和事务管理器的其余部分。

答案 7 :(得分:0)

我不确定你是否可以放弃使用某些SQL。这取决于您的开发人员是否正在使用没有定义架构的空数据库,或者表是否存在但是它们是空的。

如果从空表开始,那么可以使用Java方法生成数据。我对Maven并不熟悉,但我认为你可以创建一些使用DAO类来生成数据的任务。您甚至可以使用基于JVM的脚本语言(如Groovy)来编写它,它可以直接使用您的DAO类。您将有一个类似的任务,将清除表中的数据。然后,您的开发人员只需在命令行上运行这些任务,或者在结帐后作为手动步骤通过IDE运行。

如果您有一个新的数据库实例,我认为您只需要执行一些SQL来创建架构。你可以通过hibernate执行SQL调用来实现这一点,但这似乎不值得。

答案 8 :(得分:0)

ServletContextListener example找到此mkyong。引用文章:

  

您希望在Web之前初始化数据库连接池   应用程序启动,整个网络中是否有“main()”方法   应用

这听起来像是在哪里有代码插入初始数据库数据的正确位置。

我测试了这种方法为我的webapp插入一些初始数据;它有效。

答案 9 :(得分:0)

我在此存储库中找到了一些有趣的代码:https://github.com/resilient-data-systems/fast-stateless-api-authentication

这在我的项目中非常简洁:

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

答案 10 :(得分:0)

您可以在src / main / resources中放入一个名为data.sql的文件,它将在启动时自动读取并执行。参见this tutorial

其他答案对我不起作用。