blob: 752e47e4499665f0a84588a33831ff117f72e9b3 [file] [log] [blame]
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">&quot;yyyyMMdd&quot;</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">&quot;A date argument must be in format '&quot;</SPAN>
+ DEFAULT_DATE_FORMAT
+ <SPAN class="code-quote">&quot;': &quot;</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 &quot;newObject&quot; method. (Note that objects must be registered with DataContext to be persisted and to allow relationships with other objects.) Add this code to the &quot;main&quot; 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">&quot;Pablo Picasso&quot;</SPAN>);
picasso.setDateOfBirthString(<SPAN class="code-quote">&quot;18811025&quot;</SPAN>);</PRE>
</DIV></DIV>
<P>Note that at this point &quot;picasso&quot; 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">&quot;Metropolitan Museum of Art&quot;</SPAN>);
Painting girl = (Painting) context.newObject(Painting.class);
girl.setName(<SPAN class="code-quote">&quot;Girl Reading at a Table&quot;</SPAN>);
Painting stein = (Painting) context.newObject(Painting.class);
stein.setName(<SPAN class="code-quote">&quot;Gertrude Stein&quot;</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>