Sessions

Sessions provide a user access class for finding and counting beans. As well as saving and removing beans and their dependent beans

BeanSession (com.javelin.beans.BeanSession)

The BeanSession interface represents the machinery in the applications, where the Beans are the commodities passed about by the machinery.

The BeanSession has a couple of methods to allow the Session to be aware of its context,

The key represents the current key (User) on the system, this set on the Session before the session is got from the ClassContext. This key could be a Thread or User name and can be used by the Session to retrieve objects bound in its own context.

public Object getKey();
public void setKey( Object key );

The ClassContext represents the current ClassContex for the Session.

Public ClassContext getClassContext();
public void setClassContext( ClassContext classContext );

AbstractBeanSession (com.javelin.beans.AbstractBeanSession)

The AbstractBeanSession is a helper class that specific BeanSessions extend. The AbstractBeanSession has the following functionality: -

  • The BeanSession interface

SessionInterfaceWriter (com.javelin.generator.beans.SessionInterfaceWriter)

The SessionInterfaceWriter is responsible for writing out the BeanSession interfaces for every table.

Instantiating Sessions

BeanSessions are instantiated by the ClassContext. This is done to hide the implementation of the BeanSessions.

BeanSessions are instantiated using the following syntax: -

<ClassName>Session session = <ContextName>Context.getDefault().new<ClassName>Session();

<ClassName>Session session = <ContextName>Context.getDefault().get<ClassName>Session();

<ClassName>Session session = <ContextName>Context.getDefault()Get<ClassName>Session(Object key);

To get a new AccountSession from the NorthwindContext, all you have to do is call : -

AccountSession accountSession = NorthwindContext.getDefault().newAccountSession();

Finding Beans

Finding Beans on a Session is at the center of a lot of rich application code. However it is often very expensive, in terms of writing code, to write lots of custom findBys. JGenerator provides lots of very powerful means of generating findBys

Every BeanSession has a number of findBy methods created on it by default. The default findBy methods are: -

  • findByPrimaryKey
  • findByAll
  • findByQuery

All findBy methods follow the EJB naming standard. FindBys either return a Bean, an Iterator or an Enumeration. The findBys by default return Iterators. It is possible to change the system wide setting so that Enumerations or Arrays are returned. To do this set: -

indexType=Iterator
indexType=Enumeration
indexType=Array

In addition a findBy method is written out for every Index (see the Indexes section). Indexes provide a way of defining how you want to search for your data, so Indexes and findBys are very closely linked. Creating an Index does not mean that an Index is created on the database, as there is a concept of a pseudo index.

For unique indexes the findBy methods return a single Bean for non-unique indexes the findBy methods returns either Iterators or Enumerations.

This default to create a findBy for an Index can be switched off for any specific index using the following syntax: -

<ClassName>.<IndexName>.find=false

Alternatively the default to create findBys for Indexes for a whole table can be switched off using the following syntax:-

<ClassName>Find=false

Alternatively the default to create findBys for a Indexes for a whole database can be switched off using the following syntax:-

find=true

FindBy PrimaryKey

The findByPrimaryKey is always created, where a primary key exists, and returns a single bean : -

public <ClassName> findByPrimaryKey( Object <className>Key ) throws FinderException, ValidationException;

FindBy All

By default the findByAll is created. The findByAll return all the Beans, or up to a maximum row count.

Public Iterator findByAll() throws FinderException, ValidationException;
public Iterator findByAll( int maxmaxRows ) throws FinderException, ValidationException;

FindBy Query

The findByQuery return all the Beans for a query, or up to a maximum row count.

Public Iterator findByQuery( String query ) throws FinderException, ValidationException;
public Iterator findByAll( String query, int maxmaxRows ) throws FinderException, ValidationException;

For SQL based queries, the findByQuery methods accept the query part of a piece of SQL following the table declaration for the Bean. The table declaration is given an alias which is the same name as the table so that it can be referenced. This means that another table declaration can be made, or the developer can start with the WHERE clause. The SELECT part of the query is pre-made

