Spring - Can you recompile a StoredProcedure after initialization? - java

I have a StoredProcedure which is compiled on initialization.
Occasionally the logic requires that I use a different stored procedure.
I have tried and failed to reset the name of the stored procedure. The data is still retrieved using the original stored procedure.
Is there a way to achieve this?
Here is a reduced version of the class showing initialization and attempted recompiling with the name of a different stored procedure:
import org.springframework.jdbc.object.StoredProcedure;
public class MyDAOImpl extends StoredProcedure implements MyDAO {
#Autowired
public MyDAOImpl(DataSource dataSource, String originalSPName) {
super(dataSource, originalSPName); // invokes StoredProcedure constructor
compile();
}
public List<String> useOtherStoredProcedure(){
super.setSql("otherSPName");
compile();
// Error: Data is still retrieved with original StoredProcedure name
Map<String, Object> data = this.executeSP();
}
}

Rather than org.springframework.jdbc.object.StoredProcedure pls use org.springframework.jdbc.core.simple.SimpleJdbcCall. The beauty of SimpleJdbcCall is that you can dynamically specify the schema, package and stored procedure names. Pls find the respective code below :-
#Autowired
private JdbcTemplate jdbcTemplate;
public Date getSPData() {
SimpleJdbcCall spCall = new SimpleJdbcCall(jdbcTemplate).withSchemaName("schema")
.withCatalogName("catalog")
.withProcedureName("proc")
.withoutProcedureColumnMetaDataAccess()
.useInParameterNames("ref_id")
.declareParameters(
new SqlParameter("ref_id", Types.NUMERIC),
new SqlOutParameter("dt", Types.DATE));
SqlParameterSource in = new MapSqlParameterSource()
.addValue("ref_id", 12345);
Map<String, Object> out = spCall.execute(in);
Date date = (Date) out.get("dt");
return date;
}

I believe this might be what you want:
public class MyDAOAdapter implements MyDAO {
private volatile MyDAO currentDao;
private final MyDAO myDAOImpl1;
private final MyDAO myDAOImpl2;
#Autowired
public MyDAOImpl(#Qualifier("myDAOImpl1") MyDAO myDAOImpl1, #Qualifier("myDAOImpl2") MyDAO myDAOImpl2) {
this.myDAOImpl1 = myDAOImpl1;
this.myDAOImpl2 = myDAOImpl2;
currentDao = myDAOImpl1;
}
public void switchToFirstDao() {
currentDao = myDAOImpl2;
}
public void switchToSecondDao() {
currentDao = myDAOImpl2;
}
// do you really need this?
public List<String> useOtherStoredProcedure(){
return myDAOImpl2.executeSP();
}
// Delegate all the methods to current DAO selected
public List<String> executeSP(){
return currentDao.executeSP();
}
// implement all other methods by delegating calls to currentDao
}
This should be autowired by all the code and the switch between the two StoredProcedure implementations would be hidden inside this adapter. Still I didn't understand if you want this switch to be global for all code or only for some cases.

