How to unit test ResultSetExtractor? - java

I need advice on how to unit test a ResultSetExtractor when using jdbc.
String sql = "SELECT * FROM item where item_id = ?";
return (Item) jdbcTemplate.query(sql, new ResultSetExtractor<Item>() {
#Override
public Item extractData(final ResultSet rs) throws ... {
if (rs.next()) {
Item item = new Item (rs.getString(1), rs.getString(2));
return item ;
} else
return null;
}
}, itemId);
}
With the above approach of using an anonymous class, I would have to use argumentCaptor to get the ResultSetExtractor. The test I have so far is below. I don't know how what to do next in terms of the ResultSetExtractor.
#Test
public void testGetItemByID() {
ArgumentCaptor<ResultSetExtractor> rseCaptor = ArgumentCaptor.forClass(ResultSetExtractor.class);
ArgumentCaptor<String> stringCaptor = ArgumentCaptor.forClass(String.class);
String sql = "SELECT * FROM item where item_id = ?";
itemDAOImpl.getItemByID(testId);
verify(mockJdbcTemplate, times(1)).query(stringCaptor.capture(), rseCaptor.capture(), stringCaptor.capture());
assertEquals(stringCaptor.getAllValues().get(0),sql);
assertEquals(stringCaptor.getAllValues().get(1),testId);
}
I dont know if using an anonymous class would make it harder to unit test. I could always pull the anonymous class out of the jdbc call and make a class like below, but I dont know where to start testing this class.
public class ItemResultSet implements ResultSetExtractor<Item> {
#Override
public Item extractData(ResultSet rs) throws ...{
if (rs.next()) {
Item item = new Item (rs.getString(1), rs.getString(2));
return item ;
} else
return null;
}
}
The test below would pass with either implementations but I want to avoid using any(ResultSetExtractor.class). What I want is to test the actual implementation of extractData(final ResultSet rs) if possible.
#Test
public void testGetItemByID() {
String sql = "SELECT * FROM item where item_id = ?";
when(mockJdbcTemplate.query(anyString(), any(ResultSetExtractor.class), anyString()))
.thenReturn(testItem);
assertEquals(testItem, itemDAOImpl.getItemByID(testId));
}

You could do the following
private ResultSet getMockResultSet() throws Exception {
ResultSet rs = Mockito.mock(ResultSet.class);
Mockito.when(rs.next()).thenReturn(true).thenReturn(false);
Mockito.when(rs.getString(1)).thenReturn("Value 1");
Mockito.when(rs.getString(2)).thenReturn("Value 2");
return rs;
}
private ResultSet getEmptyMockResultSet() throws Exception {
ResultSet rs = Mockito.mock(ResultSet.class);
Mockito.when(rs.next()).thenReturn(false);
return rs;
}
#Test
public void testDataExists() {
Item item = new ResultSetExtractor<Item>() {
#Override
public Item extractData(final ResultSet rs) throws ... {
if (rs.next()) {
Item item = new Item (rs.getString(1), rs.getString(2));
return item ;
} else
return null;
}
}.extractDatae(getMockResultSet());
assertEquals(item.getFirst(), "Value 1");
assertEquals(item.getSecond(), "Value 2");
}
#Test
public void testNoDataExists() {
Item item = new ResultSetExtractor<Item>() {
#Override
public Item extractData(final ResultSet rs) throws ... {
if (rs.next()) {
Item item = new Item (rs.getString(1), rs.getString(2));
return item ;
} else
return null;
}
}.extractData(getEmptyMockResultSet());
assertNull(item);
}

Related

How to read ArrayList<String> field from database with PostgreSQL JDBC