For example, to find the first 1000 cars with a registration starting with the letter R: -

carSession.findByQuery( "WHERE SUBSTRING( Car.registration, 0 ) == 'R'", 1000);

Alternatively get all the cars with an engine capacity greater than 2000: -

carSession.findByQuery( ", Engine Engine WHERE Car.engineKey=Engine.engineKey and Engine.engineCapacity >= 2000" );

Custom find by Queries can be created, that return Iterators, using the following syntax: -

<ClassName>.findMethod.<index>=findBy<Name>(<arguments>)
<ClassName>.findQuery.<Index>=<SQL>

The example below creates a findByIndividual method on the ModelSession, the particular Jdbc or EJB Bean managed rendering uses the SQL to implement the query.

Model.findMethod.0=findByIndividual(Object individualPK)
Model.findQuery.0=",Portfolio P where P.individualKey=\"+individualPK+\" AND P.portfolioKey=portfolioKey"

This will create a findBy with the method described: -

ModelSession.findByIndividual(Object individualPK) throws FinderException, ValidationException

FindByQuery methods do not work for container managed EJB, as the servers wrap the SQL in single quote marks.

FindBy ForeignKey

By default indexes are not created for foreignKeys or primaryKeys on a table. However this feature can be switched on either across the whole database, on an individual table, for the all keys, all foreignKeys or just the primaryKey. See the section on Indexes for more information. By switching on the automatic creating of indexes for foreignKeys this triggers the automatic creation of findBys for foreignKeys.

Indexes can be automatically created for all keys, primary keys or foreign keys for all tables by setting one of the following: -

index=key
index=pkey
index=fkey

Indexes can be automatically created for keys for specific tables by setting one of the following

<ClassName>Index=key
<ClassName>Index=pkey
<ClassName>Index=fkey

Creating a primaryKey Index or a foreignKey using this method does not create an Index on the database, as they are pseudo defined as indexes. Real Indexes can be created one at time for particular keys, and this gives control on whether the index is a real index or a pseudo index. See the section on Indexes for more information.

For example a Wheel table may have a non-unique index on its car foreignKey column, this will create the following findBy method: -

public Enumeration findByCar( Object carKey ) throws FinderException, ValidationException;

Another example is an Engine table may have a unique index on its car foreignKey column, this will create the following findBy method: -

public Engine findByCar( Object carKey ) throws FinderException, ValidationException;

Custom FindBy Index

JGenerator allows custom findBys to be created by creating custom Indexes. See the section on creating Custom Indexes for more details.

For example the code below creates 2 indexes, with the following findByMethods. JGenerator knows the types of the columns. The SQL index that is created is the <IndexName>, however by default the find by method names are synthetically generated.

UserProfile.index.0=Age(age)
UserProfile.index.1=Info(sex,age)

UserProfileSession.findByAge( int age ) throws FinderException, ValidationException
UserProfileSession.findBySexAndAge(sex,age) throws FinderException, ValidationException

Synthetic findBy names can be overridden (except for the primary key) using the syntax: -

<ClassName>.<IndexName>.longName=<name>

For example: -

MyTable.XYIdex(x,y)
MyTable.XAndY.longName=SillyName

Will create the following: -

MyTable.findBySillyName() throws FinderException, ValidationException

FindBy Expressions

More complex findBy can be also created, that have conditional expressions. By default default to the parameter in the findBy is == (equal). This means that a findBy does a straight forward match. The column name, in the index speciifcation (above) can also have the syntax: -

propertyName[operator[expression]]

Where an operator can be one of =,==,>,<,>=,<=,!=

For example, this will create a findBy that uses the > operator: -

UserProfile.index.0=AgeIdx(age>)

UserProfilesession.findByGreaterAge(int age)

It is also possible to define the expression used in the query. The following examples, will create a find bys that uses the >= , >, < and <= operators. The find by methods generated will not pass any parameters, as a constant value is used. The find by names are synthetically created, (and may need longname defined): -

