Download Java Database Connectivity (JDBC)

Survey
yes no Was this document useful for you?
   Thank you for your participation!

* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project

Document related concepts

Concurrency control wikipedia , lookup

Entity–attribute–value model wikipedia , lookup

Extensible Storage Engine wikipedia , lookup

Microsoft Access wikipedia , lookup

Database wikipedia , lookup

Oracle Database wikipedia , lookup

Microsoft Jet Database Engine wikipedia , lookup

Microsoft SQL Server wikipedia , lookup

Clusterpoint wikipedia , lookup

Relational model wikipedia , lookup

SQL wikipedia , lookup

Database model wikipedia , lookup

PL/SQL wikipedia , lookup

Open Database Connectivity wikipedia , lookup

Transcript
Workshop III: Java Database Connectivity (JDBC)
Author: Gergely Mátéfi
WORKSHOP III: JAVA DATABASE CONNECTIVITY (JDBC)................................................. 29
AUTHOR: GERGELY MÁTÉFI ............................................................................................................... 29
INTRODUCTION ................................................................................................................................ 29
DATABASE MANAGEMENT IN A CLIENT-SERVER ARCHITECTURE ............................... 30
THE JDBC 1.2 API .............................................................................................................................. 31
THE BASIC FRAMEWORK FOR DATA ACCESS ....................................................................................... 31
MANAGING DATABASE CONNECTIONS ................................................................................................ 32
EXECUTING SQL STATEMENTS ........................................................................................................... 32
MANAGING RESULT TABLES ............................................................................................................... 34
ERROR HANDLING .............................................................................................................................. 35
TRANSACTION HANDLING ................................................................................................................... 36
DATABASE INFORMATION................................................................................................................... 36
THE ORACLE JDBC DRIVERS ....................................................................................................... 36
A DEMO WEBSTART APPLICATION ........................................................................................... 37
JAVA WEB START TECHNOLOGY ........................................................................................................ 37
SAMPLE APPLICATION, JAVAFX ......................................................................................................... 37
REFERENCES ..................................................................................................................................... 43
APPENDIX A: ACCESSING ORACLE DATA TYPES FROM JDBC.......................................... 43
APPENDIX B: BRIEF JDBC HISTORY........................................................................................... 44
Introduction
Java Database Connectivity (JDBS) is a manufacturer-independent de facto standard
of Java-based database access. The JDBC application programming interface (API)
contains Java classes and interfaces which provide low-level access to relational
databases, such as: connecting, executing SQL statements, processing results.
Interfaces are implemented by the own drivers of the manufacturers. According to the
Java principles – the drivers must only be available at runtime, thus application
developers are able to create the Java application independently from the database
management (DBMS) system.
The aim of the present workshop is to introduce database-related, client-server
application development in a Java and JDBC environment. In the first chapter the
client-server architecture will be described; which is followed by the enumeration of
the main JDBC language elements. Finally, the usage of JDBC will be illustrated
through a real-life example. According to the workshop environment the workshop is
limited to introducing the JDBC version 1.2 API only.
29
Database management in a client-server architecture1
In client-server architecture the application running on the client connects to the
DBMS through a network, using the driver provided by the manufacturer. Depending
on the application the driver might be a C library, ODBC or JDBC.
Client
Oracle DBMS
Tablespaces
Optimizer
Parser
Application
SQL area
Cursor_1
Database driver
Cursor_n
Buffer Cache
Process Global Area
Net8
By authenticating himself towards the DBMS the client has to create a databaseconnection (session) before starting to execute database operations. During the
session creation in the Oracle system the DBMS allocates resources for the session,
by reserving memory (Process Global Area – PGA) and by starting a server process2.
During the lifecycle of the session the client can initiate database operations which are
forwarded to the DBMS in the form of SQL statements. The SQL statement is
processed by the DBMS in several steps. At the processing start the server process
separates a memory area within the PGA to store processing information – such as the
compiled SQL statement, the actual position within the result set (see below). The
descriptor of the separated memory area is called cursor; the start of processing is
called opening a cursor. A session might have more than one opened cursor at a time.
The first step of processing is the parsing of the SQL statement, where the DBMS
compiles the string containing the statement and checks the valid access privileges to
the affected database objects. Then an execution plan is created by the Optimizer for
the successfully compiled statement. The execution plan contains the steps of
physically selecting the rows affected by the statement: it specifies the starting table,
the indexed to be used, how the selection is done. Since parsing and creating
execution plans requires a lot of resources, the execution plans of the latest SQL
statements are stored in the DBMS cache (SQL area).
The basic concepts will be introduced by using the (simplified) operation of Oracle RDBMS,
however these concepts are not Oracle-specific
1
2
Server processes can be shared, as well
30
Usually a database-related application employs a few SQL statements with a specified
structure but different parameters. A billing software for example queries the same
data from different customers; therefore it is only the customer ID that changes from
statement to statement. Therefore SQL enables calling these statements by using
parameters:
SELECT NAME, ADDRESS, TAX_NR FROM CUSTOMER WHERE CUSTOMER_ID = ?
An SQL statement with parameters is compiled by the DMBS during processing it for
the first time, in latter cases there is no need to re-compile the statement. Cache usage
is enabled by the parameters being substituted only after parsing and execution plan
creation.
After the execution plan creation and the parameter substitution the SQL statement is
executed. In SELECT type queries the selected lines logically create a result table,
whose lines can be retrieved by the client one-by-one3, using the fetch operation. After
retrieving the result table, or finishing the transaction (commit / rollback) the memory
area allocated for the processing is freed, and the cursor is closed.
The JDBC 1.2 API
The basic framework for data access
The JDBC API is a set of Java classes and interfaces. The most important classes and
interfaces are:






