750 likes | 876 Views
EE557: Server-Side Development. Application Design. EE557: Server-Side Development. Model 1 Architecture. EE557: Server-Side Development. Model 1 Architecture Problems. Duplication of functionality in servlets/JSPs Ties business logic and presentation logic together!
E N D
EE557: Server-Side Development Application Design
EE557: Server-Side Development Model 1 Architecture
EE557: Server-Side Development Model 1 Architecture Problems • Duplication of functionality in servlets/JSPs • Ties business logic and presentation logic together! • Difficult to create a new view (eg. WML interface) • Internationalisation • Any of these familiar?
EE557: Server-Side Development Model 2 Architecture • ‘Controller’ receives all requests for the application. • It processes the request and interacts with the ‘Model’ • Prepares data to present to the ‘View’. Response to user is presented in the ‘View’ • ‘View’ typically JSPs
EE557: Server-Side Development Model 2 Architecture • MVC is a design pattern - a blueprint for constructing a • time-tested solution to a given problem • It is not an implementation • Three layers of functionality: • - Model Components: The data and business logic • - View Components: Presentation • - Controller Components: The flow control
EE557: Server-Side Development Model Components • Provide access to our business data and logic to manipulate data • Typically interacts with a persistent storage mechanism (DBMS) • Controller components typically don’t embed code for • manipulating data -> instead they communicate with the Model • which performs data access and manipulation • Model components can be implemented as JavaBeans, Enterprise • JavaBeans (EJBs) and a range of other options
EE557: Server-Side Development View Components • Responsible for displaying data from the Model to the user • Typically simple JSPs or HTML pages • Seperation of business and presentation logic, allows other view • technologies such as WML • Open to future presentation technologies
EE557: Server-Side Development Controller Components • Core part of MVC Architecture • Handle all requests from the user and selects the View to return • Typically a servlet, which manages the flow of data between the • Model and the View • When Controller receives a request it forwards the details to an • appropriate Handler, which determines what action to take • The handler might call on the Model to perform a specific function • After the Controller selects the View to send back to the user • based on the state of the Model’s data
EE557: Server-Side Development Internationalisation (i18n) & Localisation (l10n) • The means of adapting computer software to different languages • and regional differences • Acrynoms based on counting letters between first and last letter • Creating multiple versions of applications is NOT a viable option • - updating becomes a nightmare • Internationalisation is the process of designing an application so • that it can adapt to various languages and regions WITHOUT • engineering changes! • Localisation is the process of adapting internationalised software • for a specific region or language by adding locale-specific components • and translating text.
EE557: Server-Side Development Internationalisation (i18n) & Localisation (l10n) • Internationalisation and localisation handled in most situations by • the abstraction of language away from underlying code • Most MVC frameworks will provide functionality for doing so • Example Struts: • - ApplicationResources.properties • - ApplicationResources_fr.properties • - ApplicationResources_de.properties • etc. • When browsers make requests, they pass along a HTTP header • (Accept-Language) that indicates the user’s Locale • (country, language etc.)
EE557: Server-Side Development ApplicationResources.properties #errors associated with the Login page error.username.required=username is required. error.password.required=password is required. error.login.invalid=Login Failure: The DCU Novell username/password …. #login page text login.title=Welcome to EE557 - Please Login login.message=Enter your username and password: login.username=Username: login.password=Password: login.button.commit=Login login.help.message=Login Help #loggedin page text loggedin.title=Login Project loggedin.msg=Welcome, {0}. You are now logged in.
EE557: Server-Side Development Object-Relational Persistence • Majority of Applications require persistent data, typically in a DBMS • Relational DBMS are designed to be understandable in a human way • Java Applications are written with an object-oriented approach • Relational tables in rows and columns vs objects with attributes • and associations • There is an overhead in moving from objects -> relational data and from relational data -> objects • This programming overhead is known as the object-relational gap • Let’s look at some code!
EE557: Server-Side Development Object-Relational Persistence public class Customer { private Long ID; private String surname; private String firstname; private String email; // Constructor public Customer(Long ID, String surname, String firstname, String email) { this.ID = ID; this.surname = surname; this.firstname = firstname; this.email = email; } // Now the get methods public Long getID() { return ID; } public String getName() { return firstname + " " + surname; } public String getSurname() { return surname; } public String getFirstname() { return firstname; } public String getEmail() { return email; } // And some set methods public void setID(Long value) { ID = value; } public void setSurname(String value) { surname = value; } public void setFirstname(String value) { firstname = value; } public void setEmail(String value) { email = value; } }
EE557: Server-Side Development Object-Relational Persistence • We can manually perform something like the following • public boolean addCustomer(Customer customer) { • // JDBC Connection and statement setup..... • PreparedStatement pstmt = con.prepareStatement("INSERT INTO CUSTOMERS (ID,SURNAME,FIRSTNAME,EMAIL) VALUES (?,?,?,?)"); • pstmt.clearParameters(); • pstmt.setString(1, customer.getID()); • pstmt.setString(2, customer.getFirstname()); • pstmt.setString(3, customer.getSurname()); • pstmt.setString(4, customer.getEmail()); • pstmt.executeUpdate(); // handle closing down of connections etc. • } • Rather simply we just map each field of the object to one column in a • database table called CUSTOMERS • This works correctly!
EE557: Server-Side Development Object-Relational Persistence • However, what happens with our objects have many separate multi-dimensional • fields and nested relationships? • Consider if we wanted to add a Vector of billing addresses to our Customer object • ... private String email; • private Vector billingAddresses; .... • public Vector getBillingAddresses() { return billingAddresses; } • public void setBillingAddresses(Vector value) { billingAddresses = value; } .... • So how do we handle this two dimensional array of addresses? • - Create a nested table in the DBMS (if supported) ? • - Create a separate table CUSTOMERADDRESSES + FK/PK relationship • How to handle relationships with other object tpes… Eg. Customers with Orders • As the data structures become more complicated so do our DBMS structures and • persistence code
EE557: Server-Side Development Object-Relational Persistence • Estimated by some, that 35% of application code is spent on these conversions • Look at some options! • Hand-Coding • There are design patterns which help handwrite the JDBC code to interact • with databases. • However, the work involved is often considerable as we have discussed • Serialization • As we have seen, serialization provides the ability to write objects to a byte-stream • We can then persist this byte-stream in a database • Not suitable for searches or arbitrary data retrieval. Eg. to change an email address • of a customer you would need to deserialise, change the object and serialise again
EE557: Server-Side Development Object-Relational Persistence • Entity Beans (EJBs) • Very popular approach for persisting object data • BMP (Bean Managed Persistence) requires handcoded JDBC, with associated • problems • CMP (Container Managed Persistence) Beans let the container handle persistence • with the database • CMP mapping configuration difficult, discrepancies between application servers • and varying capabilities many developers looking for alternative solutions
EE557: Server-Side Development Object-Relational Persistence • Object-Oriented Database Systems • In the mid-90s object-oriented database management systems (OODBMS) gained • attention • OODBMS offer seamless integration into the object-oriented application • environment • OODBMS have not had much uptake and are not a popular solution • RDBMS still contain the vast majority of the market share
EE557: Server-Side Development Object-Relational Persistence • Object/Relational Mapping (ORM) • ORM is the automated persistence of objects in a Java application to tables in • a relational database • This is achieved through configuration, where you define the way you map your • classes to tables once (which property to which column, which class to which table • etc.) • Now we can: • orm.save(myCustomer); • or • myCustomer = orm.load(Customer.class, customerID); • ORM implementations are complex and not recommended for small projects due • to their steep learning curve. However they have considerable advantages.
EE557: Server-Side Development Object-Relational Persistence • Object/Relational Mapping (ORM) • Advantages • Productivity – Eliminates much of the grunt work for developers • Maintainability – Few lines of developer code, modifications to data structure only • require changes to the ORM configuration (rather than code + SQL) • Performance – ORM is known for its database efficiency • Vendor Independence – ORMs abstract your application away from underlying • SQL databases, SQL dialects and reliance on application servers (like with EJBs)
EE557: Server-Side Development Object-Relational Persistence • Hibernate • Hibernate is the most mature and complete open-source, object relational mapper • Developed by a team of Java software developers from around the world • It can significantly reduce development time on large-scale projects • Has experienced considerable growth in popularity in recent years How does it work?
EE557: Server-Side Development Hibernate package edu.ee.beans; public class Customer { private int id; private String username; private String password; private String firstname; private String surname; private String email; public Customer(int id, String username, String password, String firstname, String surname, String email) { super(); this.id = id; this.username = username; this.password = password; this.firstname = firstname; this.surname = surname; this.email = email; } public Customer() { } public int getId() { return id; } public void setId(int id) { this.id = id; }
EE557: Server-Side Development Hibernate public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getFirstname() { return firstname; } public void setFirstname(String firstname) { this.firstname = firstname; } public String getSurname() { return surname; } public void setSurname(String surname) { this.surname = surname; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } }
EE557: Server-Side Development Hibernate • Hibernate code to create a new Customer: • Customer customer = new Customer(); • customer.setUsername("smithj"); • customer.setPassword("mypass"); • customer.setFirstname("John"); • customer.setSurname("Smith"); • customer.setEmail("john.smith@dcu.ie"); • Session hibernateSession = HibernateUtil.getSession(); hibernateSession.save(customer); • OK – so there’s a little more to it than this!
EE557: Server-Side Development Hibernate • Where is the SQL? There isn’t any! • Behind the scenes: Hibernate will use some mapping information • to generate appropriate SQL for the underlying database, such as: • insert into CUSTOMERS (ID, USERNAME, PASSWORD, FIRSTNAME,SURNAME, EMAIL) values (1, ‘smithj’, ‘mypass’, ‘John’, ‘Smith’, ‘john.smith@dcu.ie’); • Most of the work in Hibernate is on developing these mappings • - Configuration of which database/details we will be using • - Configuration of the mappings for CRUD operations on our classes • Developers still need to understand SQL for a range of reasons • In particular, even for Hibernate we still need a strong understand of • primary and foreign key relationships and data integrity
EE557: Server-Side Development Hibernate • Previous technique for performing mappings • <hibernate-mapping> • <class name=”edu.ee.beans.Customer table=”Customer”> • <id name=”id” column=”id”> • <generator class=”native”/> • </id> • <property name=”username” column=”username”> • <property name=”password” column=”password”> • <property name=”firstname” column=”firstname”> • <property name=”surname” column=”surname”> • <property name=”email” column=”email”> • </class> • </hibernate-mapping>
EE557: Server-Side Development Hibernate • New technique using Java Annotations, introduced in Java 1.5 • Removes reliance on awkward XML configuration files (apart from one) • Rather than having a separate configuration file, we “annotate” our • JavaBeans directly putting the mapping information into the beans
Annotated Bean package edu.ee.beans; import javax.persistance.*; @Entity public class Customer { private int id; private String username; private String password; private String firstname; private String surname; private String email; public Customer(int id, String username, String password, String firstname, String surname, String email) { super(); this.id = id; this.username = username; this.password = password; this.firstname = firstname; this.surname = surname; this.email = email; } public Customer() { } @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } ….. ….. other getter and setter methods as before…..
EE557: Server-Side Development Hibernate • @Entity - tells Hibernate that it will be responsible for handing the • database operations for this bean • @Id - identifies the primary key • @GeneratedValue - indicates that the primary key will be automatically • generated • By default, all of the remaining fields are mapped to columns of the same • name (hence, few annotations in this example – can be overridden)
EE557: Server-Side Development Handwritten JDBC vs Hibernate • Hibernate takes time to learn – temptation to just continue writing JDBC/SQL • Want to change to a different database system (RDBMS) which has a considerably different implementation of SQL? Hand-written JDBC Code: Big problem! Rewrite of most embedded SQL.Hibernate: No problem! Change three lines in a Hibernate configuration file! • Fed up manually creating table structures using the ‘CREATE TABLE’ statement?Hand-written JDBC Code: Keep doing it (or use a UI)Hibernate: ONE line of code can be used to create ALL of your tables automatically, in whichever database dialect you have configured. • Tired of constantly getting database errors because you are moving between Java objects and relational database tables?Hand-written JDBC Code: No other optionHibernate: work with objects 100% of the time and never write SQL manually again!
EE557: Server-Side Development Hibernate – Getting Started • What do we need? • A JDBC compliant database: Hibernate will work with all popular databases. (Already have this!) • JDK 5.0 or better: as annotations didn’t exist before Java 5. (Already have this!) • JDBC Drivers for your database: the JDBC drivers are used by Hibernate to connect to the database. (Already have this!) • Eclipse: our development environment (Already have this!) • Hibernate: we require the various JAR files and libraries which form Hibernate. (New!) • A hibernate.cfg.xml file on your classpath (New!)
EE557: Server-Side Development Hibernate – Getting Started • 5. Hibernate JAR Files and Libraries • To use Hibernate we require a number of JAR files, containing both • Hibernate and Hibernate Annotations support. • Up until Hibernate Version 3.4 Hibernate Core and Annotations libraries • Were separate downloads • Version 3.5.x onwards Hibernate Annotations is bunded with Core • There are additionally a number of support libraries that Hibernate uses, • which we must also include – these are also bundled • We actually include about a dozen individual JAR files in our Hibernate • applications (import into WEB-INF/lib and set up build paths)
EE557: Server-Side Development Hibernate – Getting Started • 6. A Working hibernate.cfg.xml file • Special configuration file that tells Hibernate • where our database is • what database driver to use to connect to the database • what type of database “dialect” to use • what the connection URL is • what the database username and password are • other configurations…
EE557: Server-Side Development Hibernate • <?xml version='1.0' encoding='utf-8'?> • <!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN" "http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd"> • <hibernate-configuration> • <session-factory> <!-- Database connection settings --> • <property name="connection.driver_class">oracle.jdbc.driver.OracleDriver</property> • <property name="connection.url">jdbc:oracle:thin:@136.206.35.131:1521:SSD</property> • <property name="connection.username">ee_user</property> • <property name="connection.password">ee_pass</property> • <!-- JDBC connection pool (use the built-in) --> • <property name="connection.pool_size">0</property> <!-- SQL dialect --> • <property name="dialect">org.hibernate.dialect.Oracle10gDialect</property> • <property name="transaction.factory_class">org.hibernate.transaction.JDBCTransactionFactory</property> • <!-- Enable Hibernate's current session context --> • <property name="current_session_context_class">thread</property> • <!-- Echo all executed SQL to stdout --> • <property name="hibernate.show_sql">true</property> • <!-- Echo all executed SQL to stdout --> • <property name="show_sql">true</property> • </session-factory> • </hibernate-configuration> • Do not try to learn off these configuration files!
EE557: Server-Side Development HibernateUtil.java • Not listed in Steps 1-6 as it is an additional optional step for our setup • Support helper class we will use to directly interact with Hibernate • Provides a number of methods which make interacting with Hibernate • easier and with fewer lines of code • Open HibernateUtil in Eclipse • Number of methods in HibernateUtil
EE557: Server-Side Development HibernateUtil.java (don’t learn this off!) • getInitializedConfiguration(): Configures and AnnotationConfiguration • object. This is where we tell Hibernate to manage responsibility for • database operations for our beans. Simply annotating beans is not enough! • recreateDatabase() – once the AnnotationConfiguration object has been • configured, Hibernate can automatically generated the SQL required to • automatically create all of the applicable tables • Note: We need to be careful with this method! • getSession() – the key to performing database operations. When we are • calling Hibernate operations, we always open a session • beginTransaction() – most database operations are performed within a • ‘Transaction’ providing the scope to commit and rollback. • commitTransaction(), rollbackTransaction(), closeSession()
EE557: Server-Side Development Hibernate Eclipse Project • Download the hibernate.zip to your local harddrive • In Eclipse, select File -> Import -> General -> Existing • Projects into Workspace -> Next • Select Archive File -> Browse to the hibernate.zip file -> • Select the Project -> Next
EE557: Server-Side Development Creating,Reading,Updating,Deleting • Previously covered in SQL using INSERT, SELECT, UPDATE and DELETE • We want to use Hibernate code to automatically generate these SQL statements • Will demonstrate using the existing template we have created • i.e. the Customer bean
EE557: Server-Side Development Creating • Simple create a new Customer object, populate it and ask a Hibernate Session to • ‘save’ the object • Session hibernateSession = HibernateUtil.beginTransaction(); • Customer customer = new Customer(); • customer.setUsername("smithj"); • customer.setPassword("mypass"); • customer.setFirstname("John"); • customer.setSurname("Smith"); • customer.setEmail("john.smith@dcu.ie"); • hibernateSession.save(customer); • HibernateUtil.commitTransaction();
EE557: Server-Side Development Retrieving (HQL) • As with SQL queries, a little more complicated than adding a record: • Session hibernateSession = HibernateUtil.beginTransaction(); • List<Customer> allCustomers = null; • Query queryResult = hibernateSession.createQuery("from Customer"); • allCustomers = (List<Customer>) queryResult.list(); • for (int i = 0; i < allCustomers.size(); i++) { • Customer customer = (Customer) allCustomers.get(i); • System.out.println("Customer name is : " + customer.getFirstname() + " " + customer.getSurname()); • } • HibernateUtil.commitTransaction();
EE557: Server-Side Development Retrieving (HQL) • Looks suspiciously like SQL • Query queryResult = hibernateSession.createQuery("from Customer"); • This is a HQL (Hibernate Query Language) query • Object oriented equivalent of ‘select * from Customer_David123’ • Note the case on ‘Customer’. HQL operates on the objects themselves, we are • not referring to a database table called Customer. Case sensitive! (unlike SQL) • Instead of returning rows, we are returning a List of Customer objects • No need to convert back into object oriented form – use your beans immediately! • More on HQL later!
EE557: Server-Side Development Retrieving a Unique Entity • Returning all entities not always efficient (tables with millions of rows?) • Frequently we want to return one entity, based on a primary key value • We have already defined ‘id’ as the primary key of our Customer class • How we would do it in JDBC handwritten code: • // All of the code to create connection above here • String mySQLQuery = "select * from customer_david123 where id = ?"; PreparedStatement ps = con.prepareStatement(mySQLQuery); • ps.setString(1, idVariable); • ResultSet rs = ps.executeQuery(); • if (rs.next()) { • // continue code here.. manually create the Customer object using the various row • // components...
EE557: Server-Side Development Retrieving a Unique Entity • Using Hibernate, we do something similar to PreparedStatements • In Hibernate we achieve ‘variable injection’ by preceding the variable name with • a colon • Because we are expecting a single entity to be returned, we don’t return a List like • before. Rather we return a Customer object directly • Session hibernateSession = HibernateUtil.beginTransaction(); • String queryString = "from Customer where id = :id"; • Query query = session.createQuery(queryString); • query.setInteger("id", idVariable); • Customer customer = (Customer) query.uniqueResult(); System.out.println("Customer Name = " + customer.getFirstname() + " " + • customer.getSurname()); • HibernateUtil.commitTransaction();
EE557: Server-Side Development Updating • Updating is straightforward – really a combination of retriving, modifying the entity • and then saving. • Example to change the passwords of all Customer objects • save(): new entry, update(): existing entry, alternatively saveOrUpdate() • Session hibernateSession = HibernateUtil.beginTransaction(); • List<Customer> allCustomers = null; • Query queryResult = hibernateSession.createQuery("from Customer"); allCustomers = (List<Customer>) queryResult.list(); • for (int i = 0; i < allCustomers.size(); i++) { • Customer customer = (Customer) allCustomers.get(i); customer.setPassword("password"); • hibernateSession.update(customer); • } • HibernateUtil.commitTransaction();
EE557: Server-Side Development Deleting • Delete is the same as the update() example, except we use delete() instead of • update() • In previous example, update() to delete() would delete all Customer entities • More commonly we want to delete a specific Customer (by primary key) • Session hibernateSession = HibernateUtil.beginTransaction(); • Customer customer = new Customer(); • customer.setId(1); • hibernateSession.delete(customer); • HibernateUtil.commitTransaction()
EE557: Server-Side Development Combined CRUD Example • Deploy and demonstrate CRUDExample.java • View all of the generated SQL in the output of the application
EE557: Server-Side Development Moving Database Vendor • Consider our previous example (and any other code we might write). To move: • Handwritten SQL • Rewrite all ‘CREATE TABLE’ statements and any other DDL • Download new JDBC JAR file, import into project • Rewrite all methods containing JDBC code (potentially) • Debug and test SQL • Hibernate • Download new JDBC JAR file, import into project • Edit 5 lines in hibernate.cfg.xml
EE557: Server-Side Development Hibernate Query Language • Why learn another query language!? I just spent ages learning SQL! • Number of differences between SQL and HQL • Different syntax – need to be learned separately • Database Independence: HQL is database independent, SQL is not • Relational vs Object Based: HQL is object based, SQL is relational • Relationship with Database Structure: in SQL, statements explicitly refer • to the database table and column names. In HQL instead we rely on the • mappings. Hence changes in database structures, renaming of tables • and fields are easier to handle under HQL. Only the mappings need to • be changed, not the HQL • Later on we will look at the Criteria API which will even remove much of the • Need to learn HQL
EE557: Server-Side Development Basic HQL To retrieve all Customer objects: Session session = HibernateUtil.beginTransaction(); Query query = session.createQuery("from Customer"); List customers = query.list(); for (int i = 0; i < customers.size(); i++) { Customer customer = (Customer) customers.get(i); / / Now we can do whatever we want with these objects System.out.println("Customer firstname is " + customer.getFirstname()); }
EE557: Server-Side Development Basic HQL • Don’t always want to return all columns • To return particular columns only • Query query = session.createQuery("SELECT firstname from Customer"); • List names = query.list(); • for (int i = 0; i < names.size(); i++) { • String name = (String) names.get(i); • System.out.println("Name is " + name); • } • Example with a where clause: • Query query = session.createQuery("from Customer where firstname='David'); • // List results in the usual manner using Customer objects