UserProfile.pseudoindex.0=OldAge(age>=60)
UserProfile.pseudoindex.0=MiddleAge(age>20,age<60)
UserProfile.pseudoindex.0=YoungAge(age<=20)

Will create the following findBys: -

UserProfile.findByAgeGEqualOldAge() throws FinderException, ValidationException
UserProfile.findByAgeGreaterMiddleAgeAgeLessMiddleAge() throws FinderException, ValidationException
UserProfile.findByAgeLEqualYoungAge() throws FinderException, ValidationException

Where column are Strings it is more than often desirable to create partial matching using LIKE. LIKE Indexes are automatically created for indexes containing Strings when the findByLike property is set to true, at the index, table or database level. For example the following index property: -

UserProfile.index=lastName(surname)

will create the following findBys: -

UserProfile.findByLastName(String surname) throws FinderException, ValidationException

UserProfile.findByLikeLastName(String surname) throws FinderException, ValidationException

if one of the following are set :

UserProfile.lastName.findByLike=true

UserProfile.findByLike=true

findByLike=true

 

FindBy Sort Order

Often the a defined (a fixed) sort order for a query may be needed. Columns for a index can be sorted using the syntax

<ClassName>.<IndexName>.sortOrder=<propertyName>[DESC][, ...]

For example: -

Invoice.X10.sortOrder=dateSent DESC, clientName

FindBy maxRows

The maximum row count that can returned can be limited using the syntax

<ClassName>.<indexName>.maxRows=<rows>

For example: -

Invoice.X10.maxRows=100

Alternatively the maximum number of rows can be set dynamically. This will add an argument to the findBy method allowing the maximum number fo rows to be passed. If the maxRows is 0 then an unlimited number of rows will be passed. This can be set using the following syntax:

<ClassName>.<indexName>.maxRowsMethod=true|false

For example

Invoice.X10.maxRowsMethod=true

maxRowsAllowed

The maximum rows that can be created

<ClassName>.maxRowsAllowed=<rows>

For example: -

Invoice.X10.maxRowsAllowed=2

This can be used to extend the maxRows so that they are limited to say a foreignKey.

<ClassName>.maxRowsAllowedQuery=Java String for Count By Query

For example to limit the number of addresses in a Line.

<ClassName>.maxRowsAllowedQuery="Address.addressKey="+address.getAddressKey()

 

Singleton Beans

Singleton Beans are Beans that have only one instance. This means that all the findBy's method signature will all return the Bean, and not a collection. Similar to saying maxRowsAllowed=1, but limits creation to 1.

To specify singletons, use the following pattern:

<ClassName>.singleton=true

AutoCreate

AutoCreate - is where there is one row automatically created, in the constructor for the Session. The defaults for the Bean that is created can be set using the default property.

To specify autoCreate, use the following pattern:

<ClassName>.autoCreate=true

This can be used to automatically create Beans where they don't exist

<ClassName>.autoCreateQuery=Java String findByQuery

For example to create a new single row for each User.

<ClassName>.autoCreateQuery="User.userId=\"UserUtil.getUser()\""

Counting Beans

Applications often want to count the number of beans without having to return all the Beans and count them one by one.

CountBys have same syntax as findBys except they return an int. For example: -

public int countBySurname( String surname )

Counting beans is switched of by default. For every findBy there is an (optional) countBy method. One difference is that countBy methods, with an optional maxRows are not generated. The generation of these methods can be switched on using the following syntax:-

<ClassName>.<IndexName>.count=true

Alternatively to switch countBys on for a specific table use the following syntax:-

<ClassName>Count=true

Alternatively to switch countBys on for every table use the following syntax:-

count=true

In addition there is a countByQuery generated for every session and home that appends the findByQuery SQL to a SELECT COUNT(*) clause, instead of a SELECT * clause.

Loading Beans

Sessions expose the load method to allow a bean, and all it's dependent beans to be reloaded from the database. To load a single bean use the Home's load method. If the beans primary key is set then it is reloaded, otherwise an exception is thrown.

The load method has the following pattern: -

public void load( <Bean> <bean> ) throws LoadException, ValidationException

