Command Line argument Parsing in Java

Apache commons-cli can be used for parsing command line arguments that might be supplied to the java program. The program below is a sample-program that uses commons-cli’s CommandLineParser utility to parse the arguments.

import org.apache.commons.cli.CommandLine;
import org.apache.commons.cli.CommandLineParser;
import org.apache.commons.cli.BasicParser;
import org.apache.commons.cli.Option;
import org.apache.commons.cli.Options;

public class CommandParserUtility {

	public static void main(String[] args) {
		CommandLineParser parser =  new BasicParser();
		Option helpOption = new Option("help",false," Program Help.");
		Option authorOption = new Option("author",false," Program Author.");
		Option userOption = new Option("user",true," Wish User.");
		Options options = new Options();
		options.addOption(authorOption);
		options.addOption(userOption);
		options.addOption(helpOption);
		CommandLine cli = parser.parse(options, args);
		if(cli.hasOption(helpOption.getOpt()))
			System.out.println("CommandParserUtility Usage - \n \nCommandParserUtility -user <User Name> [-author (Show Author Name)][-help|h (Show Usage Help)]\n");
		else if(cli.hasOption(userOption.getOpt()))
			System.out.println("Hey "+cli.getOptionValue(userOption.getOpt())+". You are running the CommandParserUtility.");
		else if(cli.hasOption(authorOption.getOpt()))
			System.out.println("CommandParserUtility authored by Vageesh.");
		else
			System.out.println("CommandParserUtility Usage - \n \nCommandParserUtility -user <User Name> [-author (Show Author Name)][-help|h (Show Usage Help)]\n");
		System.exit(0);
	}

}

In the above program, the command line options available are help, user and author.

Option userOption = new Option("user",true," Wish User.");
Option helpOption = new Option("help",false," Program Help.");

Specifies that the argument is user and the Boolean value true specifies that the option takes value when used as argument and in case of help/author, the Boolean value of false indicates that it does not take any arguments with it.

cli.hasOption(helpOption.getOpt())

This code returns a Boolean indicating whether one of the specified options was passed as argument to the program. If you need a list of all arguments passed to the program that is not from the specified argument list, then the following code can be used which returns a list of all unprocessed arguments

ArrayList<Object> res = cli.getArgList();

Finally the values specified for a particular argument can be accessed using the command –

cli.getOptionValue (“user”);

Data sharing between jobs – Stateful Jobs using Quartz Scheduler

Note: Updated the code to reflect quartz version 2.2.x

The data manipulated by different jobs can be persisted by using the interface StatefulJob. The sample program below shows how stateful jobs work in quartz scheduler.

The updated PrintStatefulJob.java-

package ramstore;

import org.quartz.*;

@PersistJobDataAfterExecution
public class PrintStatefulJob implements Job{

	@Override
	public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
		JobDataMap jobDataMap = jobExecutionContext.getJobDetail().getJobDataMap();
		Integer count = jobDataMap.getInt("count");
		count++;
		System.out.println("Printing count : "+count);
		jobDataMap.put("count",count);
	}

}

JobListener interface can be used to listen to job execution so as to have customized action on job execution. For example, the program below prints out the incremented count using the PrintJobListener class which implements the JobListener interface-

PrintRamScheduler.java-

package ramstore;

import org.quartz.*;
import org.quartz.impl.StdSchedulerFactory;
import org.quartz.impl.matchers.KeyMatcher;

import static org.quartz.JobBuilder.newJob;
import static org.quartz.SimpleScheduleBuilder.simpleSchedule;

public class PrintRamScheduler {