I'm having an issue with getting the value of stored ArrayList<String> as VARCHAR[] in the database.
Somehow, when I'm debugging, I can see that the value returning from the database is:
I am expecting to store an ArrayList<String> by converting it to VARCHAR[] and when reading from the DB I expect to convert VARCHAR[] to ArrayList<String>
Code:
public Message<ArrayList<String>> getPlayersInSession(Object data) throws SQLException, ClassNotFoundException {
String sessionCode = (String) data;
Session session = PostgreSQLJDBC.sessionJDBCInstance().getSession(sessionCode);
//session.getPlayers() is the problem I have
return new Message<>(Message.RequestCode.RECEIVE_PLAYERS_IN_SESSION, session.getPlayers());
(Message.RequestCode.RECEIVE_PLAYERS_IN_SESSION, players);
}
public class Session {
private int _id;
private String _code;
private ArrayList<String> _players;
private ArrayList<Pair<String, String>> _leaderboard;
public Session() {
_code = "";
_players = new ArrayList<>();
_leaderboard = new ArrayList<>();
}
...
}
public class SessionJDBC implements SessionSQL {
private final Connection _connection;
public SessionJDBC(String url, String user, String password) throws ClassNotFoundException, SQLException {
Class.forName("org.postgresql.Driver");
_connection = DriverManager.getConnection(url, user, password);
if (!sessionTableExists()) createTable();
}
#Override
public void createTable() throws SQLException {
String sql = "CREATE TABLE session(" +
"id SERIAL PRIMARY KEY," +
"code VARCHAR," +
"players VARCHAR[]," +
"leaderboard VARCHAR[]" +
")";
PreparedStatement ps = _connection.prepareStatement(sql);
ps.executeUpdate();
}
#Override
public void addSession(Session session) throws SQLException {
String sql = "INSERT INTO session(code, players, leaderboard)"
+ "VALUES (?,?,?)";
PreparedStatement ps = _connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
ps.setString(1, session.getCode());
ps.setArray(2, _connection.createArrayOf("VARCHAR", session.getPlayers().toArray()));
ps.setArray(3, _connection.createArrayOf("VARCHAR", session.getLeaderboard().toArray()));
ps.executeUpdate();
ResultSet generatedKeys = ps.getGeneratedKeys();
if (generatedKeys.next()) {
session.setId(generatedKeys.getInt(1));
}
}
#Override
public void removeSession(Session session) throws SQLException {
String sql = "DELETE FROM session WHERE id = ?";
PreparedStatement ps = _connection.prepareStatement(sql);
ps.setInt(1, session.getId());
ps.executeUpdate();
}
#Override
public Session getSession(String code) throws SQLException {
return getAllSessions().stream().filter(session -> session.getCode().equals(code)).findFirst().orElse(null);
}
#Override
public ArrayList<Session> getAllSessions() throws SQLException {
ArrayList<Session> array = new ArrayList<>();
ResultSet result = _connection.prepareStatement("SELECT * FROM session").executeQuery();
while (result.next()) {
Session session = new Session();
session.setCode(result.getString("code"));
session.setId(result.getInt("id"));
session.setPlayers(new ArrayList(Collections.singletonList(result.getArray("players"))));
session.setLeaderboard(new ArrayList(Collections.singletonList(result.getArray("leaderboard"))));
array.add(session);
}
result.close();
return array;
}
#Override
public boolean sessionTableExists() throws SQLException {
DatabaseMetaData dbm = _connection.getMetaData();
ResultSet tables = dbm.getTables(null, null, "session", null);
return tables.next();
}
}
I don't know how the code example compiles given that the ArrayList shown in the debugger is actually of type ArrayList<PgArray> rather than ArrayList<String>.
The problem is occurring in these lines:
session.setPlayers(new ArrayList(Collections.singletonList(result.getArray("players"))));
session.setLeaderboard(new ArrayList(Collections.singletonList(result.getArray("leaderboard"))));
For a start result.getArray("players") is returning a java.sql.Array object, more specifically a PgArray implementation. To get the real underlying data you need to do:
(String[])result.getArray("players").getArray();
The next problem is that you are using Collections.singletonList(). What this does is produce an ArrayList with only one element. Instead what you should use is Arrays.asList. Full solution:
session.setPlayers(new ArrayList(Arrays.asList((String[])result.getArray("players").getArray)));
session.setLeaderboard(new ArrayList(Arrays.asList((String[])result.getArray("leaderboard").getArray)));
Another thing that is interesting about this code is that you are selecting all rows from the table and then filtering in memory by streaming over the results. Why not select on the code field in an SQL query?

com.ibm.db2.jcc.am.SqlException: [jcc][t4][10120][10898][4.19.49] Invalid operation: result set is closed. ERRORCODE=-4470, SQLSTATE=null

