blob: 469cbf8bf5f28eefd6b8399e2f86cf433c06d754 [file] [log] [blame]
Title: Remote Object Persistence Tutorial Client Code
<H2><A name="RemoteObjectPersistenceTutorialClientCode-StartingCommandLineClient"></A>Starting Command Line Client</H2>
<P>One of the benefits of ROP is that the client code is no different from the server code - it uses the same ObjectContext interface for access, same query and commit API. So the code below will be similar to the code presented in the first <A href="tutorial-objectcontext.html" title="Tutorial ObjectContext">Cayenne tutorial</A>, although with a few ROP-specific parts required to bootstrap the ObjectContext.</P>
<P>Let's start by creating an empty Main class with the standard main() method in the client project:</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java"><SPAN class="code-keyword">package</SPAN> org.example.cayenne.persistent.client;
<SPAN class="code-keyword">public</SPAN> class Main {
<SPAN class="code-keyword">public</SPAN> <SPAN class="code-keyword">static</SPAN> void main(<SPAN class="code-object">String</SPAN>[] args) {
}
}</PRE>
</DIV></DIV>
<P>Now the part that is actually different from regular Cayenne - establishing the server connection and obtaining the ObjectContext:</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java">ClientConnection connection = <SPAN class="code-keyword">new</SPAN> HessianConnection(<SPAN class="code-quote">&quot;http:<SPAN class="code-comment">//localhost:8080/tutorial/cayenne-service&quot;</SPAN>);
</SPAN>DataChannel channel = <SPAN class="code-keyword">new</SPAN> ClientChannel(connection);
ObjectContext context = <SPAN class="code-keyword">new</SPAN> CayenneContext(channel);</PRE>
</DIV></DIV>
<P>Note that the &quot;channel&quot; can be used to create as many peer ObjectContexts as needed over the same connection, while ObjectContext is a kind of isolated &quot;persistence session&quot;, similar to the server-side context. A few more notes. Since we are using HTTP(S) to communicate with ROP server, there's no need to explicitly close the connection (or channel, or context).</P>
<P>So now let's do the same persistent operaions that we did in the first tutorial &quot;Main&quot; class. Let's start by creating and saving some objects:</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java"><SPAN class="code-comment">// creating <SPAN class="code-keyword">new</SPAN> Artist
</SPAN>Artist picasso = context.newObject(Artist.class);
picasso.setName(<SPAN class="code-quote">&quot;Pablo Picasso&quot;</SPAN>);
<SPAN class="code-comment">// Creating other objects
</SPAN>Gallery metropolitan = context.newObject(Gallery.class);
metropolitan.setName(<SPAN class="code-quote">&quot;Metropolitan Museum of Art&quot;</SPAN>);
Painting girl = context.newObject(Painting.class);
girl.setName(<SPAN class="code-quote">&quot;Girl Reading at a Table&quot;</SPAN>);
Painting stein = context.newObject(Painting.class);
stein.setName(<SPAN class="code-quote">&quot;Gertrude Stein&quot;</SPAN>);
<SPAN class="code-comment">// connecting objects together via relationships
</SPAN>picasso.addToPaintings(girl);
picasso.addToPaintings(stein);
girl.setGallery(metropolitan);
stein.setGallery(metropolitan);
<SPAN class="code-comment">// saving all the changes above
</SPAN>context.commitChanges();</PRE>
</DIV></DIV>
<P>Now let's select them back:</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java"><SPAN class="code-comment">// SelectQuery examples
</SPAN>SelectQuery select1 = <SPAN class="code-keyword">new</SPAN> SelectQuery(Painting.class);
List&lt;Painting&gt; paintings1 = context.performQuery(select1);
Expression qualifier2 = ExpressionFactory.likeIgnoreCaseExp(
Painting.NAME_PROPERTY, <SPAN class="code-quote">&quot;gi%&quot;</SPAN>);
SelectQuery select2 = <SPAN class="code-keyword">new</SPAN> SelectQuery(Painting.class, qualifier2);
List&lt;Painting&gt; paintings2 = context.performQuery(select2);</PRE>
</DIV></DIV>
<P>Now, delete:</P>
<DIV class="code panel" style="border-width: 1px;"><DIV class="codeContent panelContent">
<PRE class="code-java">Expression qualifier = ExpressionFactory.matchExp(Artist.NAME_PROPERTY,
<SPAN class="code-quote">&quot;Pablo Picasso&quot;</SPAN>);
SelectQuery selectToDelete = <SPAN class="code-keyword">new</SPAN> SelectQuery(Artist.class, qualifier);
Artist picasso = (Artist) DataObjectUtils.objectForQuery(context,
selectToDelete);
<SPAN class="code-keyword">if</SPAN> (picasso != <SPAN class="code-keyword">null</SPAN>) {
context.deleteObject(picasso);
context.commitChanges();
}</PRE>
</DIV></DIV>
<P>This code is exactly the same as in the first tutorial. So now let's try running the client and see what happens. In Eclipse open main class and select <TT>&quot;Run &gt; Rus As &gt; Java Application&quot;</TT> from the menu (assuming the ROP server started in the previous step is still running). You will some output in both server and client process consoles. Client:</P>
<DIV class="preformatted panel" style="border-width: 1px;"><DIV class="preformattedContent panelContent">
<PRE>INFO: Connecting to [http://localhost:8080/tutorial/cayenne-service] - dedicated session.
INFO: === Connected, session: org.apache.cayenne.remote.RemoteSession@9e09a4[sessionId=10qsakj1mj806] - took 219 ms.
INFO: --- Message 0: Bootstrap
INFO: === Message 0: Bootstrap done - took 71 ms.
INFO: --- Message 1: flush-cascade-sync
INFO: === Message 1: flush-cascade-sync done - took 1342 ms.
INFO: --- Message 2: Query
INFO: === Message 2: Query done - took 58 ms.
INFO: --- Message 3: Query
INFO: === Message 3: Query done - took 21 ms.
INFO: --- Message 4: Query
INFO: === Message 4: Query done - took 22 ms.
INFO: --- Message 5: Query
INFO: === Message 5: Query done - took 16 ms.
INFO: --- Message 6: Query
INFO: === Message 6: Query done - took 2 ms.
INFO: --- Message 7: Query
INFO: === Message 7: Query done - took 2 ms.
INFO: --- Message 8: Query
INFO: === Message 8: Query done - took 2 ms.
INFO: --- Message 9: flush-cascade-sync
INFO: === Message 9: flush-cascade-sync done - took 30 ms.</PRE>
</DIV></DIV>
<P>As you see client prints no SQL statmenets, just a bunch of query and flush messages sent to the server. The server side is more verbose, showing the actual client queries executed against the database:</P>
<DIV class="preformatted panel" style="border-width: 1px;"><DIV class="preformattedContent panelContent">
<PRE>...
INFO: SELECT NEXT_ID FROM AUTO_PK_SUPPORT WHERE TABLE_NAME = ? FOR UPDATE [bind: 1:'GALLERY']
INFO: SELECT NEXT_ID FROM AUTO_PK_SUPPORT WHERE TABLE_NAME = ? FOR UPDATE [bind: 1:'ARTIST']
INFO: SELECT NEXT_ID FROM AUTO_PK_SUPPORT WHERE TABLE_NAME = ? FOR UPDATE [bind: 1:'PAINTING']
INFO: --- will run 3 queries.
INFO: INSERT INTO GALLERY (ID, NAME) VALUES (?, ?)
INFO: [batch bind: 1-&gt;ID:200, 2-&gt;NAME:'Metropolitan Museum of Art']
INFO: === updated 1 row.
INFO: INSERT INTO ARTIST (DATE_OF_BIRTH, ID, NAME) VALUES (?, ?, ?)
INFO: [batch bind: 1-&gt;DATE_OF_BIRTH:NULL, 2-&gt;ID:200, 3-&gt;NAME:'Pablo Picasso']
INFO: === updated 1 row.
INFO: INSERT INTO PAINTING (ARTIST_ID, GALLERY_ID, ID, NAME) VALUES (?, ?, ?, ?)
INFO: [batch bind: 1-&gt;ARTIST_ID:200, 2-&gt;GALLERY_ID:200, 3-&gt;ID:200, 4-&gt;NAME:'Girl Reading at a Table']
INFO: [batch bind: 1-&gt;ARTIST_ID:200, 2-&gt;GALLERY_ID:200, 3-&gt;ID:201, 4-&gt;NAME:'Gertrude Stein']
INFO: === updated 2 rows.
INFO: +++ transaction committed.
INFO: --- will run 1 query.
INFO: --- transaction started.
INFO: SELECT t0.GALLERY_ID, t0.ARTIST_ID, t0.NAME, t0.ID FROM PAINTING t0
INFO: === returned 2 rows. - took 15 ms.
INFO: +++ transaction committed.
INFO: --- will run 1 query.
INFO: --- transaction started.
INFO: SELECT t0.GALLERY_ID, t0.ARTIST_ID, t0.NAME, t0.ID FROM PAINTING t0 WHERE UPPER(t0.NAME) LIKE UPPER(?) [bind: 1-&gt;NAME:'gi%']
INFO: === returned 1 row. - took 9 ms.
INFO: +++ transaction committed.
INFO: --- will run 1 query.
INFO: --- transaction started.
INFO: SELECT t0.DATE_OF_BIRTH, t0.ID, t0.NAME FROM ARTIST t0 WHERE t0.NAME = ? [bind: 1-&gt;NAME:'Pablo Picasso']
INFO: === returned 1 row. - took 7 ms.
INFO: +++ transaction committed.
INFO: --- will run 2 queries.
INFO: --- transaction started.
INFO: DELETE FROM PAINTING WHERE ID = ?
INFO: [batch bind: 1-&gt;ID:200]
INFO: [batch bind: 1-&gt;ID:201]
INFO: === updated 2 rows.
INFO: DELETE FROM ARTIST WHERE ID = ?
INFO: [batch bind: 1-&gt;ID:200]
INFO: === updated 1 row.
INFO: +++ transaction committed.</PRE>
</DIV></DIV>
<P>You are done with the basic ROP client!</P>
<HR>
<P><B>Next Step: <A href="remote-object-persistence-tutorial-authentication.html" title="Remote Object Persistence Tutorial Authentication">Remote Object Persistence Tutorial Authentication</A></B></P>
<HR>