About this TutorialThis tutorial is intended to give first-time users an introduction to using Torque, an object-relational tool. Torque was developed as part of the Turbine web application framework. Until recently, it was tightly coupled in that framework, and could not be used independently. This tutorial is an introduction to the decoupled version of Torque. This will include how to obtain the Torque distribution, setting up a database, and writing a sample application to demonstrate the power of Torque. The tutorial is not meant to be an exhaustive introduction to Torque, please refer to the documentation on the Torque homepage for more detailed information. The example used throughout this tutorial is based on an email sent to the turbine-user mailing list by Steven F. Davis called torque outside turbine - detailed example (long). Step 1: Obtaining the Torque DistributionFirst, you need to obtain the Torque distribution. As of this writing, the standalone version of Torque has not been released; however, there is a beta version available. After you have obtained your copy of the Torque, you can either unpack the jar and integrate all of the Torque files into your source tree, or keep Torque in the jar and use it via the classpath. This tutorial will take the former approach. See jar guide for a description of the latter approach. You need to unpack the Torque jar to a directory where you want to develop your application. This will create a directory called torque. It is here that you will configure Torque and build your application. Step 2: Configuring TorqueThe following section outlines the necessary steps to define your database schema and configure Torque to use your schema. Upon completion, you'll be able to use Torque to create your object model and all of the Java classes that support it. In addition, Torque can generate and execute all of the appropriate SQL commands to create your database, freeing you from doing it yourself. To accomplish all of the above, you only need to create/edit three files: the Torque build properties, the Torque database schema, and the Torque run-time properties. Each of these files is covered in the following sections. Torque Build PropertiesTorque is a system that literally builds Java source/class files representing your object model, SQL statements for your specific database, and documentation. To accomplish these tasks, it uses Ant to control its build process, and ant uses the build.properties file in the top-level Torque directory to setup your development environment. It is this file that we will now edit. Keep in mind, this tutorial is going to show you the bare minimum to get your first Torque application up and running. However, the build.properties file is thoroughly commented, so please refer to it if you have a question regarding part of the file that is not covered here. Make the following changes and edit appropriately for your environment. The properties are described in the table following (note: you'll need to add the torque.database.buildUrl property): torque.project = bookstore torque.database = mysql torque.targetPackage = com.kazmier.om torque.database.createUrl = jdbc:mysql://127.0.0.1/mysql torque.database.buildUrl = jdbc:mysql://127.0.0.1/bookstore torque.database.url = jdbc:mysql://127.0.0.1/bookstore torque.database.driver = org.gjt.mm.mysql.Driver torque.database.user = adminuser torque.database.password = adminpassword torque.database.host = 127.0.0.1 For a reference as to what each property, and others, controls, please see the properties reference. Setting these properties correctly is very important. These enable Torque to generate all of the required sources and SQL for your specific database. If you experience problems later in this tutorial, it would be wise to double-check these values. Torque Database SchemaThe second file that you must edit to configure Torque is the database schema. The database schema is an XML file that represents your SQL database in Torque. This is where you define all of your tables, column names and types, as well as the keys used to index these tables. The database schema file is located in the torque/schema directory. Here you will find two XML files: id-table-schema.xml and project-schema.xml. The id-table-schema.xml file is used internally by Torque's IDBroker service (which is a database independent method for generating unique IDs). project-schema.xml is where you'll define your database schema. Historically, the name of your database schema file was required to be in the format of name-schema.xml where name was the same as the project property defined in build.properties; otherwise, Torque was not be able to find your database schema file. This is no longer the case, name is no longer restricted to the project name. However, it must end with -schema.xml because Torque will only generate object models for files ending with that pattern. For this tutorial, we will use a simple database that might be used to support a bookstore application. The database will contain three tables: author, publisher, and book. The first table will contain author information (first and last name). The second table will contain publisher information (name). And the third table will contain book information (title, and ISBN). The author id and publisher id will be foreign keys in the book table. The schema representation for this database is as follows:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE database SYSTEM
"http://jakarta.apache.org/turbine/dtd/database.dtd">
<database
name="bookstore"
defaultIdMethod="idbroker">
<table name="book" description="Book Table">
<column
name="book_id"
required="true"
primaryKey="true"
type="INTEGER"
description="Book Id"/>
<column
name="title"
required="true"
type="VARCHAR"
size="255"
description="Book Title"/>
<column
name="isbn"
required="true"
type="VARCHAR"
size="24"
javaName="ISBN"
description="ISBN Number"/>
<column
name="publisher_id"
required="true"
type="INTEGER"
description="Foreign Key Publisher"/>
<column
name="author_id"
required="true"
type="INTEGER"
description="Foreign Key Author"/>
<foreign-key foreignTable="publisher">
<reference
local="publisher_id"
foreign="publisher_id"/>
</foreign-key>
<foreign-key foreignTable="author">
<reference
local="author_id"
foreign="author_id"/>
</foreign-key>
</table>
<table name="publisher" description="Publisher Table">
<column
name="publisher_id"
required="true"
primaryKey="true"
type="INTEGER"
description="Publisher Id"/>
<column
name="name"
required="true"
type="VARCHAR"
size="128"
description="Publisher Name"/>
</table>
<table name="author" description="Author Table">
<column
name="author_id"
required="true"
primaryKey="true"
type="INTEGER"
description="Author Id"/>
<column
name="first_name"
required="true"
type="VARCHAR"
size="128"
description="First Name"/>
<column
name="last_name"
required="true"
type="VARCHAR"
size="128"
description="Last Name"/>
</table>
</database>
Edit project-schema.xml to reflect the above database schema. If you would rather create your own schema file, be sure the filename ends in -schema.xml, and delete project-schema.xml because Torque will generate an object model for that file as well. Do not delete id-table-schema.xml if you plan on using Torque's IDBroker service, which is used in this tutorial. There are several items of importance to note. The database element's name attribute must be the same as the database name specified by the databaseUrl property in build.properties; likewise, the run-time properties (described in the next section) should also reflect this value. Failure to do so will prevent Torque from creating your database tables (if instructed to do so) or prevent your object model from working properly. Another item of importance is the database element's defaultIdMethod attribute. This attribute specifies the default method that Torque will use to generate IDs for primary keys (columns with the primaryKey attribute set to true: book_id, publisher_id, and author_id) in your database tables. There are several possible values:
The defaultIdMethod selected will be used for all tables in your schema unless an individual table element contains the idMethod attribute, in which case, its value will override the defaultIdMethod. idMethod takes the same values as defaultIdMethod. One common reason that a table might override the defaultIdMethod is when a table is composed only of foreign keys (i.e. a junction entity in database-speak). In this case, all columns should be defined as primary keys because they are all needed to declare a row as unique. However, Torque should not generate primary key IDs for objects in this table because the objects that compose the table already have primary key IDs. Thus, the idMethod attribute of the table must be set to none. For example, if the book table defined above did not have any additional attributes other than a publisher_id and author_id, the schema for the book table should be defined as:
<table name="book" idMethod="none" description="Book Table">
<column
name="publisher_id"
required="true"
primaryKey="true"
type="INTEGER"
description="Foreign Key Publisher"/>
<column
name="author_id"
required="true"
primaryKey="true"
type="INTEGER"
description="Foreign Key Author"/>
<foreign-key foreignTable="publisher">
<reference
local="publisher_id"
foreign="publisher_id"/>
</foreign-key>
<foreign-key foreignTable="author">
<reference
local="author_id"
foreign="author_id"/>
</foreign-key>
</table>
Another common mistake is to forget that XML is case-sensitive. All of the elements and attributes must be specified according to the DTD for the database schema. In addition, you must include the XML declaration and DTD specification in your database schema file. Failure to do so can result it errors. Finally, you must also edit (or add if its not present) the name attribute to the database element in id-table-schema.xml. The value should be identical to the value in your database schema file. This will instruct Torque to create id-table in the same database as your schema. Below is the file used in this example:
<?xml version="1.0" encoding="ISO-8859-1" standalone="no"?>
<!DOCTYPE database SYSTEM
"http://jakarta.apache.org/turbine/dtd/database.dtd">
<database name="bookstore">
<table name="ID_TABLE" idMethod="idbroker">
<column
name="ID_TABLE_ID"
required="true"
primaryKey="true"
type="INTEGER"/>
<column
name="TABLE_NAME"
required="true"
size="255"
type="VARCHAR"/>
<column
name="NEXT_ID"
type="INTEGER"/>
<column
name="QUANTITY"
type="INTEGER"/>
<unique>
<unique-column name="TABLE_NAME"/>
</unique>
</table>
</database>
Torque uses the database schema files to generate your object model and Java classes to support it. In addition, Torque generates SQL that can be used to create your databases and tables from these schemas. In the next section, we will conclude the configuration of Torque by editing the Torque run-time properties. For additional information on the XML elements and attributes, please refer to the Torque Schema Reference. Torque Run-Time PropertiesThe last step in the configuration of Torque are the Torque run-time properties. As the name suggests, these properties are used when your application is executing the object model code generated by Torque. The run-time properties control logging and database parameters such as drivers, usernames, and passwords. These properties can be saved in any file because your application must explicitly initialize Torque (as you'll see later in this document). There is a sample run-time properties file included in the Torque distribution called Torque.properties located in the torque/schema directory. However, for simplicity, we'll just create our own. Again, this tutorial will guide you through the bare minimum to get your application up and running. For more information regarding the Torque run-time properties, refer to the comments in the sample file included in the distribution. Create a new file called Torque.properties in the top-level torque directory (to avoid overwriting the sample property file) and add the following lines to it: log4j.rootCategory = DEBUG, default log4j.appender.default = org.apache.log4j.FileAppender log4j.appender.default.file = ./torque.log log4j.appender.default.layout = org.apache.log4j.SimpleLayout torque.database.default=bookstore torque.database.bookstore.driver = org.gjt.mm.mysql.Driver torque.database.bookstore.url = jdbc:mysql://127.0.0.1/bookstore torque.database.bookstore.username = user torque.database.bookstore.password = password
It is worth re-iterating that these run-time properties are not used by Torque when generating your object model and creating your database. They are used only by the application utilizing the Torque-generated object model classes at run-time. That completes the configuration of Torque. You are now ready to start building your object model and creating your database. Step 3: Invoking TorqueWith the configuration of Torque completed, you can now generate the object model to support your database, and optionally create your database and all of its associated tables. As mentioned earlier in this tutorial, Torque utilizes Ant to perform these tasks. Each of these tasks is covered in the following sections. Generating the Object Model and Associated SQLThe generation of your object model will produce Java source files that can be used to represent your database. These classes enable you to create, edit, delete, and select objects that represent rows in your database tables. In addition, Torque will generate SQL to create your database tables (you have the option of executing the SQL as demonstrated later in this tutorial). The object model consists of four classes for each table in your schema. For example, the author table, defined in this tutorial, will result in the following four classes: Author, AuthorPeer, BaseAuthor, and BaseAuthorPeer (a discussion on the use of these classes is deferred until we write our sample application). To generate your object model and the associated SQL, type the following command in the top-level torque directory: ant -f build-torque.xml Upon a successful build, indicated by the BUILD SUCCESSFUL message, you will find a new torque/src directory. It is here that you will find the generated Java classes and generated SQL. The Java classes are located in the java directory and will be in a directory hierarchy matching that of the targetPackage you specified in your Torque build.properties. These are the files that will be compiled into your object model classes. The SQL files are located in the sql directory. For each database schema in your torque/schema directory, there will be a corresponding file with a .sql extension instead of .xml extension. The contents of these files are the SQL commands that can be used to manually or automatically (see next section) create your database tables. If you encounter errors while building, it is more than likely a formatting error of your database schema file. Check the format of the file and make sure it conforms to the Torque Schema Reference. Creating the Database and TablesAs mentioned previously, Torque can automatically create your database and all of the associated tables for you. However, you must first make sure that the appropriate database driver (the one you defined in build.properties) is in your classpath so that Torque can connect to your database and execute the generated SQL commands. The easiest way to accomplish that is to add your database driver to the torque/lib directory. Ant's build file automatically adds all of the jar files in this directory to its classpath. Note: Torque will drop the database and tables that it is about to create if they exist! You should skip this step if you are working with an existing database full of data. To create your database, type the following command in the top-level torque directory: ant -f build-torque.xml create-db To create your tables, type the following commands in the top-level torque directory: ant -f build-torque.xml id-table-init-sql ant -f build-torque.xml insert-sql Note: if this tutorial had not utilized Torque's idbroker method (as described earlier), it would not have been necessary to execute the id-table-init-sql target. Success will be indicated by the BUILD SUCCESSFUL message. You can also validate this by checking your database. For example, the bookstore-schema.xml and id-table-schema.xml, defined in this tutorial, should have created a database called bookstore, with the following tables: ID_TABLE, author, book, and publisher. If you encounter errors while creating your database, it is more than likely a misconfiguration of your build.properties. Another common problem is that the user specified in the build.properties does not have sufficient privilege to create databases and tables. In either case, refer to the section above that explains the build.properties file. Now that you have generated all of your object model classes and created your database, you are now ready to build your first Torque application. Step 4: Writing a Sample ApplicationCongratulations, you have finally reached the fun the part of this tutorial. This is where you'll discover the power of Torque. Be warned, you'll never want to write another SQL statement ever again! As mentioned earlier, when Torque created your object model, it created four Java classes for each table defined in your database schema. For example, the book table, defined in the database schema presented earlier, will result in the following classes: Book, BookPeer, BaseBook, and BaseBookPeer. Book and BookPeer are subclasses of BaseBook and BaseBookPeer respectively. The two Base classes (BaseBook and BaseBookPeer) contain Torque-generated logic and should not be modified because Torque will overwrite your changes if you happen to generate your object model again (via ant). Any business logic that you might want to add should be placed in the Book and BookPeer classes. This is covered later in the tutorial. You might be asking yourself, what is the difference between the Peer classes (BookPeer and BaseBookPeer) and their counterparts (Book and BaseBook), also known as Data Objects? The Peer classes wrap their associated database tables and provide static methods to manipulate those tables such as doSelect, doInsert, and doUpdate. Data Objects, on the other hand, wrap individual rows within those tables and provide getters/mutators for each column defined in those tables as well as the convenient save method. Both Peer and Data Objects have a one-to-one mapping to a table defined in your database schema. For a more in-depth discussion on Peers and Data Objects, refer to the Peers HOWTO. An example of adding logic to both the Peer and Data Objects is presented later in the tutorial. Now that we've covered the basics of the object model that Torque generated for you, the rest of this section describes the Torque-way of doing database inserts, selects, updates, and deletes illustrated with small segments of code. These segments of code are part of a sample application that is presented in full after a brief discussion on extending the object model classes. Finally, instructions on how to compile and run the application are detailed. Inserting RowsInserting rows into your tables is easy with Torque. Simply instantiate a new Data Object of the appropriate class, set its properties using the mutators named after the table's columns, then invoke the Data Object's save method. Note: It is not necessary to set the object's primary key ID because Torque will do this for you automatically unless you've specified otherwise (see the Database Schema Configuration section above). For example, to insert a new row in the author table (as defined in this tutorial's database schema): instantiate a new Author object, invoke the object's setFirstName and setLastName methods with appropriate values, then call the save method. Thats it. The following is from the sample application:
Publisher addison = new Publisher();
addison.setName("Addison Wesley Professional");
addison.save();
Author bloch = new Author();
bloch.setFirstName("Joshua");
bloch.setLastName("Bloch");
bloch.save();
It is also possible to insert a row using the Peer class directly instead of invoking the save method of your Data Object. Recall, the Peer class provides static methods to perform operations on a table. One of these operations is the ability to insert rows via the doInsert method. The Data Object's save method actually calls doInsert for you (or doUpdate if the object is not new and must be updated). For example, you can use AuthorPeer.doInsert as an alternative method to insert a new row in the author table. The following is from the sample application:
Author stevens = new Author();
stevens.setFirstName("W.");
stevens.setLastName("Stevens");
AuthorPeer.doInsert(stevens);
It should also be noted for completeness that doInsert can be passed a Criteria object (discussed in the next section) instead of a Data Object (see the Javadoc for details). However, the most common method for the insertion of rows in a table is via the save method of the Data Object rather than directly using the Peer's doInsert method. Inserting a row in a table that contains a foreign key is also simple. As a convenience, Torque creates a mutator for the specific Data Object class that represents the foreign-key in the object model. The name of this method is setTable where Table is the name of the foreign-key's table (as defined in the database schema). Upon calling this method with a reference to the appropriate Data Object, Torque will automatically extract and insert the foreign-key for you. For example, the book table (as defined in the database schema) contains two foreign-keys: author_id and publisher_id. To insert a row in this table, follow the same procedure as above, but instead of explicitly setting the foreign-keys (via setAuthorId and setPublisherId), use setAuthor and setPublisher and pass references to an Author and Publisher Data Object. Both methods are illustrated in the following code which builds upon the earlier objects that were created:
/*
* Using the convenience methods to handle
* the foreign keys.
*/
Book effective = new Book();
effective.setTitle("Effective Java");
effective.setISBN("0-618-12902-2");
effective.setPublisher(addison);
effective.setAuthor(bloch);
effective.save();
/*
* Inserting the foreign-keys manually.
*/
Book tcpip = new Book();
tcpip.setTitle("TCP/IP Illustrated, Volume 1");
tcpip.setISBN("0-201-63346-9");
tcpip.setPublisherId(addison.getPublisherId());
tcpip.setAuthorId(stevens.getAuthorId());
tcpip.save();
As you can see, inserting rows into your database is very easy to do with your Torque object model. Selecting RowsSelecting rows from your database is just as easy as inserting rows. The Peer class associated with a table defines a static method called doSelect which is used to pull data out of the table. The argument to doSelect is a Critieria object. It is this object that specifies the criteria to be used when selecting data from the database. As a result of the query, doSelect returns a vector of Data Objects representing the rows of data selected. To use these Data Objects in your application, you must cast them to the appropriate type in your object model. For example, to select all of the rows from the book table that were inserted in the previous section, you must first create an Criteria object. Because we want to select everything from the table, no criteria will be specified (i.e. no WHERE clause in the underlying SELECT statement). To perform the query, the empty Criteria object is passed to BookPeer.doSelect, as illustrated below: Criteria crit = new Criteria(); List v = BookPeer.doSelect(crit); The results are stored in a vector which can then be iterated over to access the individual Book objects retrieved from the table. The following code prints the Book to standard output (a better approach is presented later):
Iterator i = v.iterator();
while (i.hasNext())
{
Book book = (Book) i.next();
System.out.println("Title: " + book.getTitle() + "\n");
System.out.println("ISBN: " + book.getISBN() + "\n");
System.out.println("Publisher: " +
book.getPublisher().getName() + "\n");
System.out.println("Author: " +
book.getAuthor().getLastName() + ", " +
book.getAuthor().getFirstName() + "\n");
}
In the above example, you may have noticed that by calling getAuthor and getPublisher, the object model automatically retrieved the Author and Publisher Data Objects for you. This results in an additional behind-the-scenes SQL query for each table. Although getAuthor is called twice, only a single SQL query occurs because all of the Author columns are selected in behind-the-scenes query.
To select a specific Book from the table, create a Criteria object (or just reuse the previous one) and use the add method to specify some criteria. Specifying criteria is simply a matter of choosing a column (defined as static constants in your Peer class) and some value you want to match. Thus, selecting a book with the following ISBN, 0-618-12902-2, is as simple as: Criteria crit = new Criteria(); crit.add(BookPeer.ISBN, "0-618-12902-2"); List v = BookPeer.doSelect(crit); This section has only skimmed the surface of Criteria objects. Criteria can be used to specify very simple to very complex queries. For a much more in-depth discussion of Criteria, please refer to the Criteria HOWTO. Updating RowsUpdating a row in a table is only a matter of changing one or more properties of the Data Object that represents the row by invoking one or more mutators and then calling its save method. When a mutator is called, the Data Object sets an internal flag to indicate that its been modified. This flag is checked when save is invoked to determine if the Peer's doInsert or doUpdate is called to perform the database operation. For example, changing the author of the Effective Java book created earlier is as simple as: effective.setAuthor(stevens); effective.save(); Alternatively, instead of calling the Data Object's save method, the Peer's doUpdate method may be called directly with a Data Object that has been modified as the argument. This is illustrated in the following fragment of code that changes the author of the TCP/IP Illustrated book: tcpip.setAuthor(bloch); BookPeer.doUpdate(tcpip); Again, for completeness, doUpdate could have been passed a Criteria object to update a row (see the Javadoc for details). However, the most common method to update rows in a table is via the Data Object's save method rather than directly using the Peer's doUpdate method. Deleting RowsDeleting rows from a table is easy as well. The Peer class defines a static method doDelete which can be used for this purpose. Similar to the other Peer methods, doDelete may be passed a Criteria object or a Data Object to specify which row or rows to delete. It should be noted that there is no corresponding method in the Data Object to delete a row. For example, the following code deletes all of the rows from the three tables that were inserted during the course of this tutorial using both forms of doDelete. First, the books are deleted by specifying Criteria, then the authors and publishers are deleted by passing the Data Objects directly to doDelete. crit = new Criteria(); crit.add(BookPeer.ISBN, "0-618-12902-2"); BookPeer.doDelete(crit); crit = new Criteria(); crit.add(BookPeer.ISBN, "0-201-63346-9"); crit.add(BookPeer.TITLE, "TCP/IP Illustrated, Volume 1"); BookPeer.doDelete(crit); AuthorPeer.doDelete(bloch); AuthorPeer.doDelete(stevens); PublisherPeer.doDelete(addison); Note: Deleting a row from a table that contains foreign-keys does not automatically delete the foreign-keys from their tables. If you want to delete the foreign-keys, you must do so explicitly as shown in the above example. I.e., deleting the books from the book table does not automatically delete the corresponding rows in the author and publisher tables.
Adding Functionality to the Object ModelThis section will provide examples of adding functionality to both the Peer and Data Object classes. As you may recall, Torque generated four classes for each table defined in the database schema. Two of these classes (the Base Data Object and Base Peer class) contain Torque-generated logic while the other two are empty subclasses that you can use to include business logic. By now, you should have a decent understanding of the type of logic that might be added to these classes. Keep in mind, Torque will overwrite any changes that are inadvertently added to the Base classes if you regenerate your object model; however, it will not overwrite changes in the non-Base classes. The first change that we'll make to our object model is to provide our Data Objects with adequate toString methods. Theses methods can then be used to print the Data Objects without adding unnecessary code to the core of the application. The following are the modified Book, Author, and Publisher classes, which are located in a directory hierarchy matching that of the targetPackage you specified in your Torque build.properties:
// Book.java
public class Book
extends com.kazmier.om.BaseBook
implements Persistent
{
public String toString()
{
StringBuffer sb = new StringBuffer();
try
{
sb.append("Title: " + getTitle() + "\n");
sb.append("ISBN: " + getISBN() + "\n");
sb.append("Publisher: " + getPublisher() + "\n");
sb.append("Author: " + getAuthor() + "\n");
}
catch (Exception ignored)
{
}
return sb.toString();
}
}
// Author.java
public class Author
extends com.kazmier.om.BaseAuthor
implements Persistent
{
public String toString()
{
return getLastName() + ", " + getFirstName();
}
}
// Publisher.java
public class Publisher
extends com.kazmier.om.BasePublisher
implements Persistent
{
public String toString()
{
return getName();
}
}
The next change that we'll make is to the Peer classes. For convenience (and based on the suggestion in the Peers Howto) we'll add doSelectAll methods which will return a List of all the Data Objects in a table. The following are the modified BookPeer, AuthorPeer, and PublisherPeer classes which are located in the same directory as the Data Objects:
// BookPeer.java
import org.apache.torque.util.*;
public class BookPeer
extends com.kazmier.om.BaseBookPeer
{
public static List doSelectAll() throws Exception
{
Criteria crit = new Criteria();
return doSelect(crit);
}
}
// AuthorPeer.java
import org.apache.torque.util.*;
public class AuthorPeer
extends com.kazmier.om.BaseAuthorPeer
{
public static List doSelectAll() throws Exception
{
Criteria crit = new Criteria();
return doSelect(crit);
}
}
// PublisherPeer.java
import org.apache.torque.util.*;
public class PublisherPeer
extends com.kazmier.om.BasePublisherPeer
{
public static List doSelectAll() throws Exception
{
Criteria crit = new Criteria();
return doSelect(crit);
}
}
In order to execute the full application presented at the end of this tutorial, you must make the above changes to your object model. After you have made the changes, proceed to the next section. Full ApplicationThe following is the sample bookstore application in its entirety. It should look very familiar if you've been following this tutorial. In fact, its almost identical with the exception that it utilizes the new functionality that was added to the object model in the previous section.
package com.kazmier;
import java.util.*;
import com.kazmier.om.*;
import org.apache.torque.Torque;
import org.apache.torque.util.Criteria;
public class Bookstore
{
public static void main(String[] args)
{
try
{
/*
* Initializing Torque
*/
Torque.init("Torque.properties");
/*
* Creating new objects. These will be inserted
* into your database automatically when the
* save method is called.
*/
Publisher addison = new Publisher();
addison.setName("Addison Wesley Professional");
addison.save();
Author bloch = new Author();
bloch.setFirstName("Joshua");
bloch.setLastName("Bloch");
bloch.save();
/*
* An alternative method to inserting rows
* in your database.
*/
Author stevens = new Author();
stevens.setFirstName("W.");
stevens.setLastName("Stevens");
AuthorPeer.doInsert(stevens);
/*
* Using the convenience methods to handle
* the foreign keys.
*/
Book effective = new Book();
effective.setTitle("Effective Java");
effective.setISBN("0-618-12902-2");
effective.setPublisher(addison);
effective.setAuthor(bloch);
effective.save();
/*
* Inserting the foreign-keys manually.
*/
Book tcpip = new Book();
tcpip.setTitle("TCP/IP Illustrated, Volume 1");
tcpip.setISBN("0-201-63346-9");
tcpip.setPublisherId(addison.getPublisherId());
tcpip.setAuthorId(stevens.getAuthorId());
tcpip.save();
/*
* Selecting all books from the database and
* printing the results to stdout using our
* helper method defined in BookPeer
* (doSelectAll).
*/
System.out.println("Full booklist:\n");
List booklist = BookPeer.doSelectAll();
printBooklist(booklist);
/*
* Selecting specific objects. Just search for
* objects that match this criteria (and print
* to stdout).
*/
System.out.println("Booklist (specific ISBN):\n");
Criteria crit = new Criteria();
crit.add(BookPeer.ISBN, "0-201-63346-9");
booklist = BookPeer.doSelect(crit);
printBooklist(booklist);
/*
* Updating data. These lines will swap the
* authors of the two books. The booklist is
* printed to stdout to verify the results.
*/
effective.setAuthor(stevens);
effective.save();
tcpip.setAuthor(bloch);
BookPeer.doUpdate(tcpip);
System.out.println("Booklist (authors swapped):\n");
booklist = BookPeer.doSelectAll();
printBooklist(booklist);
/*
* Deleting data. These lines will delete the
* data that matches the specified criteria.
*/
crit = new Criteria();
crit.add(BookPeer.ISBN, "0-618-12902-2");
BookPeer.doDelete(crit);
crit = new Criteria();
crit.add(BookPeer.ISBN, "0-201-63346-9");
crit.add(BookPeer.TITLE, "TCP/IP Illustrated, Volume 1");
BookPeer.doDelete(crit);
/*
* Deleting data by passing Data Objects instead of
* specifying criteria.
*/
AuthorPeer.doDelete(bloch);
AuthorPeer.doDelete(stevens);
PublisherPeer.doDelete(addison);
System.out.println("Booklist (should be empty):\n");
booklist = BookPeer.doSelectAll();
printBooklist(booklist);
}
catch (Exception e)
{
e.printStackTrace();
}
}
/*
* Helper method to print a booklist to standard out.
*/
private static void printBooklist(List booklist)
throws Exception
{
Iterator i = booklist.iterator();
while (i.hasNext())
{
Book book = (Book) i.next();
System.out.println(book);
}
}
}
Save this code in the torque/src/java directory hierarchy with a filename of Bookstore.java. The above example must be placed in torque/src/java/com/kazmier directory because of its package definition. Your application might go elsewhere depending on the package that you've selected. Compiling and RunningNow that you've generated your object model with Torque, and created a sample application, you are now ready to compile everything. Again, Ant is used to control the build process. To compile, type the following in the Torque top-level directory: ant -f build-torque.xml compile If you've done everything correctly, this should build without any errors. All of the resulting Java class files are placed in the torque/bin/classes directory. Should you encounter errors, go back and review your application code. Before you run the sample application, you must first set your classpath (this was done automatically for you via Ant's build file when you compiled). The classpath must include: all of the jars in the torque/lib directory, the driver for your database, and all of your application and object model classes located in torque/bin/classes. An easy way to set your classpath (if you're using a bourne-shell or one of its derivatives on a un*x-based system) is to type the following in the top-level Torque directory (first add your database driver to the torque/lib directory if you haven't already): [kaz@coco torque]$ CLASSPATH=bin/classes [kaz@coco torque]$ for i in lib/* > do > CLASSPATH=$CLASSPATH:$i > done [kaz@coco torque]$ export CLASSPATH With your classpath set, you are now ready to finally run the application. From the top-level directory with your Torque run-time properties, type the following, replacing the name of the class with your class: java com.kazmier.Bookstore If all goes well, you should see the following output: Full booklist: Title: Effective Java ISBN: 0-618-12902-2 Publisher: Addison Wesley Professional Author: Bloch, Joshua Title: TCP/IP Illustrated, Volume 1 ISBN: 0-201-63346-9 Publisher: Addison Wesley Professional Author: Stevens, W. Booklist (specific ISBN): Title: TCP/IP Illustrated, Volume 1 ISBN: 0-201-63346-9 Publisher: Addison Wesley Professional Author: Stevens, W. Booklist (authors swapped): Title: Effective Java ISBN: 0-618-12902-2 Publisher: Addison Wesley Professional Author: Stevens, W. Title: TCP/IP Illustrated, Volume 1 ISBN: 0-201-63346-9 Publisher: Addison Wesley Professional Author: Bloch, Joshua Booklist (should be empty): If your application throws an exception, it could be for one of many reasons, most of which are not very descriptive unfortunately. For example, mistyping the username or password in your Torque run-time properties file results in a NullPointerException, as do many other types of errors. Do not be discouraged if your application does not run the first time. Carefully retrace all of the steps outlined in this tutorial. If you are still not able to get your application to run, use the Turbine mailing list to your advantage. Where to Go From HereCongratulations! You have completed the Torque tutorial. Although this has only been an introduction to Torque, it should be sufficient to get you started with Torque in your applications. For those of you seeking additional information, there are several other documents on this site that can provide details on various subjects. Lastly, the source code is an invaluable resource when all else fails to provide answers! |