The DataBean package is a java library providing functionalities of mapping a database table to a Java class (databean). The coding of the databean class must conform to a preset pattern so that the instances of the databean classes can perform common database operations without using much of the ugly SQL. It will not completely eliminate the SQL code in your application, but in many cases, all you need is to write some simple SQL condition, like:
String cond = "id=" + id;or
String cond = "username='" + username + "';
DataBean is functional, stable, light-weighted, and easy to use. It can make your application much easier to code when dealing with not so sophisticated database operations.
DataBean is free. you can download it from sourceforge.
When designing an application, the most common approach is to start from the data model, in which you will draw the table schemas that your application will use. After that you will write Java classes that will work with these tables. There are many ways to do this and many frameworks exists for this purpose, for example, the Enterprise Java Bean(EJB) framework from Sun and some XML based object-relational mapping tools. Those frameworks are absolutely good, there are no questions about that. But in too many cases, you only have a small to medium sized project, and these frameworks are over-weighted to fit into your application.
This was the situation I had a year ago when I started work on a digital library project. The most common database operations the project had were just select, update, delete, and insert from or into one or a few tables. I was looking around for a object to relational mapping package. My requirement was that it must be robust, light-weighted, functional, and easy to use, but I couldn't find a single tool that fit my needs completely. So comes this DataBean library.
In the DataBean framework, you will write one class to map to each database table. This class is a Java Bean, meaning that it is coded completely based on the Java Bean coding style. But in addition to that, this class will have codes to enable it to map to a table, which will be explained in detail in the following section. I called so patterned class a DataBean class and an instance of such a class a data bean. The database table that a data bean maps to is simply called the associated table of the data bean.
The core class of the library is the Table class, which encapsulates the common database operations in its methods calls. Each data bean class will have a class-wide(statically declared) Table object. This Table object is responsible for the database operations initiated by the actual data bean instances. When doing the mapping between the containing DataBean class and the database table, it reflects the properties of the containing class using the Java Reflection framework. This requires the DataBean class to declare its properties as public, and is something considered to be a sacrifice when using the DataBean library.
As said, DataBean class has a preset coding pattern. The variant in the coding is the table schema. Given a table schema, the DataBean class code ought to be automatically generated. Although we don't have such a tool yet, it is in the todo list. Anyone who has experience on the code generation tool is encoraged to work on this.
The current release has been tested under MySQL, the most popular open source database server. It is not tested under other DBMS due to the tight schedule I have. But it should work because all the SQL used is standard based, though some keywords may change, for example, auto_increment column on MySQL would become identity column on other platform.
To test it, you would need MySQL installed. first unpack the distribution file and modify the com.youyounet.db.DBInterface.java file. If you want to use the default connection pooling package, you will also need to include struts.jar file in your classpath. A copy of this file is included in the distribution. Modify the settings in the com.youyounet.db.DBInterface.java file and compile all the source files. Read through the following examples and you can start testing it yourself.
It's probably easier to illustrate this through an example. Let's say you have a database table called User which has the following schema
create table Users( UserID bigint not null auto_increment, primary key(UserID), Username varchar(20) not null, unique index(Username), Password varchar(20) not null, LastLogin datetime, Tmstamp timestamp )
The data bean class mapping to this database table will have the following code
import com.youyounet.db.*; import java.sql.*; import java.util.*; public class User extends DataBean{ //the following few lines of code defines the mapping of the class properties to //the table columns. public static Hashtable fieldMap = new Hashtable(); static{ fieldMap.put("userID", new ColumnAttribute("UserID", 6)); fieldMap.put("username", new ColumnAttribute("Username")); fieldMap.put("password", new ColumnAttribute("Password", 8)); fieldMap.put("lastLogin", new ColumnAttribute("LastLogin")); fieldMap.put("tmstamp", new ColumnAttribute("Tmstamp", 16)); } public static Table table = new Table("Users", //table name "com.jdb.data.User", //class name fieldMap); public Table getTable(){ return table; } //after the above mapping code, the following is just the standard Java bean definition public int userID = -1; public String username = ""; public String password = ""; public Timestamp lastLogin = null; public Timestamp tmstamp = null; public int getUserID(){ return userID; } public void setUserID(int var){ userID = var; } public String getUsername(){ return username; } public void setUsername(String var){ username = var; } public String getPassword(){ return password; } public void setPassword(String var){ password = var; } public Timestamp getLastLogin(){ return lastLogin; } public void setLastLogin(Timestamp var){ lastLogin = var; } public Timestamp getTmstamp(){ return tmstamp; } public void setTmstamp(Timestamp var){ tmstamp = var; } public int getID(){ return getUserID(); } }
All the actual data bean classes must extends the abstract DataBean super class. The first part of the class code is dedicated to the mapping of the class properties to the database table columns. No magic of this, simply put the class property names as keys and table column attributes as values in the statically decalred fieldMap. All the names are case sensitive. After that, declare a class-wide table object and pass the table name, the class name, the fieldMap as the arguments. This table object is responsible for doing the database operations for the instances of the class. Finally, give a get method for the table object. This completes the mapping. The remaining code of the class is simply standard Java Bean styled code and will not be explained here.
Once having all these defined, we can do something like this, for example:
User user = new User(); String username = request.getParameter("username"); String password = request.getParameter("password"); if(!user.load("Username='" + username + "'")) //username not in database, do something; if(!user.getPassword().equals(password)) //password not correct, do some thing; long now = System.currentTimeMillis(); user.setLastLogin(new Timestamp(now)); user.store(); //the user record will be updated.
The type of data bean properties that could map to a database table column is not limited to the known Java SQL types. Composite property can also map to a table column of type BLOB provided that the composite property can be serialized to a binary stream.
1. show the use of transactions.
The table:
CREATE TABLE resourcepolicy( policy_id INTEGER auto_increment, PRIMARY KEY(policy_id), resource_type_id INTEGER, resource_id INTEGER, action_id INTEGER, role_id INTEGER REFERENCES role(role_id), )
This is a table storing permission policies for resources. Correspondingly, we can have a data bean class to map to this table:
public class ResourcePolicy extends com.youyounet.db.DataBean{ public static Hashtable fieldMap = new Hashtable(); static{ fieldMap.put("policyID", new ColumnAttribute("policy_id", 6)); fieldMap.put("resourceTypeID", new ColumnAttribute("resource_type_id")); fieldMap.put("resourceID", new ColumnAttribute("resource_id")); fieldMap.put("actionID", new ColumnAttribute("action_id")); fieldMap.put("roleID", new ColumnAttribute("role_id")); } public static Table table = new Table("resourcepolicy", "com.youyounet.authorize.data.ResourcePolicy", fieldMap); public Table getTable(){ return table; } public int policyID = -1; public int resourceTypeID = -1; public int resourceID = -1; public int actionID = -1; public int roleID = -1; public int getPolicyID(){ return policyID; } public void setPolicyID(int var){ policyID = var; } public int getResourceTypeID(){ return resourceTypeID; } public void setResourceTypeID(int var){ resourceTypeID = var; } public int getResourceID(){ return resourceID; } public void setResourceID(int var){ resourceID = var; } public int getActionID(){ return actionID; } public void setActionID(int var){ actionID = var; } public int getRoleID(){ return roleID; } public void setRoleID(int var){ roleID = var; } public int getID(){ return getPolicyID(); } }
Now you want to add some policies to a resource (insert some policies records into the table for a resource) in a transaction. Here is the code:
public static void addPolicies(Vector policies, Resource resource){ if(policies == null || resource == null) throw new IllegalArgumentException("in addPolicies: passed in parameters" + " contains null value."); DBContext c = null; try{ c = new DBContext(); //initiate the transaction Iterator k = policies.iterator(); while(k.hasNext()){ ResourcePolicy policy = (ResourcePolicy)k.next(); policy.setResourceID(resource.getID()); policy.setResourceTypeID(resource.getType()); policy.create(c); //insert the policy record to its associated table } c.commit(); //commit the transaction }catch(Exception e){ try{ c.abort(); }catch(SQLException se){....} .... }finally{ try{ c.reset(); }catch(SQLException sqe){ ....} } }
2. Show selection of data beans
Using the same table and data bean class as above, now if we want to select policies for a specific resource, here is the code:
Vector policies = new ResourcePolicy().select("resource_id=" + resource.getID() + " and resource_type_id=" + resource.getType());
3. Show the bean iteration
Still using the same table and data bean class as above, now let's iterate through all the policies defined for a resource. We can first select a vector of policies for the resource and iterate through that vector. But we will use another method:
Iterator k = new ResourcePolicy().iterator("resource_id=" + resource.getID() + " and resource_type_id=" + resource.getType()); while(k.hasNext()){ ResourcePolicy rp = (ResourcePolicy)k.next(); .... }
4. Show the composite bean property mapping
Suppose we have a web application that requires users to fill up a multi-page form. Users will have an option on each page, letting them to save the uncompleted form. When they login back next time, the application will present them with the form at the exact place they left off last time and let them to resume the process. Let's say we have a JSP bean called SubmissionData that will contain all the information the user filled in. This information is not final, it needs to be processed until it can be stored in database tables. When the form is not completed, we cannot process the information contained in the SubmissionData bean but we need to store the bean somewhere so next time it can be fetched. Here is the table to store the uncompleted submission information:
CREATE TABLE submission( submission_id INTEGER auto_increment, PRIMARY KEY(policy_id), userID INTEGER, submission_data BLOB, //to store the SubnmissionData bean step INTEGER, //the step where was left off tmstamp TIMESTAMP )and here is the Submission data bean class:
public class Submission extends DataBean{ public static Hashtable fieldMap = new Hashtable(); static{ fieldMap.put("submissionID", new ColumnAttribute("submission_id", 6)); fieldMap.put("userID", new ColumnAttribute("user_id")); fieldMap.put("submissionData", new ColumnAttribute("submission_data")); fieldMap.put("step", new ColumnAttribute("step")); fieldMap.put("tmstamp", new ColumnAttribute("Tmstamp", 16)); } public static Table table = new Table("submission", //table name "com.youyounet.data.Submission", //class name fieldMap); public Table getTable(){ return table; } public int submissionID = -1; public int userID = -1; public SubmissionData submissionData = null; public int step = -1; public Timestamp tmstamp = null; public int getSubmissionID(){ return submissionID; } public void setSubmissionID(int var){ submissionID = var; } public int getUserID(){ return userID; } public void setUserID(int var){ userID = var; } public SubmissionData getSubmissionData(){ return submissionData; } public void setSubmissionData(SubmissionData var){ submissionData = var; } public int getStep(){ return step; } public void setStep(int var){ step = var; } public Timestamp getTmstamp(){ return tmstamp; } public void setTmstamp(Timestamp var){ tmstamp = var; } }
When the submission is suspended, we can save the submission data in the following code:
//if the submission is suspended the first time ..... Submission submission = new Submission(); submission.setSubmissionData(data); //data is the instance of SubmissionData submission.setStep(step); submission.setUserID(uid); //the submitter's id submission.create(); //insert the row ..... //if the submission was suspended before and was suspended again ..... submission.setSubmissionData(data); //data is the instance of SubmissionData submission.setStep(step); submission.store(); //update the row .....
You don't have to worry about the timestamp column, it will be taken care automatically. Next time when the user is logging back, we can fetch the umcompleted submission data as easily as like this:
Submission submission = new Submission(); if(submission.load("user_id=" + uid)){ //uid is the user's ID SubmissionData data = submission.getSubmissionData(); //then populate the form with the stored data and present it to the user }else{ //the user didn't leave an uncompleted form //do something else; }
That is it. There is one requirement though, the SubmissionData bean must be serializable so that it could be written to a binary stream.
OK, so you have some idea about what DataBean is. The examples above only demonstrate some of the functionalities that DataBean has. For more information you can refer to the API doc (also included with the distribution). I encourage you to use it, test it, contribute to it, and make it a better library that everybody can use.
Mike Zhang