| Title: Tutorial DataObjects |
| |
| |
| |
| <P>Persistent classes in Cayenne implement a <A href="dataobjects.html" title="DataObjects">DataObject interface</A>. If you inspect any of classes <A href="tutorial-generate-database-and-java-classes.html" title="Tutorial Generate Database and Java Classes">generated earlier</A> in this tutorial (e.g. <TT>cayenne.tutorial.Artist</TT>), you'll see that it extends a class with the name that starts with underscore (<TT>cayenne.tutorial.auto._Artist</TT>), which in turn extends from <TT>org.objectstyle.cayenne.CayenneDataObject</TT>. Splitting each persistent class into user-customizable subclass (Xyz) and a generated superclass (_Xyz) is a useful technique to avoid overwriting of the custom code when refreshing classes from the mapping model.</P> |
| |
| <P>Let's add a utility method to the Artist class that sets Artist date of birth, taking a string argument for the date:</P> |
| <DIV class="code panel" style="border-width: 1px;"><DIV class="codeHeader panelHeader" style="border-bottom-width: 1px;"><B>Artist.java</B></DIV><DIV class="codeContent panelContent"> |
| <PRE class="code-java"><SPAN class="code-keyword">package</SPAN> cayenne.tutorial; |
| |
| <SPAN class="code-keyword">import</SPAN> java.text.ParseException; |
| <SPAN class="code-keyword">import</SPAN> java.text.SimpleDateFormat; |
| <SPAN class="code-keyword">import</SPAN> java.util.Date; |
| |
| <SPAN class="code-keyword">import</SPAN> cayenne.tutorial.auto._Artist; |
| |
| <SPAN class="code-keyword">public</SPAN> class Artist <SPAN class="code-keyword">extends</SPAN> _Artist { |
| |
| <SPAN class="code-keyword">static</SPAN> <SPAN class="code-keyword">final</SPAN> <SPAN class="code-object">String</SPAN> DEFAULT_DATE_FORMAT = <SPAN class="code-quote">"yyyyMMdd"</SPAN>; |
| |
| /** |
| * Sets date of birth using a string in format yyyyMMdd. |
| */ |
| <SPAN class="code-keyword">public</SPAN> void setDateOfBirthString(<SPAN class="code-object">String</SPAN> yearMonthDay) { |
| <SPAN class="code-keyword">if</SPAN> (yearMonthDay == <SPAN class="code-keyword">null</SPAN>) { |
| setDateOfBirth(<SPAN class="code-keyword">null</SPAN>); |
| } |
| <SPAN class="code-keyword">else</SPAN> { |
| |
| Date date; |
| <SPAN class="code-keyword">try</SPAN> { |
| date = <SPAN class="code-keyword">new</SPAN> SimpleDateFormat(DEFAULT_DATE_FORMAT).parse(yearMonthDay); |
| } |
| <SPAN class="code-keyword">catch</SPAN> (ParseException e) { |
| <SPAN class="code-keyword">throw</SPAN> <SPAN class="code-keyword">new</SPAN> IllegalArgumentException(<SPAN class="code-quote">"A date argument must be in format '"</SPAN> |
| + DEFAULT_DATE_FORMAT |
| + <SPAN class="code-quote">"': "</SPAN> |
| + yearMonthDay); |
| } |
| |
| setDateOfBirth(date); |
| } |
| } |
| }</PRE> |
| </DIV></DIV> |
| |
| |
| <P>We'll continue by creating a bunch of new objects and saving them to the database.</P> |
| |
| <P>An object is created and registered with DataContext using "newObject" method. (Note that objects must be registered with DataContext to be persisted and to allow relationships with other objects.) Add this code to the "main" method:</P> |
| |
| <DIV class="code panel" style="border-width: 1px;"><DIV class="codeHeader panelHeader" style="border-bottom-width: 1px;"><B>Main.java</B></DIV><DIV class="codeContent panelContent"> |
| <PRE class="code-java">Artist picasso = (Artist) context.newObject(Artist.class); |
| picasso.setName(<SPAN class="code-quote">"Pablo Picasso"</SPAN>); |
| picasso.setDateOfBirthString(<SPAN class="code-quote">"18811025"</SPAN>);</PRE> |
| </DIV></DIV> |
| |
| <P>Note that at this point "picasso" object is only stored in memory and is not saved in teh database. Let's continue by adding a Metropolitan Museum gallery object and a few Picasso paintings:</P> |
| |
| <DIV class="code panel" style="border-width: 1px;"><DIV class="codeHeader panelHeader" style="border-bottom-width: 1px;"><B>Main.java</B></DIV><DIV class="codeContent panelContent"> |
| <PRE class="code-java">Gallery metropolitan = (Gallery) context.newObject(Gallery.class); |
| metropolitan.setName(<SPAN class="code-quote">"Metropolitan Museum of Art"</SPAN>); |
| |
| Painting girl = (Painting) context.newObject(Painting.class); |
| girl.setName(<SPAN class="code-quote">"Girl Reading at a Table"</SPAN>); |
| |
| Painting stein = (Painting) context.newObject(Painting.class); |
| stein.setName(<SPAN class="code-quote">"Gertrude Stein"</SPAN>);</PRE> |
| </DIV></DIV> |
| |
| <P>Now we can link the objects together, establishing relationships. Note that in each case below relationships are automatically estabslished in both directions (e.g. <TT>picasso.addToPaintings(girl)</TT> has exactly the same effect as <TT>girl.setToArtist(picasso)</TT>).</P> |
| |
| <DIV class="code panel" style="border-width: 1px;"><DIV class="codeHeader panelHeader" style="border-bottom-width: 1px;"><B>Main.java</B></DIV><DIV class="codeContent panelContent"> |
| <PRE class="code-java">picasso.addToPaintings(girl); |
| picasso.addToPaintings(stein); |
| |
| girl.setGallery(metropolitan); |
| stein.setGallery(metropolitan);</PRE> |
| </DIV></DIV> |
| |
| <P>Now lets save all five new objects:</P> |
| |
| <DIV class="code panel" style="border-width: 1px;"><DIV class="codeHeader panelHeader" style="border-bottom-width: 1px;"><B>Main.java</B></DIV><DIV class="codeContent panelContent"> |
| <PRE class="code-java">context.commitChanges();</PRE> |
| </DIV></DIV> |
| |
| <P>Now you can run the application again as described <A href="tutorial-datacontext.html" title="Tutorial DataContext">here</A>. The new output will look like this:</P> |
| |
| <DIV class="preformatted panel" style="border-width: 1px;"><DIV class="preformattedContent panelContent"> |
| <PRE>INFO QueryLogger: Created connection pool: jdbc:derby:/Users/andrus/Desktop/testdb;create=true |
| Driver class: org.apache.derby.jdbc.EmbeddedDriver |
| Min. connections in the pool: 1 |
| Max. connections in the pool: 1 |
| INFO QueryLogger: Opening connection: jdbc:derby:/Users/andrus/Desktop/testdb;create=true |
| Login: null |
| Password: ******* |
| INFO QueryLogger: +++ Connecting: SUCCESS. |
| INFO QueryLogger: Detected and installed adapter: org.objectstyle.cayenne.dba.derby.DerbyAdapter |
| INFO QueryLogger: SELECT NEXT_ID FROM AUTO_PK_SUPPORT WHERE TABLE_NAME = ? FOR UPDATE [bind: 'GALLERY'] |
| INFO QueryLogger: --- transaction started. |
| INFO QueryLogger: SELECT NEXT_ID FROM AUTO_PK_SUPPORT WHERE TABLE_NAME = ? FOR UPDATE [bind: 'ARTIST'] |
| INFO QueryLogger: SELECT NEXT_ID FROM AUTO_PK_SUPPORT WHERE TABLE_NAME = ? FOR UPDATE [bind: 'PAINTING'] |
| INFO QueryLogger: --- will run 3 queries. |
| INFO QueryLogger: INSERT INTO GALLERY (ID, NAME) VALUES (?, ?) |
| INFO QueryLogger: [bind: 200, 'Metropolitan Museum of Art'] |
| INFO QueryLogger: === updated 1 row. |
| INFO QueryLogger: INSERT INTO ARTIST (DATE_OF_BIRTH, ID, NAME) VALUES (?, ?, ?) |
| INFO QueryLogger: [bind: '1881-10-25 00:00:00.0', 200, 'Pablo Picasso'] |
| INFO QueryLogger: === updated 1 row. |
| INFO QueryLogger: INSERT INTO PAINTING (ARTIST_ID, GALLERY_ID, ID, NAME) VALUES (?, ?, ?, ?) |
| INFO QueryLogger: [bind: 200, 200, 200, 'Girl Reading at a Table'] |
| INFO QueryLogger: === updated 1 row. |
| INFO QueryLogger: [bind: 200, 200, 201, 'Gertrude Stein'] |
| INFO QueryLogger: === updated 1 row. |
| INFO QueryLogger: +++ transaction committed. |
| </PRE> |
| </DIV></DIV> |
| |
| <P>The first few SELECT's are done by Cayenne primary key generator (note that we did not assign any explicit PKs to the objects - Cayenne does this automatically). Following them are the statements generated to save the objects that we created.</P> |
| |
| <HR> |
| <P><B>Next Step: <A href="tutorial-mapping-query.html" title="Tutorial Mapping Query">Tutorial Mapping Query</A></B></P> |
| <HR> |