java.sql.DriverManager is a class responsible for URL resolution and creating new
database sessions;
java.sql.Connection is an interface representing a database connection;
java.sql.DatabaseMetaData provides (meta)information on the database;
java.sql.Statement controls SQL statement execution;
java.sql.ResultSet is an interface providing access to a given query results
java.sql.ResultSetMetaData is an interface providing meta-information on the
result table
DriverManager
Connection
Connection
Connection
DatabaseMetaData
Statement
Statement
Statement
Resultset
Resultset
ResultSetMetaData
3
In order to be more efficient the database driver might fetch more lines in a batch-processing fashion
31
Managing database connections
The management of JDBC drivers and the creation – closing of connections is done
by the java.sql.DriverManager class. The attributes and methods of
DriverManager are static, thus there is no need to instantiate the class in the
application. A new connection is created in the DriverManager using the following
statement:
Connection con = DriverManager.getConnection(url,
"myLogin", "myPassword");
The first parameter of the getConnection method is the URL string that identifies the
database, the second and the third parameters are the name and the password of the
database user. The content of the URL is database-dependent, according to the
convention its structure is as follows:
jdbc:<subprotocol>:<subname>
where <subprotocol> identifies the mechanism used to connect to the database, and
<subname> contains the parameters related to the mechanism specified in the
<subprotocol> part. For example connecting to the ODBC data source identified by
„Fred” can be done by the following statements:
String url = "jdbc:odbc:Fred";
Connection con = DriverManager.getConnection(url, "Fernanda", "J8");
If the URL requested by the driver contains username and password already, the
second and the third parameters of the method might be omitted. By calling the
method getConnection DriverManager starts querying every single registered JDBC
drivers and creates the database connection using the first driver which is able to
resolve the URL. After carrying out the required operations the connection can be
closed using the Connection.close method of the DriverManager. Upon the deletion
of the Connection object (garbage collection) the method close is called
automatically.
Before use, all drivers must be loaded and registered at the DriverManager. Usually,
application developers only have to take care about loading the driver; the drivers
automatically register themselves in their static initialization method. The easiest way
to load a driver is to use the Class.forName method4, for example:
Class.forName("oracle.jdbc.driver.OracleDriver")
Due to security reasons and applet might only use drivers that are located on the local
computer, or have been downloaded from the same address as the applet code. Unlike
applets from „un-trusted sources”, „trusted” applets are run as complete programs by
JVM, without any constraints.
Executing SQL statements
Simple SQL statements are executed using Statement the interface. First the class
implementing the interface must be instantiated, then the relevant (SQL statementdependent) execution method of the instance must be called in order to execute an
In JDK 1.1.x due to a design error the static methods of a class loaded by the Class.forName method
sometimes cannot be run; in these cases the JDBC driver must be registered using the registerDriver
method of the DriverManager. The error usually occurs under Internet Explorer 4.x but not under
Netscape Navigator 4.x
4
32
SQL statement. The Statement instance can be created by calling the
createStatement method of the Connection instance representing the database
connection:
Statement stmt = con.createStatement();
The execute series are the most often used of Statement's methods:

