Как заполнить приложение Java (web) исходными данными с помощью Spring/JPA/Hibernate
Я хочу программно настроить мою базу данных с исходными данными. Я хочу заполнить свою базу данных для прогонов разработки, а не для пробных запусков (это легко). Продукт построен поверх Spring и JPA/Hibernate.
- Разработчик проверяет проект
- Разработчик запускает команду / script для настройки базы данных с исходными данными
- Разработчик запускает приложение (сервер) и начинает разработку/тестирование
то
- Разработчик запускает команду / script, чтобы очистить базу данных и настроить ее новыми исходными данными, поскольку были изменены структуры баз данных или исходный набор данных.
Я хочу настроить свою среду по необходимым частям, чтобы вызвать мои DAO и вставить новые объекты в базу данных. Я не хочу создавать исходные наборы данных в сыром SQL, XML, принимать дампы базы данных или что-то еще. Я хочу программно создавать объекты и сохранять их в базе данных, как и в обычной логике приложения.
Одним из способов добиться этого было бы запустить мое приложение в обычном режиме и запустить специальный сервлет, который выполняет инициализацию. Но действительно ли это путь? Я хотел бы выполнить начальную настройку данных как задачу Maven, и я не знаю, как это сделать, если я возьму подход сервлета.
Существует несколько схожий вопрос. Я быстро взглянул на предлагаемые DBUnit и Unitils. Но они, похоже, в большой степени сосредоточены на настройке тестовых сред, чего я здесь не хочу. DBUnit делает начальную совокупность данных, но только с использованием xml/csv fixtures, и это не то, что я здесь. Затем у Maven есть плагин SQL, но я не хочу обрабатывать raw SQL. У Maven также есть плагин Hibernate, но он, похоже, помогает только в конфигурации конфигурации Hibernate и схемы таблицы (а не в заполнении db данными).
Как это сделать?
Частичное решение 2010-03-19
Предлагаемые альтернативы:
- Использование модульных тестов для заполнения базы данных # 2423663
- Использование ServletContextListener для получения контроля над запуском веб-контекста # 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. Так как DB также пуст.
Ответы
Ответ 1
Вы можете написать unit test для заполнения базы данных, используя JPA и обычную Java. Этот тест будет вызываться Maven как часть стандартного жизненного цикла сборки.
В результате вы получите полностью инициализированную базу данных, используя Maven, JPA и Java в соответствии с запросом.
Ответ 2
Обычный способ сделать это - использовать SQL script. Затем вы запускаете определенный файл bash, который заполняет db, используя ваш .sql
Если вы хотите программно установить свою БД во время запуска WebApp, вы можете использовать прослушиватель веб-контекста.
Во время инициализации вашего веб-контекста вы можете использовать прослушиватель контекста сервлета, чтобы получить доступ к вашему DAO (Service Layer.. what), создайте ваши сущности и сохраните их, как вы это делаете в своем java-коде
p.s. как ссылочный жизненный цикл сервлета
Если вы используете Spring, вы должны посмотреть раздел Стандартные и пользовательские события в ссылке. Это лучший способ реализовать "Spring прослушиватель", который знает Spring Контекст (в случае, если вам нужно получить ваши службы)
Ответ 3
Вы можете создавать объекты JPA в чистом Java-классе и сохранять их. Этот класс может быть вызван сервлетом, но также имеет метод main
и вызывается в командной строке maven (с помощью Exec Maven Plugin) или даже завершен как плагин Maven.
Но окончательный рабочий процесс не ясен (вы хотите, чтобы init был частью запуска приложения или выполнялся во время сборки?) и требует некоторого уточнения.
Ответ 4
Я бы выбрал 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.
}
}
Ответ 5
В зависимости от вашего db. Лучше иметь script для установки db
Ответ 6
- В вышеприведенном
ServletContextListener
или в общем месте запуска поместите весь предстоящий код
- Определите свои данные в удобном формате - XML, JSON или даже сериализацию Java.
- Проверьте, существуют ли исходные данные (или флаг, указывающий успешный первоначальный импорт)
- Если он существует, пропустите. Если он не существует, получите новый DAO (используя
WebApplicationContextUtils.getRequiredWebApplicationContext().getBean(..)
), выполните итерацию всех предопределенных объектов и сохраните их через EntityManager
в базе данных.
Ответ 7
У меня такая же проблема. Я попытался использовать init-метод в bean, но работает на raw bean без AOP и, следовательно, не может использовать @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.
У меня возникает соблазн создать управляемый объект entitymanagerfactory, не связанный с spring, и делать все непосредственно, но бояться, что помешало бы остальной части управляемого объекта spring и менеджера транзакций.
Ответ 8
Я не уверен, что вы можете уйти от использования какого-либо SQL. Это будет зависеть, если ваши разработчики смотрят с пустой базой данных без какой-либо схемы или если таблицы есть, но они пусты.
Если вы начинаете с пустых таблиц, вы можете использовать Java-подход для генерации данных. Я не знаком с Maven, но я предполагаю, что вы можете создать некоторую задачу, которая будет использовать ваши классы DAO для генерации данных. Возможно, вы даже можете записать его с использованием языка сценариев на основе JVM, например Groovy, который сможет напрямую использовать ваши классы DAO. У вас будет аналогичная задача, которая очистит данные из таблиц. Затем ваши разработчики просто запускают эти задачи в командной строке или через их IDE в качестве ручного шага после проверки.
Если у вас есть новый экземпляр базы данных, который, я думаю, вам нужно будет выполнить некоторый SQL, чтобы создать схему. Вы можете технически сделать это с выполнением SQL-вызовов с помощью спящего режима, но это действительно не стоит.
Ответ 9
Нашел этот пример ServletContextListener mkyong. Цитирование статьи:
Вы хотите инициализировать пул соединений с базой данных перед веб-сайтом приложение запускается, существует ли метод "main()" во всей сети приложение?
Это звучит для меня как правильное место, где есть код для вставки исходных данных БД.
Я протестировал этот подход, чтобы вставить некоторые исходные данные для моего webapp; он работает.
Ответ 10
Я нашел в этом репозитории интересный код: 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");
}
}
Ответ 11
Вы можете поместить файл с именем data.sql
в src/main/resources, он будет прочитан и автоматически выполнен при запуске. Смотрите этот урок.
Другие ответы не спомогли мне.