For example: -

public void load( Car car ) throws LoadException, ValidationException

Saving Beans

Unlike EJB the JBeans standard has a save method to save a bean, and all it's dependent beans. This method can be used to both create and store beans. The method must decide whether to call the create or store method on the underlying home class, based on the bean persisted property.

This design is more Object-Oriented than the EJB pattern where bags of variable are marshaled around until creation time. The save method has the following patterns: -

public void save( <Bean> <bean> ) throws CreateException, StoreException, ValidationException

public void save( List beans ) throws CreateException, StoreException, ValidationException

This example show how to save (create) a Car, with a mandatory engine: -

CarSession carSession = AutoContext.getDefault().getCarSessionb=();
Car car = AutoContext.getDefault().newCar();
car.setEngine( AutoContext.getDefault().newEngine() );
carSession.save( car );

UndoOn Rollback

By default all the changes made to beans during a transaction are rolled back when that transaction fails.

This default is that beans are returned to their original state before saving.In the Session when the transaction fails this means the beans are not rolledback.

sessionUndoOnRollback=false

For example:

<className>.sessionUndoOnRollback=false

 

 

Removing Beans

Sessions expose the remove method to allow a bean, and all it's dependent beans to be removed from the database. The Bean does not have a remove method. To remove a single bean use the Home remove method.

The remove method has the following pattern: -

public void remove( <Bean> <bean> ) throws RemoveException, ValidationException

For example: -

public void remove( Car car ) throws RemoveException, ValidationException

Intercepting Sessions

Sessions call a number of methods during transactions that can be overriden to give access to beans before and after the transactions.

public void preLoad( List beans )
protected void preLoad( Bean bean )
public void postLoad( List beans, Exception e )
public void postLoad( Bean bean, Exception e )

public void preSave( List beans )
public void preSave( Bean bean )
public void postSave( List beans, Exception e )
public void postSave( Bean bean, Exception e )

public void detach( List beans ) throws ValidationException

public void preRemove( List beans )
public void preRemove( Bean bean )
public void postRemove( List beans, Exception e )

These methods can be overridden as follows:

<Table>Session.method.0=public void postSave( Bean bean, Exception e )
<Table>Session.code.0=System.out.println( bean );

Standalone Sessions

By default Sessions are created by specifying the $CREATE $CREATOR to create Entity Bean based Sessions.

A Session can also be created that is not related to a bean, and only uses the Memory Session, this means that it has no find, create, store or remove methods; only custom methods, and no persistence. This is known as a standalone session.

The example below creates a standalone session with 2 methods: -

MyCalculationSession.method.0=public int add( int x, int y )
MyCalculationSession.code.0=return x + y;

MyCalculationSession.method.1=public String getDateTime()
MyCalculationSession.code.1=return new java.util.Date().toString();

MyCalculation=$CREATE $STANDALONECREATOR

Casading Transaction

Dependent objects are also inserted, updated or deleted when a root bean is saved or deleted.

In addtion any beans removed off lists are removed when the the root bean in the dependent tree is saved. JBeans keeps track of these beans.

In addtion any associates that point to removed beans have their keys updated to null.

 

Transacting Lists

It is possible to load or store a number of beans in lists and save or delete them in one go.

Here is an example of loading beans

List beans = new ArrayList();

bean1.listRelations( beans, Bean.DEPENDENTS );
bean2.listRelations( beans, Bean.DEPENDENTS );
bean3.listRelations( beans, Bean.DEPENDENTS ); beans.add( beanLyingAround );

anyOldBeanSession.load( beans );

 

 

Unit Of Work

JGen Supports Units of Work. This means you can save or delete a number of beans anywhere on the stack all under the same transaction.

You can only have one transaction open at a time in the same thread, and you must run the UserTransaction from the same thread.

Each of the methods called from within the doUserTransaction() (e.g. doUpdateAnotherThing() ) get the Context, and save(bean) and delete(bean) just like normal, but they don't commit the transaction to the database until the commit() method is called in the finally clause. This is done using reference counting inside the WorkUserTransaction.