The solution I found for my own situation was to implement a third stored procedure on SQL Studio, which acts as a common accessor and routes the query to the right stored procedure based on a parameter.
CREATE PROCEDURE [dbo].[Router_SP]
#spNumber as INTEGER
AS
BEGIN
IF(#spNumber = 1) EXECUTE originalSPName
ELSE IF (#spNumber = 2) EXECUTE otherSPName
ELSE RETURN 'Unrecognised stored procedure'
END
Leaving this question open for now to see if a better solution comes up.

Related

JUnit: mocking jdbcTemplate's query() method with a RowCallbackHandler

I have the following code in my Java class in a Spring Boot (v. 2.2.1.RELEASE) application:
#Inject
private JdbcTemplate jdbcTemplate;
#Inject
private MyRowCallbackHandler myRowCallbackHandler;
public void myMethod() {
jdbcTemplate.query(MY_QUERY, myRowCallbackHandler);
}
The JDBC template object is an implementation of org.springframework.jdbc.core.JdbcTemplate and the handler is an implementation of org.springframework.jdbc.core.RowCallbackHandler.
With JUnit version 4 and Mockito, can I mimic the retrieval of one or more rows from a database by the query method, thus calling the handler's processRow() method?
Thanks for any assistance.
I ran into this problem in my own code, thought I'd share the solution here, even though it's slightly different than the situation above as I mock the jdbcTemplate as well.
#InjectMocks
private JdbcOperationRepository jdbcOperationRepository;
#Mock
private NamedParameterJdbcTemplate mockJdbcTemplate;
#Test
public void testMyResults() {
final ResultSet mockResult1 = mock(ResultSet.class);
when(mockResult1.getString(MY_COLUMN)).thenReturn(value);
// ... other when statements to mock the returned data
doAnswer(invocation -> {
RowCallbackHandler callbackHandler = invocation.getArgument(2);
callbackHandler.processRow(mockResult1);
callbackHandler.processRow(mockResult2);
return null;
}).when(mockJdbcTemplate).query(any(), any(), any(RowCallbackHandler.class));
}
I was struggling with the same problem. First, you have to keep in mind that you can mock almost anything in spring boot using Junit tests.
I am writing the solution in a generalized pattern so that everyone can get an idea how it gets implemented and used.
Suppose, we have a function implemented in our BookService.java class:
#Service
public class BookService {
#Inject
private NamedParameterJdbcOperations jdbcTemplate;
#Inject
private FileHelper fileHelper;
public List<BookDTO> getBooks(Long libraryId){
String sql = fileHelper.getFileContents("SQL_FILE_PATH");
Map<String, Object> parameterMap = new HashMap<>();
if(libraryId!=null){
parameterMap.put("libraryId", libraryId);
}
List<BookDTO> books = new ArrayList<>();
jdbcTemplate.query(sql, parameterMap, rs -> {
BookDTO book = new BookDTO();
book.setId(rs.getLong("id"));
book.setName(rs.getString("name"));
if(rs.getObject("librarian") != null){
book.setLibrarian(rs.getString("librarian"));
}
books.add(book);
});
return books;
}
}
Now, we want to mock the functionality of the jdbcTemplate.query() method for the above service class.
Our test should be written in this pattern:
public class BookServiceMockTest {
#Mock
private NamedParameterJdbcOperations jdbcTemplate;
#Mock
private FileHelper fileHelper;
private BookService mockBookService;
#Before
public void setup(){
mockBookService = new BookService();
// using reflectionUtils setFields of the fileHelper and jdbcTemplate.
// ....
}
#Test
public void getBooksTest(){
when(fileHelper.getFileContents("SQL_FILE_PATH")).thenReturn("SOME SQL");
doAnswer(invocation -> {
// we are going to mock ResultSet class.
ResultSet rs = Mockito.mock(ResultSet.class);
when(rs.getLong("id")).thenReturn(1L);
when(rs.getString("name")).thenReturn("Game of Thrones");
// this will mock the if() statement
when(rs.getObject("librarian")).thenReturn("John Doe");
// this will mock the actual get statement call on getString() inside the if statement
when(rs.getString("librarian")).thenReturn("John Doe");
// the argument index is important here.
// we are doing getArgument(2).
// This means the third parameter passed in the jdbcTemplate.query(Param0, Param1, Param2) in the BookService.getBooks() function.
RowCallbackHandler rch = (RowCallbackHandler) invocation.getArgument(2);
// as we are mocking only one row..
rch.processRow(rs);
/* // if we wanter two or more results:
when(rs.getLong("id")).thenReturn(1L).thenReturn(2L);
when(rs.getString("name")).thenReturn("Game of Thrones").thenReturn("Dance of the Dragon");
int n = 2; // no of rows..
for(int i=0; i<n; i++){
rch.processRow(rs);
}
*/
return null;
})
// the parameters used here are important. Any mismatch will result in unsuccessful.
.when(jdbcTemplate).query(eq("SOME SQL"), anyMap(), any(RowCallbackHandler.class));
List<BookDTO> books = mockBookService.getBooks(anyLong());
verify(jdbcTemplate, times(1)).query(eq("SOME SQL"), anyMap(), any(RowCallbackHandler.class));
assertThat(books).hasSize(1);
}
}
I hope this answered what you were looking for!

Casting List<T> to my own object

I've got a method called getBillingCodes that returns a List of objects called BillingCodes.
#Service
public class BillingCodeService {
private static final Logger log = LoggerFactory.getLogger(BillingCodeService.class);
String sql;
#Autowired
private BillingCodeRowMapper billingCodeRowMapper;
#Autowired
private JdbcTemplate jdbcTemplate;
public List<BillingCode> getBillingCodes(int billingCodeId, int limit) {
sql = [SQL_STATEMENT]";
List<BillingCode> billingCodeList;
billingCodeList = jdbcTemplate.query(sql, new Object[]{billingCodeId, limit}, billingCodeRowMapper);
return billingCodeList;
}
}
I would like to change the getBillingCodes method so that it still returns a List of objects but I like to specify the List of objects in a class like this
#Component
public class BillingCodeList {
private List<BillingCode> billingCodeList;
Getter and Setters removed,,.
}
And then change the change the getBillingCodes method to:
#Service
public class BillingCodeService {
private static final Logger log = LoggerFactory.getLogger(BillingCodeService.class);
String sql;
#Autowired
private BillingCodeRowMapper billingCodeRowMapper;
#Autowired
private JdbcTemplate jdbcTemplate;
#Autowired
private BillingCodeList billingCodeList;
public BillingCodeList getBillingCodes(int billingCodeId, int limit) {
sql = "[SQL_STATEMENT]";
billingCodeList = jdbcTemplate.query(sql, new Object[]{billingCodeId, limit}, billingCodeRowMapper);
return billingCodeList;
}
}
Setting billingCodeList = jdbcTemplate.query,,. gives me an error no instance(s) of variable(s) T exist so that List<T> conforms to BillingCodeList. Therefore I've tried to cast the List<T> to BillingCodeList like this:
billingCodeList = (BillingCodeList)jdbcTemplate.query(sql, new Object[]{billingCodeId, limit}, billingCodeRowMapper);
But that gives me a casting error java.lang.ClassCastException: java.util.ArrayList cannot be cast to com.test.model.BillingCodeList. How can I cast java.util.ArrayList to com.test.model.BillingCodeList
You can not cast List to a class object.One thing you can do you can pass BillingCodeList object to BillingCodeRowMapper through constructor and populate the BillingCodeList after creating the BillingCode.
You don't want to do what you're asking (in effect, you're have an XY problem moment).
To explain, I see that BillingCodeList is annotated with #Component, which I'm guessing means that it's a Spring component - and you need to do some very fancy footwork to make this work (and it would be inefficient as hell).
I'm guessing that you don't want to re-read all the billing codes each time, and instead you want to cache them in memory. In this case, something like this will work better:
#Component
public class BillingCodeListCache {
#Autowired
private BillingCodeService billingCodeService;
private List<BillingCode> billingCodeList;
public List<BillingCode> getBillingCodes() {
if(billingCodeList == null) {
billingCodeList = billingCodeService.getBillingCodes();
}
return billingCodeList; // -- or a sublist.
}
}
This way you can also control cache eviction.
If the only reason you are doing this is to have a method to print out the values of the list, then just extend ArrayList or implement the List interface in your class. Then you can override the toString method to do what you want.
I went with the solution suggested by Lew Bloch. Passing the List as an argument in the BillingCodes constructor.
#Autowired
private BillingCodes billingCodesToString(List<BillingCode> billingCodes) {
return new BillingCodes(billingCodes);
}
And then overriding the toString() method in the BillingCodes class giving me the ability to to get the String like so:
billingCodesToString(billingCodes).toString()

Mockito void method how to inspect private variable?

I am trying to unit test a void method which gets data from database, formats into map and passes onto another class.
I have mocked out the database call and returned my own data, and I want to inspect the map which has been formatted contains the right number of elements and keys in right locations.
I have tried playing around with argumentCaptor to do this but struggling to get my head round it.
Method to test:
public class MyClass {
#Autowired
private Dao dao;
#Autowired
private AnotherClass anotherClass;
public void format(String, Date) {
Map<Object,Object> map = getDataAndFormat(String, Date);
anotherClass.doSomething(map);
}
private Map<Object, Object> getDataAndFormat(String, Date) {
Map map;
if (String.equals("A") {
map = dao.getData(Date);
}
else {
map = dao.getDataSomeThingElse(Date);
}
}
}
Any help much appreciated
Thanks,
so: This is what I have so far:
#InjectMocks
#Autowired
//class to test
private MyClass myClass;
#Mock
Dao dao;
#Captor
private ArgumentCaptor<String> argumentCaptor;
#Captor
private ArgumentCaptor<Date> argumentCaptorB;
public void testFormat()
{
when(dao.getData(Matchers.any())).thenReturn(data());
myClass.format("A",new Date());
}
So i want to use argument captors (but not entirely sure how to) to get the map from the format method call and inspect the returned map. The code i currently have is hitting all my code but I can't assert on anything hence why i wanted to check the map contains what I expect it to. Hope that makes sense
It appears that what you want to do is to confirm the contents of the map that is passed to AnotherClass.
If AnotherClass is an interface, you can create a mock instance of AnotherClass and inject it into your MyClass. You can then verify that it has been called with your arguments of choice:
Mockito.verify(mockAnotherClass).doSomething(expectedMap);

Spring unitTest on DAO class using jdbc JAVA

I am trying to make a unit test on a method from a DAO class. Dao class is using JDBC. I am trying to unit test it but without using test data base. I must test it using some data structure for storing all the information.
public class UserProfilesDao extends JdbcDaoSupport {
#Autowired
private MessageSourceAccessor msa;
public long getUserServiceId(long userId, int serviceId) {
String sql = msa.getMessage("sql.select.service_user_id");
Object[] params = new Object[] { userId, serviceId };
int[] types = new int[] { Types.INTEGER, Types.INTEGER };
return getJdbcTemplate().queryForLong(sql, params, types);
}
}
getJdbcTemplate() appears to be a method that you want to mock out.
In your unit test, declare a UserProfilesDao member as follows:
#Spy
private UserProfilesDao classToTest;
the #Spy is a mockito annotation.
in your unit test declare a setup method as follows:
#Before
public void preTestSetup()
{
MockitoAnnotations.initMocks(this);
doReturn(what ever you want).when(classToTest).queryForLong(
any(xxx.class),
any(yyy.class),
any(zzz.class));
}
where xxx, yyy, and zzz are the queryForLong parameter types.
You can store data in DTO Objects and maintain DTOs in a Map with proper keys.
For test, retrieve data from map using key according to your need!

Unit testing a DAO class that uses Spring JDBC

I have several DAO objects that are used to retrieve information from a database and I really want to write some automated tests for them but I'm having a hard time figuring out how to do it.
I'm using Spring's JdbcTemplate to run the actual query (via a prepared statement) and map the results to the model object (via the RowMapper class).
If I were to write unit tests, I'm not sure how I would/should mock the objects. For example, since there are only reads, I would use the actual database connection and not mock the jdbcTemplate, but I'm not sure that's right.
Here's the (simplified) code for the simplest DAO of the batch:
/**
* Implementation of the {#link BusinessSegmentDAO} interface using JDBC.
*/
public class GPLBusinessSegmentDAO implements BusinessSegmentDAO {
private JdbcTemplate jdbcTemplate;
private static class BusinessSegmentRowMapper implements RowMapper<BusinessSegment> {
public BusinessSegment mapRow(ResultSet rs, int arg1) throws SQLException {
try {
return new BusinessSegment(rs.getString(...));
} catch (SQLException e) {
return null;
}
}
}
private static class GetBusinessSegmentsPreparedStatementCreator
implements PreparedStatementCreator {
private String region, cc, ll;
private int regionId;
private GetBusinessSegmentsPreparedStatementCreator(String cc, String ll) {
this.cc = cc;
this.ll = ll;
}
public PreparedStatement createPreparedStatement(Connection connection)
throws SQLException {
String sql = "SELECT ...";
PreparedStatement ps = connection.prepareStatement(sql);
ps.setString(1, cc);
ps.setString(2, ll);
return ps;
}
}
public GPLBusinessSegmentDAO(DataSource dataSource) {
jdbcTemplate = new JdbcTemplate(dataSource);
}
public Collection<BusinessSegment> getBusinessSegments(String cc, String ll) {
return jdbcTemplate.query(
new GetBusinessSegmentsPreparedStatementCreator(cc, ll),
new BusinessSegmentRowMapper());
}
}
Any idea would be appreciated.
Thanks!
Please have a look at below links:
Testing SQL queries with Spring and DbUnit
MockObjects or DBUnit for testing Code using JdbcTemplate
Hope that helps.
EDIT:
Here is the GitHub version of RowMapperTests for easy reference.
I recommend breaking your dependency on JdbcTemplate class, and using the JdbcOperations interface instead, e.g.
public class GPLBusinessSegmentDAO implements BusinessSegmentDAO {
private final JdbcOperations jdbc;
public GPLBusinessSegmentDAO(DataSource dataSource) {
this(new JdbcTemplate(dataSource));
}
public GPLBusinessSegmentDAO(JdbcOperations jdbc) {
this.jdbc = jdbc;
}
// ... DAO methods here
}
Your unit test can invoke the second constructor, passing in a mock JdbcOperations object. Since all DB operations are performed via the jdbc object, you can mock that easily enough.
Your live code can call the first constructor as before.
To write a true unit test for this, you would not be touching a real database.
You may however find it more practical to pass in a real DataSource to your underlying db, and test the getBusinessSegments() method returns 0, 1 and many results depending on the cc and ll values you pass in.
Another option worth investigating would be to pass in a DataSource of an embedded Java DB that was initialised with your schema in a setUp/#Before method. I guess what you really want to test is that the SELECT... query maps correctly to the schema, so such a test would catch any errors that arise at runtime when the schema, say, changes.

Categories

Resources