In our Struts2 application For Fetching data we got result set is closed. we are using one structure for fetching resulset data. We are using BaseResultSetHandler fetch resulset data in DAO file.I am not able to find cause why resultset is closed. In the below i have mention code kindly help us
we are having BaseResultSetHandler abstract class
public abstract class BaseResultSetHandler {
protected ResultSet resultSet;
public abstract Object handleResult() throws SQLException;
public Object handleResultSet(ResultSet rs) throws SQLException{
setResultSet(rs);
Object obj = handleResult();
return obj;
}
public void setResultSet(ResultSet resultSet) {
this.resultSet = resultSet;
}
public ResultSet getResultSet() {
return resultSet;
}
public Integer getInteger(String columnName,ResultSet resultSet) throws SQLException{
return StringUtility.getInt(resultSet.getInt(columnName));
}
public String getString(String columnName,ResultSet resultSet) throws SQLException{
return StringUtility.getString(resultSet.getString(columnName));
}
public String getString(String columnName) throws SQLException{
return StringUtility.getString(resultSet.getString(columnName));
}
}
We have one DatabaseManger class For handling database operation in that we have function for fetch data from database
protected Object executePreparedQuery(String query, List arguments, BaseResultSetHandler BaseResultSetHandler, Connection useThisConnection)
throws SQLException, InstantiationException, Exception, Throwable
{
ResultSet resultSet = null;
try {
if (ReferenceCheckUtlity.isNull(useThisConnection)) {
this.connection = getConnection();
}else{
this.connection = useThisConnection;
}
this.preparedStatement = this.connection.prepareStatement(query);
if (arguments != null) {
Iterator argumentsIterator = arguments.iterator();
int index = 1;
while (argumentsIterator.hasNext()) {
this.preparedStatement.setObject(index++, argumentsIterator.next());
}
}
resultSet = this.preparedStatement.executeQuery();
Object localObject2 = BaseResultSetHandler.handleResultSet(resultSet);
} finally {
if(resultSet!=null){
resultSet.close();
}
if(this.statement!=null){
this.statement.close();
}
if(this.preparedStatement!=null){
this.preparedStatement.close();
}
if(this.connection!=null){
this.connection.close();
}
}
return localObject2;
}
In DAO File i have share only 2 method here there are lot of method in file approximate 25+ classes using BaseResultSetHandler
public String getCheckId(String Id) throws NumberFormatException, SQLException, InstantiationException, Exception, Throwable {
ArrayList<Object> argumentList=new ArrayList<Object>();
argumentList.add(Id);
int cnt=Integer.parseInt(""+executePreparedQuery(getCheckIdSql(), argumentList, new getCheckIdHandler(), null));
if(cnt==0){
return "N";
}else{
return "Y";
}
}
private String getCheckIdSql() {
StringBuffer query=new StringBuffer();
query.append("select count(1) as cnt from EMPLOYEES where ID=? for read only ");
return query.toString();
}
class getCheckIdHandler extends BaseResultSetHandler {
#Override
public Object handleResult() throws SQLException {
int cnt=0;
while(resultSet.next()){
cnt=getInteger("cnt");
}
return cnt;
}
}
public HashMap<String,String> getMajorData() throws InstantiationException, SQLException, Exception, Throwable{
LinkedHashMap<String,String> list = new LinkedHashMap<String,String>();
list=(LinkedHashMap<String, String>) executePreparedQuery(getMajorDataSql(), null, new getMajorDataHandler(), null);
return list;
}
private String getMajorDataSql() {
StringBuffer query=new StringBuffer();
query.append("select RMH_ode,MH_Name from RECMH order by MH_NM for read only");
return query.toString();
}
class getMajorDataHandler extends BaseResultSetHandler {
#Override
public Object handleResult() throws SQLException {
LinkedHashMap<String,String> list = new LinkedHashMap<String,String>();
while(resultSet.next()){
String RMH_CD=resultSet.getString("RMH_CD");
String MH_NM=resultSet.getString("MH_Name");
list.put(RMH_CD+"*"+MH_Name, RMH_CD+"-"+MH_Name);
}
return list;
}
}
And I have to also know it is sharing BaseResultSetHandler resultSet Object

Ambiguous behavior of ResultSet

