How to intercept and change sql query dynamically in mybatis - java

I use mybatis to perform sql queries in my project. I need to intercept sql query before executing to apply some changed dynamically. I've read about #Interseptors like this:
#Intercepts({#Signature(type= Executor.class, method = "query", args = {...})})
public class ExamplePlugin implements Interceptor {
public Object intercept(Invocation invocation) throws Throwable {
return invocation.proceed();
}
public Object plugin(Object target) {
return Plugin.wrap(target, this);
}
public void setProperties(Properties properties) {
}
}
And it really intercepts executions, but there is no way to change sql query since appropriate field is not writable. Should I build new instance of whole object manually to just replace sql query? Where is the right place to intercept query execution to change it dynamically? Thank.

I hope it will help you:
#Intercepts( { #Signature(type = Executor.class, method = "query", args = {
MappedStatement.class, Object.class, RowBounds.class,
ResultHandler.class
})
})
public class SelectCountSqlInterceptor2 implements Interceptor
{
public static String COUNT = "_count";
private static int MAPPED_STATEMENT_INDEX = 0;
private static int PARAMETER_INDEX = 1;
#Override
public Object intercept(Invocation invocation) throws Throwable
{
processCountSql(invocation.getArgs());
return invocation.proceed();
}
#SuppressWarnings("rawtypes")
private void processCountSql(final Object[] queryArgs)
{
if (queryArgs[PARAMETER_INDEX] instanceof Map)
{
Map parameter = (Map) queryArgs[PARAMETER_INDEX];
if (parameter.containsKey(COUNT))
{
MappedStatement ms = (MappedStatement) queryArgs[MAPPED_STATEMENT_INDEX];
BoundSql boundSql = ms.getBoundSql(parameter);
String sql = ms.getBoundSql(parameter).getSql().trim();
BoundSql newBoundSql = new BoundSql(ms.getConfiguration(),
getCountSQL(sql), boundSql.getParameterMappings(),
boundSql.getParameterObject());
MappedStatement newMs = copyFromMappedStatement(ms,
new OffsetLimitInterceptor.BoundSqlSqlSource(newBoundSql));
queryArgs[MAPPED_STATEMENT_INDEX] = newMs;
}
}
}
// see: MapperBuilderAssistant
#SuppressWarnings({ "unchecked", "rawtypes" })
private MappedStatement copyFromMappedStatement(MappedStatement ms,
SqlSource newSqlSource)
{
Builder builder = new MappedStatement.Builder(ms.getConfiguration(), ms
.getId(), newSqlSource, ms.getSqlCommandType());
builder.resource(ms.getResource());
builder.fetchSize(ms.getFetchSize());
builder.statementType(ms.getStatementType());
builder.keyGenerator(ms.getKeyGenerator());
// setStatementTimeout()
builder.timeout(ms.getTimeout());
// setParameterMap()
builder.parameterMap(ms.getParameterMap());
// setStatementResultMap()
List<ResultMap> resultMaps = new ArrayList<ResultMap>();
String id = "-inline";
if (ms.getResultMaps() != null)
{
id = ms.getResultMaps().get(0).getId() + "-inline";
}
ResultMap resultMap = new ResultMap.Builder(null, id, Long.class,
new ArrayList()).build();
resultMaps.add(resultMap);
builder.resultMaps(resultMaps);
builder.resultSetType(ms.getResultSetType());
// setStatementCache()
builder.cache(ms.getCache());
builder.flushCacheRequired(ms.isFlushCacheRequired());
builder.useCache(ms.isUseCache());
return builder.build();
}
private String getCountSQL(String sql)
{
String lowerCaseSQL = sql.toLowerCase().replace("\n", " ").replace("\t", " ");
int index = lowerCaseSQL.indexOf(" order ");
if (index != -1)
{
sql = sql.substring(0, index);
}
return "SELECT COUNT(*) from ( select 1 as col_c " + sql.substring(lowerCaseSQL.indexOf(" from ")) + " ) cnt";
}
#Override
public Object plugin(Object target)
{
return Plugin.wrap(target, this);
}
#Override
public void setProperties(Properties properties)
{
}
}

