Many current O/R mapping framework depends on XML to hold large amount of information. S2Dao does not require XML. O/R mapping is accomplished by just adding metadate to Java source code.
Does not require JDBC knowledge
Java applications use JDBC to connect to a database.
When using S2Dao, database connections are handled by S2Dao. Developers are able to connect to a database without knowing intricate detailsof JDBC api. It is not necessary to write codes to connect and disconnect from the database.
SQL programmers and Java programmers can work separately on what they know the best
S2Dao is able to read SQL commands written in external files. These SQL commands may be tested using tools like Oracle SQL*Plus. Java mappings are written as comments in these files. Thus, these SQL commands can still be executed. Files which contain SQL commands with Java codes in comments are called 2 way SQL. It is also possible to map with Java read in by S2Dao.
As stated, SQL commands are written in external files separately from Java logic programming files. This allows SQL programmers to work independently from Java logic programmers which results in shortening the overall development schedule.
Automatic generation of SQL commands
If Java method names are named in a prescribed manner, it is possible to generate SQL commands. In most circumstances, generated SQL command will suffice, but in cases of complex SQL commands or when SQL command needs to be customized, SQL commands should be written manually.
Separation of interface and implementation
By just defining interfaces, it is possible to apply Aspect and to generate SQL commands.
As is required by Seasar2, S2Dao requires JDK1.4 or later. Extract the S2DaoVx.x.x.zip archive file to some directory. Open Eclipse project and import extracted files in s2dao directory by selecting [File]- [Existing Projects into Workspace]. Samples files are located under src/examples directory.
Files necessary to use S2Dao are included in the s2dao/lib directory. HSQLDB RDBMS is included to allow easier testing with a database. To use HSQLDB, it is necessary that it is started. To start HSQLDB, execute /bin/runHsqldb.bat file (for Windows users). lib/hsqldb.jar is required to use HSQLDB, but it is unnecessary in a production environment which does not use HSQLDB. To use S2Dao, add to the CLASSPATH *.jar files under the lib directory (excep hsqldb.jar) and j2ee.dicon, dao.diocon, log4j.properties under the src directory. If S2Dao files are imported to an Eclipse workspace, it is not necessary to add these files to the CLASSPATH.
JavaBeans is used to associate with a table.
To associate a JavaBeans to a table, following constants declaration and method implementation are necessary:
Table columns are associated using COLUMN annotation. COLUMN annotation constant is declared as follows:
- public static final String Property name_COLUMN = "column name";
EMPNO column is associated to property employeeNo as follows:
public static final String employeeNo_COLUMN = "EMPNO";
Note: If the property name is the same as the column name, it is not necessary to define COLUMN annotation. Properties not in the table are ignored - statement to avoid these properties are not necessary.
N:1 mapping is when several rows are associated with one row. For example, several employees are mapped to one department. To use N:1 mapping, it is required to declare constants RELNO and RELKEYS. Constant RELNO is declared as follows:
- public static final int Property name_RELNO
= value;
Constant RELNO is consecutive number in N:1 mapping. For example, if AAA table has N:1 mapping to tables BBB and CCC, RELNO are 0 and 1 for tables BBB and CCC respectively.
RELNO is used to identify columns in result set with the associated table. For example, if there is a SELECT command like follows: SELECT
..., BBB.HOGE AS HOGE_0, ... FROM AAA, BBB ...
HOGE_0 implies it is associated with column HOGE in table BBB. RELKEYS constant is as follows:
- public static final String Property name_RELKEYS = "column name of the table with N rows: column name the table with 1 row";
N:1 mapping key is specified by RELKEYS constant. When there are several keys, keys are separaed by commas ( , ). For example, mykey1:yourkey1, mykey2:yourkey2. DEPTNUM column in EMP table is associated to DEPTNO in DEPT table as follows:
public static final int department_RELNO = 0;
public static final String department_RELKEYS = "DEPTNUM:DEPTNO";
When column names in both tables are the same, column name of thetable with 1 row may be omitted. Following is an example associating DEPTNO in EMP table with DEPT table when both tables have column DEPTNO.
public static final String department_RELKEYS = "DEPTNO";
Furthermore, when associating columns when columns in both tables are the same and when the column is a primary key in the table with 1 row, RELKEYS declaration may be omitted.
ID (primary key) values that are automatically set by RDBMS may be automatically set to generated Beans by using ID annotation. ID annotation is specified as follows: property name_ID = "identity".
public static final String id_ID = "identity";
SEQUENCE may be used. Replace myseq in the following example with the actual SEQUENCE to be used.
public static final String id_ID = "sequence, sequenceName=myseq";
When ID is set manually, there is no other required setting. Primary keys are recognized from table definition (JDBC metadata). However, it is also possible to explicitly specify a primary key by "assign".
Columns are recognized as persistent from table definition (JDBC metadata). However, it is also possible to explicitly specify a column as non-persistent by using NO_PERSISTENT_PROPS. When NO_PERSISTENT_PROPS is set to an empty string, information from JDBC metadata will not be used and all properties are considered to be persistent.
public static final String NO_PERSISTENT_PROPS = "dummy1, dummy2";
Following is an example to declare a variable empno which is to be associated with column named EMPNO of type NUMBER to a variable. (Related topic:COLUMN annotation)
private long empno;
Note: When a column is Null, Java variable of primitive type returns 0 (zero) when Null. To get Null from a variable, use Java wrapper class (e.g. java.lang.Integer instead of primitive type int).
Implements getter/setter methods associated with table column properties. Method naming convention is as follows:
getter method
- public type getProperty name()
setter method
- public void setproperty name(argument)
For example, getter/setter methods for property private long empno is as follows:
private long empno;
public long getEmpno() {
return empno;
}
public void setEmpno(long empno) {
this.empno = empno;
}
JavaBans associated with EMP table having the above settings are as follows:
import java.io.Serializable;
public class Employee implements Serializable {
public static final String TABLE = "EMP";
public static final int department_RELNO = 0;
public static final String department_RELKEYS = "DEPTNUM:DEPTNO";
private long empno;
private String ename;
private Short deptnum;
private Department department;
public Employee() {
}
public Department getDepartment() {
return department;
}
public void setDepartment(Department department) {
this.department = department;
}
public Short getDeptnum() {
return deptnum;
}
public void setDeptnum(Short deptnum) {
this.deptnum = deptnum;
}
public long getEmpno() {
return empno;
}
public void setEmpno(long empno) {
this.empno = empno;
}
public String getEname() {
return ename;
}
public void setEname(String ename) {
this.ename = ename;
}
}
Dao is created as an interface. By separating persistent data and logic, aim of Dao to have persistent bean is achieved. There is a one to one relationship between a JavaBean and Dao. So, there is one Dao for one JavaBean. By calling on Dao method, SQL command written in the associated SQL file is executed. To create a Dao, followings are required:
Constant declaration of JavaBeans association (BEAN annotation)
Following annotations are used to reference method argument in SQL command and to specify property which uses insert/update SQL commands WHERE clause and/or ORDER clause.
ARGS annotation is used to reference method argument in SQL commands. Method argument name can not be referenced using reflection. ARGS annotation has the following syntax:
- public static final String method name_ARGS
= " argument name";
Following is an example that defines argument name used in a public Employee getEmployee(int empno) method which is defined in Dao.
public static final String getEmployee_ARGS = "empno";
When the method argument is associated with a table column name, argument name is set to the table column name. For example. if method argument name is "empno" and table column name is "employeeno" then employeeno is specified. When there are several arguments, they are separated by a comman. When there is only one argument, ARGS annotation may be omitted.
QUERY annotation is used to add WHERE clause and ORDER BY clause to automatically generated SELECT statement. QUERY Annotation has the following syntax:
- public static final String Method name_QUERY = "WHERE clause ORDER BY clause";
Following is an example to query employees within a specified salary range:
public static final String getEmployeesBySal_QUERY = "sal BETWEEN ? AND ? ORDER BY empno";
public List getEmployeesBySal(Float minSal, Float maxSal);
"?" in the above example is called a bind variable. Bind variables in QUERY annotation are replaced with values in method arguments in order they are used. It is not required to have an ARGS annotation. When using only ORDER BY clause, start with ORDER BY. SQL comment may be used. Following is an example of using SQL comment.
public static final String getEmployees_QUERY =
"job = /*job*/'CLERK'/*IF deptno != null*/ AND deptno = /*deptno*/20/*END*/";
The following example add the condition to get only those rows which have non-null deptno and which have deptno value equivalent to the value in the argument. Refer to SQL comment for further details.
By calling Dao method, SQL command in associated SQL file is executed. There is a naming convention to these INSERT, UPDATE, and DELETE methods. S2Dao determines type of SQL commands by matching the called method name with the naming convention. Currently, method can not be overloaded.
INSERT
Name of method to INSERT data is required to start with either insert, add, or create. Method return type must be either void or int. When return type is int, number of updated row is returned. Argument type must match with entity type. Following is an example of using this method:
public void insert(Department department);
public int addDept(Department department);
public void createDept(Department department);
UPDATE
Name of method to UPDATE data is required to start with either update, modify, or store. Method return type must be either void or int. When return type is int, number of updated row is returned. Argument type must match with entity type. Following is an example of using this method:
public int update(Department department);
public int modifyDept(Department department);
public void storeDept(Department department);
DELETE
Name of method to DELET data is required to start with either delete or remove. Method return type must be either void or int. When return type is int, number of updated row is returned. Argument type must match with entity type. Following is an example of using this method:
public void delete(Department department);
public int removeDept(Department department);
Query (SELECT)
To retrieve information from a query, type of return value must be specified. When return type is set to java.util.List, SELECT command returns a list of entities. When return type is set to an entity type array, array of entity type is returned. When return type is set to just an entity type, an entity is returned. Other return type returns only one column from one row as when SELECT count(*) FROM emp returns only one int value.
To include only specified properties in a SQL command during an update, PERSISTENT_PROPS annotation is used.
public static final String insert_PERSISTENT_PROPS = "deptno";
In the above example, primary key, versionNo, and timestamp along with properties specified in PERSISTENT_PROPS annotation are treated as persistent in an insert method.
SQL annotation became available in version 1.0.28. As in SQL file, SQL annotation supports both SQL command and SQL comment.
There is a name SQL Annotation naming convention.
Associating SQL file with a method defined in Dao
SQL annotation is associated with method defined in Dao as follows:
- method name_SQL
SQL annotation associated with examples.dao.EmployeeDao#getAllEmployees() method is as follows:
public static final String getAllEmployees_SQL = "SELECT
emp.*, dept.dname dname_0, dept.loc loc_0 FROM emp, dept
WHERE emp.deptno = dept.deptno ORDER BY emp.empno;";
Multiple DBMS support
It is possible to switch SQL annotation for differing DBMSs. Database that is currently being used is automatically determined by S2Dao using the java.sql.DatabaseMetadata#getDatabaseProductName() method. S2Dao determines which SQL annotation to use by comparing the currently used DBMS with SQL annotation suffix. For example, SQL annotation for oracle should have "oracle" as a suffix as in "getAllEmployees_oracle_SQL".
Following is a list of suffixes with their corresponding DBMS.
dicon file registers Dao as a component to a container. To use Dao, it is necessary to apply AOP to the registered Dao. dicon file may be place any where but it is usually place in the directory as the Dao. Refer to DIContainer for further information.
Applying S2DaoInterceptor
To use Dao, it is necessary to apply AOP to Dao registered with org.seasar.dao.interceptors.S2DaoInterceptor. Refer to AOP for further information on AOP. Following is an example of registering Dao(example.dao.EmployeeDao) as a component:
<component class="org.seasar.dao.implDaoMetaDataFactoryImpl"/> becomes an argument to S2DaoInterceptor constructor. So, org.seasar.dao.impl.DaoMetaDataFactoryImpl is also written.
When a method defined in Dao is called, query and update SQL commands are written in SQL file is executed. These SQL files should be place in the same directory as the Dao files. Note: S2Dao will automatically create SQL commands when there is no corresponding SQL file.
SQL file name
S2Dao and SQL files should be named in accordance with their naming convention.
Associating SQL file with method defined in Dao
To associate SQL file to a method defined in Dao, SQL file should be named as described below:
- Dao class name_method name.sql
SQL file associated with examples.dao.EmployeeDao#getAllEmployees() is as follows:
examples/dao/EmployeeDao_getAllEmployees.sql
Multiple DBMS support
It is possible to switch SQL file for differing DBMSs. Database that is currently being used is automatically determined by S2Dao using the java.sql.DatabaseMetadata#getDatabaseProductName() method. S2Dao determines which SQL file to use by comparing the currently used DBMS with SQL file name suffix. For example, SQL file name for oracle should have "oracle" as a suffix as in "EmployeeDao_getAllEmployees_oracle.sql".
Following is a list of suffixes with their corresponding DBMS.
DBMS
Suffix
Oracle
oracle
DB2
db2
MSSQLServer
mssql
MySQL
mysql
PostgreSQL
postgre
Firebird
firebird
HSQL
hsql
SQL command
In a SQL file, ordinary SQL commands such as "SELECT * FROM EMP" and "DELETE FROM EMP WHERE EMPNO = 7788" may be written. It is also possible to write a statement that dynamically changes condition in a WHERE clause. Refer to SQL comment for details.
In S2Dao, association of arguments in a method to SQL bind variables are written within comment declaration such as within "/**/" and "--". SQL commands may still be executed because non-SQL association statements are written within comments. As a best practice, it is better to first write and test SQL commands and then write associations within comments.
To write actual comments in SQL file, insert a space after "/*" before the comment string. For example, /* hoge*/. S2Dao will recognize the space after the "/*" and treat the content as an actual comment string.
Bind variable comment
To use value of an argument in a method defined by Dao, bind variable comment is used. Literal to the right of bind variable comment is automatically replace with a value and executed. Bind variable comment is written as follows:
- /*variable name*/Literal
Argument that is a JavaBeans is written as follows:
- /*argument name.Property name*/Literal
Argument name is required to be the same as the ARGS annotation value set in Dao. (However, when there is only one argument, there is not name restriction.)
public String getEmployee_ARGS = "empno";
public Employee getEmployee(int empno);
When the above method is defined in Dao, SQL file (EmploeeDao_getEmployee.sql) may use bind variable as follows. Value of getEmployee method is automatically set.
SELECT * FROM emp WHERE empno = /*empno*/7788
Following is an example of using bind variable in an IN clause.
- IN /*argument name*/(...)
IN /*names*/('aaa', 'bbb')
Argument is java.util.List or an array. For the IN clause in the above example, argument is written as follows:
String[] names = new String[]{"SCOTT", "SMITH", "JAMES"};
String array names is automatically replaced with bind variables.
Following is an example of using "LIKE":
ename LIKE /*ename*/'hoge'
To use a wild card character, include it in a method argument. To specify to include "COT", wild card character is embedded in the argument as follows:
employeeDao.findEmployees("%COT%");
Embedded variable comment
To embed argument value of a method defined in Dao directly into a SQL command as a string, variable comment is written in a SQL command. Literal to the right of the variable comment is replace with the argument value. Embedded variable comment has the following syntax:
- /*$variable name*/literal
When an argument is a JavaBean, embedded variable comment has the following syntax:
- /*$variable name.property name*/literal
IF comment
To change SQL command during execution based on a condition. IF comments are used. IF comment has the following syntax:
- /*IF condition*/ .../*END*/
Following is an example of an IF comment:
/*IF hoge != null*/hoge = /*hoge*/'abc'/*END*/
When the condition results in true, statements in /*IF*/ and /*END*/ are evaluated. In the above example, only when variable hoge is not null will hoge = /*hoge*/'abc' be evaluated. ELSE comment may be used to execute statements when condition is false. Following is an example of IF comment with an ELSE.
When the variable hoge is not null, expression "hoge is null" is evaluated.
BEGIN comment
BEGIN comment is used to not output WHERE clause when all IF comment in a WHERE clause, which does not include an ELSE, is false. BEGIN comment is used with IF comment. Syntax of BEGIN comment is as follows:
In the above example, when job and deptno are null, WHERE clause will not be outputted. When job == null,deptno != null, "WHERE depno = ?" is outputted. When job != null,deptno == null, "WHERE job = ?" is outputted. When job != null,deptno != null, "WHERE job = ? AND depno = ?" is outputted.
By using EntityManager, WHERE clause and ORDER BY clause may be inserted automatically into a SELECT statement. Syntax is the same as QUERY annotation. It is mainly used to dynamically create a query command. To use EntityManager, extend the following class
- org.seasar.dao.impl.AbstractDao
Dao interface name must end with "Dao". S2Dao treats interface implementation of class extending AbstractDao with name ending with "Dao" as Dao interface.
EntityManager has the following methods:
find() method
Returns as java.util.List. Arguments are as follows: public List
find(String query); public List find(String query, Object
arg1); public List find(String query, Object arg1, Object
arg2); public List find(String query, Object arg1, Object arg2,
Object arg3); public List find(String query, Object[]
args);
findArray() method
Returns an array. Arguments are as follows: public Object[] findArray(String
query); public Object[] findArray(String query, Object
arg1); public Object[] findArray(String query, Object arg1,
Object arg2); public Object[] findArray(String query, Object
arg1, Object arg2, Object arg3); public Object[]
findArray(String query, Object[] args);
findBean() method
Returns a JavaBeans. Arguments are as follows: public Object
findBean(String query); public Object findBean(String query,
Object arg1); public Object findBean(String query, Object arg1,
Object arg2); public Object findBean(String query, Object arg1,
Object arg2, Object arg3); public Object findBean(String query,
Object[] args);
findObject() method
Returns a single value such as of type int when count(*) is executed. Arguments are as follows: public Object
findObject(String query); public Object findObject(String
query, Object arg1); public Object findObject(String query,
Object arg1, Object arg2); public Object findObject(String
query, Object arg1, Object arg2, Object arg3); public Object
findObject(String query, Object[] args);
Argument is similar to QUERY annotation. Where there are 4 or more arguments of type Object, use array of type Object.
Basic way to implement class extending AbstractDao.
Extend org.seasar.dao.impl.AbstractDao
Implement Dao
it is required to end interface name of Dao to implement with "Dao".
Implement constructor
Call super(org.seasar.dao.DaoMetaDataFactory) with org.seasar.dao.DaoMetaDataFactory as an argument.
Implement method defined in Dao
When using method supported by EntityManager, EntityManager may be retrieved by using getEntityManager() method. For example, getEntityManager().find(...);.
Following is an example of class extending AbstractDao:
package examples.dao;
import java.util.List;
import org.seasar.dao.DaoMetaDataFactory;
import org.seasar.dao.impl.AbstractDao;
public class Employee2DaoImpl extends AbstractDao implements Employee2Dao {
public Employee2DaoImpl(DaoMetaDataFactory daoMetaDataFactory) {
super(daoMetaDataFactory);
}
public List getEmployees(String ename) {
return getEntityManager().find("ename LIKE ?", "%" + ename + "%");
}
}
Update SQL command is automatically generated if the method name must abide to the naming convention and there is one method with JavaBeans in an argument. It is not necessary to create a SQL file. Following is an example to automatically generate a SQL insert command.
S2Dao is also able to automatically control locks on data. To automatically set locks, create a column in a table for locking and define versionNo as of type int in a JavaBeans. Locking will be done based on this versionNo column.
For example, when 2 users try to update versionNo=0 data, the first user will be able to update the record. When the data is updated, value of versionNo is incremented by 1. When the second user tries to update the data, S2Dao compares the versionNo column and finds that the second user's versionNo is still 0 while the data's versionNo is now 1. Because there is a difference, S2Dao raises UpdateFailureRuntimeException and aborts the data update.
Beside VersionNo, S2Dao is able to lock data based on Timestamp. Just define a property named timestamp of data type Timestamp. Be aware that when the timestamp column is set to null, lock fails because S2Dao tries to compare with value null.
SQL command is automatically is generated when in an update method, argument is set to an entity class array or a List. The generated SQL command may then be used in a batch update.
S2Dao may generate SELECT command automatically depending on method signature. SQL command with dynamically changing WHERE clause depending on an argument can be set by specifying column name in ARGS annotation.
Following definition is use to automatically generate SQL command like those above. For information on string sequence /**/ in the above example, refer to SQL comment.
public static final String getEmployeeByJobDeptno_ARGS = "job, deptno";
public List getEmployeeByJobDeptno(String job, Integer deptno);
N:1 mapped column is specified by "column name_association number". N:1 mapped Bean retrieves data as 1 SQL command by using left outer join operation. If RDBMS does not support left outer join, S2Dao will not automatically generate SQL commands. S2Dao,however, is able to recognize RDBMS, and properly treats RDBMS like Oracle which has a proprietary left outer joins operation.
It is possible to specify DTO (Data Transter Object) in an argument. In this situation, ARGS annotation should not be specified. When there is 1 argument and ARGS annotation is not specified, S2Dao will treat the argument as DTO and will use that DTO7s property to automatically generate SQL command. When property name and column name are different, COLUMN annotation is used to specify the column name. To specify N:1 mapped column, use "column name_association number". Propery (column) not in a table are ignored. It is possible to dynamically create a SQL command with changing WHERE clause based on a property. It is possible to both dynamically generate SQL command as well as have QUERY annotation starting with ORDER BY.
package examples.dao;
public class EmployeeSearchCondition {
public static final String dname_COLUMN = "dname_0";
private String job;
private String dname;
...
}
List getEmployeesBySearchCondition(EmployeeSearchCondition dto);
Define a method. Method to retrieve all rows (getAllEmployees() method) Method to retrieve rows matching employee number specified in the argument( getEmployee(int empno) method) Method to return number of employees (getCount() method) Method to retrieve rows matching job and department number specified in arguments. (getEmployeeByJobDeptno(String job, Integer deptno) method) Method to update employee data (update(Employee employee) method)
SQL command and method argument are associated using ARGS annotation.
getCount() method that returns the number of rows in the EMP table returns only one row, so the data type of return value is declared as int.
package examples.dao;
import java.util.List;
public interface EmployeeDao {
public Class BEAN = Employee.class;
public List getAllEmployees();
public String getEmployee_ARGS = "empno";
public Employee getEmployee(int empno);
public int getCount();
public String getEmployeeByJobDeptno_ARGS = "job, deptno";
public List getEmployeeByJobDeptno(String job, Integer deptno);
public int update(Employee employee);
}
Creating SQL file
UL>
Create SQL files corresponding to methods defined in Dao.
File is named in accordance with the naming convention - "class name_ method name.sql".
Create a container by passing directory path of the created dicon file (EmployeeDao.dicon) as the first argument to org.seasar.framework.container.S2Container#create() method.
Get component by passing class name (EmployeeDao.class) of registered component as the first argument to org.seasar.framework.container.S2Container#getComponent() method.
Executte method defined in Dao
package examples.dao;
import java.util.List;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;
public class EmployeeDaoClient {
private static final String PATH = "examples/dao/EmployeeDao.dicon";
public static void main(String[] args) {
S2Container container = S2ContainerFactory.create(PATH);
container.init();
try {
EmployeeDao dao = (EmployeeDao) container
.getComponent(EmployeeDao.class);
List employees = dao.getAllEmployees();
for (int i = 0; i < employees.size(); ++i) {
System.out.println(employees.get(i));
}
Employee employee = dao.getEmployee(7788);
System.out.println(employee);
int count = dao.getCount();
System.out.println("count:" + count);
dao.getEmployeeByJobDeptno(null, null);
dao.getEmployeeByJobDeptno("CLERK", null);
dao.getEmployeeByJobDeptno(null, new Integer(20));
dao.getEmployeeByJobDeptno("CLERK", new Integer(20));
System.out.println("updatedRows:" + dao.update(employee));
} finally {
container.destroy();
}
}
}
Number of rows that has been updated may be found from the value in "updatedRows". Files in this example is under the s2dao/src/examples/dao directory.
This is an example of automatically generating UPDATE, INSERT, DELETE SQL commands and of lock control using VersionNo. This example does not require creating a SQL file. Following files are created:
Dao(DepartmentDao.java)
dicon file(DepartmentDao.dicon)
Execution class(DepartmentDaoClient.java)
Creating Dao
Associate DEPT table with related JavaBeans
Define update method. Method to insert a department (insert(Department department) method) Method to update a department (update(Department
department) method) Method to delete a department (delete(Department department) method)
package examples.dao;
public interface DepartmentDao {
public Class BEAN = Department.class;
public void insert(Department department);
public void update(Department department);
public void delete(Department department);
}
Create a container by using org.seasar.framework.container.S2Container#create()method with the first argument set to the path of created dicon file (DepartmentDao.dicon).
Retrieve the component by using org.seasar.framework.container.S2Container#getComponent() method with the first argument set to the class name (DepartmentDao.class) registered with the component.
Execute method defined in Dao
package examples.dao;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;
public class DepartmentDaoClient {
private static final String PATH = "examples/dao/DepartmentDao.dicon";
public static void main(String[] args) {
S2Container container = S2ContainerFactory.create(PATH);
container.init();
try {
DepartmentDao dao = (DepartmentDao) container
.getComponent(DepartmentDao.class);
Department dept = new Department();
dept.setDeptno(99);
dept.setDname("foo");
dao.insert(dept);
dept.setDname("bar");
System.out.println("before update versionNo:" + dept.getVersionNo());
dao.update(dept);
System.out.println("after update versionNo:" + dept.getVersionNo());
dao.delete(dept);
} finally {
container.destroy();
}
}
}
Result
DEBUG 2004-09-09 19:22:10,588 [main] physical connection opened
DEBUG 2004-09-09 19:22:10,588 [main] logical connection opened
DEBUG 2004-09-09 19:22:11,447 [main] logical connection closed
DEBUG 2004-09-09 19:22:11,603 [main] logical connection opened
DEBUG 2004-09-09 19:22:11,603 [main] INSERT INTO DEPT (deptno, dname, versionNo, loc)
VALUES(99, 'foo', 0, null)
DEBUG 2004-09-09 19:22:11,666 [main] logical connection closed
before update versionNo:0
DEBUG 2004-09-09 19:22:11,666 [main] logical connection opened
DEBUG 2004-09-09 19:22:11,666 [main] UPDATE DEPT SET dname = 'bar',
versionNo = versionNo + 1, loc = null WHERE deptno = 99 AND versionNo = 0
DEBUG 2004-09-09 19:22:11,666 [main] logical connection closed
after update versionNo:1
DEBUG 2004-09-09 19:22:11,666 [main] logical connection opened
DEBUG 2004-09-09 19:22:11,666 [main] DELETE FROM DEPT WHERE deptno = 99 AND versionNo = 1
DEBUG 2004-09-09 19:22:11,681 [main] logical connection closed
DEBUG 2004-09-09 19:22:11,681 [main] physical connect closed
The reslt shows SQL commands being automatically generated. Also, JavaBeans (Departmen) has a datatype int property versionNo which is being incremented by 1 to lock rows. Before update method was called, versionNo was 0 but became 1 after the call. Files in this example is available under the s2dao/src/examples/dao directory.
This is a sample to automatically generate a SELECT command and locking based on Timestamp. SQL file is not necessary. A method that uses DTO as an argument is also shown.
Following files are created:
Dao(EmployeeAutoDao.java)
DTO(EmployeeSearchCondition.java)
dicon file(EmployeeAutoDao.dicon)
Execution class (EmployeeAutoDaoClient.java)
Creating Dao
Associate EMP table with related JavaBeans
Define method. Method (getAllEmployees() method) to query all data Method (getEmployeeByJobDeptno(String job, Integer deptno) method) to take job and department number as arguments and return matching employees. Method (getEmployeeByEmpno(int empno) method) to retrieve employee number matching the employee number passed in the argument Method (getEmployeesBySal(float minSal, float maxSal) method) to retrieve employee with salary within range of those specified in the argument. Method (getEmployeeByDname(String dname) method) to retrieve employee in department specified in the argument. Method with DTO argument (getEmployeesBySearchCondition(EmployeeSearchCondition dto) method) to retrieve employee Method to update employee (update(Employee employee) method)
package examples.dao;
import java.util.List;
public interface EmployeeAutoDao {
public Class BEAN = Employee.class;
public List getAllEmployees();
public String getEmployeeByJobDeptno_ARGS = "job, deptno";
public List getEmployeeByJobDeptno(String job, Integer deptno);
public String getEmployeeByEmpno_ARGS = "empno";
public Employee getEmployeeByEmpno(int empno);
public String getEmployeesBySal_QUERY = "sal BETWEEN ? AND ? ORDER BY empno";
public List getEmployeesBySal(float minSal, float maxSal);
public String getEmployeeByDname_ARGS = "dname_0";
public List getEmployeeByDname(String dname);
public List getEmployeesBySearchCondition(EmployeeSearchCondition dto);
public void update(Employee employee);
}
Create a container by using org.seasar.framework.container.S2Container#create() method with the first argument set to the path of created dicon file (EmployeeAutoDao.dicon).
Retrieve the component by using org.seasar.framework.container.S2Container#getComponent() method with the first argument set to the class name (EmployeeAutoDao.class) registered with the component.
Execute method defined in Dao
package examples.dao;
import java.util.List;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;
public class EmployeeAutoDaoClient {
private static final String PATH = "examples/dao/EmployeeAutoDao.dicon";
public static void main(String[] args) {
S2Container container = S2ContainerFactory.create(PATH);
container.init();
try {
EmployeeAutoDao dao = (EmployeeAutoDao) container
.getComponent(EmployeeAutoDao.class);
dao.getEmployeeByJobDeptno(null, null);
dao.getEmployeeByJobDeptno("CLERK", null);
dao.getEmployeeByJobDeptno(null, new Integer(20));
dao.getEmployeeByJobDeptno("CLERK", new Integer(20));
List employees = dao.getEmployeesBySal(0, 1000);
for (int i = 0; i < employees.size(); ++i) {
System.out.println(employees.get(i));
}
employees = dao.getEmployeeByDname("SALES");
for (int i = 0; i < employees.size(); ++i) {
System.out.println(employees.get(i));
}
EmployeeSearchCondition dto = new EmployeeSearchCondition();
dto.setDname("RESEARCH");
employees = dao.getEmployeesBySearchCondition(dto);
for (int i = 0; i < employees.size(); ++i) {
System.out.println(employees.get(i));
}
Employee employee = dao.getEmployeeByEmpno(7788);
System.out.println("before timestamp:" + employee.getTimestamp());
dao.update(employee);
System.out.println("after timestamp:" + employee.getTimestamp());
} finally {
container.destroy();
}
}
}
Result
DEBUG 2004-10-12 11:35:22,054 [main] physical connection opened
DEBUG 2004-10-12 11:35:22,069 [main] logical connection opened
DEBUG 2004-10-12 11:35:22,897 [main] logical connection closed
DEBUG 2004-10-12 11:35:23,726 [main] SELECT EMP.tstamp, EMP.empno, EMP.ename, EMP.job,
EMP.mgr, EMP.hiredate, EMP.sal, EMP.comm, EMP.deptno, department.dname AS dname_0,
department.deptno AS deptno_0, department.loc AS loc_0,
department.versionNo AS versionNo_0 FROM EMP LEFT OUTER JOIN DEPT department
ON EMP.deptno = department.deptno
DEBUG 2004-10-12 11:35:23,726 [main] logical connection opened
DEBUG 2004-10-12 11:35:23,866 [main] logical connection closed
DEBUG 2004-10-12 11:35:23,866 [main] SELECT EMP.tstamp, EMP.empno, EMP.ename, EMP.job,
EMP.mgr, EMP.hiredate, EMP.sal, EMP.comm, EMP.deptno, department.dname AS dname_0,
department.deptno AS deptno_0, department.loc AS loc_0,
department.versionNo AS versionNo_0 FROM EMP LEFT OUTER JOIN DEPT department
ON EMP.deptno = department.deptno WHERE EMP.job = 'CLERK'
DEBUG 2004-10-12 11:35:23,866 [main] ogical connection opened
DEBUG 2004-10-12 11:35:23,882 [main] logical connection closed
DEBUG 2004-10-12 11:35:23,882 [main] SELECT EMP.tstamp, EMP.empno, EMP.ename, EMP.job,
EMP.mgr, EMP.hiredate, EMP.sal, EMP.comm, EMP.deptno, department.dname AS dname_0,
department.deptno AS deptno_0, department.loc AS loc_0,
department.versionNo AS versionNo_0 FROM EMP LEFT OUTER JOIN DEPT department
ON EMP.deptno = department.deptno WHERE EMP.deptno = 20
DEBUG 2004-10-12 11:35:23,882 [main] logical connection opened
DEBUG 2004-10-12 11:35:23,913 [main] loical connection closed
DEBUG 2004-10-12 11:35:23,913 [main] SELECT EMP.tstamp, EMP.empno, EMP.ename, EMP.job,
EMP.mgr, EMP.hiredate, EMP.sal, EMP.comm, EMP.deptno, department.dname AS dname_0,
department.deptno AS deptno_0, department.loc AS loc_0,
department.versionNo AS versionNo_0 FROM EMP LEFT OUTER JOIN DEPT department
ON EMP.deptno = department.deptno WHERE EMP.job = 'CLERK' AND EMP.deptno = 20
DEBUG 2004-10-12 11:35:23,913 [main] logical connection opened
DEBUG 2004-10-12 11:35:23,929 [main] logical connection closed
DEBUG 2004-10-12 11:35:23,929 [main] SELECT EMP.tstamp, EMP.empno, EMP.ename, EMP.job,
EMP.mgr, EMP.hiredate, EMP.sal, EMP.comm, EMP.deptno, department.dname AS dname_0,
department.deptno AS deptno_0, department.loc AS loc_0,
department.versionNo AS versionNo_0 FROM EMP LEFT OUTER JOIN DEPT department
ON EMP.deptno = department.deptno WHERE sal BETWEEN 0.0 AND 1000.0 ORDER BY empno
DEBUG 2004-10-12 11:35:23,929 [main] logical connection opened
DEBUG 2004-10-12 11:35:23,944 [main] logical connection closed
7369, SMITH, CLERK, 7902, 1980-12-17 00:00:00.0, 800.0, null, 20,
1980-12-17 00:00:00.0, {20, RESEARCH, DALLAS, 0}
7900, JAMES, CLERK, 7698, 1981-12-03 00:00:00.0, 950.0, null, 30,
1980-12-17 00:00:00.0, {30, SALES, CHICAGO, 0}
DEBUG 2004-10-12 11:35:23,944 [main] SELECT EMP.tstamp, EMP.empno, EMP.ename, EMP.job,
EMP.mgr, EMP.hiredate, EMP.sal, EMP.comm, EMP.deptno, department.dname AS dname_0,
department.deptno AS deptno_0, department.loc AS loc_0,
department.versionNo AS versionNo_0 FROM EMP LEFT OUTER JOIN DEPT department
ON EMP.deptno = department.deptno WHERE department.dname = 'SALES'
DEBUG 2004-10-12 11:35:23,944 [main] logical connection opened
DEBUG 2004-10-12 11:35:23,960 [main] logical connection closed
7499, ALLEN, SALESMAN, 7698, 1981-02-20 00:00:00.0, 1600.0, 300.0, 30,
1980-12-17 00:00:00.0, {30, SALES, CHICAGO, 0}
7521, WARD, SALESMAN, 7698, 1981-02-22 00:00:00.0, 1250.0, 500.0, 30,
1980-12-17 00:00:00.0, {30, SALES, CHICAGO, 0}
7654, MARTIN, SALESMAN, 7698, 1981-09-28 00:00:00.0, 1250.0, 1400.0, 30,
1980-12-17 00:00:00.0, {30, SALES, CHICAGO, 0}
7698, BLAKE, MANAGER, 7839, 1981-05-01 00:00:00.0, 2850.0, null, 30,
1980-12-17 00:00:00.0, {30, SALES, CHICAGO, 0}
7844, TURNER, SALESMAN, 7698, 1981-09-08 00:00:00.0, 1500.0, 0.0, 30,
1980-12-17 00:00:00.0, {30, SALES, CHICAGO, 0}
7900, JAMES, CLERK, 7698, 1981-12-03 00:00:00.0, 950.0, null, 30,
1980-12-17 00:00:00.0, {30, SALES, CHICAGO, 0}
DEBUG 2004-10-12 11:35:23,960 [main] SELECT EMP.tstamp, EMP.empno, EMP.ename, EMP.job,
EMP.mgr, EMP.hiredate, EMP.sal, EMP.comm, EMP.deptno, department.dname AS dname_0,
department.deptno AS deptno_0, department.loc AS loc_0,
department.versionNo AS versionNo_0 FROM EMP LEFT OUTER JOIN DEPT department
ON EMP.deptno = department.deptno WHERE department.dname = 'RESEARCH'
DEBUG 2004-10-12 11:35:23,976 [main] logical connection opened
DEBUG 2004-10-12 11:35:23,976 [main] logical connection closed
7369, SMITH, CLERK, 7902, 1980-12-17 00:00:00.0, 800.0, null, 20,
1980-12-17 00:00:00.0, {20, RESEARCH, DALLAS, 0}
7566, JONES, MANAGER, 7839, 1981-04-02 00:00:00.0, 2975.0, null, 20,
1980-12-17 00:00:00.0, {20, RESEARCH, DALLAS, 0}
7788, SCOTT, ANALYST, 7566, 1982-12-09 00:00:00.0, 3000.0, null, 20,
2004-10-12 10:15:54.914, {20, RESEARCH, DALLAS, 0}
7876, ADAMS, CLERK, 7788, 1983-01-12 00:00:00.0, 1100.0, null, 20,
1980-12-17 00:00:00.0, {20, RESEARCH, DALLAS, 0}
7902, FORD, ANALYST, 7566, 1981-12-03 00:00:00.0, 3000.0, null, 20,
1980-12-17 00:00:00.0, {20, RESEARCH, DALLAS, 0}
DEBUG 2004-10-12 11:35:23,976 [main] SELECT EMP.tstamp, EMP.empno, EMP.ename, EMP.job,
EMP.mgr, EMP.hiredate, EMP.sal, EMP.comm, EMP.deptno, department.dname AS dname_0,
department.deptno AS deptno_0, department.loc AS loc_0,
department.versionNo AS versionNo_0 FROM EMP LEFT OUTER JOIN DEPT department
ON EMP.deptno = department.deptno WHERE EMP.empno = 7788
DEBUG 2004-10-12 11:35:23,991 [main] logical connection opened
DEBUG 2004-10-12 11:35:23,991 [main] logical connection closed
before tmestamp:2004-10-12 10:15:54.914
DEBUG 2004-10-12 11:35:23,991 [main] logical connection opened
DEBUG 2004-10-12 11:35:23,991 [main] UPDATE EMP SET tstamp = '2004-10-12 11.35.23',
ename = 'SCOTT', job = 'ANALYST', mgr = 7566, hiredate = '1982-12-09 00.00.00',
sal = 3000.0, comm = null, deptno = 20
WHERE empno = 7788 AND tstamp = '2004-10-12 10.15.54'
DEBUG 2004-10-12 11:35:24,054 [main] logical connection closed
after timestamp:2004-10-12 11:35:23.991
DEBUG 2004-10-12 11:35:24,054 [main] physical connection closed
The result shows SQL commands being automatically generated. Also, value of Timestamp before and after update is different suggesting that this value is used to lock a row. Files in this example is available under the s2dao/src/examples/dao directory.
This example uses EntityManager to query employees whose name contain the specified string. Following files are created:
Dao(Employee2Dao.java)
Class (Employee2DaoImpl.java) extending AbstractDao
dicon file(Employee2Dao.dicon)
Execution class (Employee2DaoClient.java)
Creating Dao
Interface name must end with string "Dao".
Associate with JavaBeans relating to the EMP table
Define method to query. Method to query employee (getEmployees(String ename))
package examples.dao;
import java.util.List;
public interface Employee2Dao {
public Class BEAN = Employee.class;
public List getEmployees(String ename);
}
Creating class extending AbstractDao
Extend org.seasar.dao.impl.AbstractDao
implement Employee2Dao
Implement getEmployees method. Query with string containing specified name
package examples.dao;
import java.util.List;
import org.seasar.dao.DaoMetaDataFactory;
import org.seasar.dao.impl.AbstractDao;
public class Employee2DaoImpl extends AbstractDao implements Employee2Dao {
public Employee2DaoImpl(DaoMetaDataFactory daoMetaDataFactory) {
super(daoMetaDataFactory);
}
public List getEmployees(String ename) {
return getEntityManager().find("ename LIKE ?", "%" + ename + "%");
}
}
Creating dicon file
include dao.dicon
Define as a component the class that extends AbstractDao
Apply dao.interceptor(S2DaoInterceptor) to registered component
Create a container by using org.seasar.framework.container.S2Container#create()method with the first argument set to the path of created dicon file (Employee2Dao.dicon).
Retrieve the component by using org.seasar.framework.container.S2Container#getComponent() method with the first argument set to the class name (Employee2Dao.class) registered with the component.
Specify condition to include string "CO" in a name
Execute method defined in Dao
ackage examples.dao;
import java.util.List;
import org.seasar.framework.container.S2Container;
import org.seasar.framework.container.factory.S2ContainerFactory;
public class Employee2DaoClient {
private static final String PATH = "examples/dao/Employee2Dao.dicon";
public static void main(String[] args) {
S2Container container = S2ContainerFactory.create(PATH);
container.init();
try {
Employee2Dao dao = (Employee2Dao) container
.getComponent(Employee2Dao.class);
List employees = dao.getEmployees("CO");
for (int i = 0; i < employees.size(); ++i) {
System.out.println(employees.get(i));
}
} finally {
container.destroy();
}
}
}
Result
DEBUG 2004-10-01 10:14:39,333 [main] physical connection opened
DEBUG 2004-10-01 10:14:39,333 [main] logical connection opened
DEBUG 2004-10-01 10:14:40,379 [main] logical connetion closed
DEBUG 2004-10-01 10:14:41,254 [main] SELECT EMP.empno, EMP.ename, EMP.job, EMP.mgr,
EMP.hiredate, EMP.sal, EMP.comm, EMP.deptno, department.deptno AS deptno_0,
department.dname AS dname_0,
department.loc AS loc_0, department.versionNo AS versionNo_0 FROM EMP
LEFT OUTER JOIN DEPT department ON EMP.deptno = department.deptno
WHERE ename LIKE '%CO%'
DEBUG 2004-10-01 10:14:41,270 [main] logical connection opened
DEBUG 2004-10-01 10:14:41,426 [main] logical connection closed
7788, SCOTT, ANALYST, 7566, 1982-12-09 00:00:00.0, 3000.0, null, 20
{20, RESEARCH, DALLAS, 0}
DEBUG 2004-10-01 10:14:41,442 [main] physical connection closed
Source codes of above examples are in s2dao/src/examples/dao.