import javax.transaction.UserTransaction

public void doUserTransaction() throws Exception
{
    UserTransaction userTransaction = null;

    try
    {
        userTransaction = MyContext.getWorkUserTransaction();

        doDeleteABean();
        doUpdateMoreBeans();
        doInsertAHugeNestedBeans();
    }
    catch( Exception e )
    {
        if( userTransaction != null ) userTransaction.rollback();
    }
    finally
    {
        if( userTransaction != null ) userTransaction.commit();
    }
}

Example Session

Here is an example of a (simple) Session Interfaces for Addresses to give a big picture of what is generated.

/*
* Copyright Javelinsoft Ltd, All rights reserved.
*/

package com.javelin.jcommerce;

import java.util.*;
import java.sql.*;
import com.javelin.jcommerce.*;
import com.javelin.jcommerce.*;
import com.javelin.beans.*;
import javax.jts.*;
import com.javelin.util.*;

/**
* AddressSession.java
*
* @version JCommerce - 1.0
* @author Javelin Software
* @see com.javelin.jcommerce.Address
* @see com.javelin.jcommerce.AddressHome
* @see com.javelin.jcommerce.AddressValidator
*/

public interface AddressSession extends BeanSession
{


/**
* Find All the beans.
* @return Iterator The Beans that were found.
* @exception FinderException If there was a problem finding any of the beans.
* @exception ValidationException If there was a problem validating any of the beans.
*/
public Iterator findByAll() throws FinderException, ValidationException;

/**
* Find the Address By Location
* @param locationKey
* @return Address The Bean/s that was found.
* @exception FinderException If there was a problem finding any of the beans.
* @exception ValidationException If there was a problem validating any of the beans.
*/
public Address findByLocation( Object locationKey ) throws FinderException, ValidationException;

/**
* Find the Address By PrimaryKey
* @param addressKey
* @return Address The Bean/s that was found.
* @exception FinderException If there was a problem finding any of the beans.
* @exception ValidationException If there was a problem validating any of the beans.
*/
public Address findByPrimaryKey( Object addressKey ) throws FinderException, ValidationException;

/**
* Find the Beans using a String.
* @param query A Query String
* @return Iterator The Beans that were found.
* @exception FinderException If there was a problem finding any of the beans.
* @exception ValidationException If there was a problem validating any of the beans.
* @expert
*/
public Iterator findByQuery( String query ) throws FinderException, ValidationException;

/**
* Find the Beans using a String, with a max row count.
* @param query A Query String
* @param maxRows The maximum number of rows to return.
* @return Iterator The Beans that were found.
* @exception FinderException If there was a problem finding any of the beans.
* @exception ValidationException If there was a problem validating any of the beans.
* @expert
*/
public Iterator findByQuery( String query, int maxRows ) throws FinderException, ValidationException;

/**
* Count the Beans using a String.
* @param query A Query String
* @param int A count of the beans found.
* @exception FinderException If there was a problem finding any of the beans.
* @exception ValidationException If there was a problem validating any of the beans.
* @expert
*/
public int countByQuery( String query ) throws FinderException, ValidationException;

/**
* Reload the bean, and any dependent beans from the store.
* @param Address The Bean to load.
* @exception LoadException If there was a problem loading any of the beans.
* @exception ValidationException If there was a problem validating any of the beans.
*/
public void load( Address address ) throws LoadException, ValidationException;

/**
* Create or Save the bean, and any dependent beans to the store.
* @param Address The Bean to save.
* @exception CreateException If there was a problem creating any of the beans.
* @exception StoreException If there was a problem storing any of the beans.
* @exception ValidationException If there was a problem validating any of the beans.
*/
public void save( Address address ) throws CreateException, StoreException, ValidationException;

/**
* Remove the bean, and any dependent beans from the store.
* @param Address The Bean to remove.
* @exception RemoveException If there was a problem removing any of the beans.
* @exception ValidationException If there was a problem validating any of the beans.
*/
public void remove( Address address ) throws RemoveException, ValidationException;

}