Skip to content

Instantly share code, notes, and snippets.

@snarkbait
Last active March 31, 2021 17:11
Show Gist options
  • Save snarkbait/8231d476e8cd703a2a52 to your computer and use it in GitHub Desktop.
Save snarkbait/8231d476e8cd703a2a52 to your computer and use it in GitHub Desktop.
InventoryGUI SQlite Example for /r/javaexamples
/* Inventory class
* for /r/javaexamples
* by /u/Philboyd_Studge
*
*/
package philboyd.studge;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Random;
/**
* This is a simple 'storage' class for Inventory Items used for testing various
* algorithms for <a href="http://www.reddit.com/r/javaexamples">/r/javaexamples
* </a>
* <p>All of the methods that use SQL assume a SQLite database named 'Inventory.db'
* and a table named 'Inventory' defined in package class InventoryDBAccessor.java
* and assumes a classpath for the sqlite-JDBC driver.
*
* @author /u/Philboyd_Studge
*/
public class Inventory implements Comparable<Inventory>
{
private String partnum;
private String item;
private String description;
private int qty;
private float price;
/**
* Default Constructor
*/
public Inventory()
{
partnum = "";
item = "";
description = "";
qty = 0;
price = 0.0f;
}
/**
* Constructor from SQL ResultSet
* @param rs ResultSet for a single row from table
* @throws SQLException
*/
public Inventory(ResultSet rs) throws SQLException
{
loadFromSQL(rs);
}
/**
* Constructor
* @param partnum Primary key part number - must be unique
* @param item item name
* @param description item description
* @param qty quantity on hand
* @param price price per unit
*/
public Inventory(String partnum, String item, String description, int qty, float price)
{
this.partnum = partnum;
this.item = item;
this.description = description;
this.qty = qty;
this.price = price;
}
// getters/setters
/**
* get partnum
* @return String
*/
public String getPartnum() { return partnum; }
/**
* set partnum
* @param partnum
*/
public void setPartnum(String partnum) { this.partnum = partnum; }
/**
* get description
* @return String
*/
public String getDescription() { return description; }
/**
* set description
* @param description
*/
public void setDescription(String description) { this.description = description; }
/**
* get item
* @return String
*/
public String getItem() { return item; }
/**
* set item
* @param item
*/
public void setItem(String item) { this.item = item; }
/**
* get qty
* @return int
*/
public int getQty() { return qty; }
/**
* set qty
* @param qty
*/
public void setQty(int qty) { this.qty = qty; }
/**
* get price
* @return float
*/
public float getPrice() { return price; }
/**
* set price
* @param price
*/
public void setPrice(float price) { this.price = price; }
/**
* Calculates total value of Inventory item
* @return float
*/
public float getTotal()
{
return qty * price;
}
/**
* Over-ridden toString method
* @return String
*/
@Override
public String toString()
{
return "=====================\nPart #:" + partnum + "\tItem: "
+ item + "\n" + "Quantity: " + qty + "\n"
+ "Description: " + description + "\nPrice: " + price
+ "\n====================\n";
}
/**
* Returns a Comma-separated-value string
* @return String
*/
public String toCSVString()
{
return partnum + ", " + item + "," + description + "," + qty + "," + price;
}
/**
* Get SQL Insert statement
* creates a String to properly insert new object into the database
* @return String
*/
public String getSQLInsert()
{
return "INSERT INTO Inventory (partnum, item, description, qty, price)"
+ "VALUES ('" + partnum + "', '" + item + "', '" + description +
"', " + qty + "," + price + ");";
}
/**
* Get SQL Update statement
* creates a SQL command string to update object
* part number is not update-able
* @return String
*/
public String getSQLUpdate()
{
return "UPDATE INVENTORY "
+ "SET item = '" + item + "', description = '" + description
+ "', qty = " + qty + ", price = " + price
+ " WHERE partnum ='" + partnum + "';";
}
/**
* Attempts to take the first three letters of the first two words
* of the <code>item</code> otherwise fills with random digits
* item must be at least 3 letters long.
* @return 6-digit long String
*/
public String generatePartnum()
{
// TODO: Check uniqueness
Random rand = new Random();
String retval = "";
String[] words = item.toUpperCase().split(" ");
for (int i = 0; i < words.length; i++)
{
if (i > 1) break;
if (words[i].length() < 3)
{
retval += words[i];
for (int j = 0; j < 3 - words[i].length(); j++)
{
retval += "" + rand.nextInt(10);
}
}
else retval += words[i].substring(0,3);
}
return retval;
}
/**
* loadFromSQL()
* takes ResultSet from constructor and fills the instance variables
* @param rs ResultSet from InventoryDBAccessor
* @throws SQLException
*/
private void loadFromSQL(ResultSet rs) throws SQLException
{
partnum = rs.getString("partnum");
item = rs.getString("item");
description = rs.getString("description");
qty = rs.getInt("qty");
price = rs.getFloat("price");
}
/**
* Over-ridden compareTo() sorts by item name
* @param obj Inventory object
* @return int
*/
@Override
public int compareTo(Inventory obj)
{
return this.item.compareTo(obj.getItem());
}
}
/* InventoryController.java
* for /r/javaexamples
* by /u/Philboyd_Studge
*
*/
package philboyd.studge;
import javax.swing.UIManager;
import javax.swing.UIManager.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.util.ArrayList;
import java.sql.SQLException;
import javax.swing.JOptionPane;
import javax.swing.UnsupportedLookAndFeelException;
// run with this command or use your version of SQLite-JDBC
// java -classpath ".;sqlite-jdbc-3.8.7.jar" InventoryController
/**
* Controller for Example InventoryGUI program designed for
* <a href="http://www.reddit.com/r/javaexamples">/r/javaexamples</a>
* as an example of building a GUI to access a SQLite database.
*
* @author /u/Philboyd_Studge
*/
public class InventoryController
{
private final InventoryGUI frame = new InventoryGUI();
private InventoryDBAccessor dao;
private final ArrayList<Inventory> invList;
/** current displayed record index */
private int index;
/** flag for whether 'save' and 'cancel' commands should treat as an 'add' or 'edit' */
private boolean editNotAdd;
/**
* Default Constructor
* @throws SQLException
*/
public InventoryController() throws SQLException
{
initListeners();
dao = new InventoryDBAccessor();
invList = dao.loadInventoryFromDB();
dao.close();
index = 0;
editNotAdd = false;
frame.setVisible(true);
}
/**
* Initialize Event Listeners in InventoryGUI
*/
private void initListeners()
{
// next button
frame.addNextButtonActionListener((ActionEvent evt) ->
{
index++;
if (index >= invList.size()) index = 0;
getDataEntry();
});
// previous button
frame.addPrevButtonActionListener((ActionEvent evt) ->
{
index--;
if (index < 0) index = invList.size()-1;
getDataEntry();
});
// 'add' button
frame.addAddButtonActionListener((ActionEvent evt) ->
{
frame.setEditing(true);
Inventory temp = new Inventory();
// insert new object at head of list
index = 0;
invList.add(index, temp);
getDataEntry();
});
// edit button
frame.addEditButtonActionListener((ActionEvent evt) ->
{
editNotAdd = true;
frame.setEditing(true);
});
// delete button
frame.addDeleteButtonActionListener((ActionEvent evt) -> {
int okCancel = JOptionPane.showConfirmDialog(frame,"Are you sure? This cannot be undone!"
, "Delete record?" , JOptionPane.WARNING_MESSAGE, JOptionPane.OK_CANCEL_OPTION);
if (okCancel == JOptionPane.OK_OPTION)
{
try
{
deleteFromDB(invList.get(index).getPartnum());
}
catch (SQLException e)
{
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
JOptionPane.showMessageDialog(frame, "Database error : " + e.getMessage());
}
invList.remove(index);
index++;
if (index >= invList.size()) index = 0;
getDataEntry();
}
});
// save button
frame.addSaveButtonActionListener((ActionEvent evt) ->
{
Inventory temp = new Inventory();
boolean success = false;
try
{
success = setDataObject(temp);
}
catch (SQLException e)
{
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
JOptionPane.showMessageDialog(frame, "Database error : " + e.getMessage());
}
if (success)
{
System.out.println("Save successfull.");
frame.setEditing(false);
if (editNotAdd) editNotAdd = false;
}
});
// 'cancel' button
frame.addCancelButtonActionListener((ActionEvent evt) -> {
frame.setEditing(false);
if (editNotAdd)
{
editNotAdd = false;
}
else
{
invList.remove(index);
}
getDataEntry();
});
}
/**
* Replaces single quotes in description with two
* to prevent SQL error
* @param in String
* @return String
*/
private String replaceSingleQuotes(String in)
{
return in.replace("'","''");
}
/**
* Sets Data Object from text fields from InventoryGUI
* validates and sends to DB table
*
* @param obj Inventory object
* @return <code>false</code> if not valid
* @throws SQLException
*/
public boolean setDataObject(Inventory obj) throws SQLException
{
// TODO: validate length, special characters
String item = frame.getItem();
// validate item field
if (item.length() <= 3)
{
JOptionPane.showMessageDialog(frame, "Item name must be longer than 3 letters.");
return false;
}
obj.setItem(item);
// can be blank
obj.setDescription(replaceSingleQuotes(frame.getDescription()));
// validate Number fields
int qty;
float price;
try
{
qty = Integer.parseInt(frame.getQty());
price = Float.parseFloat(frame.getPrice().replace(",",""));
}
catch (NumberFormatException nfe)
{
JOptionPane.showMessageDialog(frame, "Incorrect data format " + nfe.getMessage());
return false;
}
obj.setQty(qty);
obj.setPrice(price);
// update or insert
if (editNotAdd)
{
obj.setPartnum(frame.getPartnum());
invList.set(index, obj);
updateDB(obj);
}
else
{
// generate new partnum
obj.setPartnum(obj.generatePartnum());
frame.setPartnum(obj.getPartnum());
invList.set(0, obj);
addToDB(obj);
}
return true;
}
/**
* Adds record to database table
* @param obj Inventory object
* @throws SQLException
*/
public void addToDB(Inventory obj) throws SQLException
{
System.out.println("Adding record to database...");
dao = new InventoryDBAccessor();
dao.addToDB(obj);
dao.close();
}
/**
* Updates record in database table
* @param obj Inventory object
* @throws SQLException
*/
public void updateDB(Inventory obj) throws SQLException
{
System.out.println("Updating record...");
dao = new InventoryDBAccessor();
dao.updateDB(obj);
dao.close();
}
/**
* Deletes record from database table
* @param partnum Unique primary key partnum
* @throws SQLException
*/
public void deleteFromDB(String partnum) throws SQLException
{
System.out.println("Deleting " + partnum + "...");
dao = new InventoryDBAccessor();
dao.deleteFromDB(partnum);
dao.close();
}
/**
* Gets data from ArrayList and fills text fields
* in InventoryGUI
*/
public void getDataEntry()
{
if (invList.get(index)==null) return;
Inventory temp = invList.get(index);
frame.setPartnum(temp.getPartnum());
frame.setItem(temp.getItem());
frame.setDescription(temp.getDescription());
frame.setQty(temp.getQty());
frame.setPrice(temp.getPrice());
frame.setTotal(temp.getTotal());
}
/**
* main
* @param args not used
* @throws SQLException
*/
public static void main(String[] args) throws SQLException
{
try
{
for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels())
{
if ("Nimbus".equals(info.getName()))
{
UIManager.setLookAndFeel(info.getClassName());
break;
}
}
}
catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException e)
{
// TODO: actual exception handling here
System.exit(0);
}
InventoryController app = new InventoryController();
app.getDataEntry();
}
}
/* Inventory DBAccessor Class
* for /r/javaexamples
* by /u/Philboyd_Studge
*
*
*/
package philboyd.studge;
import java.sql.*;
import java.util.ArrayList;
import javax.swing.JOptionPane;
class InventoryDBAccessor
{
private final String JDBC_CONNECT = "jdbc:sqlite:Inventory.db";
private final String JDBC_CLASS = "org.sqlite.JDBC";
private final String DB_OPEN_SUCCESS = "Database connection opened successfully";
private final String SQL_SELECT_ALL = "SELECT * FROM Inventory ORDER BY partnum ASC;";
private final int SQL_DATABASE_ERROR = 1;
private Connection connection;
private Statement statement;
private ResultSet resultSet;
public InventoryDBAccessor()
{
try
{
connection = getConnection();
statement = connection.createStatement();
}
catch (SQLException e )
{
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
JOptionPane.showMessageDialog(null, "Database error : " + e.getMessage());
}
}
private Connection getConnection()
{
try
{
Class.forName(JDBC_CLASS);
Connection c = DriverManager.getConnection(JDBC_CONNECT);
System.out.println(DB_OPEN_SUCCESS);
return c;
}
catch (ClassNotFoundException | SQLException e )
{
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
JOptionPane.showMessageDialog(null, "Database error : " + e.getMessage());
}
return null;
}
private void createTable()
{
try
{
String sql = "CREATE TABLE Inventory (partnum STRING (6)" +
"UNIQUE ON CONFLICT FAIL PRIMARY KEY," +
"item STRING (100), description STRING (250)," +
"qty INTEGER (6), price DOUBLE (8, 2));";
// execute the statement string
statement.executeUpdate(sql);
}
catch ( SQLException e)
{
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
JOptionPane.showMessageDialog(null, "Database error : " + e.getMessage());
}
}
public ArrayList<Inventory> loadInventoryFromDB()
{
try
{
System.out.println("Loading data from table...");
ArrayList<Inventory> invList = new ArrayList<>();
resultSet = statement.executeQuery( SQL_SELECT_ALL );
while (resultSet.next())
{
invList.add(new Inventory(resultSet));
}
System.out.println("Loaded " + invList.size() + " records.");
return invList;
}
catch ( SQLException e)
{
if (e.getErrorCode() == SQL_DATABASE_ERROR)
{
createTable();
loadInventoryFromDB();
}
else
{
System.err.println( e.getClass().getName() + ": " + e.getErrorCode() );
JOptionPane.showMessageDialog(null, "Database error : " + e.getMessage());
}
}
return null;
}
public void addToDB(Inventory item)
{
try
{
statement.executeUpdate(item.getSQLInsert());
}
catch ( SQLException e)
{
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
JOptionPane.showMessageDialog(null, "Database error : " + e.getMessage());
}
}
public void updateDB(Inventory item)
{
try
{
statement.executeUpdate(item.getSQLUpdate());
}
catch ( SQLException e)
{
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
JOptionPane.showMessageDialog(null, "Database error : " + e.getMessage());
}
}
public void deleteFromDB(String partnum)
{
try
{
statement.executeUpdate("DELETE from Inventory WHERE partnum ='" + partnum + "';");
}
catch ( SQLException e)
{
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
JOptionPane.showMessageDialog(null, "Database error : " + e.getMessage());
}
}
public void close()
{
try
{
statement.close();
connection.close();
System.out.println("Database successfully closed.");
}
catch (SQLException e)
{
System.err.println( e.getClass().getName() + ": " + e.getMessage() );
JOptionPane.showMessageDialog(null, "Database error : " + e.getMessage());
}
}
}
/* InventoryGUI.java
* for /r/javaexamples
* by /u/Philboyd_Studge
*/
package philboyd.studge;
import javax.swing.*;
import java.awt.*;
import java.awt.event.*;
public class InventoryGUI extends JFrame
{
private JPanel top;
private JLabel lblTop;
private JPanel dataView;
private GroupLayout group;
private JLabel lblPartnum;
private JTextField txtPartnum;
private JLabel lblItem;
//private JComboBox cmbItem; // add later
private JTextField txtItem;
private JLabel lblDescription;
private JTextArea txtDescription;
private JScrollPane scrollDesc;
private JLabel lblQty;
private JTextField txtQty;
private JLabel lblPrice;
private JTextField txtPrice;
private JLabel lblTotal;
private JPanel buttons;
private JButton btnNext;
private JButton btnEdit;
private JButton btnDelete;
private JButton btnAdd;
private JButton btnPrevious;
private JButton btnSave;
private JButton btnCancel;
private boolean editing = false;
public InventoryGUI()
{
super("Inventory");
super.setSize(600,400);
initComponents();
}
private void initComponents()
{
// top
top = new JPanel();
lblTop = new JLabel("Inventory Control Program - Inventory Items");
lblTop.setHorizontalAlignment(SwingConstants.CENTER);
lblTop.setFont(new Font("Tahoma", 1, 12));
top.add(lblTop);
this.add(top,BorderLayout.PAGE_START);
// center
lblPartnum = new JLabel("Part #:");
lblPartnum.setHorizontalAlignment(SwingConstants.RIGHT);
txtPartnum = new JTextField();
txtPartnum.setEditable(false);
lblItem = new JLabel("Item:");
lblItem.setHorizontalAlignment(SwingConstants.RIGHT);
txtItem = new JTextField();
txtItem.setEditable(false);
lblDescription = new JLabel("Description:");
lblDescription.setHorizontalAlignment(SwingConstants.RIGHT);
txtDescription = new JTextArea();
txtDescription.setEditable(false);
scrollDesc = new JScrollPane(txtDescription);
scrollDesc.setViewportView(txtDescription);
lblQty = new JLabel("Quantity:");
lblQty.setHorizontalAlignment(SwingConstants.RIGHT);
txtQty = new JTextField();
txtQty.setEditable(false);
lblPrice = new JLabel("Price/each:");
lblPrice.setHorizontalAlignment(SwingConstants.RIGHT);
txtPrice = new JTextField();
txtPrice.setEditable(false);
lblTotal = new JLabel("Total:");
lblTotal.setHorizontalAlignment(SwingConstants.RIGHT);
// center panel
dataView = new JPanel();
// group layout
group = new GroupLayout(dataView);
group.setAutoCreateGaps(true);
group.setAutoCreateContainerGaps(true);
dataView.setLayout(group);
// group layout HORIZONTAL
group.setHorizontalGroup(group.createSequentialGroup()
.addGroup(group.createParallelGroup(GroupLayout.Alignment.TRAILING)
.addComponent(lblPartnum)
.addComponent(lblItem)
.addComponent(lblDescription)
.addComponent(lblQty)
.addComponent(lblPrice))
.addGroup(group.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(scrollDesc)
.addGroup(group.createSequentialGroup()
.addGroup(group.createParallelGroup(GroupLayout.Alignment.LEADING)
.addComponent(txtPartnum, GroupLayout.PREFERRED_SIZE, 80, GroupLayout.PREFERRED_SIZE)
.addComponent(txtItem, GroupLayout.PREFERRED_SIZE, 150, GroupLayout.PREFERRED_SIZE)
.addComponent(txtQty, GroupLayout.PREFERRED_SIZE, 100, GroupLayout.PREFERRED_SIZE)
.addGroup(group.createSequentialGroup()
.addComponent(txtPrice, GroupLayout.PREFERRED_SIZE, 100, GroupLayout.PREFERRED_SIZE)
.addComponent(lblTotal)))))
);
// group layout VERTICAL
group.setVerticalGroup(group.createSequentialGroup()
.addGroup(group.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(lblPartnum)
.addComponent(txtPartnum))
.addGroup(group.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(lblItem)
.addComponent(txtItem))
.addGroup(group.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(lblDescription)
.addComponent(scrollDesc))
.addGroup(group.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(lblQty)
.addComponent(txtQty))
.addGroup(group.createParallelGroup(GroupLayout.Alignment.BASELINE)
.addComponent(lblPrice)
.addComponent(txtPrice)
.addComponent(lblTotal))
);
this.add(dataView, BorderLayout.CENTER);
// bottom
buttons = new JPanel();
btnNext = new JButton("Next");
btnPrevious = new JButton("Previous");
btnAdd = new JButton("Add");
btnEdit = new JButton("Edit");
btnDelete = new JButton("Delete");
btnSave = new JButton("Save");
btnSave.setEnabled(false);
btnCancel = new JButton("Cancel");
btnCancel.setEnabled(false);
buttons.add(btnNext);
buttons.add(btnPrevious);
buttons.add(btnAdd);
buttons.add(btnEdit);
buttons.add(btnDelete);
buttons.add(btnSave);
buttons.add(btnCancel);
this.add(buttons,BorderLayout.PAGE_END);
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
// methods for Controller to create listeners
public void addNextButtonActionListener(ActionListener listener)
{
btnNext.addActionListener(listener);
}
public void addPrevButtonActionListener(ActionListener listener)
{
btnPrevious.addActionListener(listener);
}
public void addAddButtonActionListener(ActionListener listener)
{
btnAdd.addActionListener(listener);
}
public void addSaveButtonActionListener(ActionListener listener)
{
btnSave.addActionListener(listener);
}
public void addCancelButtonActionListener(ActionListener listener)
{
btnCancel.addActionListener(listener);
}
public void addEditButtonActionListener(ActionListener listener)
{
btnEdit.addActionListener(listener);
}
public void addDeleteButtonActionListener(ActionListener listener)
{
btnDelete.addActionListener(listener);
}
// methods for Controller class to set/get text fields
public void setPartnum(String partnum)
{
txtPartnum.setText(partnum);
}
public String getPartnum() { return txtPartnum.getText(); }
public void setItem(String item)
{
txtItem.setText(item);
}
public String getItem() { return txtItem.getText(); }
public void setDescription(String desc)
{
txtDescription.setText(desc);
}
public String getDescription() { return txtDescription.getText(); }
public void setQty(int qty)
{
txtQty.setText("" + qty);
}
public String getQty() { return txtQty.getText(); }
public void setPrice(float price)
{
txtPrice.setText(String.format("%,.2f", price));
}
public String getPrice() { return txtPrice.getText(); }
public void setTotal(float total)
{
lblTotal.setText(String.format("Total: %,.2f",total));
}
public void setEditing(boolean b)
{
editing = b;
setButtonsForEdit();
}
public boolean isEditing() { return editing; }
public void setButtonsForEdit()
{
if (editing)
{
btnNext.setEnabled(false);
btnPrevious.setEnabled(false);
btnAdd.setEnabled(false);
btnEdit.setEnabled(false);
btnDelete.setEnabled(false);
btnSave.setEnabled(true);
btnCancel.setEnabled(true);
txtItem.setEditable(true);
txtDescription.setEditable(true);
txtQty.setEditable(true);
txtPrice.setEditable(true);
txtItem.requestFocusInWindow();
txtItem.selectAll();
}
else
{
btnNext.setEnabled(true);
btnPrevious.setEnabled(true);
btnAdd.setEnabled(true);
btnEdit.setEnabled(true);
btnDelete.setEnabled(true);
btnSave.setEnabled(false);
btnCancel.setEnabled(false);
txtItem.setEditable(false);
txtDescription.setEditable(false);
txtQty.setEditable(false);
txtPrice.setEditable(false);
btnNext.requestFocusInWindow();
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment