I want to loop through documents in MongoDB. Basically here is the situation. I have some JTextfields which i want to populate from MongoDB. So each time the user click on the Next button, a new record must be fetched and display it in the JTextField. Here's my code:
public class nextstud implements ActionListener
{
public void actionPerformed(ActionEvent e) {
try {
Mongo s = new Mongo();
DB db = s.getDB( "omrs1" );
DBCollection coll = db.getCollection("Student") ;
DBCursor curs = coll.find();
if(curs.hasNext()) {
DBObject o = curs.next();
String fname = (String) o.get("Firstname") ;
String lname = (String) o.get("Lastname") ;
String sid = (String) o.get("StudentID") ;
String prg = (String) o.get("Programme") ;
String lvl = (String) o.get("Level") ;
txtfname.setText(fname) ;
}
btndelstud.setEnabled(true);
btnbkstud.setEnabled(true) ;
btnfwdstud.setEnabled(true);
} catch (UnknownHostException x) {
x.printStackTrace();
} catch (MongoException x) {
x.printStackTrace();
}
}
} // end class
However, it does not work. It only displays the first record each time i press the next button. If i change
if(curs.hasNext()) {
to
while(curs.hasNext()) {
It displays only the last record. Help please?
As Kevin mentioned, the problem is that you're fetching a new cursor on each button press, so it's always starting over. There are two potential approaches that will fix this problem.
Fetch the cursor once, and move through it as next is pushed. To do this, you make the cursor a field, and fetch the cursor in the listener's constructor.
public class Nextstud implements ActionListener {
private DBCursor curs;
public Nextstud() {
Mongo s = new Mongo();
DB db = s.getDB( "omrs1" );
DBCollection coll = db.getCollection("Student") ;
curs = coll.find();
}
public void actionPerformed(ActionEvent e) {
try {
if(curs.hasNext()) {
DBObject o = curs.next();
String fname = (String) o.get("Firstname") ;
String lname = (String) o.get("Lastname") ;
String sid = (String) o.get("StudentID") ;
String prg = (String) o.get("Programme") ;
String lvl = (String) o.get("Level") ;
txtfname.setText(fname) ;
}
btndelstud.setEnabled(true);
btnbkstud.setEnabled(true) ;
btnfwdstud.setEnabled(true);
} catch (UnknownHostException x) {
x.printStackTrace();
} catch (MongoException x) {
x.printStackTrace();
}
}
} // end class
The next alternative is to keep a count of how many items have been retrieved, and update the cursor's skip count:
DBCursor foo = coll.find().skip(count).limit(1);
count++;
//use one value from the cursor as before
The first approach is likely to be slightly faster. Mongo can do this iteration using a single tree traversal (as opposed to many for the second approach).
The second approach doesn't keep a cursor open between button clicks. This sort of thing is important for scalability on web apps between requests, but might not matter as much with a gui app (especially if the number of concurrent users is smaller).
Another big advantage of the second approach is that you can go backwards — DBCursor
doesn't have a previous()
method, so you'll need to use this approach if you ever add a Previous button.
Some other things you should probably do:
Add a layer of indirection so that your GUI event handling code and your MongoDB data access code aren't quite so highly coupled. This will save you a bunch of trouble if you move to a different database (perhaps unlikely), or add a previous button that integrates with the same query (perhaps more likely).
Remember to close the cursor when you're done with it. DBCursor
implementations leak, and need to be cleaned up with a timeout scheme if you don't explicitly close them. This is especially true if you don't completely iterate through the entire result set. This goes for the Mongo instance as well, but you'll only need a single one of those for the entire application.