This article is part 2nd of the building of the asynchronous JavaScript + XML (AJAX) application series using Google Web Toolkit (GWT), which describes how to build an Apache Derby database for your WEB application and use it to drive Move GWT. Part 1th of this series introduces you to GWT and demonstrates how to use it to create a rich client front-end for your WEB application. This time, you'll go behind the scenes and learn how to use the database and the code to convert the data to GWT's available format to set up the backend. After reading this article, you will be able to communicate between the front end and the back end.
In this article, you will install and configure the backend of the database--web application to create a database schema and learn some simple tools to populate it with data. The database that you will be using is the Apache derby,100% pure Java™ relational database, which was originally developed under the name of Cloudscape™. Finally, IBM® acquired the Cloudscape code and then contributed its open-source version to the Apache project. The same project was issued by Sun Microsystems's javadb name, but there was no confusion between the two.
|
View the AJAX Resource Center, which is your one-stop AJAX programming model information Centre, including articles, tutorials, forums, blogs, wikis, events, and news. Everything that happens will be introduced here. |
|
I chose Derby not because it has three names, but because it is lightweight and easy to configure. Unlike most relational databases, Derby can run in the same Java Virtual machine (JVM) as the Java-side server code. (If you like, you can also run it on a separate JVM.) This makes development and deployment easier, and Derby is a fast, ideal choice for small and medium Web applications.
Before you begin, there are a few caveats: first, to read this article, you should have a basic knowledge of relational databases, JDBC, and Structured Query Language (SQL). Second, for demonstration purposes, this article provides some content that may not be ideal in the production system in the code. I tried to point out those elements in the course of the story, but I'm not going to discuss performance tuning here.
Get Derby
Derby is provided as part of the Apache DB project. When writing this article, the latest version is the 10.1.3.1 version. If you are working in the Eclipse Integrated development Environment (IDE), it is sufficient to get the Derby_core_plugin and Derby_ui_plugin two plug-ins. If not, you can choose any other distribution that meets your needs. Some of these distributions contain only library files, some contain libraries and documents, some contain libraries with debug information, and distributions that have only source code. Derby is based on Java technology and can be run on any version 1.3 or later JVM. The code example in this article assumes that you are using Java 1.4.
do not use Eclipse to set Derby
If you do not use Eclipse, unzip the downloaded release to any location you deem convenient. When you are done, make sure that the files Lib/derby.jar and Lib/derbytools.jar are located in the CLASSPATH variable. You can do this at the system level, which may help to set the environment variable Derby_install to the directory where Derby is located (including the Derby directory itself, in/opt/bin/db-derby-10.1.3.1-bin). You can also perform this action in the IDE or in the launcher script. If you need to use Derby in client/server mode and embedded mode, the file Lib/derbyclient.jar and Lib/derbynet.jar must also be in classpath.
setting up Derby with Eclipse
If you are using Eclipse, it will be easier to make the setup work for your development. To set up Derby in Eclipse, complete the following steps: Unzip the two plug-in files. Each plug-in file has a top-level directory named plugin. Copy the contents of the directory to the Eclipse plug-in directory. Open your project in Eclipse. Click Project > Add Apache Derby Nature into Derby fantasy world. Doing so will add four library files to the project classpath and give you access to the IJ command line prompt.
Figure 1 shows the Derby menu after the derby nature has been added.
Figure 1. Eclipse Derby Menu
Even if you are using Eclipse for development, you must have a corresponding JAR file when you deploy your application. I will explain this topic in more detail in a later article.
Design your model
Before you start using the database, take a moment to understand what the database should hold. I haven't discussed the requirements of the SLICR application, so let's assume that you want the database to be able to keep basic customer information and order information.
The trick to working with databases in the early stages of a product is to keep them simple and to use as little database-specific functionality as possible, even if that means doing extra processing in Java code initially. Databases are strongly dependent on third parties, so you should avoid making database decisions to drive the rest of your application. You need to have as few points of contact between your program and the database as possible so that you can make changes when you change the system at a certain point. Pressure comes from most of the transactions that are done while improving database performance will require you to use a specific system, so try to postpone such optimizations until the last minute the project must be optimized.
The starting point for database design is simple. Customer to place the order. Orders include one or more pizzas (at this point, ignoring the possibility of selling other foods). Pizza with a topping or a variety of toppings, you can press half a piece or a whole pizza to put the toppings.
Create a Customer table
Now, you just need to be concerned about getting enough customer information to deliver and confirm the order, as shown in Listing 1.
Listing 1. Customer table
CREATE TABLE Customers (
ID int generated always as identity constraint CUST_PK primary key,
first_name varchar (25 5),
last_name varchar (255), phone varchar, address_1 varchar (+), address_2 varchar
(200), City varchar (+
), state
varchar (2),
zip varchar ()
)
|
The CREATE statement has slightly some SQL syntax that does not conform to the standard. To create an ID column, you need Derby to automatically add one for each new row in the column. The clause that specifies the behavior is: ID int generated always as identity
Other options for the Identity column are: Generate by default as identity
The difference is that generate by default allows you to put your own values into the column, but generate always does not allow that. The ID column is also identified as the primary key of the table.
You always want a database with an ID that has absolutely no connection to the actual number. There will always be a member of the final team trying to convince you to use data such as a phone number as the primary key, because it uniquely identifies the customer. Don't do that. You will never want to update the entire database because someone moved and changed the phone number.
Create order Table
For the order table (see Listing 2), you only need to associate the binding with a customer and a date, and allow discounts. You can calculate the rest of the price in your code.
Listing 2. Order Form
CREATE TABLE orders (
ID int generated always as identity constraint ORD_PK primary key,
customer_id int Constrain T Cust_foreign_key references customers,
order_time timestamp,
discount float
)
|
In addition to the ID primary key, the CUSTOMER_ID column is declared as a foreign key referencing the Customer table. (If the foreign key is not included in the declaration, Derby assumes that the primary key of the other table is referenced.) This means that Derby will verify that any customer_id added to this table actually matches the customer in the system. The system administrator will tell you that this matching operation should always be performed. However, I think that in some reasonable circumstances the database may not be required to perform rigorous validation all the time. For example, you might need to enter data before you know or verify what the foreign key is. In addition, you may need to delete a foreign key but you need to keep the table rows. For example, in this example, you might want to delete a customer, but for data collection purposes, you need to keep the customer's order. There are some tricks you can make Derby allow, but you may not be able to migrate to other database systems.
Create a table of toppings
The last database design problem is pizza and toppings. Well, the topping itself is not a problem; it's very simple, as shown in Listing 3.
listing 3. Topping table
CREATE TABLE toppings (
ID int generated always as identity constraint TOP_PK primary key,
name varchar (MB),
p Rice float
)
|
The problem is how to manage the relationship between pizza and toppings. A pizza is equivalent to an order, involving a size and a set of toppings. A typical database standard would suggest that you create a pizza table before you create a Many-to-many table that associates the pizza ID with the topping ID. There are a lot of good attributes to doing this, one of the facts is that it allows you to use countless toppings on a pizza. However, managing database relationships between tables can cause performance loss. If you don't need so many toppings, you can include several toppings fields in the pizza table (topping_1, topping_2, and so on). Conceptually, that would be easier, but it would make it difficult (for example) to dig up order data to count the most popular toppings. If you are particularly adventurous, you can populate the field with a single topping field and a bitmap or connection string. But I really don't suggest doing that.
Create a pizza table
After some consideration, I decided to use a fully formal form. You want to use enough toppings for a pizza and it's ugly to put all of them on the same table. Therefore, use the code shown in Listing 4.
Listing 4. Pizza Table
CREATE TABLE pizzas (
ID int generated always as identity constraint PIZ_PK primary key,
order_id int constraint o Rder_foreign_key references orders,
size int
)
CREATE TABLE pizza_topping_map (
ID int generated always As identity constraint PTMAP_PK primary key,
pizza_id int constraint, PIZZA_FK references pizzas,
topping_id INT Co Nstraint TOPPING_FK references toppings,
placement int
)
|
For clarity only, sizes 1, 2, 3, and 4 are used to denote small, medium, large, and oversized numbers. The toppings for the left half pizza, the entire pizza and the right half pizza are 1, 0, or 1 respectively. And each mapping must have a separate ID so that it can, for example, allow extra pepperoni to be added to the same pizza with pepperoni as the topping two times.
Note: have I mentioned that all the names that have been placed after constraint must be unique throughout the database. They have to be. Derby actually creates the index in the background, and each index must have a unique name.
For your database schema, you should do that. It can now be placed in the database.
Populating the Database
The pattern is already there; You must now set up the schema and prepare some initial data. You will create a short stand-alone program to perform this setup process. But that's not the only option. You can use the Derby IJ command line to enter SQL commands directly, or you can use a graphical SQL tool. However, the programmatic approach gives you a good control over how to start Derby and how Derby differs from other JDBC databases. In fact, it is possible to save the SQL schema in its own SQL script.
Start with some very static data--the list of pizza toppings included in the SLICR page of part 1th. Again, this method is used primarily because you want to insert static data. Set up a topping table, where each of the toppings has a name and a bottom price. The code shown in Listing 5 sets the data. For now, assume that all the toppings have the same price.
Listing 5. Set the topping table in Derby
public class Slicrpopulatr {public static final string[] toppings = new string[] {"Anchovy", "Gardiner ia "," garlic "," Green Pepper "," mushrooms "," olives "," onions "," pepperoni "," pineapple "," Saus Age ', ' spinach '} public void Populatedatabase () throws Exception {Class.forName ("ORG.APACHE.DERBY.J Dbc.
Embeddeddriver "). newinstance ();
Connection con = drivermanager.getconnection ("Jdbc:derby:slicr;create=true");
Con.setautocommit (FALSE);
Statement s = con.createstatement ();
S.execute ("DROP TABLE toppings"); S.execute ("CREATE TABLE toppings" + "ID int generated always as identity constraint top_pk key,"
+ "name varchar (m)," + "price float"); All of the other CREATE TABLE statements from above would go ...//for (int i = 0; i < toppings.length;
i++) { S.execute ("INSERT into toppings values (DEFAULT, '" + toppings[i] + "', 1.25)");
} con.commit ();
Con.close ();
try {drivermanager.getconnection ("jdbc:derby:;shutdown=true"); The catch (SQLException ignore) {}} public static void Main (string[] args) throws Exception {(new
Slicrpopulatr ()). Populatedatabase ();
} |
If you are familiar with JDBC, you will not be unfamiliar with most of the code in this code. However, there are several Derby-specific features that I must introduce. You begin by using the Class.forName method to load the driver class. The driver's class name is org.apache.derby.jdbc.EmbeddedDriver because you want to use an embedded version of Derby. Next, create the connection string. The Derby URL is in the format: Jdbc:derby: database name ; [Attr=value]
The database name is the name that is used when referencing the database. It doesn't matter what name you choose, as long as it's consistent with the name you used to open the database again in server code.
After you create the connection, you are in standard JDBC. Create a Statement to perform a command to delete and recreate the table, which allows you to reset the database through this program when the database is compromised. (Otherwise, Derby throws an exception when it tries to create a table that already exists.) After you create a table, you need to use an INSERT statement for each entry in the toppings array.
The SQL code in the INSERT statement has a feature that you might not need. I used the keyword DEFAULT as a placeholder for the Identity column. If you do not specify a field list in the INSERT statement, Derby wants to use the keyword in the Identity column.
Before a program exists, a special call needs to be made to get a connection to the URL "Jdbc:derby:;shutdown=true"-no database is required. This call tells the Derby system to shut down and release all possible active connections.
After running this applet, you will see a directory in the top-level directory of the application named Derbydb. This directory stores the binary files that Derby stores data. Please do not change those files in any way.
Get your data ready for GWT.
Once the database schema is in place and static data is loaded, you must now explain how data is being sent to the client, and vice versa. Finally, you have to serialize the data on the entire client-server connection. For serialization to run, the final data class must be in the location that GWT can see and handle, which means that these classes must be defined in the client package and compiled by the GWT Java-to-javascript compiler.
There are additional restrictions on the client class that will be serialized. For example, the class must implement an interface com.google.gwt.user.client.rpc.IsSerializable, which is a markup interface and does not define any methods. In addition, all data fields in the class themselves must be serialized. (As with normal Java serialization, you can exempt a field from serialization by marking it as transient.) )
What fields are serializable fields. First, the field can belong to a type that implements the IsSerializable, or has a superclass that implements the isserializable. Alternatively, the field can be one of the base types, including the Java primitives, all primitives wrapper classes, Date and String. The array or collection of serialized types is also serialized. However, if you want to serialize a Collection or List, GWT wants you to annotate it with a specific Javadoc annotation of the actual type so that the compiler can optimize it. Listing 6 shows the sample fields and methods.
Listing 6. Serialization of fields and methods
/**
* @gwt. Typeargs <java.lang.Integer>
*
private List alist;
/**
* @gwt. Typeargs <java.lang.Double>
* @gwt. Typeargs argument <java.lang.String>
* * Public list dosomethingthatreturnsalist (list argument) {
//Stuff goes here
}
|
Note: the parameters in the method list can only be specified by the name in the annotation, and the return value is not.
Note that the serialized object in the table lacks the content necessary to handle java.sql and JDBC. Whatever you do to get the result set as a data object, you must do so in the server-side code.
At this point, you're in the world of object-relational mapping (object-relational mapping,orm) or the object-oriented architecture of converting data from relational database structures to Java programs. For a complex Java production system, it may be necessary to use a pre-existing, mature ORM system, such as Hibernate or Castor. Both systems automatically mount the data from the database into the selected Java object. However, they also require a large number of configurations before they can begin. As this article focuses on Derby and GWT, I provide a quick transition program that provides services at the start of development. Finally, you can change it to a more powerful tool.
Simple ORM Conversion program
First, create a bean class for all data tables. I use the Topping class as an example because it is simple and already contains data. Use the normal bean naming convention for each column in the table, but convert the drop line to a case mix (for example, topping_id becomes gettoppingid). Listing 7 shows the Topping class.
Listing 7. Topping class
Package com.ibm.examples.client;
Import com.google.gwt.user.client.rpc.IsSerializable;
public class Topping implements isserializable {
private Integer ID;
private String name;
Private Double Price;
Public Integer GetId () {return
ID;
}
public void SetId (Integer id) {
this.id = ID;
}
Public String GetName () {return
name;
}
public void SetName (String name) {
this.name = name;
}
Public Double GetPrice () {return price
;
}
public void Setprice (Double price) {
This.price = Price;
}
}
|
The
is followed by a simple ORM tool, as shown in Listing 8.
Listing 8. Simple ORM Tools
Package com.ibm.examples.server;
Import Java.beans.PropertyDescriptor;
Import Java.lang.reflect.Method;
Import Java.sql.ResultSet;
Import java.util.ArrayList;
Import java.util.List; public class Objectfactory {public static string Convertpropertyname (string name) {string lowername = name.
toLowerCase ();
String[] pieces = Lowername.split ("_");
if (pieces.length = = 1) {return lowername;
} stringbuffer result = new StringBuffer (pieces[0]);
for (int i = 1; i < pieces.length i++) {result.append (character.touppercase (Pieces[i].charat (0)));
Result.append (pieces[i].substring (1));
return result.tostring ();
public static List Converttoobjects (ResultSet RS, Class cl) {List result = new ArrayList ();
try {int colcount = Rs.getmetadata (). getColumnCount ();
while (Rs.next ()) {The Object item = cl.newinstance (); for (int i = 1; I <= colcount i + 1) {String colname = Rs.getmetadata (). getColumnName
(i);
String propertyname = Convertpropertyname (colname);
Object value = Rs.getobject (i);
PropertyDescriptor PD = new PropertyDescriptor (propertyname, CL);
Method MT = Pd.getwritemethod ();
Mt.invoke (item, new object[] {value});
} result.add (item);
} catch (Exception e) {e.printstacktrace ();
return null;
return result;
}
} |
The Converttoobjects () method simply loops through the result set, infers the Getter attribute with the JavaBean mapping, and sets all values. The Convertpropertyname () method toggles between the naming convention for SQL plus drop lines and the mixed Java case conventions.
|
Features that ORM tools do not have
If you bring together all the useful ORM features that are missing from this tool, you can get a book out of it. For example, a tool cannot: avoid creating multiple versions of the same object. Allows the database to be written back. Run fast. |
|
The code may have done more work than you might have imagined. You can run it immediately on any database tool without further configuration. During early development, the pattern may change, and you do not have to keep the mapping file synchronized with the database. And it's not difficult to switch to more powerful tools when needed.
Listing 9 shows the running tool that reads back all Topping instances that were previously created.
listing 9. Test ORM Tools
public class Toppingtestr {public static final String DRIVER = "Org.apache.derby.jdbc.EmbeddedDriver";
public static final String PROTOCOL = "JDBC:DERBY:SLICR;";
public static void Main (string[] args) throws Exception {try {class.forname (DRIVER). newinstance ();
Connection con = drivermanager.getconnection (PROTOCOL);
Statement s = con.createstatement ();
ResultSet rs = s.executequery ("SELECT * from toppings");
List result = Objectfactory.converttoobjects (RS, topping.class); for (Iterator ITR = Result.iterator (); Itr.hasnext ();)
{Topping T = (Topping) itr.next (); System.out.println ("Topping" + t.getid () + ":" + t.getname () + "is ___fckpd___8quot;
+ T.getprice ());
Finally {try {drivermanager.getconnection ("jdbc:derby:;shutdown=true"); catch (SQLException ignore) {}
}
}
}
|
This test program will create a Derby connection to the SLICR database. (You will no longer require the protocol string to create a database as needed). Execute a simple SQL query and pass the results to the factory. You can then freely loop through the results list and exit the database.
Next Period Highlights
The database is now installed and configured. The database schema was created and some simple tools were found to put the data into the database. After reading the two articles in this series, the SLICR project now has a simple but working front-end and back end. The next step is communication. In the third article in this series, you will learn how GWT uses the framework to make remote procedure calls (remotely Procedure CALL,RPC) easily encoded and managed.