executeQuery: executes the SQL query
result table (ResultSet). The method
passed on as parameter, and returns a
is used to execute query (SELECT)
statements.

executeUpdate:

execute:
executes the SQL statement passed on as parameter, and returns
the number of rows affected by the modification. This method can be used to
execute both data manipulation (DML) and data definition (DDL) statements. In
case of DDL statements the return value is 0.
executes the SQL statement passed on as parameter. This method is the
generalization of the previous two. The return value is True; in case the return
value is of type ResultSet it can be retrieved by the Statement.getResultSet
method. The number of rows affected by the modification can be retrieved by the
Statement.getUpdateCount method.
In the following example we are creating a table containing all the bill data for the
billing software of CoffeeBreak Company:
int n = stmt.executeUpdate("CREATE TABLE COFFEES ( " +
"COF_NAME VARCHAR(32), " +
"SUP_ID
NUMBER(8), "
+
"PRICE
NUMBER(6,2), " +
"SALES
NUMBER(4), "
+
"TOTAL
NUMBER(6,2)");
Once the business is started, the purchases can be queried using the following
statement:
ResultSet rs = stmt.executeQuery("SELECT * FROM COFFEES");
The execution of a statement is finished when all result tables are processed (all lines
of the result tables are retrieved). However, statement execution can be terminated
manually by using the Statement.close method. By repeatedly calling the execution
method of a Statement object the earlier, unfinished execution of the same object is
automatically terminated.
Handling SQL statements with parameters somewhat differs from handling simple
SQL statements. In JDBC SQL statements with parameters are represented by the
PreparedStatement interface. The interface is created in a way similar to
Statement, by calling the prepareStatement method of the Connection instance
representing the connection and supplying the SQL statement with parameters:
PreparedStatement updateSales = con.prepareStatement(
"UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ?");
The parameter values – indicated with question marks – of the statement stored in the
PreparedStatement can be set by using the setXXX method family. For the
execution the following methods (introduced earlier at the Statement class) might be
used without any arguments specified: executeQuery, executeUpdate and
execute. The following example illustrates data entry into the COFFEES table using
SQL statements with parameters:
33
PreparedStatement updateSales;
String updateString =
"update COFFEES set SALES = ? where COF_NAME like ?";
updateSales = con.prepareStatement(updateString);
int [] salesForWeek = {175, 150, 60, 155, 90};
String [] coffees = {"Colombian", "French_Roast", "Espresso",
"Colombian_Decaf", "French_Roast_Decaf"};
int len = coffees.length;
for(int i = 0; i < len; i++) {
updateSales.setInt(1, salesForWeek[i]);
updateSales.setString(2, coffees[i]);
updateSales.executeUpdate();
}
The setXXX methods are expecting two arguments. The first argument is the index
of
the SQL parameter to be set; the parameters in the SQL statements are indexed from
left to right, starting with 1. The second argument is the value to be set. Please note,
that JDBC does not perform implicit type conversion for the input parameters;
therefore it is the programmer’s responsibility to provide right data types for the
database-manager. Null values can be set by using the method
PreparedStatement.setNull. A set parameter can be reused for multiple executions
of the SQL statement.
Stored procedures and methods can be called by using the CallableStatement
interface. The CallableStatement interface can be instantiated by calling the
prepareCall method of the Connection instance representing the connection. The
CallableStatement interface is derived from the PreparedStatement interface thus
the input parameters (IN) can be set using the setXXX methods. Output parameters
(OUT) have to be registered by specifying their type before execution by using the
CallableStatement.registerOutParameter method. As it can be inferred from the
following example, after the execution output parameters can be retrieved using the
getXXX method family.
CallableStatement stmt = conn.prepareCall("call getTestData(?,?)");
stmt.registerOutParameter(1,java.sql.Types.TINYINT);
stmt.registerOutParameter(2,java.sql.Types.DECIMAL);
stmt.executeUpdate();
byte x = stmt.getByte(1);
BigDecimal n = stmt.getBigDecimal(2);
Similar to the setXXX methods getXXX methods do not perform any type conversions:
it is the responsibility of the programmer to ensure that the data types provided by the
database are in sync with the registerOutParameter and the getXXX methods.
Managing result tables
Query results can be accessed through the java.sql.ResultSet class which is
instantiated by the executeQuery or getResultSet method of the Statement
interfaces. It is only the actual row (marked by the cursor) of the result table
represented by ResultSet that can be accessed. Initially, the cursor is pointing before
the first row and the method ResultSet.next can be used to position the cursor on
the next row5. Method next returns False in case the cursor passed the last row,
otherwise it returns True.
Positioning the cursor on the previous row – or on a particular row – is only possible in JDBC 2.0 (in
case it is also supported by the driver)
5
34
Field values of the actual row can be retrieved using the method family getXXX 6.
getXXX methods can reference fields in two ways: by column indexes and column
names. In SQL queries columns are indexed from left to right, starting with 1.
Referencing by column names is less efficient due to runtime mappings but is a more
comfortable solution. Unlike the getXXX methods of PreparedStatement and
CallableStatement, the ResultSet.getXXX methods perform automatic type
conversions. In case type conversion is not possible (for example by calling the
method getInt for a field of type VARCHAR containing the string “foo”) an
SQLException exception is raised. In case a field contains SQL NULL value getXXX
returns zero or Java null depending on the getXXX method. After retrieving the field
value method ResultSet.wasNull can be used to check whether the retrieved value
was derived from an SQL NULL or not7. Usually the programmer does not have to
deal with closing the result table since that is automatically closed when the
Statement is finished. However, the result table can be closed manually as well by
the ResultSet.close method.
Meta-information
on the result tables can be accessed through the
ResultSetMetaData interface. The object implementing the interface is returned by
the method ResultSet.getMetaData. The ResultSetMetaData method
getColumnCount returns the number of columns, while getColumnName(int
column) returns the name of the column having the index specified.
The following example illustrates the use of ResultSet and ResultSetMetaData.
The first column is integer; the second is String, while the third is an array of
bytes.
Statement stmt = conn.CreateStatement();
ResultSet r = stmt.executeQuery("SELECT a, b, c FROM table1”);
ResultSetMetaData rsmd = r.getMetaData();
for (int j = 1; j <= rsmd.getColumnCount(); j++) {
System.out.print(rsmd.getColumnName(j));
}
System.out.println;
while (r.next()) {
// Printing fields of the actual row
int i = r.getInt("a");
String s = r.getString("b");
byte b[] = r.getBytes("c");
System.out.println(i + " " + s + " " + b[0]);
}
stmt.close();
Error handling
In case any kind of error occurs during the database connection, on Java-level an
SQLException is generated. Method SQLException.getMessage returns the error
message text, SQLException.getErrorCode returns the error code, and
SQLException.getSQLState returns the state description that complies with the
X/Open SQLstate convention.
For a list of getXXX methods please refer to the Appendices.
Null check before retrieving a field value is not supported by all DBMS therefore it was excluded
from JDBC 1.2 API.
6
7
35
Transaction handling
Database connections represented by the class Connection are in auto-commit mode,
as a default. It means that every SQL statement (Statement) runs as a unique
transaction and is committed after its execution (commit). This default setting can be
overwritten using the method Connection.setAutoCommit(false). In this case the
transaction has to be committed or rolled back from the program using the method
Connection.commit and Connection.rollback, as it is illustrated in the following
example:
con.setAutoCommit(false);
PreparedStatement updateSales = con.prepareStatement(
"UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ?");
updateSales.setInt(1, 50);
updateSales.setString(2, "Colombian");
updateSales.executeUpdate();
PreparedStatement updateTotal = con.prepareStatement(
"UPDATE COFFEES SET TOTAL = TOTAL + ? WHERE COF_NAME LIKE ?");
updateTotal.setInt(1, 50);
updateTotal.setString(2, "Colombian");
updateTotal.executeUpdate();
con.commit();
con.setAutoCommit(true);
Database information
Database-related information (metadata) can be accessed through the
DatabaseMetaData interface. The class implementing the interface is returned by the
getMetaData method of the Connection instance representing the connection.
DatabaseMetaData dbmd = conn.getMetaData();
Depending on the information requested the DatabaseMetaData interface methods
return with either a simple Java type or a ResultSet. The table below lists a few
important methods.
Method name
Return value
Description
getDatabaseProductName
String
Name of the database product
getDatabaseProductVersion
String
Database product version number
getTables(String catalog,
String schemaPattern,
String tableNamePattern,
String types[])
ResultSet
Lists the tables matching the provided
patterns
The Oracle JDBC drivers
The two client-side drivers implemented by Oracle are: the JDBC OCI Driver and the
JDBC Thin Driver.
The JDBC OCI Driver implements the JDBC methods as OCI library calls. The
Oracle Call Interface (OCI) consists of a set of standard C-language software APIs
(database drivers) which provide a low-level interface to the DBMS services for highlevel development tools. By calling a method in JDBC (e.g. statement execution) the
JDBC OCI Driver is forwarding the call to the OCI layer, which is then transmitted to
the database manager via SQL*Net or Net8 protocols. Due to the C library calls the
JDBC OCI Driver is platform- and operating system dependent; however its native
coding makes it more efficient than the purely Java-based Thin Driver.
36
The JDBC Thin Driver is entirely written in Java. The Thin Driver contains a
simplified, TCP/IP-based implementation of SQL*Net/Net8 protocol, therefore herein
a JDBC method call is immediately transmitted to the database manager. Due to the
pure Java implementation the JDBC Thin Driver is platform-independent and can be
downloaded together with the Java applet. However, since the implementation is
simplified the JDBC Thin Driver does not provide all OCI features (e.g. encrypted
communication, non-IP protocols etc. are not supported).
For addressing the databases – matching the JDBC convention – Oracle uses the
following URL structure:
jdbc:oracle:drivertype:user/password@host:port:sid
where drivertype can be oci7, oci8 or thin; host is the DNS name of the databaseserver; port is the port number of the server-side TNS listener; and sid is the
database identifier. Username and password can also be provided in the second and
third parameters of the getConnection method, and in this case the user/password
part can be excluded from the URL structure.
A demo WebStart application
Java Web Start technology
WebStart is a technology, introduced from Java 1.4. It allows for platformindependent application installation and execution directly from the web. By clicking
a single link, it executes the command line formula which would be too complicated
to call for average users. Furthermore, it guarantees that always the most recent
version is loaded into the cache of the client, and this is the version that is executed
(due to this, Internet access is a must, basically). To use WebStart, we do not have to
change our Java application in any way, except for the fact that we must organize it to
JAR packages. Furthermore, we have to create and manually edit a JNLP (Java
Network Launching Protocol) file which describes the installation details.
The application started with WebStart runs in a sandbox, the same way as applets.
Sandbox is an execution environment that constrains the access to local file systems
and the network. However, the application can get unlimited privileges if all of its
components have a digital signature, if it explicitly requests these privileges in the
JNLP file, and if the user who trusts the signer and its certificate authority, and thus,
explicitly provides these rights to the application (this is a one-time procedure from
the part of the user, at first execution, later it is cached). For us, this is important,
since otherwise we would not be able to connect the database from the client (as we’d
like to use a network resource which is located elsewhere from the download
location). The other resources of the client can be accessed through the JNLP API
layer, from the sandbox (this will not be needed during the lab, though).
Sample application, JavaFX
The sample application that you can start off from, can be downloaded from
http://rapid.eik.bme.hu/~varsanyi.marton/jdbc/lab5jdbc.zip. What it does is it
connects to the database server used during the classes, and queries and displays the
first 20 records of table ‘Szemelyek’ in schema ‘Oktatas’.
The description of the file structure of the application, and the steps of its compilation
and execution are described by the students’ guide that can be reached on the course
37
website. The purpose of this description is to give an overview of the functionality
and architecture of the sample application and the techniques used for creating it. In
this guide we take a look at the interface and Java source of the sample application,
which can be found in the src and resources directories.
The application was written in Java and uses the JavaFX framework
(https://en.wikipedia.org/wiki/JavaFX) for creating the GUI (Graphical User
Interface). According to the phylosophy of JavaFX, the structure, and architecture of
the sample application corresponds to the classical Model-View-Controller layer
structure. This is supplemented by a data access layer, which will provide the the
instances of classes of the Model layer. The src directory includes the following Java
packages:

application – Contains the Controller layer of the application and the classes
used for display.
o AppMain.java – Source code of the class which is the entry point of
the application. It creates and initializes the view.
o Controller.java – Source code of the controller layer of the
application. It instantiates the data access layer, processes the requests
sent by the view layer, and calls the methods of the data access layer to
serve them. Then, it returns the results to the view layer.
o ComboBoxItem.java – an example for how to store data in
ComboBox.

dal – This package contains the source files belonging to the data access layer.
o DataAccessLayer.java - generic interface for offering the services of
the data access layer.
o ActionResult.java – Enum storing the possible results of the data
modification exercise.

dal.impl – A single – topic dependent – source file of this stores the skeleton
of the data access class.

dal.exceptions – This package contains the definitions of the exceptions used
in the data access layer.

model – Includes three classes, which also differs for different topics. These
are used for transporting data between the view and data access layers. It,
furthermore, includes a Person class, which is responsible for displaying the
results of the exampe SQL-query.
The directory resources contains a single file in the sample application, View.fxml.
This file describes the interface of the application in XML (Extensible Markup
Language – https://en.wikipedia.org/wiki/XML), the control elements on the interface
and their relations. It’s a JavaFX specific file.
The user name and password needed for connecting to the database, can be entered on
the top of the application window. To connect to the database, the Connect button has
to be pressed. The connection status will appear next to the Connect button.
The application contains three tabs, which are named Search, Edit, and Statistics. The
tabs and control elements placed on them have two roles: one, they serve as a pattern
38
for creating similar elements on the interface, and two, these will be needed to create
the application during the lab.
The Search tab includes a text field, a button and a table. Clicking the Search button
will cause the earlier mentioned rows to appear in the table.
The Edit tab contains examples for placing labels, dropdown lists, text fields, and
buttons on the interface.
The Statistics tab contains a button and a table, which helps solving the last exercise.
When examining the source code, first, let’s take a look at the class that contains the
entry point – the main method – for instance. This class is called AppMain, and is
included in AppMain.java. The code, after the necessary JavaFX imports, starts with
the creation of the application class. In case of JavaFX, our application is inherited
from the Application class.
The main method contains a single line, which instantiates and starts our application.
The actions performed when the application is started, should be included in the start
method, while the stop method is called before stopping the application. The start
method starts with loading the XML file using the FXMLLoader object. This file is
located in directory resources and describes the interface of the application. After
loading it, the hierarchy of the control elements on the interface, is created, based on
the XML file, and we get a reference to the root element in the viewRoot variable.
The topmost level container in the case of JavaFX applications is the Stage object.
The Scene object, which is the container of the elements displayed in the window,
should be the child of the Stage object. As the child of the Scene object, we can
specify the viewRoot object, which is the topmost level interface instance in the XML
file.
After the above setting, we can set the name of the window using the setTitle method,
and then display it. The above steps can be seen in this code:
// Create a loader object and load View and Controller
final FXMLLoader loader = new
FXMLLoader(getClass().getCl
assLoader().getResource("resources/View.fxml"));
final VBox viewRoot = (VBox) loader.load();
// Get controller object and initialize it
controller = loader.getController();
// Set scene (and the title of the window) and display it
Scene scene = new Scene(viewRoot);
primaryStage.setScene(scene);
primaryStage.setTitle("MyJwsApplication");
primaryStage.show();
The next step let’s examine the interface of the application, and the FXML file
describing its layout. XML files, as HTML files are built up from tags, which are
written within relation operators <>:
<VBox fx:controller="application.Controller"
xmlns:fx="http://javafx.com/fxml/1"
39
fx:id="rootLayout" alignment="CENTER" spacing="10"
prefWidth="600" prefHeight="460" minWidth="600"
minHeight="460">
In FXML files, these tags describe an interface element, like a button, a label, a text
input field, or a layout. Tags have an opening and a closing tag.
Each tag has a unique name, which can be written after the < character. In the above
example, the tag name is VBox. This is the name of the given interface element. After
this, come the attributes of the tag, which are the setting parameters of the interface
element. Each attribute has a name, after the name comes an equation mark, and the
parameter value. In the above example, prefHeight and prefWidth set the height and
width of the preferred height and width of the VBox element.
Tags (except for the first, special xml tag), have a closing element. This is written like
</tag_name>. Tags can be enclosed within each other, which means that we place
another interface element into the interface element corresponding to the enclosing
tag. If an element does not contain further elements, then the space between the
opening and closing tag will remain empty. For short, we can use a single tag instead
of an opening and closing tag by writing <tag_name/>.
XML files must start with an xmltag, which, in our case is the following:
<?xml version="1.0" encoding="UTF-8"?>
Here, we set the XML version and encoding, which will be UTF-8. Then, we must
import the class of the elements used for describing the interface. Follows an
example:
<?import javafx.scene.control.Button?>
We can put many kinds of elements on the interface. The sample application only
shows a few from among these. In the FXML file, we can see examples for using
buttons, labels, text fields, drop-down menus, tables, etc. We can also see how to use
layouts.
VBox is a layout for example. Layout is a container, which places its embedded
elements vertically. In the example, we can see that the elements are placed 10 pixels
away from each other (spacing attribute) and are aligned to the middle (alignment
attribute).
HBox is a layout too, but in its case, elements are placed horizontally.
There are four special attributes we have to mention. fx:controller sets the name of the
class responsible for interface and event handling. As can be seen in the example, this
is class Controller. This attribute can only be set a single time, in the tag of the root
element of the interface (in our case, this is the outermost VBox tag). Instead of
Controller, we must specify this value as application.Controller, as all the classes of
the sample application can be found within a package called Controller.
The xmlns:fx tag defines the name space from which the used tag names come from.
The name space in this case, is the Java fxml name space, This can be specified only a
single time as well.
He fx:id attribute sets the unique identifier of the interface element. This is important,
because we can reference the interface element from the interface management class,
using this identifier.
40
Finally, if we’d like to handle events, we have to assign event handlers to the interface
elements. This can be done by specifying the name of the event handler method
preceded by character #, in the onAction attribute of the control element (i.e. Button):
<!--Search button-->
<Button fx:id="searchButton" text="Search"
onAction="#searchEventHandler" />
The first line of the above example is a comment. So this is how to insert comments
into the code.
Now let’s take a closer look at the Controller class (Controller.java). In the
constructor of this class, we instantiate the data access layer:
public Controller() {
dal = new VideoDal();
}
In the following, the Controller class will use this data access layer to connect to the
database, and perform query and data manipulation operations.
For the methods, which we reference from the FXML file, we have to use the
@FXML decorator. Event handling methods like the event handlers of the Connect
and Search buttons are like this. The code piece below shows an example for an event
handling method:
@FXML
public void searchEventHandler() {
//TODO: replace this query with your solution.
try {
List<Person> people = dal.sampleQuery();
searchTable.setItems(FXCollections.observableArrayList(people));
} catch (NotConnectedException e) {
e.printStackTrace();
}
}
In the event handler methods, we usually want to access an interface element, which
we modify (for example, we modify the contents of a text input field). To do this, we
have to have a reference for the given interface element. We can access the control
elements, and layouts, by creating objects at the beginning of the code the names of
which equal the IDs of the interface elements and their types (classes) equal the types
(names) of the interface elements. Beyond this, we have to use the earlier mentioned
@FXML decorator. See the following example:
@FXML
private TextField usernameField;
In the example, we create an object of type TextField for the text field having the ID
of usernameField. Of course, the necessary JavaFX classes (like TextField) have to be
imported at the beginning of the code.
We furthermore, have to emphasize the initialize method, which is automatically
called after building up the interface. Initializing interface elements (for example,
clearing text fields) have to be done within this method. This is needed becaouse
when instantiating the class, the interface elements are nulls. After the initialize
41
method, however, we can be sure that the framework has connected the appropriate
controller to the tag variable.
@Override
public void initialize(URL location, ResourceBundle resources) {
// TODO: initalize property-value factories
//EXAMPLE: sampleCombo stores two strings.
sampleCombo.getItems().add(new ComboBoxItem<String>("Value A", "a"));
sampleCombo.getItems().add(new ComboBoxItem<String>("Value B", "b"));
//EXAMPLE: this is how we bind the private variables to a data cell
nameColumn.setCellValueFactory(new PropertyValueFactory<>("name"));
identityNumberColumn.setCellValueFactory(
new PropertyValueFactory<>("identityNumber"));
}
When binding data, like in the above example, what we do is tell the framework, from
which tag variable of the given row it should read the value to be displayed. These tag
variables are name and identityNumber in the example above.
Finally, the data access layer (*Dal.java, e.g. VideoDal.java) contains the logic related
to database management. This class connects to the database, and performs the query
in the example. Before connection the database, the Oracle JDBC driver is loaded:
// Load the specified database driver
Class.forName(driverName);
Querying data from the database can be seen in method sampleQuery:
@Override
public List<Person> sampleQuery() throws NotConnectedException {
checkConnected();
List<Person> result = new ArrayList<>();
try (Statement stmt = connection.createStatement()) {
try (ResultSet rset = stmt.executeQuery(
"SELECT nev, szemelyi_szam FROM OKTATAS.SZEMELYEK "
+ "ORDER BY NEV "
+ "OFFSET 0 ROWS FETCH NEXT 20 ROWS ONLY")) {
while (rset.next()) {
Person p = new Person(rset.getString("name"),
rset.getString("id"));
result.add(p);
}
return result;
}
} catch (SQLException e) {
e.printStackTrace();
return null;
}
}
The first line checks whether we are connected to the database already, using a private
method. We create a Statement object, then we execute the query. We iterate through
its records, and create a Person object from each of these, which will be placed in the
list to be returned. During the lab, it is recommended to use the try-with-resources
structure of Java 8, which you can see in more detail in the following link:
https://docs.oracle.com/javase/tutorial/essential/exceptions/tryResourceClose.html
The errors occurring in the database can be caught in the exception handling branches,
in the form of SQLExceptions.
42
Classes in the model package are POJO (Plain Old Java Object) classes, the private
variables of which can be reached using getter/setter methods.
The JavaFX application introduced above, gets a WebStart “frame” by creating a
WebStart controller configuration file (JNLP) for it:
<?xml version="1.0" encoding="UTF-8"?>
<jnlp codebase="http://rapid.eik.bme.hu/~xxxxxx/jdbc"
href="application.jnlp">
<information>
<title>My Java Webstart JDBC Application</title>
<vendor>Test student</vendor>
<icon href="logo.png" kind="default" />
</information>
<security>
<all-permissions />
</security>
<resources arch="" os="">
<j2se version="1.7+" />
<jar href="ojdbc7.jar" />
<jar href="MySignedApplication.jar" main="true" />
</resources>
<application-desc />
</jnlp>
The HTML page embedding WebStart is below:
<a href="application.jnlp">
<font size="4">My Java Webstart JDBC Application</font><br />
<img src="logo.png" alt="My Java Webstart JDBC Application" />
</a>
References
The JDBC API Version 1.20, Sun Microsystems Inc., 1997 (available at http://www.javasoft.com)
S. White, M. Fisher, R. Cattell, G. Hamilton, and M. Hapner: JDBC 2.0 API Tutorial and Reference,
Second Edition: Universal Data Access for the Java 2 Platform, 1999 (available at
http://www.javasoft.com)
S. Kahn: Accessing Oracle from Java, Oracle Co., 1997. (available at http://www.oracle.com )
Oracle8 Server Concepts, Release 8, Oracle Co.
Nyékiné et al. (szerk): Java 1.1 útikalauz programozóknak, ELTE TTK Hallgatói Alapítvány, 1997.
Appendix A: Accessing Oracle data types from JDBC
Access method
CHAR
VARCHAR2
NUMBER
byte
getByte
x
x
x
short
getShort
x
x
x
int
getInt
x
x
x
long
getLong
x
x
x
float
getFloat
x
x
x
double
getDouble
x
x
x
43
DATE
Java type
java.Math.BigDecimal
getBigDecimal
x
x
x
boolean
getBoolean
x
x
x
String
getString
X
X
x
java.sql.Date
getDate
x
java.sql.Time
getTime
x
java.sql.TimeStamp
getTimeStamp
X
x
Signage:
x: the getXXX method can be used to access that particular SQL type
X: the getXXX method is recommended to access that particular SQL type
Appendix B: Brief JDBC history
JDBC has been changing a lot during its history. Originally, at the beginning of 1997
JDBC 1.0 API was introduced as a simple, manufacturer-independent, unified – and
therefore featuring minimal functionality – programming interface supplementing
Java Development Kit (JDK) 1.0. In the latter JDK 1.1 JDBC was already thoroughly
integrated, with its classes being part of the Java base classes (java.sql.*). The
JDBC 2.0 which was presented with JDK 1.2 contains two packages. The java.sql
package containing the JDBC 2.0 Core API enhances the original JDBC API with
new functionality. New functionalities include: positionable result tables, result table
modification support with direct JDBC methods, handling of SQL99 specification
standard basic (BLOB, CLOB, Array) and user-defined (User Defined Type, UDT)
data types. Apart from the DriverManager architecture aiming to directly handle the
drivers the JDBC 2.0 Optional Package8 (javax.sql) introduces the data sourcebased (DataSource) access model, which provides connection pooling and distributed
transaction handling. As of now,9 the JDBC 3.0 API is fine-tuning the earlier
modifications and integrates JDBC more closely with other Java 2 technologies (such
as Connector Architecture etc.).
8
9
Under its former name: JDBC Standard Extension API
In January, 2002.
44