This content is not examinable so please don't study it if you come across it! It is merely a place for me to put perfectly good examples and code that isn't currently in the module syllabus. Hibernate (Retired)One Class to Two Tables (Retired)Let us consider a scenario where our application defines a single class, but the database maintains the corresponding information in two seperate tables. This process is relatively straightforward. To map one Java class to two database tables we define the first table mapping as we normally would and then specify the name of the second table your class uses with the @SecondaryTable annotation. Then when you get to a field that should be stored in the second table, you just mention the second table's name in the @Column annotation. Let's take an example. Let us expand on our Customer object and add a few fields to represent a billing address: street, city and postcode. We wish our mapping to look like the following:
You will have noticed that I have renamed the class to Customer2 and the corresponding table names will be calledCustomer_David456 and Billing_David456. While we could have left the class and the first table with the same names as before, it would overwrite our previous examples. To avoid this situation, we have simply given them new names. Let us take a look at our annotated Customer2.java file which will perform this mapping to two tables. package edu.ee.beans; import javax.persistence.*; /** * Our Customer JavaBean with Annotations (POJO) * @author David Molloy * Don't forget to change the table names to your own values! */ @Entity @Table (name="Customer_David456") @SecondaryTable(name="Billing_David456") public class Customer2 { private int id; private String username; private String password; private String firstname; private String surname; private String email; private String street; private String city; private String postcode; public Customer2(int id, String username, String password, String firstname, String surname, String email, String street, String city, String postcode) { super(); this.id = id; this.username = username; this.password = password; this.firstname = firstname; this.surname = surname; this.email = email; this.street = street; this.city = city; this.postcode = postcode; } public Customer2() { } @Id @GeneratedValue public int getId() { return id; } public void setId(int id) { this.id = id; } 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; } @Column(table="Billing_David456") public String getStreet() { return street; } public void setStreet(String street) { this.street = street; } @Column(table="Billing_David456") public String getCity() { return city; } public void setCity(String city) { this.city = city; } @Column(table="Billing_David456") public String getPostcode() { return postcode; } public void setPostcode(String postcode) { this.postcode = postcode; } } There are a few minor differences between Customer2 and the original Customer. Firstly, the new properties have been added to the bean, the appropriate getter and setter methods and a few extra annotations. Specifically, the new annotations are: @SecondaryTable(name="Billing_David456") at the top of the class where we define the @Entity and: @Column(table="Billing_David456") placed above each of the getter() methods for the fields we have stored in the second database table. So let's write a quick application which will demonstrate this annotated bean in action: import edu.ee.beans.Customer2; import edu.ee.hibernate.HibernateUtil; import org.hibernate.Criteria; import org.hibernate.Session; import org.hibernate.criterion.Example; /** * Example Class to show Hibernate CRUD Operations * @author David Molloy * */ public class OneToTwoExample { /** * Main method which runs first... we can modify the methods * that are called for different results */ public static void main(String[] args) { Session session = HibernateUtil.beginTransaction(); // Comment this next line if you want to stop dropping and recreating tables every execution HibernateUtil.recreateDatabase(); // First create some customer entities using a method we have created CreateCustomer2(session, "Michael", "Reilly", "reillym", "password", "michael.reilly@email.com", "123 Fake Street", "Faketown", "Fake123"); showUniqueCustomer2ByUsername(session, "reillym"); // Display all of our Customer entities HibernateUtil.commitTransaction(); HibernateUtil.closeSession(); } /** * This method creates a new Customer entity in the database using the * fields provided as arguments */ public static void CreateCustomer2(Session session, String firstname, String surname, String username, String password, String email, String street, String city, String postcode) { System.out.println("Creating new Customer2 with Billing"); Customer2 customer2 = new Customer2(); customer2.setFirstname(firstname); customer2.setSurname(surname); customer2.setUsername(username); customer2.setPassword(password); customer2.setEmail(email); customer2.setStreet(street); customer2.setCity(city); customer2.setPostcode(postcode); session.save(customer2); System.out.println("Customer2 Saved!"); } /** * This method will print out the name of the Customer2 who matches a username * This will be a unique single user as we will assume a unique username */ private static void showUniqueCustomer2ByUsername(Session session, String username) { Customer2 c = new Customer2(); c.setUsername(username); Example example = Example.create(c); Criteria criteria = session.createCriteria(Customer2.class); criteria.add(example); Customer2 customer2 = (Customer2) criteria.uniqueResult(); System.out.println("\nUnique customer matchining username " + username + " is " + customer2.getFirstname() + " " + customer2.getSurname()); } } As a brief explanation of the above:
There is one other step which needs to be added. As we are using a seperate JavaBean called 'Customer2' this needs to be added to the list of JavaBeans for which Hibernate will manage persistence. To do this you need to add the line: config.addAnnotatedClass(Customer2.class); to the HibernateUtil.java file in the appropriate place. If we omit to do this, the JavaBean will still be technically error free, but Hibernate will not perform any database operations. The output from this application should look something like the following: 16:08:10,906 DEBUG SchemaExport:377 - drop table Billing_David456 cascade constraints drop table Customer_David123 cascade constraints 16:08:10,984 DEBUG SchemaExport:377 - drop table Customer_David123 cascade constraints drop table Customer_David456 cascade constraints 16:08:11,000 DEBUG SchemaExport:377 - drop table Customer_David456 cascade constraints drop sequence hibernate_sequence 16:08:11,031 DEBUG SchemaExport:377 - drop sequence hibernate_sequence create table Billing_David456 (city varchar2(255 char), postcode varchar2(255 char), street varchar2(255 char), id number(10,0) not null, primary key (id)) 16:08:11,062 DEBUG SchemaExport:377 - create table Billing_David456 (city varchar2(255 char), postcode varchar2(255 char), street varchar2(255 char), id number(10,0) not null, primary key (id)) create table Customer_David123 (id number(10,0) not null, email varchar2(255 char), firstname varchar2(255 char), password varchar2(255 char), surname varchar2(255 char), username varchar2(255 char), primary key (id)) 16:08:11,109 DEBUG SchemaExport:377 - create table Customer_David123 (id number(10,0) not null, email varchar2(255 char), firstname varchar2(255 char), password varchar2(255 char), surname varchar2(255 char), username varchar2(255 char), primary key (id)) create table Customer_David456 (id number(10,0) not null, email varchar2(255 char), firstname varchar2(255 char), password varchar2(255 char), surname varchar2(255 char), username varchar2(255 char), primary key (id)) 16:08:11,125 DEBUG SchemaExport:377 - create table Customer_David456 (id number(10,0) not null, email varchar2(255 char), firstname varchar2(255 char), password varchar2(255 char), surname varchar2(255 char), username varchar2(255 char), primary key (id)) alter table Billing_David456 add constraint FKCEE3086588C8C38E foreign key (id) references Customer_David456 16:08:11,140 DEBUG SchemaExport:377 - alter table Billing_David456 add constraint FKCEE3086588C8C38E foreign key (id) references Customer_David456 create sequence hibernate_sequence 16:08:11,140 DEBUG SchemaExport:377 - create sequence hibernate_sequence 16:08:11,156 INFO SchemaExport:268 - schema export complete 16:08:11,156 INFO DriverManagerConnectionProvider:170 - cleaning up connection pool: jdbc:oracle:thin:@136.206.35.131:1521:SSD Creating new Customer2 with Billing Hibernate: select hibernate_sequence.nextval from dual Customer2 Saved! Hibernate: insert into Customer_David456 (email, firstname, password, surname, username, id) values (?, ?, ?, ?, ?, ?) Hibernate: insert into Billing_David456 (city, postcode, street, id) values (?, ?, ?, ?) Hibernate: select this_.id as id1_0_, this_.email as email1_0_, this_.firstname as firstname1_0_, this_.password as password1_0_, this_.surname as surname1_0_, this_.username as username1_0_, this_1_.city as city2_0_, this_1_.postcode as postcode2_0_, this_1_.street as street2_0_ from Customer_David456 this_ left outer join Billing_David456 this_1_ on this_.id=this_1_.id where (this_.username=?) Unique customer matchining username reillym is Michael Reilly Looking at the output, there are a few points of note:
JDBC DriversIndividual databases are accessed via a specific JDBC driver that implements the java.sql.Driver inferface. JDBC drivers are available for most database platforms and from a number of different vendors. There are four different driver types as follows:
The following diagram shows the JDBC architecture options using the different driver types: Figure 7.4. JDBC Driver Architecture Java API for XML Processing (JAXP)When dealing with XML programmatically, one of the first things you have to do is take an XML document and parse it. Parsing is the process of dissecting a body of text into its individual component pieces. For example, if that body of text is a paragraph, parsing would break the paragraph into sentences. It would then break a sentence down to a subject and predicate. In turn, the subject and predicate would then be broken down into their components like nouns, verbs and adjectives. In the same way, we use parsing to obtain our data and meta-data from our XML documents. The Java API for XML Processing (JAXP) is for processing XML data using applications written in the Java programming language. JAXP uses the parser standards SAX (Simple API for XML Parsing) and DOM (Document Object Model) so that you can choose to parse your data as a stream of events or to build an object representation of it. JAXP also supports the XSLT (XML Stylesheet Language Transformations) standard, giving you control over the presentation of data and enabling you to convert the data to other XML documents or to other formats, such as HTML. The main JAXP APIs are defined in the javax.xml.parsers package. That package contains two vendor-independent factory classes:
Simple API for XML (SAX)The "Simple API for XML" (SAX) is the event-driven mechanism that does element-by-element processing. For server-side and high-performance applications it is typically SAX which is used. When it comes to fast, efficient reading of XML data, SAX is hard to beat. It requires little memory, because it does not construct an internal representation (tree structure) of the XML data. Instead, it simply sends data to the application as it is read - your application can then do whatever it wants to do with the data it sees. SAX is an event driven process to identify the elements as the parser reads them. It then informs the application of events, such as the start and end of elements. In effect, the SAX API acts like a serial I/O stream - you see the data as it streams in, but you can't go back to an earlier position or leap ahead to a different position. In general it works well when you simply want to read data and have the application act on it. As the data is parsed the various element events call handling methods such as startDocument, endDocument, startElement, endElement and characters. Document Object Model (DOM)The Document Object Model (DOM) API is generally an easier API to use. It provides a relatively familiar tree structure of objects. The DOM API is ideal for interactive applications because the entire object model is present in memory, where it can be accessed and manipulated by the user. On the negative side, constructing the DOM requires reading the entire XML structure and holding the object tree in memory, so it is much more CPU and memory intensive. For that reason, the SAX API will tend to be preferred for server-side applications and data filters that do not require an in-memory representation of the data. However, when you need to modify an XML structure - especially when you need to modify it interactively, an in-memory structure like the DOM may make more sense. While DOM provides many powerful capabilities for large-scale documents (like books), it also requires a lot of complex coding. Figure 3.4. DOM Tree Structure representing XML SAX vs DOMSAX
DOM
In previous years, more detail was looked at in relation to both SAX and DOM. However, due to the adding of other material, something had to give... so this is all we will look at in relation to SAX and DOM. If you are interested, you can find the material from a previous module at: http://wiki.eeng.dcu.ie/ee557/g2/626-EE.html Extensible StyleSheet Language (XSL)IntroductionThe eXtensible Stylesheet Language (XSL) is the language that describes how XML documents should be displayed. XSL was developed by the W3C Working Group and consists of three parts:
Style SheetsA stylesheet is an XML document contained within a stylesheet element. The style sheet transforms the input/source document's tree into a new structure. As usual, Style Sheets are probably best explaining by providing an example. First let us consider the XML data we will work on - this is a slightly modified version of the previous personnel.xml file. We have added one extra line, which makes reference to the XSL Style Sheet we wish to use.
So, now consider the following stylesheet:
As we previously stated, the stylesheet is an XML document contained within a stylesheet element. An xsl:stylesheet element must have a version attribute, indicating which version of XSLT that the style sheet requires. The xsl:stylesheet element, which is the root of the XSL Stylesheet XML tree, can have eleven child elements. In this module, we will not be describing all of these elements, but will be only covering a small number. One of these is the xsl:template element. This element is the most important top-level element in an XSL stylesheet. While all other elements add to a stylesheet, xsl:template is its core. xsl:template defines the template construction rules for translating one XML document to another format. XSL utilizes templates to define how to output XML elements. Each template contains match patterns that determine where the template applies. In this example, the template attribute match="/" associates that template to the root of the source document. The <xsl:for-each select="personnel/person"> line and corresponding closing tag is our equivalent to a Java 'for' loop within XSL. In english, for every <person> element within <personnel> we output the HTML shown within the loop. Within that loop, reference is made to <xsl:value-of select="firstname" /> which simply inserts the value of the <firstname> element encountered. Note: If you want to get the text value of attributes, you should use an @ symbol in front of the name. For example: <xsl:value-of select="@attributename" />. This is not used within this example, but is used in the later example for the account 'type'. As can be seen, our XSL document in this example, is a mixture of HTML and specific XSL elements. In this section on XSL we are merely touching on the very basics of XSL. It is quite a complicated yet very powerful language, yet for the purpose of this module, the information provided in the notes is sufficient. Client-side TransformationsMicrosoft Internet Explorer 5.0 and upwards actually goes a long way towards implementing client-side support for XML and XSL. However, as with any bleeding-edge product, MSIE 5.0 shipped before the standards had solidified, and unfortunately, much has changed. In addition MSIE 5.0 added to XML in many non-standard ways. While browser differences have not been completely ironed out in later versions of Internet Explorer, it is sufficient for use for this example. The following examples should function in recent versions of the following browsers (according to my testing!):
Figure 3.5. Client-Side vs Server-Side XSL Transformations So now, all that remains is the see the client-side transformation in action. It is for this reason that I left the link blank under the example above. In IE5.0+ if you type a URL which references an XML document and that document in turn references an XSL stylesheet, IE will perform a client-side transformation automatically and display the HTML. View the output in your browser: xslexample1.xml In case you wish to create the XML file in your own directory, here is the XSL file you will also require: xslexample1.xsl Note: If you try to use the above link using Netscape 4.7 or other non-XML supporting browsers, it will not be able to open the file without a third-party application. For the benefit of students who do not have Internet Explorer I provide the output of the above link as a screenshot viewed within IE5.0. Figure 3.6. Internet Explorer Client-Side Transformation Output Server-side TransformationReferencing stylesheets from within XML documents is not the optimal method for transformations. This method depends on the widespread adoption of browsers supporting the XSL recommendation--something you cannot count on if you are a web developer. By using the server's ability to detect the client's browser, you can have one XML file and apply a specific stylesheet based on the client profile. This allows the XML document on the server to be transformed using a specific stylesheet and then sent to the client as pure XHTML or other formats. Using the server's power to transform will also benefit slow connections and small devices. The benefits of server-side processing easily outweigh those of client-side processing. This fact should make server-side processing the preferred method for transformations. One obvious way of handling the transformation on the server-side is to use a stylesheet processor such as Xalan. Xalan provides high-performance XSLT stylesheet processing. Xalan fully implements the W3C XSLT and XPath recommendations and is available in Java and C++. So show Xalan in action, we can use it at the command line:
After this command is executed, a file called 'xslexample1.html' will be generated in the same directory. Hence, one obvious way of performing server-side transformations is to use a Xalan transformation at command line on the server and make the HTML document available. The primary advantage of taking such an option is that only the HTML is transported to the client browser and no transformation is expected to be performed there. This means that the XML output (in this case HTML) can be viewed from within non-supporting browsers such as Netscape 4.7, just as easily as within Internet Explorer 5.0. This server-side transformation also removes the concerns about lack of standards compliance from within the seperate browsers, even those who claim to support XML client-side transformations. Note: In order to get this example to work, you will need to have downloaded 'xalan.jar' which can come as part of the full distribution of Xalan, downloadable from the Apache XML Project. Alternatively, you can download the file itself (local JAR) which will allow you to get the example to run. XSL Example 2Question: Take this XML document and convert it to a similar HTML document as the one that is shown.
Source: xslcustomers.xml Expected HTML Output: xslcustomers.html Once you have created your stylesheet, perform a client-side transformation within Internet Explorer (if you have it) and a manual transformation using Xalan. AnswerThe following is the stylesheet that I created - it works similarly to the introduction example in the notes, except it has two 'for' loops instead of just one. Because there can be multiple 'account's per 'customer' we need the first loop to enumerate our customers and a second internal loop to enumerate through the individual accounts.
XSL Source: xslcustomers.xsl For the final parts of the question we do the following:
If you have created your stylesheet correctly, the output of either should be identical (or very close) to the original question requirement. One 'trick' which can be used for creating your stylesheet, so that your HTML is identical, is to view the source (right click on the page in IE) of the HTML document your wish to recreate. You can then see the HTML code necessary for displaying the page, which you can then utilize in your stylesheet. XSLT and JAXP (Java API for XML Processing)While server-side transformations may be performed using Xalan at command line, these are not real-time. Often it is necessary to perform transformations 'on-the-fly', creating customized HTML/XHTML/etc. views of your XML data. A simple example of this, is that you may wish to present your data in different ways depending on whether the client user is accessing your site using IE or Firefox or Chrome. The browser information is sent with the clients request and the server could then decide to render the HTML document in different ways. For this and many other reasons, we frequently need to be able to perform transformations from within our Java Applications. JAXP allows us to do this! There are four packages within JAXP, which we use for XSLT transformations:
A TransformerFactory object is instantiated, and used to create a Transformer. The source object is the input to the transformation process. A source object can be created from SAX reader, from a DOM, or from an input stream. Similarly, the result object is the result of the transformation process. That object can be a SAX event handler, a DOM, or an output stream. When the transformer is created, it may be created from a set of transformation instructions, in which case the specified transformations are carried out. If it is created without any specific instructions, then the transformer object simply copies the source to the result. Figure 3.7. JAXP XSLT API in Action The following example shows the JAXP XSLT transformation API in action.
Source: XMLtoHTML.java Usage Example: java XMLtoHTML xslcustomers.xsl xslcustomers.xml customeroutput.html |