	private Scheduler scheduler;
	public PrintRamScheduler() {
		try {
			//create scheduler factory
			scheduler = new StdSchedulerFactory().getScheduler();
			scheduler.start();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	public void schedule() throws SchedulerException {
		JobKey jobKey = JobKey.jobKey("printjob", "printjobgroup");
		//create a job
		JobDetail job = newJob(PrintStatefulJob.class).withIdentity(jobKey).build();
		//put a count variable that we can keep incrementing
		job.getJobDataMap().put("count",0);
		//create a trigger
		Trigger trigger = TriggerBuilder.newTrigger().withIdentity("printTrigger", "printtriggergroup")
				.startNow().withSchedule(simpleSchedule().withIntervalInMilliseconds(100l).repeatForever()).build();
		//schedule the job
		scheduler.scheduleJob(job, trigger);
		//add a listner only for specific job - It is also possible to add a generic listener for all jobs
		scheduler.getListenerManager().addJobListener(new PrintJobListener(), KeyMatcher.keyEquals(jobKey));
	}

	public void stopScheduler() throws SchedulerException {
		//scheduler shutdown
		scheduler.shutdown();
	}

	public static void main(String[] args) {
		PrintRamScheduler printScheduler = new PrintRamScheduler();
		try {
			printScheduler.schedule();
			Thread.sleep(10000l);
			printScheduler.stopScheduler();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

}

PrintJobListener.java-

package ramstore;

import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;
import org.quartz.JobListener;

public class PrintJobListener implements JobListener {
	@Override
	public String getName() {
		return "PRINT_JOB_LISTENER";
	}

	@Override
	public void jobToBeExecuted(JobExecutionContext jobExecutionContext) {

	}

	@Override
	public void jobExecutionVetoed(JobExecutionContext jobExecutionContext) {
		//called when a job is vetoed via the TriggerListener#vetoJobExecution
		//details - http://www.quartz-scheduler.org/api/2.2.1/org/quartz/TriggerListener.html#vetoJobExecution(org.quartz.Trigger,%20org.quartz.JobExecutionContext)
	}

	@Override
	public void jobWasExecuted(JobExecutionContext jobExecutionContext, JobExecutionException e) {
		System.out.println("Job was executed for count "
				+String.valueOf(jobExecutionContext.getMergedJobDataMap().get("count")));
	}
}

Code @ Git – https://github.com/vageeshhoskere/blog

Using JobDataMap in Quartz Scheduler

The last post was an introduction to scheduling Jobs using Quartz Scheduler. This post is a continuation to it which shells out some more information on using Quartz Scheduler.

At the end of the last post I mentioned that for every run of the scheduled job, a new instance of the job class is created and run, thus making data persistence across different runs of jobs useless with local variables, also inability in passing arguments.

This problem can be overcome by using the JobDataMap. A jobDataMap is actually a MAP which contains key-value pairs of data that can be shared across scheduled jobs in configuring/tracking the job. Below is a sample application which demos the usage of JobDataMap which shares author name.

AlarmJob.java –

import java.util.Date;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobDataMap;
import org.quartz.JobExecutionException;

public class AlarmJob implements Job {

	@Override
	public void execute(JobExecutionContext jeContext) throws JobExecutionException {
		JobDataMap jdMap = jeContext.getJobDetail().getJobDataMap();
		String auth_name = jdMap.get("auth_name").toString();
		System.out.println("WAKE UP CALL "+new Date()+" by "+auth_name);
	}

}

AlarmSchedule.java –

import java.util.Date;

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.TriggerUtils;
import org.quartz.impl.StdSchedulerFactory;

public class AlarmSchedule {

	public AlarmSchedule(){
		try{
			SchedulerFactory schdFact = new StdSchedulerFactory();
			Scheduler schd = schdFact.getScheduler();
			schd.start();
			JobDetail jd = new JobDetail("alarmjob", Scheduler.DEFAULT_GROUP, AlarmJob.class);
			jd.getJobDataMap().put("auth_name", "vageesh");
			Trigger t = TriggerUtils.makeDailyTrigger("alarmtrigger", 06, 00);
			t.setStartTime(new Date());
			schd.scheduleJob(jd, t);
		}
		catch(SchedulerException e){
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		new AlarmSchedule();
	}

}

 

Please note that in the above program the jobDataMap is associated with the JobDetail. However, a jobDataMap can also be associated with a trigger and not just jobDetail or even both trigger and jobDetail.

The statement

JobDataMap jdMap = jeContext.getJobDetail().getJobDataMap();

Gets the JobDataMap associated with a jobDetail.
In order to access the JobDataMap associated with a Trigger, the following statement can be used –

JobDataMap jdMap = jeContext.getTrigger().getJobDataMap();

In case jobDataMap associated with both jobDetail and Trigger are used, the jobDataMap accessible in the job instance is actually a merge of the two maps with the values of the latter overriding that of the former. The code below describes the usage in such cases –

JobDataMap jdMap = jeContext.getMergedJobDataMap();

One important point to note here is that any changes that are made to the Map during the job execution are not reflected outside the job. That means any changes to the map made during the job execution is limited to that particular execution of the job and is immediately lost once the execution is complete.

Job Scheduling using Quartz Scheduler

Quartz Scheduler can be used to schedule jobs running periodically. The sample application below uses the Quartz Scheduler to implement an Alarm at 6 AM every morning.

This program includes two files – AlarmJob.java which specifies the job that is to be scheduled and AlarmSchedule.java which includes the scheduler to schedule the job.

AlarmJob.java –

import java.util.Date;

import org.quartz.Job;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

public class AlarmJob implements Job {

	@Override
	public void execute(JobExecutionContext arg0) throws JobExecutionException {
		System.out.println("WAKE UP CALL "+new Date());
	}

}

AlarmSchedule.java –

import java.util.Date;

import org.quartz.JobDetail;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.quartz.SchedulerFactory;
import org.quartz.Trigger;
import org.quartz.TriggerUtils;
import org.quartz.impl.StdSchedulerFactory;

public class AlarmSchedule {

	public AlarmSchedule(){
		try{
			SchedulerFactory schdFact = new StdSchedulerFactory();
			Scheduler schd = schdFact.getScheduler();
			schd.start();
			JobDetail jd = new JobDetail("alarmjob", Scheduler.DEFAULT_GROUP, AlarmJob.class);
			Trigger t = TriggerUtils.makeDailyTrigger("alarmtrigger", 06, 00);
			t.setStartTime(new Date());
			schd.scheduleJob(jd, t);
		}
		catch(SchedulerException e){
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		new AlarmSchedule();
	}

}

 

Please Note:When scheduling the job, the scheduler creates an instance of the job class every time the job is triggered. Therefore using local variables to store values in the Job class is of no use as all the variables are reset during every trigger.

Java Resource Bundle

The resource-bundles in java are useful when dealing with localization of resources that are used in a java program. They contain locale specific resource objects which can be used depending on the target locale. This helps the code to be largely locale independent and also be easily scalable in terms of handling different locales without any changes in the code.

A resource bundle uses a key-value pair where the key is used to localize the resource. For example consider the key-value pair of (SUBMIT,submit) in English as MyResource_en and (SUBMIT, presenter) in French as MyResource_FR. Now, while using the resource bundle, the method getBundle(path-to-resource-bundle, locale) is used to specify the resource bundle that has to be used. So when the localized text for submit is needed, the key SUBMIT can be used and depending on the locale, corresponding string represents the key. That is, SUBMIT is represented as submit or presenter based on the specified locale (English or French respectively) and the getBundle method is used as getBundle(“MyResource”, Locale.ENGLISH) or getBundle(“MyResource”, Locale.FRANCE) respectively.

To create a concrete Resource Bundle one must subclass the ResourceBundle class and implement the handleGetObjects and getKeys method. Java also provides ListResourceBundle and PropertyResourceBundle which is a subclass of ResourceBundle class.

ListResourceBundle:This method uses a java file which extends the ListResourceBundle class. The program given below uses ListResourceBundle.

import java.util.ListResourceBundle;
 
public class MyResource extends ListResourceBundle {
 
        	@Override
        	protected Object[][] getContents() {
                    	return contents;
        	}
        	
        	//Resource Bundle contents in the form of {key, value} pair
static Object[][] contents = {
                    	{ "HELLO_TEXT",
        	  	"Hello everyone!" },
        		{ "GOODBYE_TEXT",
        	  	"Goodbye everyone!" }
        	};
 
}

The program below shows the method for accessing the Resource Bundle MyResource –

import java.util.Locale;
import java.util.ResourceBundle;
 
public class ResourceTest {
 
        	public static void main(String[] args) {
                    	Locale locale = Locale.getDefault();
                    	ResourceBundle res = ResourceBundle.getBundle("MyResource",locale);
                    	System.out.println(res.getString("HELLO_TEXT"));
        	}
 
}
 

Please not that as this is a java file, normal java statements can be used as value for a particular key. For example, {“TEMP_CLASS”, new Temp()} is perfectly acceptable.

PropertyResourceBundle:This method does not use any java file. Instead, it uses a PROPERTIES file (for ex, MyResource.properties). The properties file can contain only text key-value pair and not any java statements. The program given below uses PropertyResourceBundle.

The file MyResource.properties –
HELLO_TEXT=Hello everyone!
GOODBYE_TEXT=Goodbye everyone!

Reading this resource bundle is the same as that for ListResourceBundle.

One point to note in both the cases is that the resource bundle file must be in the classpath of the running program, which if not, results in MissingResourceException.

SAXParser – Parsing XML file

SAX Parser is a part of the JAVA API for XML Processing (JAXP) and is used to parse XML files. SAX parser reads the XML file and sequentially passes it to the application. No information is stored in memory while using the SAX Parser to parse XML.
The first step while using SAXParser is to create an instance of the parser and then provide path to the file that is to be parsed.
SAXParser works on event based model which can be handled using a handler class. A few methods that are available are –
startElement: which is a function that is executed on the start of element parsing event
endElement: which is a function that is executed on the end of element parsing event
characters: which is a Function called to handle the character data inside an element
endDocument: which is a Function that is executed at the end of xml document parsing

Below is a sample program that uses SAXParser to read the xml file –

<?xml version="1.0" encoding="UTF-8"?>
<bookbank>
	<book type="fiction" available="yes">
		<name>Book1</name>
		<author>Author1</author>
		<price>Rs.100</price>
	</book>
	<book type="novel" available="no">
		<name>Book2</name>
		<author>Author2</author>
		<price>Rs.200</price>
	</book>
	<book type="biography" available="yes">
		<name>Book3</name>
		<author>Author3</author>
		<price>Rs.300</price>
	</book>
</bookbank>
import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;

public class SAXParserSample extends DefaultHandler {

	private String temp=null;
	private Book book;
	private List<Book> bookBank = new ArrayList<Book>();
	
	public SAXParserSample() throws SAXException,IOException,ParserConfigurationException{
		SAXParserFactory saxParserInstance = SAXParserFactory.newInstance();
		SAXParser saxParser = saxParserInstance.newSAXParser();
		//parse xml books.xml
		saxParser.parse("books.xml", this);
	}
	
	//function which gets called to handle the start of element parsing event
	public void startElement(String uri, String localName, String qName,
		Attributes attributes) throws SAXException {
		//reset temp value
		temp = null;
		if(qName.equalsIgnoreCase("book")) {
			//create a new instance of book
			book = new Book();
			//set value of attributes type and available
			book.setType(attributes.getValue("type"));
			book.setAvailability(attributes.getValue("available"));
		}
	}

	//Function called to handle the character data inside an element
	public void characters(char[] ch, int start, int length) throws SAXException {
		temp = new String(ch,start,length);
	}

	//Function that gets executed to handle the end of an element parsing event
	public void endElement(String uri, String localName,
		String qName) throws SAXException {

		if(qName.equalsIgnoreCase("book")) {
			//add book object to the bookbank list
			bookBank.add(book);
		}else if (qName.equalsIgnoreCase("name")) {
			//element name read complete - add it to object
			book.setName(temp);
		}else if (qName.equalsIgnoreCase("author")) {
			//element author read complete - add it to object
			book.setAuthor(temp);
		}else if (qName.equalsIgnoreCase("price")) {
			//element price read complete - add it to object
			book.setPrice(temp);
		}

	}
	
	public void endDocument(){
		//end of xml reached - print the contents of the entire list
		printBookBank();
	}
	
	public void printBookBank(){
		Iterator<Book> itr = bookBank.iterator();
		while(itr.hasNext()){
			Book b = itr.next();
			System.out.println(b.getBookDetail());
		}
	}
	
	public static void main(String[] args) {
		try{
			new SAXParserSample();
		}
		catch(IOException e){
			e.printStackTrace();
		}
		catch(ParserConfigurationException e){
			e.printStackTrace();
		}
		catch(SAXException e){
			e.printStackTrace();
		}
	}

}

class Book{
	private String type=null;
	private String available=null;
	private String name=null;
	private String author=null;
	private String price=null;
	
	public String getType(){
		return type;
	}
	
	public String getAuthor(){
		return author;
	}
	
	public String getPrice(){
		return price;
	}
	
	public String getAvailability(){
		return available;
	}
	
	public String getName(){
		return name;
	}
	
	public void setName(String name){
		this.name = name;
	}
	
	public void setType(String type){
		this.type = type;
	}
	
	public void setAuthor(String author){
		this.author = author;
	}
	
	public void setPrice(String price){
		this.price = price;
	}
	
	public void setAvailability(String available){
		this.available = available;
	}
	
	public String getBookDetail(){
		return "Name: "+name+"; Author:"+author+"; Price"+price+"; type:"+type+"; availability:"+available;
	}
	
}

On executing the above program you get the following output –
Name: Book1; Author:Author1; PriceRs.100; type:fiction; availability:yes
Name: Book2; Author:Author2; PriceRs.200; type:novel; availability:no
Name: Book3; Author:Author3; PriceRs.300; type:biography; availability:yes

Custom Annotations in Java

The previous post was an introduction to Annotations. This post is on creating custom Annotations. The following code creates an Annotation for description of methods, classes etc


import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target ({ElementType.TYPE,ElementType.METHOD,ElementType.PARAMETER,ElementType.CONSTRUCTOR})

public @interface MyAnnotation {
	String value();
}

public class AnnotationTest {

	@MyAnnotation("Constructor for AnnotationTest class")
	public AnnotationTest(){
		
	}
	
	public static void main(String[] args) {
		new AnnotationTest();
	}

}

In the above example, the Interface MyAnnotation, as the name implies is the interface dealing with the description annotation. The first element @Documented signifies that the annotations go to the javadoc

The second option @Retention is used to specify the retention policy for the defined annotations. The three available retention policies are

  1. RetentionPolicy.CLASS – This ensures that the compiler retains the annotations in the class files but discards it during the runtime. (The annotations are not available during runtime).

  2. RetentionPolicy.RUNTIME – The annotations are recorded by the compiler in the class files and the VM retains it during runtime.

  3. RetentionPolicy.SOURCE – The annotations remain only in the source files and not retained either in class files or runtime

The next option @Target is used to specify the target for the current annotation. In the above example the targets are defined as
ElementType.TYPE,ElementType.METHOD,ElementType.PARAMETER,ElementType.CONSTRUCTOR
This means that MyAnnotation can be used for constructors, methods, parameters, class, interface, enums etc.. More details on Target is available here

Finally the interface for MyAnnotation. It contains elements needed for runtime processing. In the above example it contains the element value which is used by the class AnnotationTest where the description string for the constructor that is passed is the value for MyAnnotation

Java Annotations

Annotations are a part of JAVA 5 that provide metadata information related to classes, methods, parameters, Interfaces, Enums etc Annotations can also be included in the Javadoc. Besides, they can be used to provide information to the compiler so as to suppress warnings and also can be used for support tools to examine the code.

Annotations are used with @ symbol in the java source files. There are three predefined Annotations used by the java compiler. They are –

  • @Deprecated

    This annotation indicates that the method must no longer be used. A javadoc can be provided by including a comment block with this annotation which provides the reason for deprecation and the other option that must be used instead.

  • For example –

    /**
     * @deprecated
     * This method is old and deprecated as it does not release memory. Please use newMethod
     * instead of this
    */
    @Deprecated
    public void oldMethod() throws InterruptedException{
    	
    }
    
  • @Override – This annotation informs the compiler that the element is overriding a similar element defined by the super class.
  • For Example –

    
    public class Vehicle{
    	public void horn(){
    		System.out.println("Generic Vehicle blaring its generic horn");
    	}
    }
    
    public class Car extends Vehicle {
    	//Overriding method of the super class Vehicle
    	@Override
    	public void horn(){
    		System.out.println("A Car blaring its horn");
    	}
    	
    }
    
  • @SuppressWarnings

    This annotation indicates to the compiler to suppress any warnings that might have been raised if the method is executed.

  • For example, the compiler generates a warning if it encounters a deprecated element. This annotation is useful in such cases so that such warnings are suppressed.

    
    @SuppressWarnings("deprecation")
    public void oldMethod() throws InterruptedException{
    	
    }
    

Prepared Statements in Java

Prepared statements are derivatives of the Statement class but with one distinctive advantage over a Statement. When a Prepared Statement is used, a SQL statement is sent to the DBMS as soon as it is created and is pre-compiled. So, during runtime, the SQL statement need not be compiled again. This is extremely helpful in cases where a SQL Statement needs to be run many times. Using a Prepared Statement in such a case, eliminates the step of repeated compilation as in case of using a normal SQL Statement.

Prepared statements also allow you to supply parameters during runtime. The example below shows the usage of Prepared Statement to update a book name for the example bookbank database from the previous post.

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;

public class PreparedStatementTest {

	private PreparedStatement updateBookName = null;
	private Connection conn = null;
	private BufferedReader br = null;
	
	public PreparedStatementTest(){
		try{
			br = new BufferedReader(new InputStreamReader(System.in));
			Class.forName("com.mysql.jdbc.Driver").newInstance();
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bookbank","root","");
			conn.setAutoCommit(true);
			//string representation of the prepared statement. The two question marks denote that the values are set during runtime.
			String updateBookNameString = "UPDATE bookbank.books set bookname = ? where bookid= ? ";
			//use the string to generate a prepared statement
			updateBookName = conn.prepareStatement(updateBookNameString);
			System.out.println("Enter Book Id to be updated");
			int bookid = Integer.parseInt(br.readLine());
			System.out.println("Enter the new book name");
			String bookname = br.readLine();
			//set the parameters for the prepared statement
			updateBookName.setString(1, bookname);
			updateBookName.setInt(2,bookid);
			//execute the prepared statement
			updateBookName.executeUpdate();
			updateBookName.close();
			conn.close();
		}
		catch(Exception e){
			e.printStackTrace();
		}
	}
	
	public static void main(String[] args) {
		new PreparedStatementTest();
	}
}

Connecting to MYSQL database using JDBC Driver

Java programs can access MYSQL database using the java-mysql connector. Below is a sample program which implements a book bank.

import java.sql.*;
import java.io.*;

public class BookBank{

	private Connection conn=null;
	private Statement stmt=null;
	private ResultSet rs=null;
	private BufferedReader br= null;
	private int bookid = 0;

	public BookBank(){
		try{
			br = new BufferedReader(new InputStreamReader(System.in));
			//create a new Driver instance
			Class.forName("com.mysql.jdbc.Driver").newInstance();
			//establish connection to the database
			conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/bookbank","root","");
			//set auto commit ResultSet changes to the database to either true or false - if AutoCommit is false then we need to explicitly call commit if the changes have to be saved to the database
			conn.setAutoCommit(true);
			//create a Statement instance which is scrollable and updatable
			stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);
			stmt.executeUpdate("create table if not exists books (bookid int(10),bookname varchar(100))");
			rs = stmt.executeQuery("select * from books");
			while(true){
				int num,i=0;
				rs.beforeFirst();
				while(rs.next()){
				//print the content of the table books onto the console
				System.out.println("Book ID: "+rs.getInt(1)+" Book Name: "+rs.getString(2));
				System.out.println("=======================================");
			}
			System.out.println("Enter choice:");
			System.out.println("1. Insert Record");
			System.out.println("2. Update Record");
			System.out.println("3. Delete Record");
			System.out.println("4. Exit");
			int choice = Integer.parseInt(br.readLine());
			switch(choice){
				case 1:
					//insert a record to the table
					//bookid is not auto-increment, hence, we need to get the last bookid and then increment it manually. This step need not be done if the auto-increment is enabled
					//go to last row in the table
					rs.last();
					//get the last bookid
					bookid = rs.getInt(1);
					//move to temp insertrow
					rs.moveToInsertRow();
					//update the bookid in the temp row
					rs.updateInt(1,++bookid);
					System.out.println("Enter Book Name:");
					//update the book name in the temp row
					rs.updateString(2,br.readLine());
					//insert the temp row to the table
					rs.insertRow();
					//move to the current row
					rs.moveToCurrentRow();
					break;
				
				case 2:
					//get the bookid which is to be updated
					System.out.println("Enter the book id to be updated");
					bookid = Integer.parseInt(br.readLine());
					rs.last();
					//get the number of rows
					num = rs.getRow();
					rs.beforeFirst();
					i=1;
					//traverse through the rows and break at the specified bookid
					while(rs.next()){
						if(rs.getInt(1)==bookid){
							break;
						}
						else{
							i++;
						}
					}
					if(i==++num){
						System.out.println("Invalid Book-ID");
					}
					else{
						//move to the specified row number
						rs.absolute(i);
						System.out.println("Enter the updated Name: ");
						//update the book name
						rs.updateString(2,br.readLine());
						//update the row in the ResultSet
						rs.updateRow();
					}
					break;
				
				case 3:
					//get the bookid that is to be deleted
					System.out.println("Enter the book id to be deleted");
					bookid = Integer.parseInt(br.readLine());
					rs.last();
					num = rs.getRow();
					rs.beforeFirst();
					i=1;
					while(rs.next()){
						if(rs.getInt(1)==bookid){
							break;
						}
						else{
							i++;
						}
					}
					if(i==++num){
						System.out.println("Invalid Book-ID");
					}
					else{
						//move to the row which is to be deleted
						rs.absolute(i);
						//delete the current row from the ResultSet
						rs.deleteRow();
					}
					break;
				
				case 4:
					System.exit(100);
					break;
				}
		
			}
	
		}
		catch(Exception e){
			System.out.println(e.toString());
		}
	}

	public static void main(String args[]){
		new BookBank();
	}
}

The above program uses MYSQL database called bookbank and a mysql-jdbc driver to connect to the mysql database. In order to execute this program you need to download the jdbc driver and then place the driver jar in the path to your java file. In order to use the driver class, we need to create an instance of the driver in our program.

Class.forName("com.mysql.jdbc.Driver").newInstance();

The above piece of code creates a new instance of the driver class. Once the instance is created, we have to establish a new connection.

Connection conn = DriverManager.getConnection("jdbc:mysql://database_host:3306/database_name","database_username","database_password");

Please note: If the database is in your local system, you can use localhost in place of the database hostname.
After establishing connection with the database, we can use the sql Statement to query the database and ResultSet to manage the query results.

Statement stmt = conn.createStatement(ResultSet.TYPE_SCROLL_INSENSITIVE,ResultSet.CONCUR_UPDATABLE);

This will make the SQL ResultSet resulted from the queries scrollable and updatable. By default, the ResultSet can be traversed only in one direction from top to bottom and is not updatable. Specifying a statement as TYPE_SCROLL_INSENSITIVE allows bi-directional ResultSet traversal and CONCUR_UPDATABLE allows us to update the ResultSet which can then be committed to the database.

Please Note that the above sample program does all the updating and deletion on the result set itself.