I have a requirement to create separate POJO which will set/get sql ResultSet and use its methods throughout my project code like below. I have created below 2 classes
public class Tester3
{
public MyResultSet test() throws SQLException{
MyResultSet mrs = new MyResultSet();
PreparedStatement ps = null;
String values = null;
boolean flag = false;
String one = "'12'";
String two = "'jt'";
String a = null;
String b = null;
try {
if(flag==true)
{
values = "'3%'";
a =null;
b = "OR id IN(\'" +a+ "\')";
}else
{
values = "'%'";
a = one + "," + two;
b = "AND id IN("+a+")";
}
String sql = "SELECT * FROM veracodetable where orts like PARAM RAMAN";
sql = sql.replaceFirst("PARAM", values);
sql = sql.replaceFirst("RAMAN", b);
System.out.println("SQL: "+sql);
ps = new Connection1().getConnection().prepareStatement(sql);
ps.executeQuery();
mrs.setRs(ps.executeQuery());
System.out.println("ResultSet: "+mrs.getRs().next());
} catch (SQLException e) {
e.printStackTrace();
}
return mrs;
}
public static void main(String[] args) throws SQLException {
Tester3 t = new Tester3();
MyResultSet rs = t.test();
System.out.println("ResultSet: "+rs.getRs().next());
}
}
public class MyResultSet {
ResultSet rs = null;
public ResultSet getRs() {
return rs;
}
public void setRs(ResultSet rs) {
this.rs = rs;
}
}
When executed above code with separate POJO MyResultSet, I don't get any result in ResultSet. However if I skip POJO implementation and use resultSet directly, I am able to get results.
Is rs.getRs() invoking at all? If not, why?
I would separate the statements as they dont't perform the same function, and then populate;
PreparedStatemet ps = null;
ResultSet rs = null;
if(flag){
String stmt = "...?...?";
ps = con.preparedStatement(stmt);
ps.setString(0,a);
ps.setString(1,b);
rs = ps.executeQuery;
}else{
String stmt = "...?...?";
ps = con.preparedStatement(stmt);
ps.setString(0,a);
ps.setString(1,b);
rs = ps.executeQuery;
}
}

Error when no constructor method and error when constructor method

