src/java/test/org/benow/repository/Person.java.
The class is declared as normal, and extends JSQLObject:
public class Person extends JSQLObject {
The fields are declared as normal, with annotations where appropriate:
@StringLength(64) private final String firstName; @StringLength(64) @Searchable private final String lastName; @StringLength(128) private final String address; @StringLength(64) private final String city; @StringLength(16) private final String postal; private int heightInCMs;
The @StringLength annotation is used to indicate that a string field is to be stored with a specified length. If no StringLength is given for a string field, then the field is stored as a blob (or equivalent), which might have an impact on searching. Fixed length strings are easier to see when browsing the database.
The @Searchable annotation indicates that an upcase copy of the string is also to be stored. Having an upper case representation can make searching easier.
List fields are created as JSQLList objects. JSQLList (or rather the JSQLArrayList implementation) provides facilities for easing list population and storage. The declaration of a list is:
private final JSQLList<Person> friends=new JSQLArrayList<Person>(this,"friends");
The JSQLArrayList must be created with a reference to the containing JSQLObject (this) and the name of the assigned field. This information is required for list navigation and population. It's not as easy as List/ArrayList, but close. If you want to manage object associations, use a JSQLList/JSQLArrayList pairing.
The rest is standard getters and setters, with the exception of getFriends():
public List<Person> getFriends() {
fetchFieldQuiet("friends");
return friends;
}
The fetchFieldQuiet("friends") causes the list values to be fetched. If the getFriends() is called repeatedly, the values
will only be fetched the first time.
So, that's it for object creation. This is a very basic example, and more examples can be found in src/java/test/org/benow/repository directory. More advanced concepts coved in the sample classes include Abstract classes, Interfaces, etc.
Person. The doStore() method at the end of src/java/test/org/benow/repository/Tutorial.java can be run under un*x with
bin/tutorial_repository.sh create or bin/tutorial_repository.bat create in windows.
The following is performed within the main method:
Person andy=new Person("Andrew", "Taylor", "36 End of Internet Cl.", "Noosphere", "T4C 4C4");
andy.setHeightInCMs(185);
Person ben=new Person("Ben", "Yeardly", "420 Green Loop", "Sloquet Hotsprings", "T4C 0C0");
ben.setHeightInCMs(175);
Person john=new Person("John","Smith","123 Here Cres.","Heresville","910222");
john.setHeightInCMs(180);
Person mary=new Person("Mary","Smith","123 Here Cres.","Heresville","910222");
mary.setHeightInCMs(155);
andy.update();
ben.update();
john.update();
mary.update();
Instances are created for andy, ben, john and mary. All the people are then updated. The update causes the database schema to be created (if not existing) and the person values are stored to that schema.
Note that the above code is terse. Unlike in the connection tutorial, there is no repository getting, no connection taking and no transaction used. This is the simple way of working with the repository. On update, the default repository is taken and connections and transactions taken and used automatically. There is more overhead with this simple way, but it's fine for simple usage. More advanced usage, including the reuse of connections and transactions can be found in the src/java/test/org/benow/repository/ code.
The database at this point contains the following tables:
SQL> show tables;
CLASS_SCHEMA_INFO PERMISSION
PERMISSION_ROLE PERMISSION_USERS
PERSON ROLE
ROLE_USERS USERS
USERS_USER_MODULE
CLASS_SCHEMA_INFO is used internally to track schema change, PERMISSION, ROLE and USERS tables are used for security, and the PERSON table is the table created for the Person object. The PERSON table is:
SQL> show table person; ID INTEGER Not Null FIRST_NAME VARCHAR(64) Nullable default null LAST_NAME VARCHAR(64) Nullable default null LAST_NAME_S VARCHAR(64) Nullable default null ADDRESS VARCHAR(128) Nullable default null CITY VARCHAR(64) Nullable default null POSTAL VARCHAR(16) Nullable default null HEIGHT_INC_MS INTEGER Nullable default null Triggers on Table PERSON: SET_PERSON_ID, Sequence: 0, Type: BEFORE INSERT, Active
ID is an autogenerated integer for each person (as provided by JSQLObject), and the columns correspond to the fields in the Person Object. The PERSON table itself contains:
ID FIRST_NAME LAST_NAME LAST_NAME_S ADDRESS CITY POSTAL HEIGHT_INC_MS == ========== ========= =========== ====================== ============ =========== ============= 1 Andrew Taylor TAYLOR 36 End of Internet Cl. Noosphere T4C 4C4 185 2 Ben Yeardly YEARDLY 420 Green Loop Sloquet T4C 0C0 175 3 John Smith SMITH 123 Here Cres. Heresville 910222 180 4 Mary Smith SMITH 123 Here Cres. Heresville 910222 155
... which correspond to the stored objects. Note that a sequential id has been generated for each object.
doFetch() method in src/java/test/org/benow/repository/Tutorial.java, which can be run under un*x with
bin/tutorial_repository.sh fetch or bin/tutorial_repository.bat fetch in windows.
List<Person> people = JSQLQuery.getAllObjectsQuery(Person.class).getObjects();which fetches all instances of the Person class. Again, this is the terse syntax, which is made to be easy to use. The JSQLQuery helper class is used to create QueryContexts, which are the actual fetchers. The
getObjects() method fetches all objects
of the specified class, with SQL creation, object construction and object population done automatically. The getObjects()
call shown above fetches all instances of Person and keeps them in memory, so it is inefficient with a large number of objects. If you want to fetch and deal
with objects one-at-a-time, use the getObjectIterator() method:
ResultIterator extends PersistentObject> peopleI = JSQLQuery.getAllObjectsQuery(Person.class).getObjectIterator();
while (peopleI.hasNext()) {
Person p=(Person) peopleI.next();
System.out.println(p);
}
The getObjectIterator() method has the advantage of using less memory.
If you want to page through the results, there is an alternative SubList getObjects(int starting, int spanning) which may be used:
SubList<Person> somePeople=JSQLQuery.getAllObjectsQuery(Person.class).getObjects(2, 2);
System.out.println("\nFetched "+somePeople.size()+" people of: "+somePeople.getSupersetSize()+
" from index: "+somePeople.getStartPos());
That snippet fetches the 3rd and 4th Person, in id order. The SubList result can be used to guide further page based naigation. The equivalent for fetching all objects using paging is:
JSQLQuery.getAllObjectsQuery(Person.class).getObjects(SubList.FROM_START, SubList.SPANNING_ALL);Again, all these examples are using the simple syntax, which is easy to use but has more overhead. Connection and transaction sharing are prefered when many operations over many objects are being done.
Persons with a last name of Smith:
List<Person> smiths=JSQLQuery.getObjectByFieldQuery(Person.class, "lastName", "Smith").getObjects();the restriction field name (lastName) is specified exactly how it is declared in java. The repository automatically does the translation to the table column name. To fetch with multiple fields use
getObjectByFields(Class,String[],Object[]):
try {
Person johnSmith=(Person) JSQLQuery.getObjectByFieldsQuery(Person.class,
new String[] {"firstName","lastName"},
new Object[] {"John","Smith"}).getObject();
System.out.println(johnSmith);
} catch (NoSuchObjectException e) {
System.err.println("No John Smith");
}
Note that this is using the getObject() method, which causes a NoSuchObjectException if the expected object is not found. By trapping the exception, the non existence can be handled.
QueryContext ctx = JSQLQuery.getAllObjectsQuery(Person.class);
ctx.setOrderFields(new OrderField[]{
new OrderField("lastName",Ordering.ASCENDING),
new OrderField("firstName",Ordering.ASCENDING),
});
this fetches Person objects ordered by lastName then firstName. Again, the field names correspond to the exact field name
used in the java class declaration, and the repository does the translation to column name.
JSQLClassQuery q = new JSQLClassQuery(Person.class);
String sql=q.getSQLColumnForField("heightInCMs")+">=180";
q.setQuerySuffix(sql);
List<Person> tallPeople=q.createContext().getObjects();
This is a bit messy, but allows fine control. First, the query for a Person is created. The SQL column name for the heightInCMs field (as declared in java) is included in the SQL query suffix, which is then assigned. When the SQL query is created, the field retrieval (select) will be combined with the suffix and invoked to return people over 180 CMs. The generated SQL query can be seen if debugging is turned on for org.benow.repository within etc/logging.xml:
select p.ID,p.FIRST_NAME,p.LAST_NAME,p.LAST_NAME_S,p.ADDRESS,p.CITY,p.POSTAL,p.HEIGHT_INC_MS from PERSON p where p.HEIGHT_INC_MS>=180By using
getSQLColumnForField(), there is no need to know the underlying SQL schema.
remove() method:
Person mary=(Person) JSQLQuery.getObjectByFieldQuery(Person.class, "firstName", "Mary").getObject(); mary.remove();Mary is fetched, then removed. To remove all instances of a class, call
deleteObjects(Class):
JSQLQuery.deleteObjects(Person.class);Object removal code can be found in the
doDelete() method in src/java/test/org/benow/repository/Tutorial.java, which can be run under un*x with
bin/tutorial_repository.sh delete or bin/tutorial_repository.bat delete in windows.