本文介绍来自Database-Rider中关于数据库导入时的策略模式案例
首先来看一下使用案例
@Test
@DataSet(value = "datasets/yml/users.yml", strategy = SeedStrategy.CLEAN_INSERT)
public void shouldSeedDataSetDisablingContraints() {
User user = (User) EntityManagerProvider.em().createQuery("select u from User u where u.id = 1").getSingleResult();
assertThat(user).isNotNull();
assertThat(user.getId()).isEqualTo(1);
}
从上述代码中可以了解到,@DataSet这个注解用于在测试用例执行前将来自value属性指定的数据文件users.yml插入到数据库中,通过strategy属性来指定数据库的插入方式为先清空数据库文件中涉及到的目标表,然后插入数据文件中提供的数据。
来看一下有哪些策略可供使用
public enum SeedStrategy {
CLEAN_INSERT(DatabaseOperation.CLEAN_INSERT),
TRUNCATE_INSERT(new CompositeOperation(DatabaseOperation.TRUNCATE_TABLE, DatabaseOperation.INSERT)),
INSERT(DatabaseOperation.INSERT),
REFRESH(DatabaseOperation.REFRESH),
UPDATE(DatabaseOperation.UPDATE);
}
命名上看,各种操作还是非常容易理解的,这里就不展开说明了。不过其中的CompositeOperation说明这其中还使用了组合模式,这个可以在后续解读。
策略抽象类
策略模式通过一个接口或者抽象类来定义策略的运行方法。
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package org.dbunit.operation;
import java.sql.SQLException;
import org.dbunit.DatabaseUnitException;
import org.dbunit.database.IDatabaseConnection;
import org.dbunit.dataset.IDataSet;
public abstract class DatabaseOperation {
public static final DatabaseOperation NONE = new DatabaseOperation.DummyOperation();
public static final DatabaseOperation UPDATE = new UpdateOperation();
public static final DatabaseOperation INSERT = new InsertOperation();
public static final DatabaseOperation REFRESH = new RefreshOperation();
public static final DatabaseOperation DELETE = new DeleteOperation();
public static final DatabaseOperation DELETE_ALL = new DeleteAllOperation();
public static final DatabaseOperation TRUNCATE_TABLE = new TruncateTableOperation();
public static final DatabaseOperation CLEAN_INSERT;
public DatabaseOperation() {
}
public static final DatabaseOperation TRANSACTION(DatabaseOperation operation) {
return new TransactionOperation(operation);
}
public static final DatabaseOperation CLOSE_CONNECTION(DatabaseOperation operation) {
return new CloseConnectionOperation(operation);
}
public abstract void execute(IDatabaseConnection var1, IDataSet var2) throws DatabaseUnitException, SQLException;
static {
CLEAN_INSERT = new CompositeOperation(DELETE_ALL, INSERT);
}
private static class DummyOperation extends DatabaseOperation {
private DummyOperation() {
}
public void execute(IDatabaseConnection connection, IDataSet dataSet) {
}
}
}
在此案例中,使用了抽象类的方式,并提供了execute这个抽象方法。InsertOperation、UpdateOperation等具体策略只要继承并实现上述方法即可。
来看一下这个案例的类图(部分)
从类图上看,@DataSet注解使用到的数据库操作属于CUD部分,也就是从AbstractOperation这个抽象类继承而来的部分。
数据库导入操作类
策略模式中,一般都会有一个Context类来作为使用某种策略的类。
DBRider定义了一个DataSetExecutorImpl,用于实现对数据库的各项操作。其中用于处理@DataSet注解的方法是createDataSet。
@Override
public void createDataSet(DataSetConfig dataSetConfig) {
//......
DatabaseOperation operation = getOperation(dataSetConfig);
operation.execute(getRiderDataSource().getDBUnitConnection(), resultingDataSet);
这其中,通过getOperation方法来获取具体的DatebaseOperation策略类,并执行其中的execute方法。
获取策略的工厂方法
而在实际开发中为了将具体策略类的创建和使用者隔离,还会结合工厂模式。在DBRider这个案例中,只是简单地使用了getOperation方法。
具体代码如下:
private DatabaseOperation getOperation(DataSetConfig dataSetConfig) {
SeedStrategy strategy = dataSetConfig.getstrategy();
if (getRiderDataSource().getDBType() == RiderDataSource.DBType.MSSQL && dataSetConfig.isFillIdentityColumns()) {
switch (strategy) {
case INSERT:
return InsertIdentityOperation.INSERT;
case REFRESH:
return InsertIdentityOperation.REFRESH;
case CLEAN_INSERT:
return InsertIdentityOperation.CLEAN_INSERT;
case TRUNCATE_INSERT:
return new CompositeOperation(DatabaseOperation.TRUNCATE_TABLE,
InsertIdentityOperation.INSERT);
}
}
return strategy.getOperation();
}