19 мар. 2010 г.

Java. Загрузка данных в БД в XML формате.

В эту тяпницу зародилась у меня очередная бредовая идея. Очень часто в веб проектах возникает необходимость добавить возможность загрузки данных в БД через веб интерфейс. Проще всего сделать это путем загрузки на сервер какого-то sql файла. Или в моем случае это будет XML файл в формате DbUnit.


Итак, для начала надо понять как работает этот самый DbUnit. Есть такой интерфейс IDatabaseTester, который конфигуряется настройками соединения с БД, setUp и tearDown операциями. Ему передается объект типа IDataSet после чего можно вызывать метод onSetup() - выполнится setup операция, onTearDown() - tearDown операция.



Удобнее всего для решения поставленной задачи создать на стороне back-end-а сервис, который будет получать в качестве параметра некий InputStream и результатом работы которого будет обновленная (или нет) БД. Вот Мое решение:




// PopulationService.java
package ga.common.domain;

import java.io.InputStream;

/**
* @author Ivan Khalopik
* @version $Revision: 21 $ $Date: 2010-03-17 11:18:19 +0200 (Ср, 17 мар 2010) $
*/
public interface PopulationService {

void populate(InputStream inputStream);

}





// DbUnitPopulationService.java
package ga.common.domain;

import org.dbunit.IDatabaseTester;
import org.dbunit.dataset.IDataSet;
import org.dbunit.dataset.xml.FlatXmlDataSetBuilder;

import java.io.InputStream;

/**
* @author Ivan Khalopik
* @version $Revision: 21 $ $Date: 2010-03-17 11:18:19 +0200 (Ср, 17 мар 2010) $
*/
public class DbUnitPopulationService implements PopulationService {
private final IDatabaseTester databaseTester;
private final FlatXmlDataSetBuilder builder;

public DbUnitPopulationService(IDatabaseTester databaseTester) {
this.databaseTester = databaseTester;
builder = new FlatXmlDataSetBuilder();
}

public void populate(InputStream inputStream) {
try {
final IDataSet dataSet = builder.build(inputStream);
databaseTester.setDataSet(dataSet);
databaseTester.onSetup();
} catch (Exception e) {
throw new IllegalStateException(e);
} finally {
databaseTester.setDataSet(null);
}
}
}



Внутри сервиса на основе InputStream-а создается FlatXmlDataSet (ну или можно любой другой по желанию) и вызывается метод onSetup(). Можно пойти еще дальше и в секции catch выполнить метод onTearDown(). Однако для работы такого сервиса необходимо еще создать объект IDatabaseTester. Я предлагаю это сделать с помощью спрингового FactoryBean-а:




// DatabaseTesterFactoryBean.java
package ga.common.dbunit;

import org.dbunit.DataSourceDatabaseTester;
import org.dbunit.IDatabaseTester;
import org.dbunit.operation.DatabaseOperation;
import org.springframework.beans.factory.config.AbstractFactoryBean;

import javax.sql.DataSource;

/**
* @author Ivan Khalopik
* @version $Revision: 21 $ $Date: 2010-03-17 11:18:19 +0200 (Ср, 17 мар 2010) $
*/
public class DatabaseTesterFactoryBean extends AbstractFactoryBean {
private DataSource dataSource;

public void setDataSource(DataSource dataSource) {
this.dataSource = dataSource;
}

@Override
public Class getObjectType() {
return IDatabaseTester.class;
}

@Override
protected IDatabaseTester createInstance() throws Exception {
final IDatabaseTester tester = new DataSourceDatabaseTester(dataSource);
tester.setSetUpOperation(DatabaseOperation.REFRESH);
tester.setTearDownOperation(DatabaseOperation.NONE);
return tester;
}
}



Забыл сказать, что операции бывают следующие:
• NONE - ничего не делать
• UPDATE - апдейтнуть данные
• INSERT - заинсертить данные
• REFRESH - заинсертить те, которых не хватает, апдейтнуть те, которые уже есть
• DELETE - удалить данные
• DELETE_ALL - очистить БД
• CLEAN_INSERT то же, что DELETE_ALL, а затем INSERT
Я использовал здесь REFRESH на сетапе, так как в данном случае больше всего подходит.


Итак, мы имеем сервис умеющий загружать данные в БД, теперь его можно применять на UI или в сервисах, например для автоинициализации БД при старте приложения. Например код страницы на Tapestry 5 будет выглядеть так:




// Populate.java
package ga.web.pages.security;

import ga.common.domain.PopulationService;
import ga.web.base.pages.AbstractPage;
import org.apache.tapestry5.annotations.Component;
import org.apache.tapestry5.corelib.components.Form;
import org.apache.tapestry5.ioc.annotations.Inject;
import org.apache.tapestry5.upload.services.UploadedFile;

/**
* @author Ivan Khalopik
* @version $Revision: 22 $ $Date: 2010-03-17 11:19:53 +0200 (Ср, 17 мар 2010) $
*/
public class Populate extends AbstractPage {

@Inject
private PopulationService populationService;

private UploadedFile file;

@Component
private Form populateForm;

public UploadedFile getFile() {
return file;
}

public void setFile(UploadedFile file) {
this.file = file;
}

public void onPopulate() {
if (populateForm.isValid()) {
populationService.populate(file.getStream());
}
}
}



Полный вариант исходников можно найти здесь


Комментариев нет: