本文介紹來自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等具體策略只要繼承并實現上述方法即可。
來看一下這個案例的類圖(部分)
image.png
從類圖上看,@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();
}