im having a problem with my tableview. whenever i add a item to my database, it appears on my tableview, but only after i close and reopen the tableview. However i'd like to have added to my tableview immediately after having it added. So i made a refresh method, but im getting error from there. I have added comments to my code, which explains where the problem occurs.
I have removed imports and #FXML tags
package packet;
public class DataControll implements Initializable {
private static Connection con;
private static Statement stat;
private PreparedStatement prep;
ResultSet rs = null;
private ObservableList<Toidubaas> toidudata;
public void eemaldaListist() {
}
public void lisaNupp(ActionEvent event) {
try {
String query = "INSERT OR IGNORE INTO Toiduinfo (Toidu, Kalorid, Valgud, Süsivesikud, Rasvad) VALUES(?, ?, ?, ?, ?)";
prep = con.prepareStatement(query);
prep.setString(1, lisaToit.getText());
prep.setInt(2, Integer.parseInt(lisaKcal.getText()));
prep.setInt(3, Integer.parseInt(lisaValk.getText()));
prep.setInt(4, Integer.parseInt(lisaSysi.getText()));
prep.setInt(5, Integer.parseInt(lisaRasv.getText()));
prep.execute();
prep.close();
} catch (Exception er) {
System.out.println(er.getMessage());
}
clearForm();
}
private void clearForm() {
lisaToit.clear();
lisaKcal.clear();
lisaValk.clear();
lisaSysi.clear();
lisaRasv.clear();
}
#Override
public void initialize(URL url, ResourceBundle rb) {
toidudata = FXCollections.observableArrayList();
tbCal.setCellValueFactory(new PropertyValueFactory<Toidubaas, Integer>("rbCal"));
tbProt.setCellValueFactory(new PropertyValueFactory<Toidubaas, Integer>("rbProt"));
tbCarb.setCellValueFactory(new PropertyValueFactory<Toidubaas, Integer>("rbCarb"));
tbFat.setCellValueFactory(new PropertyValueFactory<Toidubaas, Integer>("rbFat"));
tbMeal.setCellValueFactory(new PropertyValueFactory<Toidubaas, String>("rbMeal"));
try {
con = DriverManager.getConnection("jdbc:sqlite:Fooditabel.sqlite");
stat = con.createStatement();
stat.executeUpdate(
"CREATE TABLE IF NOT EXISTS Toiduinfo (Toidu TEXT, Kalorid INTEGER, Valgud INTEGER, Süsivesikud INTEGER, Rasvad INTEGER)");
ResultSet rs = con.createStatement()
.executeQuery("SELECT Toidu, Kalorid, Valgud, Süsivesikud, Rasvad FROM Toiduinfo");
while (rs.next()) {
Toidubaas nt = new Toidubaas(); //if i add constructor method, im getting error here, saying the constructor is undefined
nt.rbMeal.set(rs.getString("Toidu"));
nt.rbCal.set(rs.getInt("Kalorid"));
nt.rbProt.set(rs.getInt("Valgud"));
nt.rbCarb.set(rs.getInt("Süsivesikud"));
nt.rbFat.set(rs.getInt("Rasvad"));
toidudata.add(nt);
}
tbTabelview.setItems(toidudata);
} catch (SQLException ex) {
System.out.println(ex.getMessage());
;
}
}
public void refreshTable() {
toidudata.clear();
try {
String query = "Select * FROM Toiduinfo";
prep = con.prepareStatement(query);
rs = prep.executeQuery();
while (rs.next()) {
toidudata.add(new Toidubaas( //Getting error here, if im not using constructor method, saying the method is undefined.
rs.getString("Toidu"),
rs.getInt("Kalorid"),
rs.getInt("Valgud"),
rs.getInt("Süsivesikud"),
rs.getInt("Rasvad")));
tbTabelview.setItems(toidudata);
}
prep.close();
} catch (Exception e2) {
}
}
}
and here is the class with constructor method. Please note that constructor method is commented out, so my program would populate tableview with database data.
public class Toidubaas {
public SimpleIntegerProperty rbCal = new SimpleIntegerProperty();
public SimpleIntegerProperty rbProt = new SimpleIntegerProperty();
public SimpleIntegerProperty rbCarb = new SimpleIntegerProperty();
public SimpleIntegerProperty rbFat = new SimpleIntegerProperty();
public SimpleStringProperty rbMeal = new SimpleStringProperty();
//This is the constructor method
/*public Toidubaas(String sbMeal, Integer sbCal, Integer sbProt, Integer sbCarb, Integer sbFat) {
this.rbMeal = new SimpleStringProperty(sbMeal);
this.rbCal = new SimpleIntegerProperty(sbCal);
this.rbProt = new SimpleIntegerProperty(sbProt);
this.rbCarb = new SimpleIntegerProperty(sbCarb);
this.rbFat = new SimpleIntegerProperty(sbFat);
}*/
public String getRbMeal() {
return rbMeal.get();
}
public void setRbMeal(String v) {
rbMeal.set(v);
}
public Integer getRbCal() {
return rbCal.get();
}
public void setRbCal(Integer v) {
rbCal.set(v);
}
public Integer getRbProt() {
return rbProt.get();
}
public void setRbProt(Integer v) {
rbProt.set(v);
}
public Integer getRbCarb() {
return rbCarb.get();
}
public void setRbCarb(Integer v) {
rbCarb.set(v);
}
public Integer getRbFat() {
return rbFat.get();
}
public void setRbFat(Integer v) {
rbFat.set(v);
}
}
It's really confusing what you're asking here. I'd recommend posting the actual error text rather than leaving in-code comments.
So you're attempting to use Toidubaas in two separate ways:
First (Empty Constructor)
Toidubaas nt = new Toidubaas(); //if i add constructor method, im getting error here, saying the constructor is undefined
nt.rbMeal.set(rs.getString("Toidu"));
nt.rbCal.set(rs.getInt("Kalorid"));
nt.rbProt.set(rs.getInt("Valgud"));
nt.rbCarb.set(rs.getInt("Süsivesikud"));
nt.rbFat.set(rs.getInt("Rasvad"));
toidudata.add(nt);
Second (Parameterized Constructor)
toidudata.add(new Toidubaas( //Getting error here, if im not using constructor method, saying the method is undefined.
rs.getString("Toidu"),
rs.getInt("Kalorid"),
rs.getInt("Valgud"),
rs.getInt("Süsivesikud"),
rs.getInt("Rasvad")));
tbTabelview.setItems(toidudata);
There's two ways to handle this: either standardize how you access this (IE: change the code in first to second, or vica versa), or support both methods.
Toidubaas
public class Toidubaas {
public SimpleIntegerProperty rbCal = new SimpleIntegerProperty();
public SimpleIntegerProperty rbProt = new SimpleIntegerProperty();
public SimpleIntegerProperty rbCarb = new SimpleIntegerProperty();
public SimpleIntegerProperty rbFat = new SimpleIntegerProperty();
public SimpleStringProperty rbMeal = new SimpleStringProperty();
//Empty Constructor (only need this if standardized to first approach)
public Toidubaas() {}
//Parameterized Constructor (only need this if standardized to second approach)
public Toidubaas(String sbMeal, Integer sbCal, Integer sbProt, Integer sbCarb, Integer sbFat) {
rbMeal.set(sbMeal);
rbCal.set(sbCal);
rbProt.set(sbProt);
rbCarb.set(sbCarb);
rbFat.set(sbFat);
}
//Getters/Setters
...
}
If you do not want to add a constructor with parameters, set the values one by one by calling the setters. It's also possible to overload the constructor (you shouldn't create the properties more than once though).
The row is not added to the TableView, since you never add it in case you submit the data successfully, which is pretty simple to fix though:
String query = "INSERT OR IGNORE INTO Toiduinfo (Toidu, Kalorid, Valgud, Süsivesikud, Rasvad) VALUES(?, ?, ?, ?, ?)";
try (PreparedStatement prep = con.prepareStatement(query)) {
String toidu = lisaToit.getText();
int kalorid = Integer.parseInt(lisaKcal.getText());
int valgud = Integer.parseInt(lisaValk.getText());
int süsivesikud = Integer.parseInt(lisaSysi.getText());
int rasvad = Integer.parseInt(lisaRasv.getText());
prep.setString(1, toidu);
prep.setInt(2, kalorid);
prep.setInt(3, valgud);
prep.setInt(4, süsivesikud);
prep.setInt(5, rasvad);
if (prep.executeUpdate() > 0) {
// object filled with data from input data
Toidubaas newToidubaas = new Toidubaas(toidu,
kalorid,
valgud,
süsivesikud,
rasvad);
// add new item to table here
toidudata.add(newToidubaas);
}
clearForm();
} catch (Exception er) {
System.out.println(er.getMessage());
}

