I tried mocking the JdbcTemplate jdbcTemplate ,but that didn't cover whatever is inside
new Employee(.......);
Please let me know is there any way to cover those lines inside new Employee(...)?
public List<Employee> findByCustIdAndType(long Id, String type)
{
return jdbcTemplate.query(SQL.getEmployee(Id, type),
(rs, rowNum) -> new Employee(rs.getLong("CUSTOMER_ID"),
rs.getLong("ANCHOR_CUSTOMER_ID") ,
rs.getString("SEGMENT"),
rs.getDate("END_TS")));
}
Try using Mockito to capture the lambda, which is a RowMapper<Employee>. Then invoke it with a mock ResultSet set up to return the expected values so the returned Employee can be asserted. Here's an example:
#RunWith(MockitoJUnitRunner.class)
public class EmployeeDAOTest {
private static final long CUSTOMER_ID = 1;
private static final long ANCHOR_CUSTOMER_ID = 2;
private static final String SEGMENT = "A";
private static final Date END_TS = Date.valueOf(LocalDate.now());
#InjectMocks
private EmployeeDAO dao;
#Mock
private JdbcTemplate jdbcTemplate;
#Mock
private ResultSet resultSet;
#Captor
private ArgumentCaptor<RowMapper<Employee>> rowMapperCaptor;
#Before
public void prepareTest() throws SQLException {
when(resultSet.getLong("CUSTOMER_ID")).thenReturn(CUSTOMER_ID);
when(resultSet.getLong("ANCHOR_CUSTOMER_ID")).thenReturn(ANCHOR_CUSTOMER_ID);
when(resultSet.getString("SEGMENT")).thenReturn(SEGMENT);
when(resultSet.getDate("END_TS")).thenReturn(END_TS);
}
#Test
public void test() throws SQLException {
dao.findByCustIdAndType(0, null);
verify(jdbcTemplate).query(anyString(), rowMapperCaptor.capture());
RowMapper<Employee> rowMapper = rowMapperCaptor.getValue();
Employee employee = rowMapper.mapRow(resultSet, 1);
assertEquals(CUSTOMER_ID, employee.getCustomerId());
assertEquals(ANCHOR_CUSTOMER_ID, employee.getAnchorCustomerId());
assertEquals(SEGMENT, employee.getSegment());
assertEquals(END_TS, employee.getEndTs());
}
}
Related
I have a query and it works well on the database. However, when I tried to take them as a Java object by using RowMapper, I get an invalid column name error. I checked everything, but I don't understand the reason why this error happening.
My query:
SELECT TEMP.SUMALLTXN, SUM(TEMP.SUMCARD), SUM(TEMP.SUMERRORTXN), SUM(TEMP.SUMERRORTXNCARD)
FROM
(SELECT
SUM(COUNT(*)) OVER() AS SUMALLTXN,
COUNT(mdmtxn.BIN) OVER (PARTITION BY mdmtxn.BIN) AS SUMCARD,
SUM(case when mdmtxn.MDSTATUS NOT IN ('1','9', '60') then 1 else 0 end) AS SUMERRORTXN,
SUM(case when mdmtxn.MDSTATUS NOT IN ('1','9', '60') then 1 else 0 end) OVER (PARTITION BY mdmtxn.BIN) AS SUMERRORTXNCARD
FROM MDM59.MDMTRANSACTION2 mdmtxn WHERE
mdmtxn.CREATEDDATE < TO_CHAR(SYSDATE - INTERVAL ':initialMinuteParameterValue' MINUTE ,'YYYYMMDD HH24:MI:SS') AND
mdmtxn.CREATEDDATE > TO_CHAR(SYSDATE - INTERVAL ':intervalMinuteParameterValue' MINUTE ,'YYYYMMDD HH24:MI:SS')
GROUP BY mdmtxn.MDSTATUS, mdmtxn.BIN
) TEMP
GROUP BY TEMP.SUMALLTXN
My RowMapper:
#Component
public class TotalTransactionsReportRw implements RowMapper<TotalTransactionsReportDto> {
#Override
public TotalTransactionsReportDto mapRow(ResultSet rs, int rowNum) throws SQLException {
return TotalTransactionsReportDto.builder()
.totalNumbersOfTransactions(rs.getString("SUMALLTXN"))
.totalNumbersOfCard(rs.getString("SUMCARD"))
.totalNumbersOfErrorTransactions(rs.getString("SUMERRORTXN"))
.totalNumbersOfErrorCard(rs.getString("SUMERRORTXNCARD"))
.build();
}
private static class TotalTransactionsDetailRwHolder {
private static final TotalTransactionsReportRw INSTANCE = new TotalTransactionsReportRw();
}
public static TotalTransactionsReportRw getInstance() {
return TotalTransactionsReportRw.TotalTransactionsDetailRwHolder.INSTANCE;
}
}
My Dto:
#Value
#Builder
#Data
public class TotalTransactionsReportDto {
private String totalNumbersOfTransactions;
private String totalNumbersOfCard;
private String totalNumbersOfErrorTransactions;
private String totalNumbersOfErrorCard;
}
And in my tasklet class I created a list to get all data from rowmapper:
#Slf4j
#Component
#RequiredArgsConstructor
public class NotificationTasklet implements Tasklet {
private final PofPostOfficeServiceClient pofPostOfficeServiceClient;
private final SequenceSysGuid sequenceSysGuid;
private final BatchProps batchProps;
private JdbcTemplate jdbcTemplate;
private String notificationMailSql;
private String totalTransactionsSql;
private String endOfHtmlString = "</table></body></html>";
private String endOfTableString = "</table>";
private String jobName = "vpos-notification";
private String tdClose = "</td>";`
#Override
public RepeatStatus execute(StepContribution stepContribution, ChunkContext chunkContext) throws Exception {
List<VposNotificationBatchDto> notificationList = getNotificationList();
List<TotalTransactionsReportDto> totalTransactionsList = getTotalTransactionsList();
AlertMailDto alertMailDto = createAlertMailDto(notificationList,totalTransactionsList);
if (!(notificationList.isEmpty())) {
sendMail(alertMailDto);
}
return RepeatStatus.FINISHED;
}
List<TotalTransactionsReportDto> getTotalTransactionsList() {
return jdbcTemplate.query(
totalTransactionsSql,
new TotalTransactionsReportRw());
}
#Autowired
public void setTotalTransactionsSql(#Value("classpath:sql/vposnotification/select_total_transactions_data.sql")
Resource res) {
int intervalnext = batchProps.getJobProps()
.get(jobName).getAlertProps().getIntervalMinuteParameterValue();
String intervalMinutes = String.valueOf(intervalnext);
int initialMinuteParameterValue = batchProps.getJobProps()
.get(jobName).getAlertProps().getInitialMinuteParameterValue();
String initialMinutes = String.valueOf(initialMinuteParameterValue);
this.totalTransactionsSql = SqlUtils.readSql(res);
this.totalTransactionsSql = this.totalTransactionsSql.replace(":initialMinuteParameterValue", initialMinutes);
this.totalTransactionsSql = this.totalTransactionsSql.replace(":intervalMinuteParameterValue", intervalMinutes);
}
#Autowired
public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
The problem is that your query doesn't actually have columns SUMCARD, SUMERRORTXN and SUMERRORTXNCARD. Although there are DBMSes that alias SUM columns with the name of the column that is summed, Oracle is not one of them. IIRC, Oracle aliases it as, for example, "SUM(SUMCARD)" or maybe "SUM(TEMP.SUMCARD)". However, that is an implementation detail you should not rely on in my opinion.
To get the name you want to use, you need to alias your SUM columns explicitly, e.g. SUM(TEMP.SUMCARD) AS SUMCARD.
Still playing around and trying to understand the "how" of Spring's Webflux and Reactor.
The following successfully adds a new DemoPOJO to the repo when the annotated controller is used (i.e., POST issued at //localhost:8080/v1/DemoPOJO).
However, when issuing the same POST using the router/handler implementation (i.e., //localhost:8080/v2/DemoPOJO), request.bodyToMono(DemoPOJO.class) does not appear to retrieve the DemoPOJO instance from the ServerRequest (i.e., DemoPOJO.printme() is not being invoked).
I'm "working on this", but thought I'd see if anyone can help me "get there faster". For-what-it's-worth, the router/handler implementations (i.e., GET) that don't require getting a DemoPOJO out of ServerRequest are working.
RESTful endpoints using annotation...
#RestController
public class DemoPOJOController {
private Logger logger = LoggerFactory.getLogger(DemoPOJOHandler.class);
#Autowired
DemoPOJOService service;
#RequestMapping(method = POST, value = "/v1/DemoPOJO")
public Mono<Boolean> addDemoPOJO(#RequestBody DemoPOJO demoPOJO) {
logger.debug("DemoPOJOController.addDemoPOJO( {} )", demoPOJO.getId());
return service.add(demoPOJO);
}
}
"Router" part of the corresponding router/handler implementation...
#Configuration
public class DemoPOJORouter {
private Logger logger = LoggerFactory.getLogger(DemoPOJOHandler.class);
#Bean
public RouterFunction<ServerResponse> route(DemoPOJOHandler requestHandler) {
logger.debug("DemoPOJORouter.route( DemoPOJOHandler )");
return nest(path("/v2"),
nest(accept(APPLICATION_JSON),
RouterFunctions.route(RequestPredicates.POST("/DemoPOJO"), requestHandler::add)));
}
}
"Handler" part of the router/handler implementation...
#Component
public class DemoPOJOHandler {
public static final String PATH_VAR_ID = "id";
private Logger logger = LoggerFactory.getLogger(DemoPOJOHandler.class);
#Autowired
private DemoPOJOService service;
public Mono<ServerResponse> add(ServerRequest request) {
logger.debug("DemoPOJOHandler.add( ServerRequest )");
request.bodyToMono(DemoPOJO.class).doOnSuccess(DemoPOJO::printMe);
return ServerResponse.ok().build();
}
}
DemoPOJORepo implementation (hoping to simplify my learning experience by avoiding a "real" repository)...
#Component
public class DemoPOJORepo {
private static final int NUM_OBJS = 15;
private Logger logger = LoggerFactory.getLogger(DemoPOJORepo.class);
private static DemoPOJORepo demoRepo = null;
private Map<Integer, DemoPOJO> demoPOJOMap;
private DemoPOJORepo() {
logger.debug("DemoPOJORepo.DemoPOJORepo()");
initMap();
}
public boolean add(DemoPOJO demoPOJO) {
logger.debug("DemoPOJORepo.add( DemoPOJO )");
boolean pojoAdded = false;
if (!demoPOJOMap.containsKey(demoPOJO.getId())) {
logger.debug("DemoPOJORepo.add( DemoPOJO ) -> adding for id {}", demoPOJO.getId());
demoPOJOMap.put(demoPOJO.getId(), demoPOJO);
pojoAdded = true;
}
return pojoAdded;
}
private void initMap() {
logger.debug("DemoPOJORepo.initMap()");
demoPOJOMap = new TreeMap<Integer, DemoPOJO>();
for (int ndx = 1; ndx < (NUM_OBJS + 1); ndx++) {
demoPOJOMap.put(ndx, new DemoPOJO(ndx, "foo_" + ndx, ndx + 100));
}
}
}
The objects being manipulated...
public class DemoPOJO {
private Logger logger = LoggerFactory.getLogger(DemoPOJOHandler.class);
public static final String DEF_NAME = "DEFAULT NAME";
public static final int DEF_VALUE = 99;
private int id;
private String name;
private int value;
public DemoPOJO(int id) {
this(id, DEF_NAME, DEF_VALUE);
}
public DemoPOJO(#JsonProperty("id") int id, #JsonProperty("name") String name, #JsonProperty("value") int value) {
logger.debug("DemoPOJO.DemoPOJO( {}, {}, {} )", id, name, value);
this.id = id;
this.name = name;
this.value = value;
}
// getters and setters go here
public void printMe() {
logger.debug("DemoPOJO.printMe()");
System.out.printf("id->%d, name->%s, value->%d%n", id, name, value);
}
}
i am guesstimating here since i am writing from mobile. But i think this is your problem.
request.bodyToMono(DemoPOJO.class).doOnSuccess(DemoPOJO::printMe);
return ServerResponse.ok().build();
You are thinking imperative, that first row will be executed then the second which is not the case in webflux. You have to think events-callbacks.
return request.bodyToMono(DemoPOJO.class)
.doOnSuccess(DemoPOJO::printMe)
.thenReturn(ServerResponse.ok().build());
I think this is it but i could be wrong.
I mock articleElementSelector.getTag() to return a string and use InjectMocks annotation to inject the mock into the constructor. In the debugger, I see that articleElementSelector is mocked (because of the CGLib stuff), but when getTag() is invoked, it returns null.
HomePageScraperTest:
public class HomePageScraperTest extends UnitTest {
private static final String ARTICLE_TAG = "article";
private static final String URL_HOME_ARTICLE_1 = "http://www.home1.com";
private static final String URL_HOME_ARTICLE_2 = "http://www.home2.com";
#InjectMocks
private HomePageScraper homePageScraper;
#Mock
private JsoupParser jsoupParser;
#Mock
private ArticleScraper articleScraper;
#Mock
private ArticleElementSelector articleElementSelector;
#Mock
private Document homeDocument;
#Mock
private Element element1;
#Mock
private Element element2;
#Mock
private Elements elements1;
#Mock
private Elements elements2;
private URL homeUrl;
private Elements homeArticleElements = new Elements();
private Article homeArticle1;
private Article homeArticle2;
#Before
public void setUp() throws Exception {
homeUrl = new URL(URL_HOME_ARTICLE_1);
homeArticleElements.addAll(asList(element1, element2));
homeArticle1 = anArticle().withTitle("article1").build();
homeArticle2 = anArticle().withTitle("article2").build();
}
#Test
public void scrape() {
HomePage homePage = new HomePage(homeUrl);
when(articleElementSelector.getTag()).thenReturn(ARTICLE_TAG);
when(jsoupParser.parse(homeUrl)).thenReturn(homeDocument);
when(homeDocument.select(ARTICLE_TAG)).thenReturn(homeArticleElements);
when(element1.select("a")).thenReturn(elements1);
when(elements1.attr("href")).thenReturn(URL_HOME_ARTICLE_1);
when(element2.select("a")).thenReturn(elements2);
when(elements2.attr("href")).thenReturn(URL_HOME_ARTICLE_2);
when(articleScraper.scrape(URL_HOME_ARTICLE_1)).thenReturn(homeArticle1);
when(articleScraper.scrape(URL_HOME_ARTICLE_2)).thenReturn(homeArticle2);
List<Article> articles = homePageScraper.scrape(homePage);
assertThat(articles).containsOnly(homeArticle1, homeArticle2);
}
}
HomePageScraper (only relevant code)
#Component
public class HomePageScraper extends AbstractPageScraper {
private static final int HEADLINER_COUNT = 5;
public HomePageScraper(JsoupParser parser, ArticleElementSelector articleElementSelector, ArticleScraper articleScraper) {
super(parser, articleElementSelector, articleScraper);
}
}
AbstractPageScraper
public abstract class AbstractPageScraper {
private final String ARTICLE_TAG;
private JsoupParser parser;
ArticleScraper articleScraper;
public AbstractPageScraper(JsoupParser parser, ArticleElementSelector articleElementSelector, ArticleScraper articleScraper) {
this.parser = parser;
ARTICLE_TAG = articleElementSelector.getTag(); // here the mock returns null
this.articleScraper = articleScraper;
}
}
The test worked before but after I refactored it, pulling up duplicate code in the abstract class, I bumped on this one.
I have to initialize some final variable,but these value need to be read by Spring Properties
public class CrawlerClient{
#Autowired
#Qualifier("crawlerProperties")
private Properties crawlerProperties;
private Integer final maxTopic;
public static void main(String[] args) {
//initialize();
}
#PostConstruct
private void initialize(){
List<Topic> topics = topicBusiness.getAll();
List<Blogger> bloggers = bloggerBusiness.getAll();
List<Clue> clues = clueBusiness.getAll();
ClueQueue.addAll(clues);
TopicQueue.addAll(topics);
BloggerQueue.addAll(bloggers);
}
..
}
I want to initialize the variable of "maxTopic",but value is in Properties,So I can't do it in construction,how can I do for this?I just know remove the key of "final".
Finally,I do it by this way:
final Integer maxTopic;
final Integer maxBlogger;
final Integer maxClue;
#Autowired
public CrawlerClient(#Qualifier("crawlerProperties")Properties crawlerProperties){
this.maxTopic = Integer.parseInt(crawlerProperties.getProperty("MaxTopic"));
this.maxBlogger = Integer.parseInt(crawlerProperties.getProperty("MaxBlogger"));
this.maxClue = Integer.parseInt(crawlerProperties.getProperty("MaxClue"));
}
Can anyone solve it by the better way?
I believe you can achieve what you want with constructor injection:
#Component
public class CrawlerClient{
private Properties crawlerProperties;
private final Integer maxTopic;
#Autowired
public CrawlerClient(#Qualifier("crawlerProperties") Properties crawlerProperties,
#Value("maxTopic") Integer maxTopic){
this.crawlerProperties = crawlerProperties;
this.maxTopic = maxTopic;
List<Topic> topics = topicBusiness.getAll();
List<Blogger> bloggers = bloggerBusiness.getAll();
List<Clue> clues = clueBusiness.getAll();
ClueQueue.addAll(clues);
TopicQueue.addAll(topics);
BloggerQueue.addAll(bloggers);
}
..
}
public class Database {
private String ric;
private String volume;
private String _url;
private String _userId;
private String _password;
private String _dbLib;
private String _dbFile;
private Connection _conn;
private PreparedStatement _statement;
public Database(LSE item) {
ric = item.get_ric();
volume = item.get_volume();
}
public void writeToDb() throws SQLException{
//setString
}
}
I have a ItemDispatcher class:
public class ItemDispatcher implements Runnable {
private LSE lse;
public ItemDispatcher(LSE lseItem) {
this.lse= lseItem;
}
#Override
public void run() {
try {
new Database(lse).writeToFile();
} catch (IOException e) {
e.printStackTrace();
}
}
}
run() method in ItemDispatcher runs repeatedly. I want to create database connection and prepareStatement in Database class, but doing this on Database class constuctor would create connection many times over. How can I change my design to create connection just once and not over and over again on every execution of run(). I am trying to not do this in any other class and just Database class
Within the scope of ItemDispatcher, declare private variable X of type Database. You might initialize it in a separate method (best) or in the constructor (might be ok). Use the private variable X instead of creating a new instance in method run
Do it in a static block in class Database
static {
}
But this implies that Connections and Statement will be static and then shared by all instances of Database.
Just as an example from another SO post:
public static final Map<String, String> initials = new HashMap<String, String>();
static {
initials.put("AEN", "Alfred E. Newman");
// etc.
}
Use the Singleton pattern . This will allow you to have only one instace of the Database connection.
Taking your code as an example, it would be like this :
public class Database {
private String ric;
private String volume;
private String _url;
private String _userId;
private String _password;
private String _dbLib;
private String _dbFile;
private Connection _conn;
private PreparedStatement _statement;
private static final Database INSTANCE;
private Database(LSE item) {
ric = item.get_ric();
volume = item.get_volume();
}
public static final Database getInstance(LSE item) {
if (INSTANCE == null) {
INSTANCE = new Database(LSE item);
}
return INSTANCE;
}
public void writeToDb() throws SQLException{
//setString
}
}
If your application will be using Threads (Concurrency), I suggest you also to prepare your singleton for those situations , see this question