chapter 23 relational databases. chapter goals to learn how to query a database with the structured...
TRANSCRIPT
Chapter 23
Relational Databases
CHAPTER GOALS
• To learn how to query a database with the Structured Query Language (SQL)
• To connect to databases with Java Database Connectivity (JDBC)
• To write database programs that insert, update, and query data in a relational database
Relational Database
• Stores information in tables
• Each table column has a name and data type
Product Table in a Relational Database
Product Table
• Each row corresponds to a product
• Column headers correspond to attributes of the product
• All items in the same column are the same data type
SQL • SQL stands for Structured Query Language
• SQL is a command language for interacting with databases
• Most relational databases follow the SQL standard
• Some SQL commands o QUERY o CREATE TABLE o INSERT o DROP TABLE
Some SQL Types and Their Corresponding Java Types
SQL to Create a Table
CREATE TABLE Products { Product_Code CHAR(11) Description CHAR(40) Unit_Price DECIMAL(10,2)
}
SQL to Add Data to a Database
• Use the INSERT command to insert rows into the table
• Issue one command for each row of the table INSERT INTO Products VALUES ('3554-0632-1', 'Hair dryer', 29.95)
Linking Tables • Replicating the same data in multiple rows has two problems
• It is wasteful to store the same information multiple times
• If the data needs to change it has to be changed in multiple places
• Instead you should distribute the data over multiple tables
Poor Design for an Invoice Table with Replicated Customer Data
Two Tables for Invoice and Customer Data
Linking Tables • In the table above, customer data are not replicated
• Customer table contains a single record for each customer
• Invoice table has no customer data
• The two tables are linked by the Customer_Number field
• The customer number is a unique identifier
Linking Tables
•Primary key is a column (or combination of columns) whose value uniquely specifies a table record
•Foreign key is a reference to a primary key in a linked table
•Foreign keys need not be unique
Implementing One-to-Many Relationships
• Each invoice may have many items
• Do not replicate columns, one for each item
• Do distribute the information in two tables, invoice and item
• Link each item to its invoice with an Invoice_Number foreign key in the item table
Poor Design for Invoice Table
with Replicated Columns
Linked Invoice and Item Tables
Sample Database
Queries
• Once the database is constructed, we can query it for information
o What are the names and addresses of all customers? o What are the names and addresses of all customers who live in California? o What are the names and addresses of all
customers who buy toasters? o What are the names and addresses of all
customers with unpaid invoices?
Simple Query
• Use the SQL SELECT statement to query a database •The query to select all data from the Customer table: SELECT * FROM customer •The outcome of a query is a view •A view is a "window" through which you can see some of the database
Selecting Columns •You may want to view only some of the columns in the table •The query to select the city and state of all customers from the Customer table:
SELECT City, State FROM Customer
Selecting Subsets• You can select rows that fit a particular criteria
• When you want to select a subset , use the WHERE clause
• The query to find all customers in California:
SELECT * FROM Customer WHERE State = 'CA' • The query to select all customers NOT in California
SELECT * FROM Customer WHERE State <> 'CA'
Result of SELECT * FROM Customer WHERE State = 'CA'
Selecting Subsets• You can combine expressions with logical connectives AND, OR, NOT SELECT *
FROM Product WHERE Price < 100 AND Description <> 'Toaster'
•You can select both row and column subsets
SELECT Name, City FROM Customer WHERE State = 'CA'
Calculations • Use the count function to find out how many customers there are in California SELECT COUNT(*) FROM Customer WHERE State = 'CA'
• The * means you want to calculate whole records
• Other functions are SUM, AVG, MAX, MIN
• These functions must access a specific column. Put the column name in parentheses SELECT AVG(Price) FROM Product
Joins• The information you want may be spread over multiple tables • Use Product.Product_Code to specify the Product_Code column in the Product table • Use Item.Product_Code to specify the Product_Code column in the Item table • To select all invoices that include a car vacuum
SELECT Item.Invoice_Number FROM Product, Item WHERE Product.Description
= 'Car vacuum' AND Product.Product_Code = Item.Product_Code
Joins • A query that involves multiple tables is a join
• When using a join, do these things
• List all tables that are involved in the query in the FROM clause
• Use the TableName.ColumnName syntax to refer to column names
• List all join conditions in the WHERE clause (TableName1.ColumnName1 = TableName2.ColumnName2)
Updating and Deleting Data
• The DELETE and UPDATE commands modify the database
• The DELETE command deletes rows
• To delete all customers in California DELETE FROM Customer WHERE State = 'CA'
Updating and Deleting Data
• The UPDATE query lets you update columns of all records that fulfill a certain condition
• To add one to the quantity of every item in invoice number 11731
UPDATE Item SET Quantity = Quantity + 1
WHERE Invoice_Number = '11731'
• Both UPDATE and DELETE return the number of rows updated or deleted
File TestDB.java 01: import java.sql.Connection;02: import java.sql.ResultSet;03: import java.sql.Statement;04: 05: /**06: Tests a database installation by creating and querying07: a sample table. Call this program as08: java -classpath driver_class_path;. TestDB database.properties09: */10: public class TestDB 11: {12: public static void main(String[] args) throws Exception13: { 14: if (args.length == 0)15: { 16: System.out.println(17: "Usage: TestDB propertiesFile");18: System.exit(0);19: }
20: else 21: SimpleDataSource.init(args[0]);22: 23: Connection conn = SimpleDataSource.getConnection();24: 25: Statement stat = conn.createStatement();26: 27: stat.execute("CREATE TABLE Test (Name CHAR(20))");28: stat.execute("INSERT INTO Test VALUES ('Romeo')");29: 30: ResultSet result = stat.executeQuery("SELECT * FROM Test");31: result.next();32: System.out.println(result.getString("Name"));33: result.close();34: 35: stat.execute("DROP TABLE Test");36: 37: stat.close();38: conn.close();39: }40: }
File SimpleDataSource.java
01: import java.sql.Connection;02: import java.sql.DriverManager;03: import java.sql.SQLException;04: import java.io.FileInputStream;05: import java.io.IOException;06: import java.util.Properties;07: 08: /**09: A simple data source for getting database connections. 10: */11: public class SimpleDataSource12: {13: /**14: Initializes the data source.15: @param fileName the name of the property file that 16: contains the database driver, url, username and password17: */18: public static void init(String fileName)
19: throws IOException, ClassNotFoundException
20: {
21: Properties props = new Properties();
22: FileInputStream in = new FileInputStream(fileName);
23: props.load(in);
24:
25: String driver = props.getProperty("jdbc.driver");
26: url = props.getProperty("jdbc.url");
27: String username = props.getProperty("jdbc.username");
28: String password = props.getProperty("jdbc.password");
29:
30: Class.forName(driver);
31: }
32:
33: /**
34: Gets a connection to the database.
35: @return the database connection
36: */
37: public static Connection getConnection() throws SQLException38: {39: return DriverManager.getConnection(url, 40: username, password);41: }42: 43: private static String url;44: private static String username;45: private static String password;46: }47: 48: 49: 50: 51: 52: 53: 54: 55: 56: 57:
File database.properties
1: jdbc.driver=COM.cloudscape.core.JDBCDriver
2: jdbc.url=jdbc:cloudscape:bigjava;create=true
3: jdbc.username=
4: jdbc.password=
Connecting to the DataBase• Use a Connection object to access a database from a Java program
• Load the database driver
• Ask the DriverManager for a connection
• When you are done, close the database connection String driver = ...; String url = ...; String username = ...; String password = ...;
Class.forName(driver); //load driver Connection conn = DeviceManager.getConnection(url, username, password);
//database commands
conn.close();
Connecting to the Database• Decouple connection management from the other database code • You can use the SimpleDataSource class to do this • Call its static init method with the name of the database configuration file • Configuration file is a text file containing four lines
o jdbc.driver = ... o jdbc.url = ... o jdbc.username = ... o jdbc.password = ...
Connecting to the Database• The init method uses the Properties class to read the
file • Properties class has a load method to read a file of
key/value pairs from a stream
Properties props = new Properties(); FileInputStream in = new FileInputStream(fileName); props.load(in);
• The getProperty method returns the value of a given key
String driver = props.getProperty("jdbc.driver");
• Now when you need a connection call Connection conn = SimpleDataSource.getConnection();
Executing SQL Statements• A Connection object can create Statement objects
Statement stat = conn.createStatement();
• The execute method of the Statement class executes a SQL statement stat.execute("CREATE TABLE Test (Name CHAR(20))") stat.execute("INSERT INTO Test VALUES ('Romeo')")
• Use executeQuery method of the Statement class to issue a query String queryStatement = "SELECT * FROM Test";ResultSet result = stat.executeQuery(queryStatement);
• The result of a SQL query is returned in a ResultSet object
Executing SQL Statements
• Use the executeUpdate method of the Statement class to execute a UPDATE statement • The method returns the number of rows effected String updateStatement = "UPDATE Item“ + "SET Quantity = Quantity + 1" + "WHERE Invoice_Number = '11731'"; int count = stat.executeUpdate(updateStatement);
Executing SQL Statements• You can also use a generic execute statement to execute queries • It returns a boolean value to indicate whether the SQL command yields a result set • If there is a result set, you can get it with the getResultSet method • If there is no result set, you can get the update count with the getUpdateCount method
String command = . . . ; boolean hasResultSet = stat.execute(command);
if (hasResultSet) {
ResultSet result = stat.getResultSet(); . . .
{ else
{ int count = stat.getUpdateCount(); . . .
}
Executing SQL Statements• You can reuse a Statement object to execute new SQL
commands • Each Statement object should only have one active ResultSet • If you need to look at multiple result sets at the same time,
create multiple Statement objects • Close the current ResultSet before issuing a new query on
the same statement result.close(); • When you are done with a Statement object, close it • That will also close the ResultSet stat.close();
Analyzing Query Results• Use the next method of the ResultSet to iterate
through the query results a row at a time
• When a result set is first returned from an executeQuery, no row data are available
• Use the next method to move to the first row
• The next method returns a boolean value indicating whether more rows of data are available
while (result.next()) {
//inspect column data from the current row }
Analyzing Query Results• To get the column values from a row, use one of the various get methods
• There are two get methods for each type of data (string, integer, double ...)
• One uses an integer parameter that indicates the column position
• Column positions start at 1 String productCode = result.getString(1);
Analyzing Query Results
• The other type of get method uses a string parameter for the column name
String productCode = result.getString("Product_Code");
• Use getInt to fetch an integer column value int quantity = result.getInt("Quantity");
• Use getDouble to fetch an double column value double unitPrice = result.getDouble("Price");
Result Set Meta Data• Result set meta data describes the properties
of the result set
• Use the ResultSetMetaData class to find out the column names in an unknown table
• You can get the meta data object form the result set
ResultSetMetaData metaData = result.getMetaData();
Result Set Meta Data
• Use getColumnCount method to get the number of columns
• Use getColumnLabel method to get the name of each column
• Use getColumnDisplaySize method to get the column width
for (int i = 1) i <= metaData.getColumnCount(); i++) { String columnName =
metaData.getColumnLabel(i); int columnSize =
metaData.getColumnDisplaySize(i); . . . }
Result Set Meta Data• The following program reads a file containing
SQL statements and executes them
• If there is a result set, the result set is printed
• Meta data from the result set is used to determine the column count and column label
• Run the program as java ExecSQL database.properties product.sql
• Or interactively as java ExecSQL database.properties
File cityzone.sql
1: CREATE TABLE CityZone (City CHAR(30), Zone CHAR(45))
2: INSERT INTO CityZone VALUES ('San Francisco', 'America/Los_Angeles')
3: INSERT INTO CityZone VALUES ('Kaoshiung', 'Asia/Taipei')
4: SELECT * FROM CityZone
File ExecSQL.java 001: import java.net.*;
002: import java.sql.*;
003: import java.io.*;
004: import java.util.*;
005:
006: class ExecSQL
007: {
008: /**
009: Executes all SQL statements in a file
010: @param args
011: <ul>
012: <li>args[0]: the property file for the database connection</li>
013: <li>args[1]: the file with SQL statements</li>
014: </ul>
015: */
016: public static void main (String args[])
017: {
018: try
019: {
020: if (args.length == 0)
021: {
022: System.out.println
023: ("Usage: ExecSQL propertyFile [statementFile]");
024: System.exit(0);
025: }
026: Connection con = getConnection(args[0]);
027: Statement stmt = con.createStatement();
028:
029:
030: String tableName = "";
031:
032: Reader reader;
033: if (args.length > 1)
034: reader = new FileReader(args[1]);
035: else
036: reader = new InputStreamReader(System.in);
037: BufferedReader in = new BufferedReader(reader);
038:
039:
040: String line;
041: while ((line = in.readLine()) != null)
042: {
043: boolean hasResultSet = stmt.execute(line);
044: if (hasResultSet)
045: showResultSet(stmt);
046: }
047:
048: in.close();
049: stmt.close();
050: con.close();
051: }
052: catch (SQLException ex)
053: {
054: System.out.println ("SQLException:");
055: while (ex != null)
056: {
057: System.out.println ("SQLState: "
058: + ex.getSQLState());
059: System.out.println ("Message: "
060: + ex.getMessage());
061: System.out.println ("Vendor: "
062: + ex.getErrorCode());
063: ex = ex.getNextException();
064: System.out.println ("");
065: }
066: }
067: catch (IOException ex)
068: {
069: System.out.println("IOException: " + ex);
070: ex.printStackTrace ();
071: }
072: }
073:
074: /**
075: Opens a database connection
076: @param fileName the name of the property file that contains the
077: database driver, url, username and password
078: @return the connection to the database
079: */
080: public static Connection getConnection(String fileName)
081: throws SQLException, IOException
082: {
083: Properties props = new Properties();
084: FileInputStream in = new FileInputStream(fileName);
085: props.load(in);
086:
087: String drivers = props.getProperty("jdbc.drivers");
088: if (drivers != null)
089: System.setProperty("jdbc.drivers", drivers);
090: String url = props.getProperty("jdbc.url");
091: String username = props.getProperty("jdbc.username");
092: String password = props.getProperty("jdbc.password");
093:
094: return
095: DriverManager.getConnection(url, username, password);
096: }
097:
098: public static void showResultSet(Statement stmt)
099: throws SQLException
100: {
101: ResultSet rs = stmt.getResultSet();
102: ResultSetMetaData rsmd = rs.getMetaData();
103: int columnCount = rsmd.getColumnCount();
104: while (rs.next())
105: {
106: for (int i = 1; i <= columnCount; i++)
107: {
108: if (i > 1) System.out.print(", ");
109: System.out.print(rs.getString(i));
110: }
111: System.out.println();
112: }
113: rs.close();
114: }
115: }
Case Study: A Bank Database• This is a reimplementation of the
ATMSimulation
• In the simulation each customer has a customer number, a PIN, a checking account and a savings account
• The data will be stored in two tables in a database
Tables for ATMSimulation
Case Study: A Bank Database• The Bank class needs to connect to the database
whenever it is asked to find a customer
• Its find method
o Connects to the database
o Selects the customer with the given account number
o Verifies the PIN
o Creates an customer object from the database information
Case Study: A Bank Databasepublic Customer find(int customerNumber, int pin)
throws SQLException{ Customer c = null; Connection conn = SimpleDataSource.getConnection();
Statement stat = conn.createStatement(); ResultSet result = stat.executeQuery("SELECT *"
+ " FROM Customer WHERE Customer_Number = "
+ customerNumber);
if (result.next() && pin == result.getInt("PIN"))
c = new Customer(customerNumber, result.getInt("Checking_Account_Number"),
result.getInt("Savings_Account_Number"));
result.close(); stat.close(); conn.close(); return c;}
Case Study: A Bank Database
• The BankAccount methods are different now
• The getBalance method gets the balance from the data base
• The withdraw and deposit methods update the database immediately
File ATMSimulation.java01: import javax.swing.JFrame;
02:
03: /**
04: A simulation of an automatic teller machine
05: */
06: public class ATMSimulation
07: {
08: public static void main(String[] args)
09: {
10: if (args.length == 0)
11: {
12: System.out.println(
13: "Usage: ATMSimulation propertiesFile");
14: System.exit(0);
15: }
16: else
17: {
18: try
19: {
20: SimpleDataSource.init(args[0]);
21: }
22: catch (Exception ex)
23: {
24: ex.printStackTrace();
25: System.exit(0);
26: }
27: }
28:
29: JFrame frame = new ATM();
30: frame.setTitle("First National Bank of Java");
31: frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
32: frame.pack();
33: frame.show();
34: }
35: }
File ATM.java 001: import java.awt.Container;
002: import java.awt.FlowLayout;
003: import java.awt.GridLayout;
004: import java.awt.event.ActionEvent;
005: import java.awt.event.ActionListener;
006: import java.io.FileInputStream;
007: import java.io.IOException;
008: import java.sql.SQLException;
009: import java.util.Properties;
010: import javax.swing.JButton;
011: import javax.swing.JFrame;
012: import javax.swing.JOptionPane;
013: import javax.swing.JPanel;
014: import javax.swing.JTextArea;
015:
016: /**
017: A frame displaying the components of an ATM
018: */
019: class ATM extends JFrame
020: {
021: /**
022: Constructs the user interface of the ATM application.
023: */
024: public ATM()
025: {
026: theBank = new Bank();
027:
028: // construct components
029:
030: pad = new KeyPad();
031:
032: display = new JTextArea(4, 20);
033:
034: aButton = new JButton(" A ");
035: aButton.addActionListener(new AButtonListener());
036:
037: bButton = new JButton(" B ");
038: bButton.addActionListener(new BButtonListener());
039:
040: cButton = new JButton(" C ");
041: cButton.addActionListener(new CButtonListener());
042:
043: // add components to content pane
044:
045: JPanel buttonPanel = new JPanel();
046: buttonPanel.setLayout(new GridLayout(3, 1));
047: buttonPanel.add(aButton);
048: buttonPanel.add(bButton);
049: buttonPanel.add(cButton);
050:
051: Container contentPane = getContentPane();
052: contentPane.setLayout(new FlowLayout());
053: contentPane.add(pad);
054: contentPane.add(display);
055: contentPane.add(buttonPanel);
056: try
057: {
058: setNextState(START_STATE);
059: }
060: catch (SQLException exception)
061: {
062: JOptionPane.showMessageDialog(null,
063: exception.getMessage());
064: }
065: }
066:
067: /**
068: Sets the current customer number to the keypad value
069: and sets state to PIN.
070: */
071: public void setCustomerNumber() throws SQLException
072: {
073: customerNumber = (int)pad.getValue();
074: setNextState(PIN_STATE);
075: }
076:
077: /**
078: Gets PIN from keypad, finds customer in bank.
079: If found sets state to ACCOUNT, else to START.
080: */
081: public void selectCustomer() throws SQLException
082: {
083: int pin = (int)pad.getValue();
084: currentCustomer
085: = theBank.find(customerNumber, pin);
086: if (currentCustomer == null)
087: setNextState(START_STATE);
088: else
089: setNextState(ACCOUNT_STATE);
090: }
091:
092: /**
093: Sets current account to checking or savings. Sets
094: state to TRANSACT
095: @param account one of CHECKING_ACCOUNT or SAVINGS_ACCOUNT
096: */
097: public void selectAccount(int account) throws SQLException
098: {
099: if (account == CHECKING_ACCOUNT)
100: currentAccount
101: = currentCustomer.getCheckingAccount();
102: else
103: currentAccount
104: = currentCustomer.getSavingsAccount();
105: setNextState(TRANSACT_STATE);
106: }
107:
108: /**
109: Withdraws amount typed in keypad from current account.
110: Sets state to ACCOUNT.
111: */
112: public void withdraw() throws SQLException
113: {
114: currentAccount.withdraw(pad.getValue());
115: setNextState(ACCOUNT_STATE);
116: }
117:
118: /**
119: Deposits amount typed in keypad to current account.
120: Sets state to ACCOUNT.
121: */
122: public void deposit() throws SQLException
123: {
124: currentAccount.deposit(pad.getValue());
125: setNextState(ACCOUNT_STATE);
126: }
127:
128: /**
129: Sets state and updates display message.
130: @param state the next state
131: */
132: public void setNextState(int newState)
133: throws SQLException
134: {
135: state = newState;
136: pad.clear();
137: if (state == START_STATE)
138: display.setText("Enter customer number\nA = OK");
139: else if (state == PIN_STATE)
140: display.setText("Enter PIN\nA = OK");
141: else if (state == ACCOUNT_STATE)
142: display.setText("Select Account\n"
143: + "A = Checking\nB = Savings\nC = Exit");
144: else if (state == TRANSACT_STATE)
145: display.setText("Balance = "
146: + currentAccount.getBalance()
147: + "\nEnter amount and select transaction\n"
148: + "A = Withdraw\nB = Deposit\nC = Cancel");
149: }
150:
151: private class AButtonListener implements ActionListener
152: {
153: public void actionPerformed(ActionEvent event)
154: {
155: try
156: {
157: if (state == START_STATE)
158: setCustomerNumber();
159: else if (state == PIN_STATE)
160: selectCustomer();
161: else if (state == ACCOUNT_STATE)
162: selectAccount(CHECKING_ACCOUNT);
163: else if (state == TRANSACT_STATE)
164: withdraw();
165: }
166: catch (SQLException exception)
167: {
168: JOptionPane.showMessageDialog(null,
169: exception.getMessage());
170: }
171: }
172: }
173:
174: private class BButtonListener implements ActionListener
175: {
176: public void actionPerformed(ActionEvent event)
177: {
178: try
179: {
180: if (state == ACCOUNT_STATE)
181: selectAccount(SAVINGS_ACCOUNT);
182: else if (state == TRANSACT_STATE)
183: deposit();
184: }
185: catch (SQLException exception)
186: {
187: JOptionPane.showMessageDialog(null,
188: exception.getMessage());
189: }
190: }
191: }
192:
193: private class CButtonListener implements ActionListener
194: {
195: public void actionPerformed(ActionEvent event)
196: {
197: try
198: {
199: if (state == ACCOUNT_STATE)
200: setNextState(START_STATE);
201: else if (state == TRANSACT_STATE)
202: setNextState(ACCOUNT_STATE);
203: }
204: catch (SQLException exception)
205: {
206: JOptionPane.showMessageDialog(null,
207: exception.getMessage());
208: }
209: }
210: }
211:
212: private int state;
213: private int customerNumber;
214: private Bank theBank;
215: private Customer currentCustomer;
216: private BankAccount currentAccount;
217:
218: private JButton aButton;
219: private JButton bButton;
220: private JButton cButton;
221:
222: private KeyPad pad;
223: private JTextArea display;
224:
225: private static final int START_STATE = 1;
226: private static final int PIN_STATE = 2;
227: private static final int ACCOUNT_STATE = 3;
228: private static final int TRANSACT_STATE = 4;
229:
230: private static final int CHECKING_ACCOUNT = 1;
231: private static final int SAVINGS_ACCOUNT = 2;
232: }
File Bank.java 01: import java.sql.Connection;
02: import java.sql.ResultSet;
03: import java.sql.Statement;
04: import java.sql.SQLException;
05:
06: /**
07: A bank consisting of multiple bank accounts.
08: */
09: public class Bank
10: {
11: /**
12: Finds a customer with a given number and PIN.
13: @param conn the database connection
14: @param customerNumber the customer number
15: @param pin the personal identification number
16: @return the matching customer, or null if none found
17: */
18: public Customer find(int customerNumber, int pin)
19: throws SQLException
20: {
21: Customer c = null;
22: Connection conn = SimpleDataSource.getConnection();
23: Statement stat = conn.createStatement();
24: ResultSet result = stat.executeQuery("SELECT *"
25: + " FROM Customer WHERE Customer_Number = "
26: + customerNumber);
27:
28: if (result.next() && pin == result.getInt("PIN"))
29: c = new Customer(customerNumber,
30: result.getInt("Checking_Account_Number"),
31: result.getInt("Savings_Account_Number"));
32: result.close();
33: stat.close();
34: conn.close();
35: return c;
36: }
37: }
File BankAccount.java 01: import java.sql.Connection;
02: import java.sql.ResultSet;
03: import java.sql.Statement;
04: import java.sql.SQLException;
05:
06: /**
07: A bank account has a balance that can be changed by
08: deposits and withdrawals.
09: */
10: public class BankAccount
11: {
12: /**
13: Constructs a bank account with a given balance.
14: @param anAccountNumber the account number
15: */
16: public BankAccount(int anAccountNumber)
17: {
18: accountNumber = anAccountNumber;
19: }
20:
21: /**
22: Deposits money into a bank account.
23: @param accountNumber the account number
24: @param amount the amount to deposit
25: */
26: public void deposit(double amount)
27: throws SQLException
28: {
29: Connection conn = SimpleDataSource.getConnection();
30: Statement stat = conn.createStatement();
31: stat.execute("UPDATE Account"
32: + " SET Balance = Balance + " + amount
33: + " WHERE Account_Number = " + accountNumber);
34: stat.close();
35: conn.close();
36: }
37:
38: /**
39: Withdraws money from a bank account.
40: @param accountNumber the account number
41: @param amount the amount to withdraw
42: */
43: public void withdraw(double amount)
44: throws SQLException
45: {
46: Connection conn = SimpleDataSource.getConnection();
47: Statement stat = conn.createStatement();
48: stat.execute("UPDATE Account"
49: + " SET Balance = Balance - " + amount
50: + " WHERE Account_Number = " + accountNumber);
51: stat.close();
52: conn.close();
53: }
54:
55: /**
56: Gets the balance of a bank account.
57: @param accountNumber the account number
58: @return the account balance
59: */
60: public double getBalance()
61: throws SQLException
62: {
63: double balance = 0;
64: Connection conn = SimpleDataSource.getConnection();
65: Statement stat = conn.createStatement();
66: ResultSet result = stat.executeQuery("SELECT Balance"
67: + " FROM Account WHERE Account_Number = "
68: + accountNumber);
69: if (result.next())
70: balance = result.getDouble(1);
71: result.close();
72: stat.close();
73: conn.close();
74: return balance;
75: }
76:
77: private int accountNumber;
78: }
File Customer.java 01: /**
02: A bank customer with a checking and savings account.
03: */
04: public class Customer
05: {
06: /**
07: Constructs a customer with a given number and PIN.
08: @param aCustomerNumber the customer number
09: @param checkingAccountNumber the checking account number
10: @param savingsAccountNumber the savings account number
11: */
12: public Customer(int aCustomerNumber,
13: int checkingAccountNumber, int savingsAccountNumber)
14: {
15: customerNumber = aCustomerNumber;
16: checkingAccount = new BankAccount(checkingAccountNumber);
17: savingsAccount = new BankAccount(savingsAccountNumber);
18: }
19:
20: /**
21: Gets the checking account of this customer.
22: @return the checking account
23: */
24: public BankAccount getCheckingAccount()
25: {
26: return checkingAccount;
27: }
28:
29: /**
30: Gets the savings account of this customer.
31: @return the checking account
32: */
33: public BankAccount getSavingsAccount()
34: {
35: return savingsAccount;
36: }
37:
38: private int customerNumber;
39: private BankAccount checkingAccount;
40: private BankAccount savingsAccount;
41: }
Transactions• A transaction is a set of database updates that should
either succeed in their entirety or not happen at all • If you transfer money from one account to another,
you want both the withdraw and deposit to happen or neither
• Use COMMIT and ROLLBACK commands to manage transactions
• To transfer money from one account to another UPDATE Account SET Balance = Balance - 1000
WHERE Account_Number = '95667-2574' UPDATE Account SET Balance = Balance + 1000 WHERE Account_Number = '82041-1196' COMMIT
Transactions
• COMMIT makes the updates permanent
• ROLLBACK command undoes all changes up to the last COMMIT
• By default, the JDBC library commits all database updates
• For transaction processing, turn off autocommit mode
Connection conn . . . ; conn.setAutoCommit(false);
Statement stat = conn.getStatement();
Transactions • Issue the updates that form the transaction then call the commit
method of the Statement class stat.executeUpdate( "UPDATE Account SET Balance = Balance -" + amount + " WHERE Account_Number = " +fromAccount);
stat.executeUpdate( “UPDATE Account SET Balance = Balance +" + amount + " WHERE Account_Number = " +toAccount); COMMIT
• If you encounter an error, call the rollback method
• This is usually done in an exception handler try { . . . } catch(SQLException exception) { stat.rollback(); }
Scrollable and Updatable Result Sets• Because the result set of a query may be very
large,the ResultSet object does not contain the entire result set
• It is a gateway to a result set stored in the database
• Each time you call next a single row is transferred into the ResultSet object
• The user may want to look back at previous row.
• Use a scrollable result set to do this
Scrollable and Updatable Result Sets
• To get a scrollable result set, you first need to create a Statement object with its type and concurrency parameters set with constants from the ResultSet classStatement stat = conn.createStatement(ResultSet.TYPE_SROLL_SENSITIVE,
ResultSet.CONCUR_READ_ONLY); ResultSet result = stat.executeQuery(. . . );
Scrollable and Updatable Result Sets
• Possible type constants o TYPE_SCROLL_SENSITIVE
Result set scrollable Result set is updated when other processes change the information in the database
o TYPE_SCROLL_INSENSITIVE Result set scrollable Result set is NOT updated when other processes change the information in the database
oTYPE_FORWARD_ONLY Result set not scrollable This is the default
• Possible concurrency constants o CONCUR_UPDATABLE You can update the database by editing the result
set o CONCUR_READ_ONLY
You can NOT update the database by editing the result set
Scrollable and Updatable Result Sets• Methods for navigating a scrollable result
set
o previous() - moves to the previous row
o first() and last() - moves to first or last row
o relative(n) - moves by n rows
o absolute(n) - moves to row number n • A scrollable result set effectively gives you
random access to the result set
Updatable Result Sets• An updatable result set makes it easy to
modify the contents of a database without using SQL commands
• Create a statement Statement stat = conn.createStatement(
ResultSet.TYPE_FORWARD_ONLY,
ResultSet.CONCUR_UPDATABLE);
• Get a result set String accountQuery = "SELECT * WHERE Account_Number = "
+ account;
ResultSet result = stat.executeQuery(accountQuery);
• Get the value from the Balance column double balance = result.getDouble("Balance");
• Update the balance in the current row, but not in the database
result.updateDouble("Balance", balance + deposit);
• Change the data in the database result.updateRow()
Updatable Result Sets
• Use an updatable result set to insert new rows
• Move the cursor to a special position called the insert row
• Update all column values
• Call updateRow method result.moveToInsertRow();
result.updateString("Account_Number","54382-5526"); result.updateString("Customer_Number","992345");
result.updateDouble("Balance",1000); result.updateRow();
Speeding Queries Up
• Indexing
• Prepared statements • Stored procedures
Indexing• If a table has a primary key , the database can
build an index file
• Index file stores information on how to access a row quickly when its primary key is given
• If the primary key is a single column, you can tag the column with the keyword PRIMARY KEY
CREATE TABLE Products (
Product_Code CHAR(10) PRIMARY KEY, Description CHAR(40),
Price DECIMAL(10,2) )
• A table index can speed up database queries
Prepared Statements
• Use a prepared statement for frequently repeated database queries and updates
• When you send a prepared statement to a database
o The database formulates a query strategy
o Saves it with the prepared statement
Prepared Statements• To prepare a query to get the bank account
information for a given account String query = "SELECT * WHERE Account_Number = ?";
PreparedStatement prepStat = conn.prepareStatement(query);
• The ? denotes variables that you fill in when you make an actual query
• Use the set method to set the variables prepStat.setString(1,accountNumber);
• When you have all variables set, call the executeQuery method
ResultSet result = prepStat.executeQuery();
Stored Procedures
• Are executed in the database kernel
• This is much faster than issuing sequences of SQL commands
• Databases don't all use the same syntax for stored procedures
• You must first download the stored procedure into the database
• You can use the CREATE PROCEDURE statement to do this
Stored Procedures
• A procedure for transferring money from one account to another
CREATE PROCEDURE transfer( :from INTEGER, :to INTEGER, :amount
DECIMAL(10,2) BEGIN
UPDATE Accounts SET Balance = Balance - :amount WHERE Account_Number = :from;
UPDATE Accounts SET Balance = Balance + :amount WHERE Account_Number = :to;
END
• In Java, download a stored procedure by executing SQL statement that defines the procedure
String definition = "CREATE PROCEDURE transfer("
+ . . . + ") BEGIN" + . . . +"END";
stat.execute(definition);
Stored Procedures
• To call the stored procedure, use the CallableStatement class
• You get a CallableStatement from the prepareCall method of the Connection class
• The { } tell the JDBC driver that the call is not standard SQL and needs to be translated into the syntax of this database
CallableStatement callStat = conn.prepareCall( "{call transfer(?,?,?)});
• You need to set values in the ? placeholders before calling the procedure
callStat.setInt(1, fromAccount); callStat.setInt(2, toAccount); callStat.setDouble(3, amount);
• Call the procedure callStat.execute();