Class for manipulating with table in DB- Java MySQL

I should make separate class with reading library of books, updating etc.
Class books should have:
parametric constructor plus override for toString()
Method getAll static to read all data for DB
Method getById is reading data when we give ID, also static.
but i did next:
Main.java
try {
ArrayList<String[]> allBooks = Books.getAll("SELECT * FROM books");
for (String[] izd : allBooks) {
System.out.println("Books id: " + izd[0] + " Date of publishing: " + izd[1] + " Id of books: " + izd[2]+...);
}
} catch (Exception e) {
System.out.println(e);
}
Books.java
static ArrayList<String[]> getAll(String stt) throws ClassNotFoundException, InstantiationException, IllegalAccessException, SQLException
{
connect();
Statement stmt = conn.createStatement();
stmt.executeQuery(stt);
ResultSet rs = stmt.getResultSet();
int columnsNum = rs.getMetaData().getColumnCount();
ArrayList<String[]> result = new ArrayList<String[]>();
while(rs.next())
{
String[] rowArr = new String[columnsNum];
for(int i=0;i<columnsNum;i++)
rowArr[i]=rs.getString(i+1).toString();
result.add(rowArr);
}
close();
return result;
}
I really don't understand how.. Any help will be like :D
You are not really giving much information about your goal here, so I can only guess. I would expect something like this:
public class Book {
private String title;
//...
//constructor
public Book(String theTitle) {
this.title = theTitle;
}
public static ArrayList<Book> getAll(String stt) { /* ... */ }
public static Book getById(int id) { /* ... */ }
}
And in your getAll function you probably want something like this:
while(rs.next())
{
Book aBook = new Book(rs.getString("title"));
result.add(aBook);
}

Categories