8. Case Study: Data System Client Application, Applets Server Applications Servlets Databases
System Goals
✦
Develop a system for managing large numbers of double-precision data values. – Create – Store – Display
Chart Data System Data generation Client
1
Data viewer Client
Data viewer Client
3
Browser: pure HTML
Java application
Browser: 4 Java applet
RMI
http 2
RMI
5 Java Application
Java Servlet
JDBC
JDBC RDMS Web Server
Middleware / Web Server
Database Schema extra
chart id: Integer value: Double label: Char[10]
✦ ✦ ✦ ✦ ✦
maxID: Integer id
va lu e
1 2 3… … … 11,497
la b e l 0.75 "A B C " 0.5 "H a lf" … … 0.785 "S in e "
Two simple tables. id is primary key for chart table. Data values are double-precision. Each value has an associated label. Only one row in extra table.
maxID 11,497
Sample Programs Servlet
3-Tier Applet/Application
Sample Programs ✦
✦
✦
✦
ChartServlet - data generation client that uses JDBC to insert data into database. ChartApp - data display client. Can be run as applet or as stand-alone application. ServerApp - middleware that uses JDBC to query database. Relational Databases – InstantDB 100% Pure Java relational database. – Sybase SQL Anywhere 5.0.
9. Case Study: Data Input Servlet Servlets Databases
Objectives ✦
✦
Be able to find performance bottlenecks in a servlet. Be able to more efficiently insert data into a SQL database. – use JDBC prepared statements. – use JDBC transactions.
Chart Data System Data generation Client
1
Browser: pure HTML
http 2 Java Servlet
JDBC RDMS Web Server
Database Schema chart id: Integer value: Double label: Char[10]
✦ ✦ ✦ ✦ ✦
id
va lu e
1 2 3… … … 11,497
la b e l 0.75 "A B C " 0.5 "H a lf" … … 0.785 "S in e "
extra maxID: Integer
Two simple tables. id is primary key for chart table. Data values are double-precision. Each value has an associated label. Only one row in extra table.
maxID 11,497
ChartServlet Sample Program
"chart.html" Calls Servlet <FORM ACTION="http://northptpc:8080/servlet/chart" METHOD="POST" ENCTYPE="application/x-www-form-urlencoded"> <H1>Add Chart Data</H1> Number of data points to add: <INPUT TYPE="TEXT" NAME="number" SIZE="7" MAXLENGTH="7"></H3> <INPUT TYPE="RADIO" NAME="RadioGroup" VALUE="random"> Random</P> <P><INPUT TYPE="RADIO" NAME="RadioGroup" VALUE="sine"> Sine</P> <P><INPUT TYPE="RADIO" NAME="RadioGroup" VALUE="half"> 0.5</P> <INPUT TYPE="SUBMIT" NAME="Submit" VALUE="Submit"></H3> </FORM>
✦ ✦
Calls servlet named "chart" when Submit button is pushed. Servlet gets values of named parameters.
chartServlet.properties File servlet.chart.code=ChartServlet servlet.chart.initArgs=\ driver=jdbc.idbDriver,\ url=jdbc:idb:d:\\+Java\\idb3_00\\ChartData2\\sample.prp,\ user=dba,\ pw=sql
✦ ✦
First line maps "chart" name used in HTML file to Java class name. initArgs is used to pass arguments to the servlet. – like parameters in HTML applet tag.
Servlet: Put Data Into DB HttpServlet
ChartServlet* fDriver fURL fUser fPassword fConnection fStart fStop kMaxIDQuery fMaxIDQueryStmt kID kValue kLabel kInsert fInsertStmt init destroy displayInitParameters doPost getMaxID fillRandom fillSine fillHalf prepareStatements openDBConnection
SingleThreadModel
✦
✦
init() and doPost() called when Submit button pressed. Connects to database and inserts data.
ChartServlet Methods public class ChartServlet extends HttpServlet implements SingleThreadModel { public void init(ServletConfig config); public void destroy(); public void doPost(HttpServletRequest req, HttpServletResponse res); protected void openDBConnection(); protected void prepareStatements(); protected void displayInitParameters(); protected int getMaxID(); protected void fillRandom(); protected void fillSine(); protected void fillHalf(); }
ChartServlet Variables public class ChartServlet extends HttpServlet implements SingleThreadModel { private String fDriver; private String fURL; private String fUser; private String fPassword; private Connection fConnection; private int fStart; private int fStop; private PreparedStatement fMaxIDQueryStmt; private PreparedStatement fInsertStmt; private final static String kInsert = "INSERT INTO chart VALUES(?,?,?)"; private final static String kMaxIDQuery = "SELECT maxID FROM extra"; }
ChartServlet.init() Method public void init(ServletConfig config) throws ServletException { super.init(config); fDriver = getInitParameter("driver"); fURL = getInitParameter("url"); fUser = getInitParameter("user"); fPassword = getInitParameter("pw"); try { openDBConnection(); prepareStatements(); fStart = getMaxID(); } catch (Exception e) {e.printStackTrace();} }
doPost() Action public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { fStop = fStart + Integer.parseInt(req.getParameter("number")); String fillMethod = req.getParameter("RadioGroup"); try { if (fillMethod.equals("random")) fillRandom(); else if (fillMethod.equals("sine")) fillSine(); else if (fillMethod.equals("half")) fillHalf(); updateMaxID(); â&#x20AC;Ś // response stuff next slide } catch (SQLException e) {e.printStackTrace();} }
Inserting Data into Database protected void fillRandom() throws SQLException { Statement stmt = fConnection.createStatement(); for (int i = fStart; i<= fStop; i++) { String cmd = "INSERT INTO chart " + "VALUES ( " + i + "," + Math.random() + ", 'rdm' )"; stmt.executeUpdate(cmd); } stmt.close(); }
✦ ✦
Simplest way to do it. May not be the fastest.
doPost() Response public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { ... res.setContentType("text/html"); PrintWriter toClient = res.getWriter(); toClient.println("<html>"); toClient.println( "<title>Data Modified!</title>"); toClient.println( "Num data points: " + getMaxID()); toClient.println("</html>"); toClient.close(); ...
Using ServletRunner call setJini :: enables servlet stack tracing SET JAVA_COMPCMD=STA(CKTRACE) SET sDir=d:\Samples\13_ChartServlet SET root=d:\Samples\13_ChartServlet SET propertiesFile= d:\Samples\13_ChartServlet\chartServlet.properties %servletDir%\bin\servletrunner -d %sDir% -r %root% -s %propertiesFile%
â&#x153;Ś
Starts servletRunner.
Performance OK? ✦
✦ ✦
Relational databases – Sybase SQL Anywhere 5.0. ◆ Sun's JDBC-ODBC bridge driver. ◆ Does not like JDK 1.2.2 HotSpot! – InstantDB 3.0. ◆ Own Java driver. ◆ Only one JVM at a time. Windows 95; 266 MHz Pentium; 128 MB RAM. Up to 20000 rows of data used.
Find Bottlenecks Рюд
OptimizeIt script:
call setjini SET OPTIT_LIB=d:\OptimizeIt30\lib\optit.jar; SET JSDK_LIB=d:\jsdk2.0\lib\jsdk.jar; SET IDB_LIB=d:\sample\idb3_00\classes\idb.jar SET CLASSPATH =%CLASSPATH%;%OPTIT_LIB%;%JSDK_LIB%;%IDB_LIB% SET OPTIT_PATH=d:\OptimizeIt30\lib SET SERVLET_DIR=d:\sample\PerfSamples_10-09-99\09b_ChartServlet java -Xrunoii -Djava.compiler=NONE -Xnoclassgc -classpath "%CLASSPATH%" intuitive.audit.Audit -pause -dmp -dllpath "%OPTIT_PATH%" sun.servlet.http.HttpServer -d %SERVLET_DIR% -r %SERVLET_DIR% -s %SERVLET_DIR%\chartServletIDB.properties
Using OptimizeIt for Servlet ✦ ✦
✦ ✦
Run the servlet script Attatch the servlet into OptimizeIt program
Start CPU profiling. Submit Add Chart Data information.
Do Exercise 9
OptimizeIt CPU Profiler
✦ ✦ ✦
Only 2% of time spent with maxID - good. 63% of time spent in fillHalf(). May need a faster database to reduce this significantly. These tests had client and server on same machine. Bad idea.
Fix Bottlenecks ✦
✦
Use Prepared Statements. – Database can precompile parameterized statements. Best for repeated statements. Use JDBC Transactions for groups of statements. – By default, each executeUpdate() statement is treated as a separate transaction. ◆ Database may lock records. ◆ Commit is called each time. – Better to group all updates into a single JDBC transaction.
Create Prepared Statements private final static String kInsert = "INSERT INTO chart VALUES(?,?,?)"; private final static String kMaxIDQuery = "SELECT max ID FROM extra"; //-------------------------------------------protected void prepareStatements() throws SQLException { fInsertStmt = fConnection.prepareStatement(kInsert); fMaxIDQueryStmt = fConnection.prepareStatement(kMaxIDQuery); }
â&#x153;Ś
Create precompiled SQL statements for later execution.
Using PreparedStatment protected void fillHalf() throws SQLException { for (int i = fStart; i<= fStop; i++) { fInsertStmt.setInt(kID, i); fInsertStmt.setDouble(kValue, 0.5); fInsertStmt.setString(kLabel, "half"); fInsertStmt.executeUpdate(); } }
â&#x153;Ś
Faster than regular statement if execute it many times.
Use JDBC Transaction: try protected void fillSine() { try { fConnection.setAutoCommit(false); for (int i = fStart; i<= fStop; i++) { fInsertStmt.setInt(kID, i); fInsertStmt.setDouble(kValue, Math.sin(i)); fInsertStmt.setString(kLabel, "sine"); fInsertStmt.executeUpdate(); } fConnection.commit(); } catch // see next slide }
â&#x153;Ś
All or nothing. May be the fastest.
Use JDBC Transaction: catch protected void fillSine() { try { // see previous slide } catch (SQLException e) { try { fConnection.rollback(); e.printStackTrace(); } catch (SQLException ex) { ex.printStackTrace(); } } finally { try { fConnection.setAutoCommit(true); } catch (Exception ex2) { ex2.printStackTrace(); } } }
Performance Comparison M e th o d / D B
sq lA n y 2 0 0 (se c) ID B 5 0 0 (se c) 1 .4 8 .0 S ta te m e nt (ra ndo m ) 0 .9 6 .0 P re pS tm t (ha lf) 0 .5 3 .0 P re pS tm t + T ra ns (sine ) â&#x153;Ś
â&#x153;Ś
PreparedStatement faster than Statement. PreparedStatement + transactions is best in this example.
Summary ✦ ✦
✦
Can use OptimizeIt or JProbe to profile servlet. Use PreparedStatement for repeated commands. Use Transactions for grouped commands.
Notes