Table of Contents
This chapter shows how to work with Cayenne in a web application.
The web part of the web application tutorial is done in JSP, which is the least common denominator of the Java web technologies, and is intentionally simplistic from the UI perspective, to concentrate on Cayenne integration aspect, rather than the interface. A typical Cayenne web application works like this:
Cayenne configuiration is loaded when an application context is started, using a special servlet filter.
User requests are intercepted by the filter, and the DataContext is bound to the request thread, so the application can access it easily from anywhere.
The same DataContext instance is reused within a single user session; different sessions use different DataContexts (and therefore different sets of objects). The context can be scoped differently depending on the app specifics. For the tutorial we'll be using a session-scoped context.
So let's convert the tutorial that we created to a web application:
In IDEA under "tutorial" project folder create a new folder
"src/main/webapp/WEB-INF
".
Under "WEB-INF
" create a new file "web.xml
" (a standard web app descriptor):
web.xml
<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> <web-app> <display-name>Cayenne Tutorial</display-name> <!-- This filter bootstraps ServerRuntime and then provides each request thread with a session-bound DataContext. Note that the name of the filter is important, as it points it to the right named configuration file. --> <filter> <filter-name>cayenne-project</filter-name> <filter-class>org.apache.cayenne.configuration.web.CayenneFilter</filter-class> </filter> <filter-mapping> <filter-name>cayenne-project</filter-name> <url-pattern>/*</url-pattern> </filter-mapping> <welcome-file-list> <welcome-file>index.jsp</welcome-file> </welcome-file-list> </web-app>
Create the artist browser page src/main/webapp/index.jsp
file with the
following contents:
webapp/index.jsp
<%@ page language="java" contentType="text/html" %> <%@ page import="org.example.cayenne.persistent.*" %> <%@ page import="org.apache.cayenne.*" %> <%@ page import="org.apache.cayenne.query.*" %> <%@ page import="org.apache.cayenne.exp.*" %> <%@ page import="java.util.*" %> <% ObjectContext context = BaseContext.getThreadObjectContext(); List<Artist> artists = ObjectSelect.query(Artist.class) .orderBy(Artist.NAME.asc()) .select(context); %> <html> <head> <title>Main</title> </head> <body> <h2>Artists:</h2> <% if(artists.isEmpty()) {%> <p>No artists found</p> <% } else { for(Artist a : artists) { %> <p><a href="detail.jsp?id=<%=Cayenne.intPKForObject(a)%>"> <%=a.getName()%> </a></p> <% } } %> <hr> <p><a href="detail.jsp">Create new artist...</a></p> </body> </html>
Create the artist editor page src/main/webapp/detail.jsp
with the following
content:
webapp/detail.jsp
<%@ page language="java" contentType="text/html" %> <%@ page import="org.example.cayenne.persistent.*" %> <%@ page import="org.apache.cayenne.*" %> <%@ page import="org.apache.cayenne.query.*" %> <%@ page import="java.util.*" %> <%@ page import="java.text.*" %> <%@ page import="java.time.format.DateTimeFormatter" %> <% ObjectContext context = BaseContext.getThreadObjectContext(); String id = request.getParameter("id"); // find artist for id Artist artist = null; if(id != null && id.trim().length() > 0) { artist = SelectById.query(Artist.class, Integer.parseInt(id)).selectOne(context); } if("POST".equals(request.getMethod())) { // if no id is saved in the hidden field, we are dealing with // create new artist request if(artist == null) { artist = context.newObject(Artist.class); } // note that in a real application we would so dome validation ... // here we just hope the input is correct artist.setName(request.getParameter("name")); artist.setDateOfBirthString(request.getParameter("dateOfBirth")); context.commitChanges(); response.sendRedirect("index.jsp"); } if(artist == null) { // create transient artist for the form response rendering artist = new Artist(); } String name = artist.getName() == null ? "" : artist.getName(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMdd"); String dob = artist.getDateOfBirth() == null ? "" :artist.getDateOfBirth().format(formatter); %> <html> <head> <title>Artist Details</title> </head> <body> <h2>Artists Details</h2> <form name="EditArtist" action="detail.jsp" method="POST"> <input type="hidden" name="id" value="<%= id != null ? id : "" %>" /> <table border="0"> <tr> <td>Name:</td> <td><input type="text" name="name" value="<%= name %>"/></td> </tr> <tr> <td>Date of Birth (yyyyMMdd):</td> <td><input type="text" name="dateOfBirth" value="<%= dob %>"/></td> </tr> <tr> <td></td> <td align="right"><input type="submit" value="Save" /></td> </tr> </table> </form> </body> </html>
We need to provide javax servlet-api for our application.
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>3.1.0</version> <scope>provided</scope> </dependency>
Also to run the web application we'll use "maven-jetty-plugin". To activate it,
let's add the following piece of code to the "pom.xml
" file, following the "dependencies"
section and save the POM:
<build> <plugins> <plugin> <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-maven-plugin</artifactId> <version>9.3.14.v20161028</version> </plugin> </plugins> </build>
Go to "Select Run/Debug Configuration" menu, and then "Edit Configuration..."
Click "+" button and select "Maven". Enter "Name" and "Command line" as shown on screenshot:
Click "Apply" and "Run". On the first execution it may take a few minutes for Jetty plugin to download all dependencies, but eventually you'll see the logs like this:
[INFO] ------------------------------------------------------------------------ [INFO] Building tutorial 0.0.1-SNAPSHOT [INFO] ------------------------------------------------------------------------ ... [INFO] Configuring Jetty for project: tutorial [INFO] webAppSourceDirectory not set. Trying src/main/webapp [INFO] Reload Mechanic: automatic [INFO] Classes = /.../tutorial/target/classes [INFO] Logging initialized @1617ms [INFO] Context path = / [INFO] Tmp directory = /.../tutorial/target/tmp [INFO] Web defaults = org/eclipse/jetty/webapp/webdefault.xml [INFO] Web overrides = none [INFO] web.xml file = file:/.../tutorial/src/main/webapp/WEB-INF/web.xml [INFO] Webapp directory = /.../tutorial/src/main/webapp [INFO] jetty-9.3.0.v20150612 [INFO] Started o.e.j.m.p.JettyWebAppContext@6872f9c8{/,file:/.../tutorial/src/main/webapp/,AVAILABLE}{file:/.../tutorial/src/main/webapp/} [INFO] Started ServerConnector@723875bc{HTTP/1.1,[http/1.1]}{0.0.0.0:8080} [INFO] Started @2367ms [INFO] Started Jetty Server
So the Jetty container just started.
Now go to http://localhost:8080/ URL. You should see "No artists found message" in the web browser and the following output in the IDEA console:
INFO: Loading XML configuration resource from file:/.../tutorial/target/classes/cayenne-project.xml INFO: loading user name and password. INFO: Connecting to 'jdbc:derby:memory:testdb;create=true' as 'null' INFO: +++ Connecting: SUCCESS. INFO: setting DataNode 'datanode' as default, used by all unlinked DataMaps INFO: Detected and installed adapter: org.apache.cayenne.dba.derby.DerbyAdapter INFO: --- transaction started. INFO: No schema detected, will create mapped tables INFO: CREATE TABLE GALLERY (ID INTEGER NOT NULL, NAME VARCHAR (200), PRIMARY KEY (ID)) INFO: CREATE TABLE ARTIST (DATE_OF_BIRTH DATE, ID INTEGER NOT NULL, NAME VARCHAR (200), PRIMARY KEY (ID)) INFO: CREATE TABLE PAINTING (ARTIST_ID INTEGER, GALLERY_ID INTEGER, ID INTEGER NOT NULL, NAME VARCHAR (200), PRIMARY KEY (ID)) INFO: ALTER TABLE PAINTING ADD FOREIGN KEY (ARTIST_ID) REFERENCES ARTIST (ID) INFO: ALTER TABLE PAINTING ADD FOREIGN KEY (GALLERY_ID) REFERENCES GALLERY (ID) INFO: CREATE TABLE AUTO_PK_SUPPORT ( TABLE_NAME CHAR(100) NOT NULL, NEXT_ID BIGINT NOT NULL, PRIMARY KEY(TABLE_NAME)) ... INFO: SELECT t0.DATE_OF_BIRTH, t0.NAME, t0.ID FROM ARTIST t0 ORDER BY t0.NAME INFO: === returned 0 rows. - took 17 ms. INFO: +++ transaction committed.
You can click on "Create new artist" link to create artists. Existing artists can be edited by clicking on their name:
You are done with the tutorial!