如何使用 Spring/JPA/Hibernate 用初始数据填充 Java (web) 应用程序
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2423466/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to populate Java (web) application with initial data using Spring/JPA/Hibernate
提问by Tuukka Mustonen
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.
我想以编程方式使用初始数据设置我的数据库。我想填充我的数据库用于开发运行,而不是用于测试运行(这很容易)。该产品建立在 Spring 和 JPA/Hibernate 之上。
- Developer checks out the project
- Developer runs command/script to setup database with initial data
- Developer starts application (server) and begins developing/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.
我想要的是通过所需的部分设置我的环境,以便调用我的 DAO 并将新对象插入数据库。我不想在原始 SQL、XML 中创建初始数据集,也不想转储数据库或其他任何内容。我想以编程方式创建对象并将它们保存在数据库中,就像在正常的应用程序逻辑中一样。
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.
实现此目的的一种方法是正常启动我的应用程序并运行执行初始化的特殊 servlet。但这真的是要走的路吗?我很想将初始数据设置作为 Maven 任务执行,如果我采用 servlet 方法,我不知道该怎么做。
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).
有一些类似的问题。我快速浏览了建议的 DBUnit 和 Unitils。但是他们似乎非常专注于设置测试环境,这不是我在这里想要的。DBUnit 进行初始数据填充,但仅使用 xml/csv 固定装置,这不是我在这里所追求的。然后,Maven 有 SQL 插件,但我不想处理原始 SQL。Maven 也有 Hibernate 插件,但它似乎只对 Hibernate 配置和表模式创建有帮助(而不是用数据填充数据库)。
How to do this?
这个怎么做?
Partial solution 2010-03-19
部分解决方案 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
- 使用单元测试填充数据库 #2423663
- 使用 ServletContextListener 控制 Web 上下文启动 #2424943 和 #2423874
- 使用 Spring ApplicationListener 和 Spring 的标准和自定义事件 #2423874
I implemented this using Spring's ApplicationListener:
我使用 Spring 的 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:
应用上下文.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).
出于某种原因,我无法启动 ContextStartedEvent,所以我选择了 ContextRefreshedEvent,它也在启动时启动(还没有遇到其他情况)。
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.
如何刷新数据库?目前,我只是删除了 HSQLDB 工件,并在启动时由 Hibernate 生成了一个新模式。由于 DB 也是空的。
回答by Olivier Croisier
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.
您可以使用 JPA 和普通 Java 编写单元测试来填充数据库。Maven 将调用此测试作为标准构建生命周期的一部分。因此,您将获得一个完全初始化的数据库,根据要求使用 Maven、JPA 和 Java。
回答by mickthompson
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
执行此操作的常用方法是使用 SQL 脚本。然后您运行一个特定的 bash 文件,使用您的 .sql 填充数据库
如果您希望能够在 WebApp 启动期间以编程方式设置您的数据库,您可以使用Web Context Listener。
在您的 webContext 初始化期间,您可以使用 Servlet Context Listener 来访问您的 DAO(服务层..无论如何)创建您的实体并像您在 Java 代码中所做的那样持久化它们
p.s. as a reference Servlet Life Cycle
ps 作为参考Servlet 生命周期
If you use Spring you should have a look at the Standard and Custom Eventssection 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)
如果您使用 Spring,您应该查看参考的标准和自定义事件部分。这是实现了解 Spring 上下文的“Spring Listener”的更好方法(如果您需要从它检索您的服务)
回答by Pascal Thivent
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 mainmethod and be invoked on the command line, by maven (with the Exec Maven Plugin) or even wrapped as a Maven plugin.
您可以在纯 Java 类中创建 JPA 实体并保留它们。这个类可以由 servlet 调用,但也有一个main方法,可以在命令行上调用,由 maven(使用 Exec Maven 插件)甚至包装为 Maven 插件。
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.
但是您的最终工作流程尚不清楚(您希望 init 成为应用程序启动的一部分还是在构建期间完成?)并且需要一些说明。
回答by Martijn Burger
I would us a Singleton bean for that:
我会为此使用一个 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.
}
}
回答by DHM
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.
我有同样的问题。我试过在 bean 上使用 init 方法,但它在没有 AOP 的原始 bean 上运行,因此不能使用 @Transactional。@PostConstruct 和其他 bean 生命周期机制似乎也是如此。
Given that, I switched to ApplicationListener w/ ContextRefreshedEvent; however, in this case, @PersistenceContext is failing to get an entity manager
鉴于此,我切换到带有 ContextRefreshedEvent 的 ApplicationListener;但是,在这种情况下,@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)
Using spring 2.0.8, jpa1, hibernate 3.0.5.
使用 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.
我很想创建一个非 spring 管理的 entitymanagerfactory 并直接做所有事情,但担心这会干扰 Spring 管理的实体和事务管理器的其余部分。
回答by Bozho
- In the aforementioned
ServletContextListeneror in a common startup place put all the forthcoming code - Define your data in an agreeable format - XML, JSON or even java serialization.
- Check whether the initial data exists (or a flag indicating a successful initial import)
- 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 theEntityManagerin the database.
- 在上述
ServletContextListener或在一个共同的启动地方把所有即将到来的代码 - 以可接受的格式定义您的数据 - XML、JSON 甚至 java 序列化。
- 检查初始数据是否存在(或初始导入成功的标志)
- 如果存在,则跳过。如果它不存在,则获取一个新的 DAO(使用
WebApplicationContextUtils.getRequiredWebApplicationContext().getBean(..)),迭代所有预定义的对象并通过EntityManager数据库中的 将它们持久化。
回答by Artic
Depend on your db. It is better to have script to set up db
取决于你的数据库。最好有脚本来设置数据库
回答by Joseph Victor Zammit
Found this ServletContextListener exampleby mkyong. Quoting the article:
通过mkyong找到了这个ServletContextListener 示例。引用文章:
You want to initialize the database connection pool before the web application is start, is there a “main()” method in whole web application?
你想在web应用启动前初始化数据库连接池,整个web应用中是否有“main()”方法?
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.
我测试了这种方法来为我的 webapp 插入一些初始数据;有用。
回答by David W Crook
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.
我不确定您是否可以避免使用某些 SQL。这取决于您的开发人员是否正在使用未定义架构的空数据库,或者表是否存在但它们是空的。
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.
如果您从空表开始,那么您可以使用 Java 方法来生成数据。我对 Maven 不太熟悉,但我假设您可以创建一些使用 DAO 类生成数据的任务。您甚至可以使用基于 JVM 的脚本语言(如 Groovy)来编写它,这样可以直接使用您的 DAO 类。您将有一个类似的任务来清除表中的数据。然后,您的开发人员只需在命令行上或通过他们的 IDE 运行这些任务,作为结帐后的手动步骤。
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.
如果你有一个新的数据库实例,我认为你需要执行一些 SQL 来创建模式。从技术上讲,您可以通过使用 hibernate 执行 SQL 调用来做到这一点,但这似乎并不值得。
回答by charlie_pl
I found some interesting code in this repository: https://github.com/resilient-data-systems/fast-stateless-api-authentication
我在这个存储库中发现了一些有趣的代码: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");
}
}

