* Your assessment is very important for improving the workof artificial intelligence, which forms the content of this project
Download Java Database Connectivity (JDBC)
Concurrency control wikipedia , lookup
Entity–attribute–value model wikipedia , lookup
Extensible Storage Engine wikipedia , lookup
Microsoft Access wikipedia , lookup
Oracle Database wikipedia , lookup
Microsoft Jet Database Engine wikipedia , lookup
Microsoft SQL Server wikipedia , lookup
Clusterpoint wikipedia , lookup
Relational model wikipedia , lookup
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