06
  • 6.1
  • 6.2
  • 6.3
  • 6.4
  • 6.5
  • 6.6
  • Data Persistence in Web Applications

    Applications often require data to be persistent for longer than the application remains running and longer than one user interacts with the application.

    Examples of such data include:
    The contents of your web application, such as product information in a web store
    User details, such as login in information ( more about that next week)
    Data that stems from users interacting with your application that the application wants to retain for a longer period of time, such as how long a user is active on your site or which products they have looked at.

    Therefore most web applications require to access a “data store”. The figure below outlines a three-tier architecture, which includes the data access layer (here a database server).

    The user interacts with the client (user agent, web browser), which communicates using the http protocol sends a request to the middle tier (which processes the business logic). The middle tier then sends the commands to the data source in a language the data source understands. The data source processes the commands and communicates the results back to the middle tier, which then uses the results to compile the response sent to the client.

    Data Persistence in Web Applications

    Examples of Data Persistence Technologies
    Databases (RDBMS, non-relational databases)
    Data stores
    Files

    This week’s lecture notes make use of the dbDemo sample project, which is available from GCULearn.

  • Steps for working with Databases in Java

    1. Establish a connection to the database.

    2. Create a statement object (Java specific).

    3. Execute the query (Java specific).

    4. Process the ResultSet object (Java specific).

    5. Close the connection.

  • Establish a Connection to the database

    Java provides 2 methods of connecting with a database:
    Using the DriverManager class and
    Using the DataSource class.

    Connecting to the DBMS using the DriverManager class involves calling the method DriverManager.getConnection().

    The getConnection() method returns a connection to the database.

    Example:

    Connection con = DriverManager.getConnection("jdbc:odbc:somedb", "user", "passwd");
    This method requires a database URL, user name and password that allow the application to access the database.
    You may have to initialize your JDBC driver by calling the method Class.forName and provide the database driver for your database, which is of type java.sql.Driver. Alternatively JDBC 4.0 drivers that are found in the class path are automatically loaded.
    The database manual will provide instructions on how to connect this database using Java and from where the JDBC drivers can be downloaded.
    It throws SQLException and ClassNotFoundException.
  • Data Access Object (DAO)

    It is often sensible to separate the domain object from the object that implements the operations on the database, the data access object
    This concept is present in several design patterns: see Table Data Gateway, Row Data Gateway or Data Access Object design patterns, see Fowler, pg. 144
    For example think of a domain object Person
    implements the properties of a person
    And a data access object is H2Person
    implements the functions that operate on the database
  • Prepared Statement

    Is given a (partial) SQL statement when created,
    that is sent to the db and pre-compiled.
    That reduces the execution time for repeated execution.
    Is often used for statements that use parameters, you can use the same statement with different arguments.

    1. Create the SQL statement with "?" instead of input parameters

    2. Provide the argument values using the getXXX() or setXXX() methods.

    Example (for illustration):

    Create a prepared update statement object (con is the identifier of a connection object)

    String updateString = "update " + dbName + ".COFFEES " + "set SALES = ? where COF_NAME = ?";
    
    PreparedStatement updateSales = con.prepareStatement(updateString);
    

    Provide the argument values (e is the name of an object that has methods returning the values for sales and coffee name)

    updateSales.setInt(1, e.getValue().intValue());
    updateSales.setString(2, e.getKey());
    updateSales.executeUpdate();
    

    Using prepared statements is more secure in case of SQL Injection (next week).
  • Implementation Example dbdemo

    Class architecture

    Runner
    starts the program - contains main()
    examine the start() method
    sets up Jetty server on port 9000
    maps urls .../index.html and .../add to (a) PersonServlet (object)

    PersonServlet
    provides the (http) responses to requests to the above urls
    for 2 types of request:
    http get request (responded to via doGet())
    http post request (responded to via doPost())

    Person (in package .../model)
    a typical POJO (a plain old Java object)
    models the domain object "person"
    described by first name, last name and email address

    H2Person (in .../db)
    data access object, an object that implements the database operation for a particular domain object, such as adding a person to the database or retrieving a person from a database

    MustacheRenderer (in .../util/mustache)
    compiles the specified template (first argument) with the data from the specified object (second argument)

    index.mustache (in resources/templates)
    the template that is rendered
    This page has static components and a small dynamic component.
    Activity: Which contents are generated dynamically?

    DB demo Screenshot
    Where does the database come from?
    The database is H2
    There is an H2 dependency declared in the pom.xml file
    That loads the H2 jar file when the program starts

    <dependency>
          <groupId>com.h2database</groupId>
          <artifactId>h2</artifactId>
          <version>1.4.194</version>
    </dependency>
    

    Accessing the Data Store
    Principle: to separate the domain logic (the classes that represent the objects in our application) from the objects needed to access the data store for our objects (here: the database).
    dbdemo has one domain object: Person.
    The H2Person class implements the operations on the database:
    adding a person (inserting a new row into the person table)
    finding all persons (retrieving all rows in the
    The method getConnection() sets up a connection to the database.
    The driver is specified in the H2 manual (see below)
    Here the database runs in memory, see in-memory URL (MEMORY) is used
    The constructor sets up the connection and the tables (see file person.sql).

    Where do the tables come from?
    The SQL script that sets up the table is in person.sql
    It is run when the H2Person object is constructed.

    CREATE TABLE IF NOT EXISTS person (
      id int AUTO_INCREMENT PRIMARY KEY,
      first VARCHAR(255),
      last VARCHAR(255),
      email VARCHAR(255)
    );
    

    Connecting to the database

    static Connection getConnection(String db) throws SQLException, ClassNotFoundException {
       Class.forName("org.h2.Driver");
       return DriverManager.getConnection(db, user: "sa", password: "");
    }
    

    The getConnection() method returns a connection to the database
    This method requires a database URL, here the constant MEMORY
    Class.forName requires an object of type java.sql.Driver, see documentation for the relevant database for the correct driver
    DriverManager.getConnection uses the default login for H2

    The Prepared Statement

    public void addPerson(Person person) {
        final String ADD_PERSON_QUERY = "INSERT INTO person (first, last, email) VALUES (?, ?, ?)";
        try (PreparedStatement ps = connection.PreparedStatement(ADD_PERSON_QUERY)) {
            ps.SetString( parameterIndex: 1, person.getFirst());
            ps.setString( parameterIndex: 2, person.getlast());
            ps.setString( parameterIndex: 3, person.getEmail());
            ps.execute();
        } catch (SQLException e) {
            throw new RunTimeException(e);
        }
    }
    

    Create the SQL statement with "?" instead of input parameters, i.e.
    Create the SQL query with the input parameters
    Then create the PreparedStatment with that query String
    and set the replacement values by using getter and setter methods of the domain object, e.g. Person person.getFirst(),

    Close the Connection
    Once a connection to the database has been set up but is no longer needed you need to release any resources held by this connection.
    Otherwise the database keeps the connection open but continues to acquire new connections, most of which will be un-used.
    This will slow the database down.
    If single threaded, no other process can connect.
    If an connection exists, call the close() method.
    Set the connection object to null.

    Handling Requests

    There are 2 types of request:
    http GET via doGet()
    http POST via doPost()
    Handling Requests Screenshot
    doGet()
    Obtain a list of Person objects by querying the database, see findPersons() method in the H2Person class
    Call the MustacheRenderer with the template index.mustache (in resources/templates) and inner class Result as object providing the data (an int indicating the size of the persons list).

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        List<Person> persons = h2Person.findPersons();
        String html = mustache.render( templateName: "index.mustache", new Result(persons,size()));
        response.setContentType("text/html");
        response.setStatus(200);
        response.getOutputStream().write(html.getBytes(Charset.forName("utf-8")));
    }
    

    doPost()
    Instantiate a Person object with the values taken from the request parameters.
    Call the addPerson() method of the H2Person class, providing the new Person object as argument
    Create another GET request to index.html -> see previous slide for the handling of the GET request

    @Override
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        String first = request.getParameter( s: "first");
        String last = request.getParameter( s: "last");
        String email = request.getParameter( s: "email");
        Person person = new Person(first, last, email);
        h2Person.addPerson(person);
        response.sendRedirect( s: "/index.html");
    }
    

    Activity: When running the demo monitor the request in the web browser
School of Computing, Engineering and Built Environment