| 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">"http:<SPAN class="code-comment">//localhost:8080/tutorial/cayenne-service"</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 "channel" can be used to create as many peer ObjectContexts as needed over the same connection, while ObjectContext is a kind of isolated "persistence session", 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 "Main" 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">"Pablo Picasso"</SPAN>); |
| |
| <SPAN class="code-comment">// Creating other objects |
| </SPAN>Gallery metropolitan = context.newObject(Gallery.class); |
| metropolitan.setName(<SPAN class="code-quote">"Metropolitan Museum of Art"</SPAN>); |
| |
| Painting girl = context.newObject(Painting.class); |
| girl.setName(<SPAN class="code-quote">"Girl Reading at a Table"</SPAN>); |
| |
| Painting stein = context.newObject(Painting.class); |
| stein.setName(<SPAN class="code-quote">"Gertrude Stein"</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<Painting> paintings1 = context.performQuery(select1); |
| |
| Expression qualifier2 = ExpressionFactory.likeIgnoreCaseExp( |
| Painting.NAME_PROPERTY, <SPAN class="code-quote">"gi%"</SPAN>); |
| SelectQuery select2 = <SPAN class="code-keyword">new</SPAN> SelectQuery(Painting.class, qualifier2); |
| List<Painting> 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">"Pablo Picasso"</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>"Run > Rus As > Java Application"</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->ID:200, 2->NAME:'Metropolitan Museum of Art'] |
| INFO: === updated 1 row. |
| INFO: INSERT INTO ARTIST (DATE_OF_BIRTH, ID, NAME) VALUES (?, ?, ?) |
| INFO: [batch bind: 1->DATE_OF_BIRTH:NULL, 2->ID:200, 3->NAME:'Pablo Picasso'] |
| INFO: === updated 1 row. |
| INFO: INSERT INTO PAINTING (ARTIST_ID, GALLERY_ID, ID, NAME) VALUES (?, ?, ?, ?) |
| INFO: [batch bind: 1->ARTIST_ID:200, 2->GALLERY_ID:200, 3->ID:200, 4->NAME:'Girl Reading at a Table'] |
| INFO: [batch bind: 1->ARTIST_ID:200, 2->GALLERY_ID:200, 3->ID:201, 4->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->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->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->ID:200] |
| INFO: [batch bind: 1->ID:201] |
| INFO: === updated 2 rows. |
| INFO: DELETE FROM ARTIST WHERE ID = ? |
| INFO: [batch bind: 1->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> |
| |