You may consider using a string template library (eg Velocity, Handlebars, Mustache) to help you
As of to date, there is even MyBatis-Velocity (http://mybatis.github.io/velocity-scripting/) to help you to do scripting for the sql.

Depending on the changes you want to make, you may want to use the dynamic sql feature of mybatis 3

Related

Repository Returning Null while attempting Horizontal Scaling of a Service Class

[ISSUE] repo always returns null when I call repo methods, while stepping through, throws null pointer exception. then front end receives
500: Http failure response for http://localhost:4200/api/aiprollout/updatecsv: 500 Internal Server Error
[HAVE TRIED] Adjusting AutoWired and components and service annotations.
[QUESTIONS]
1- Does every repo method need its own service and controller method?
2- Is it okay to create a new service that uses an existing controller?
3- If this new service uses SuperCsv and I create custom CsvCellProcessors, can these cell processors also call the repo? Should these cell processors perform logic? or should it be done else where? What class annotations should these cellProcessors classes have? #Component?
Any advice is greatly appreciated, feel a little lost at this point not even sure what to do.
[CODE]
Controller:
#RestController
#EnableConfigurationProperties({SpoofingConfigurationProperties.class})
#RequestMapping(value = "")
public class AipRolloutController {
private final Logger logger = some logger
private final AipRolloutService AipRolloutService;
private final CsvParserService csvParserService;
#Autowired
public AipRolloutController(AipRolloutService aipRolloutService, CsvParserService csvParserService) {
this.AipRolloutService = aipRolloutService;
this.csvParserService = csvParserService;
}
#PostMapping(value = "/updatecsv", produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody
public ResponseEntity<?> processCsv(#RequestParam("csvFile") MultipartFile csvFile) throws IOException {
if (csvFile.isEmpty()) return new ResponseEntity(
responceJson("please select a file!"),
HttpStatus.NO_CONTENT
);
csvParserService.parseCsvFile(csvFile);
return new ResponseEntity(
responceJson("Successfully uploaded - " + csvFile.getOriginalFilename()),
new HttpHeaders(),
HttpStatus.CREATED
);
}
Service:
#Service
public class AipRolloutService {
private static final Logger logger = some logger
#Autowired
private AIPRolloutRepository AIPRolloutRepository;
New Csv parser Service
#Service
public class CsvParserService {
#Autowired private AipRolloutService aipRolloutService;
public CsvParserService(AipRolloutService aipRolloutService) {
this.aipRolloutService = aipRolloutService;
}
public void parseCsvFile(MultipartFile csvFile) throws IOException {
CsvMapReader csvMapReader = new CsvMapReader(new InputStreamReader(csvFile.getInputStream()), CsvPreference.STANDARD_PREFERENCE);
parseCsv(csvMapReader);
csvMapReader.close();
}
private void parseCsv(CsvMapReader csvMapReader) throws IOException {
String[] header = csvMapReader.getHeader(true);
List<String> headers = Arrays.asList(header);
verifySourceColumn(headers);
verifyPovColumn(headers);
final CellProcessor[] processors = getProcessors(headers);
Map<String, Object> csvImportMap = null;
while ((csvImportMap = csvMapReader.read(header, processors)) != null) {
CsvImportDTO csvImportDto = new CsvImportDTO(csvImportMap);
if ( activationTypeP(csvImportDto) ){
int mssValue = Integer.parseInt(csvImportDto.getMssValue());
aipRolloutService.updateAipRollout(csvImportDto.getSource(),
csvImportDto.getPov(),
csvImportDto.getActivationType(),
mssValue);
}
}
}
private CellProcessor[] getProcessors(List<String> headers) {
CellProcessor[] processors = new CellProcessor[headers.size()];
int index = 0;
for (String header : headers) {
if (header.contains(SOURCE_ID)) {
processors[index++] = new CsvSourceIdCellParser();
} else if (header.contains(POV)) {
processors[index++] = new CsvPovCellParser();
} else if (header.contains(ACTIVATION_TYPE)) {
processors[index++] = new CsvActivationTypeCellParser();
} else if (header.contains(ACTIVATION_DATE)) {
processors[index++] = new Optional();
} else if (header.contains(DEACTIVATION_DATE)) {
processors[index++] = new Optional();
} else if (header.contains(MSS_VALUE)) {
processors[index++] = new CsvMssValueCellParser();
} else {
processors[index++] = null; // throw exception? wrong header info instead of allowing null?
}
}
return processors;
}
Custom Cell Processor that calls repo and returns null
public class CsvSourceIdCellParser extends CellProcessorAdaptor {
#Autowired AIPRolloutRepository aipRolloutRepository;
public CsvSourceIdCellParser(){ super(); }
// this constructor allows other processors to be chained
public CsvSourceIdCellParser(CellProcessor next){ super(next); }
#Override
public Object execute(Object value, CsvContext csvContext) {
// throws an Exception if the input is null
validateInputNotNull(value, csvContext);
// get rid of description only need first 3 #'s
value = value.toString().substring(0,3);
// check if WH exists
if( aipRolloutRepository.dcExistsInDatabase(value.toString()) )
return value;
else
throw new RuntimeException("Check Warehouse Value, Value Not Found "
+ "Row number: " + csvContext.getRowNumber()
+ " Column number: " + csvContext.getColumnNumber());
}
}
Repository
#Repository
public class AIPRolloutRepository {
private static final Logger logger = LoggerFactory.getLogger(AIPRolloutRepository.class);
#Autowired
JdbcTemplate jdbcTemplate;
public AIPRolloutRepository() {
}
public boolean dcExistsInDatabase(String dc){
// Query for a count saves time and memory, query for distinct saves time and memory on execution
boolean hasRecord =
jdbcTemplate
.query( "select count (distinct '" + dc +"')" +
"from xxcus.XX_AIP_ROLLOUT" +
"where DC = '" + dc + "';",
new Object[] { dc },
(ResultSet rs) -> {
if (rs.next()) {
return true;
}
return false;
}
);
return hasRecord;
}

Can I create an abstraction to hide my "checking the cache" logic?

I have getResponse() methods which build a response from data fetched from the database (or pre-saved in a SavedData object). I would like to find a way to abstract out the "check savedData" logic from my getResponse() methods. Ideally, I want to figure out a way where my getResponse() methods don't even know SavedData exists, it is just hidden behind some interface. Is there a good abstraction here I can use to clean up this code?
The following is just pseudo-code. For each field which is returned in the getResponse() JSON object, they first check if that field has been saved in some SavedData and use it if it has, otherwise, they need to query the DB for the field.
interface ResponseGetter {
public Response getResponse(String userID, SavedData savedData);
}
class A implements ResponseGetter {
public Response getResponse(String userID, SavedData savedData) {
List<String> foo;
int bar;
String bizz;
foo = savedData.get(userID, "foo");
if (foo == null) {
foo = loadFooFromDB(userID);
}
bar = savedData.get(userID, "bar");
if (bar == null) {
bar = loadBarFromDB(userID);
}
bizz = savedData.get(userID, "bizz");
if (bizz == null) {
bizz = loadBizzFromDB(userID);
}
JSONObject json = new JSONObject();
json.put("foo", foo);
json.put("bar", bar);
json.put("bizz", biz);
return new Response(json);
}
private List<String> loadFooFromDB(String userID) {
List<String> returnList = new ArrayList<String>();
DB db = this.getDB();
String query = "SELECT foo FROM SomeTable WHERE user_id=" + userID;
Results results = db.executeQuery(query);
for (Result result : results) {
returnList.add(result.toString());
}
return returnList;
}
}
class B implements ResponseGetter {
public Response getResponse(String userID, SavedData savedData) {
List<String> baz;
int qux;
String corge;
baz = savedData.get(userID, "baz");
if (baz == null) {
baz = loadBazFromDB(userID);
}
qux = savedData.get(userID, "qux");
if (qux == null) {
qux = loadQuxFromDB(userID);
}
corge = savedData.get(userID, "corge");
if (corge == null) {
corge = loadCorgeFromDB(userID);
}
JSONObject json = new JSONObject();
json.put("baz", baz);
json.put("qux", qux);
json.put("corge", corge);
return new Response(json);
}
}
I would use generics and the Strategy Design Pattern.
I didn't really know what types and classes you could give up, since you provided a pseudo-code.
If I forgot to put something importante, please comment.
But I would go with something like this:
interface ResponseGetter<R> {
R getResponse(String userId, String dataToCheck, SavedData<R> savedData, Function<String, R> lambda);
}
class DB<R> {
public R executeQuery(String query) {
return null;
}
}
interface SavedData<R> {
public R get(String userId, String name);
}
class SomeClass<R> implements ResponseGetter<R> {
#Override
public R getResponse(String userId, String dataToCheck, SavedData<R> savedData, Function<String, R> lambda) {
R checkedData = savedData.get(userId, dataToCheck);
return checkedData == null ? loadDataFromDB(userId, lambda) : checkedData;
}
public R loadDataFromDB(String userId, Function<String, R> lambda) {
return lambda.apply(userId);
}
}
class SavedDataImpl<R> implements SavedData<R> {
#Override
public R get(String userId, String name) {
return null;
}
}
class Main {
public static void main(String[] args) {
// foo
SomeClass<List<String>> someClass = new SomeClass<>();
SavedDataImpl<List<String> savedDataImpl = new SavedDataImpl<>();
DB<List<String>> db = new DB<>();
List<String> foo = someClass.getResponse("1", "foo", savedDataImpl, (String userId) -> {
List<String> result = db.executeQuery("SELECT foo FROM SomeTable WHERE user_id=" + userId);
return result;
});
// bar
SomeClass<Integer> someClassTwo = new SomeClass<>();
SavedDataImpl<Integer savedDataImplTwo = new SavedDataImpl<>();
DB<Integer> dbTwo = new DB<>();
Integer bar = someClassTwo.getResponse("1", "bar", savedDataImplTwo, (String userId) -> {
Integer result = dbTwo.executeQuery("SELECT bar FROM SomeTable WHERE user_id=" + userId);
return result;
});
// bizz
SomeClass<String> someClassThree = new SomeClass<>();
SavedDataImpl<String savedDataImplThree = new SavedDataImpl<>();
DB<String> dbThree = new DB<>();
String bizz = someClassThree.getResponse("1", "bizz", savedDataImplThree, (String userId) -> {
String result = dbThree.executeQuery("SELECT bizz FROM SomeTable WHERE user_id=" + userId);
return result;
});
Map json = new HashMap();
json.put("foo", foo);
json.put("bar", bar);
json.put("bizz", bizz);
}
}
That way you are abstracting the behavior of the how to get the data and wich data to get.
I just formatted the response in a Map for simplicity.

Executing a stored procedure with custom types gives null result

I have written java code for the below plsql code to execute stored procedure. I am not getting result when I run the procedure from Java code.
PLSQL
declare
CAMFORMLIST_ID_TY CAMFORMLIST_ID_TYPES;
CAMFORMLIST_ID_T CAMFORMLIST_ID_TYPE;
OFEERS_INFO_TY OFEERS_INFO_TYPE;
begin
CAMFORMLIST_ID_T:=CAMFORMLIST_ID_TYPE('HH','K');
CAMFORMLIST_ID_TY:=CAMFORMLIST_ID_TYPES(CAMFORMLIST_ID_T);
GETOFEERSFROMCAMFORM(CAMFORMLIST_ID_TY,OFEERS_INFO_TY);
for i in OFEERS_INFO_TY.FIRST..OFEERS_INFO_TY.LAST loop
DBMS_OUTPUT.PUT_LINE('Name: '|| OFEERS_INFO_TY(i).OFFER_NAME);
end loop;
end;
My Java Stored Procedure class.
#Component
public class GetOffersFromCAMFORM extends StoredProcedure {
private static final String OFEERS_LIST = "Offers";
#Autowired
private String MySchema;
#Autowired
#Qualifier("MyDataSource")
private DataSource dataSourceMy;
#PostConstruct
public void postConstruct() {
this.setDataSource(dataSourceMy);
this.setSql(MySchema + ".getOffersFromCAMFORM");
declareParameter(new SqlParameter(SUB_FORM, Types.ARRAY, MySchema + ".CAMFORMLIST_ID_TYPES"));
declareParameter(
new SqlOutParameter(OFEERS_LIST, OracleTypes.ARRAY,
MySchema + ".OFEERS_INFO_TYPE", new ReturnType()));
}
#SuppressWarnings("unchecked")
public List<MyOffer> execute(final String subscriptionForm, final String subscriptionClass)
throws Exception {
Map<String, Object> objects = new HashMap<String, Object>();
objects.put(SUB_FORM, new AbstractSqlTypeValue() {
#Override
protected Object createTypeValue(Connection con, int SqlType, String typeName)
throws SQLException {
OracleConnection oracle =
(OracleConnection) (new DelegatingConnection(con)).getInnermostDelegate();
if (oracle != null) {
con = oracle;
}
ArrayList<Struct> CAMFORMListIdType = new ArrayList<Struct>();
CAMFORMListIdType
.add(((OracleConnection) con).createStruct(MySchema + ".CAMFORMLIST_ID_TYPE",
new String[] {subscriptionForm, subscriptionClass}));
return ((OracleConnection) con).createARRAY(MySchema + ".CAMFORMLIST_ID_TYPES",
CAMFORMListIdType.toArray(new Struct[] {}));
}
});
Map<?, ?> result = execute(objects);
List<MyOffer> items = (List<MyOffer>) result.get(OFEERS_LIST);
return items;
}
}
Return type
public class ReturnType implements SqlReturnType {
#Override
public List<PCOffer> getTypeValue(CallableStatement cs, int colIndx, int sqlType,
String typeName) throws SQLException {
Array dba = (Array) cs.getObject(colIndx);
if (dba == null || ((Object[]) dba.getArray()).length == 0) {
return null;
}
...
}
Ojdbc 7 driver is used.
I am not getting the output. When I execute the procueudre, I get empty array in ReturnType class.
Am I doing something wrong.

spring-data-couchbase error while finding all documents with PageRequest

Using Spring Data Couchbase I created a very simple repository
public interface UserDao extends PagingAndSortingRepository<User, String>
This should allow me to execute a paged findAll as follows:
Page<User> userResult = repo.findAll(new PageRequest(1, 20));
However the the following is always thrown:
Exception in thread "main" java.lang.IllegalStateException: Unknown query param: Page request [number: 1, size 20, sort: null]
at org.springframework.data.couchbase.repository.query.ViewBasedCouchbaseQuery.execute(ViewBasedCouchbaseQuery.java:47)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:337)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.couchbase.repository.support.ViewPostProcessor$ViewInterceptor.invoke(ViewPostProcessor.java:80)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:207)
at $Proxy14.findAll(Unknown Source)
at com.polycom.cloudAxis.proxymanagement.model.Main.main(Main.java:41)
This doesn't happen if I create a Query and use the skip/limit/startKeyDocId, but would like to use PagingAndSortingRepository if possible.
Any idea what could be wrong? Thanks for all friendly help :)
i also had this same issue, here was the approach i took. its actually a very small change to the core implementation (which currently only support Query object types), essentially all I did was add another instance of check:
if (param instanceof Query) {
query = (Query) param;
} else if (param instanceof Pageable) {
pageable = (Pageable) param;
}
then added the logic for paging here, its been working so far but i havent fully vetted it so any comments from the community would be appreciated.
if (pageable != null) {
CouchbaseClient client = operations.getCouchbaseClient();
View view = client.getView(designDocName(), viewName());
// Paginator p = new Paginator(client, view, query,
// pageable.getPageSize());
Paginator paginator = client.paginatedQuery(view, query, pageable.getPageSize());
// now we need to goto the start point
ViewResponse viewResponse = null;
// views are 0 base
int i = 0;
while (paginator.hasNext()) {
viewResponse = paginator.next();
if (pageable.getPageNumber() == i++) {
LOGGER.debug("Found the response for this page: {} ", i);
break;
}
}
if (viewResponse == null) {
LOGGER.debug("no view response so leaving now");
return null;
}
Class<?> type = method.getEntityInformation().getJavaType();
final List result = new ArrayList(viewResponse.size());
for (final ViewRow row : viewResponse) {
result.add(operations.findById(row.getId(), type));
}
return result;
}
To get this wired up i had to do some things i didnt want to :D, i wanted to just override one method however to get to it required me to override many other things, so i ended up copying a little bit of code, ideally i would like to get this added as part of https://jira.spring.io/browse/DATACOUCH-93
And the whole impl here:
public class DCORepositoryFactory extends CouchbaseRepositoryFactory {
CouchbaseOperations couchbaseOperations;
MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext;
public DCORepositoryFactory(CouchbaseOperations couchbaseOperations) {
super(couchbaseOperations);
mappingContext = couchbaseOperations.getConverter().getMappingContext();
this.couchbaseOperations = couchbaseOperations;
}
#Override
protected Object getTargetRepository(RepositoryMetadata metadata) {
// TODO Auto-generated method stub
CouchbaseEntityInformation<?, Serializable> entityInformation = getEntityInformation(metadata.getDomainType());
final DCORepository simpleCouchbaseRepository = new DCORepository(entityInformation, couchbaseOperations);
simpleCouchbaseRepository.setViewMetadataProvider(ViewPostProcessor.INSTANCE.getViewMetadataProvider());
return simpleCouchbaseRepository;
}
#Override
protected QueryLookupStrategy getQueryLookupStrategy(QueryLookupStrategy.Key key) {
return new CouchbaseQueryLookupStrategy();
}
/**
* Currently, only views are supported. N1QL support to be added.
*/
private class CouchbaseQueryLookupStrategy implements QueryLookupStrategy {
#Override
public RepositoryQuery resolveQuery(Method method, RepositoryMetadata metadata, NamedQueries namedQueries) {
CouchbaseQueryMethod queryMethod = new CouchbaseQueryMethod(method, metadata, mappingContext);
return new PagingViewBasedCouchbaseQuery(queryMethod, couchbaseOperations);
}
}
private static class PagingViewBasedCouchbaseQuery extends ViewBasedCouchbaseQuery {
private static final org.slf4j.Logger LOGGER = org.slf4j.LoggerFactory
.getLogger(DCORepositoryFactory.PagingViewBasedCouchbaseQuery.class);
private CouchbaseOperations operations;
private CouchbaseQueryMethod method;
public PagingViewBasedCouchbaseQuery(CouchbaseQueryMethod method, CouchbaseOperations operations) {
super(method, operations);
this.operations = operations;
this.method = method;
}
/*
* (non-Javadoc)
*
* #see org.springframework.data.couchbase.repository.query.
* ViewBasedCouchbaseQuery#execute(java.lang.Object[]) added the ability
* to support paging
*/
#Override
public Object execute(Object[] runtimeParams) {
Query query = null;
Pageable pageable = null;
for (Object param : runtimeParams) {
if (param instanceof Query) {
query = (Query) param;
} else if (param instanceof Pageable) {
pageable = (Pageable) param;
} else {
throw new IllegalStateException(
"Unknown query param: (btw null is also not allowed and pagable cannot be null) " + param);
}
}
if (query == null) {
query = new Query();
}
query.setReduce(false);
if (pageable != null) {
CouchbaseClient client = operations.getCouchbaseClient();
View view = client.getView(designDocName(), viewName());
// Paginator p = new Paginator(client, view, query,
// pageable.getPageSize());
Paginator paginator = client.paginatedQuery(view, query, pageable.getPageSize());
// now we need to goto the start point
ViewResponse viewResponse = null;
// views are 0 base
int i = 0;
while (paginator.hasNext()) {
viewResponse = paginator.next();
if (pageable.getPageNumber() == i++) {
LOGGER.debug("Found the response for this page: {} ", i);
break;
}
}
if (viewResponse == null) {
LOGGER.debug("no view response so leaving now");
return null;
}
Class<?> type = method.getEntityInformation().getJavaType();
final List result = new ArrayList(viewResponse.size());
for (final ViewRow row : viewResponse) {
result.add(operations.findById(row.getId(), type));
}
return result;
} else {
return operations.findByView(designDocName(), viewName(), query, method.getEntityInformation()
.getJavaType());
}
}
/**
* Returns the best-guess design document name.
*
* #return the design document name.
*/
private String designDocName() {
if (method.hasViewAnnotation()) {
return method.getViewAnnotation().designDocument();
} else {
return StringUtils.uncapitalize(method.getEntityInformation().getJavaType().getSimpleName());
}
}
/**
* Returns the best-guess view name.
*
* #return the view name.
*/
private String viewName() {
if (method.hasViewAnnotation()) {
return method.getViewAnnotation().viewName();
} else {
return StringUtils.uncapitalize(method.getName().replaceFirst("find", ""));
}
}
}
#Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata repositoryMetadata) {
return DCORepository.class;
}
}

How to Mock repository Items in ATG

I am trying to create a Mock class for droplet. I am able to mock the repository calls and req.getParameter but need help on how to mock the repository item list from the repository. Below is the sample code.
for (final RepositoryItem item : skuList) {
final String skuId = (String) item.getPropertyValue("id");
final String skuType = (String) item.getPropertyValue("skuType");
if (this.isLoggingDebug()) {
this.logDebug("skuType [ " + skuType + " ]");
}
final String skuActive = (String) item.getPropertyValue("isActive");
if EJSD.equalsIgnoreCase(skuType) && (skuActive.equals("1"))) {
eSkuList.add(item);
skuCode = (String) item.getPropertyValue(ESTConstants.SKU_MISC1);
} else (PJPROMIS.equalsIgnoreCase(skuType) && skuId.contains("PP") && (skuActive.equals("1"))) {
personalSkuList.add(item);
String tmp = "";
if (skuId.lastIndexOf("-") > -1) {
tmp = skuId.substring(skuId.lastIndexOf("-") + 1);
tmp = tmp.toUpperCase();
if (this.getDefaultDisplayNameMap() != null) {
String val = this.getDefaultDisplayNameMap().get(tmp);
if (StringUtils.isNotEmpty(val)) {
displayNameMap.put(skuId, val);
} else {
val = (String) item.getPropertyValue("displayName");
displayNameMap.put(skuId, val);
}
} else {
final String val = (String) item.getPropertyValue("displayName");
displayNameMap.put(skuId, val);
}
}
}
}
There are a multitude of ways to 'mock' the list. I've been doing it this was as I feel it is more readable.
#Mock private RepositoryItem skuMockA;
#Mock private RepositoryItem skuMockB;
List<RepositoryItem> skuList = new ArrayList<RepositoryItem>();
#BeforeMethod(groups = { "unit" })
public void setup() throws Exception {
testObj = new YourDropletName();
MockitoAnnotations.initMocks(this);
skuList = new ArrayList<RepositoryItem>();
skuList.add(skuMockA);
skuList.add(skuMockB);
Mockito.when(skuMockA.getPropertyValue("id")).thenReturn("skuA");
Mockito.when(skuMockA.getPropertyValue("skuType")).thenReturn(ActiveSkuDroplet.EJSD);
Mockito.when(skuMockA.getPropertyValue(ESTConstants.SKU_MISC1)).thenReturn("skuCodeA");
Mockito.when(skuMockA.getPropertyValue("displayName")).thenReturn("skuADisplayName");
Mockito.when(skuMockB.getPropertyValue("id")).thenReturn("skuB-PP");
Mockito.when(skuMockB.getPropertyValue("skuType")).thenReturn(ActiveSkuDroplet.PJPROMIS);
Mockito.when(skuMockB.getPropertyValue(ESTConstants.SKU_MISC1)).thenReturn("skuCodeB");
Mockito.when(skuMockB.getPropertyValue("displayName")).thenReturn("skuBDisplayName");
}
So when you then call this within a test it will be something like this:
Mockito.when(someMethodThatReturnsAList).thenReturn(skuList);
So the key really is that you are not mocking the List but instead the contents of the List.
Creating a mock using mockito is a good option.
But I am here explaining a different way of mocking the repository item.
Create a common implementation for RepositoryItem, say MockRepositoryItemImpl like this in your test package.
Public MockRepositoryItemImpl implements RepositoryItem {
private Map<String, Object> properties;
MockRepositoryItemImpl(){
properties = new HashMap<>();
}
#override
public Object getPropertyValue(String propertyName){
return properties.get(propertyName);
}
#override
public void setPropertyValue(String propertyName, Object propertyValue){
properties.put(propertyName, propertyValue);
}
}
Use this implementation to create the mock object in your test case.
RepositoryItem mockSKU = new MockRepositoryItemImpl();
mockSKU.setPropertyValue("id", "sku0001");
mockSKU.setPropertyValue("displayName", "Mock SKU");
mockSKU.setPropertyValue("skuType", "Type1");
mockSKU.setPropertyValue("isActive", "1");

Categories