|
|
Developed
at the Polish-Japanese Institute of Information
Technology © Copyright by ODRA team, © Copyright by PJIIT |
|
ODRA – Object
Database for Rapid Application development Description and Programmer Manual |
|
|
by Radosław Adamus, Tomasz Wardziak and the ODRA team |
|
15. Interoperability with Java |
|
15.1 Accessing Java Libraries via ReflectionODRA supports calling external
Java code directly from SBQL programs. You can invoke any arbitrary code
written in Java, pass parameters to it and utilize its return value. The
method is based on the reflexive capabilities of Java. The following example creates a
new java.util.Random object and
invokes the nextInt method: rndref:integer; //1 rndref:=external
load_class("java.util.Random"); //2 external new_object(rndref); //3 external init_parameters(rndref); //4 external add_parameters(rndref, 5); //5 external invoke_integer(rndref,
"nextInt"); //6
Line 1 is
responsible for creation of rndref
variable that will store the reference to an external Java object. Line 2
loads the java.util.Random class
and stores its reference on the rndref
variable. In this way any Java class can be loaded. Line 3
creates a new instance of Java Random
object. Line 4 is
always mandatory. It initializes an internal collection of parameters that
will be used to invoke a method from an object. Line 5
adds one parameter that will be used as an argument for the nextInt method. In this case number 5
is loaded as a first and only argument of the method. You can load as many
arguments as the Java method requires. Consult Java documentation for method
signatures. Line 6
performs execution of external call and returns value to the SBQL stack
(unless the return value is of type void). In this case invoke_integer method is executed, but you should choose one of
the following methods to invoke, depending of a return type of Java method: ·
Invoke_integer
– method’s return type is integer ·
Invoke_void
– method’s return type is void ·
Invoke_library
– method’s return type is reference ·
Invoke_string
– method’s return type is string ·
Invoke_boolean
– method’s return type is boolean ·
Invoke_real
– method’s return type is double At this moment Java arrays are not
supported and methods returning Java arrays should be wrapped with methods
returning Java collections. Sample external invocations: data:integer; data:=external
load_class("java.util.Date"); external new_object(data); return external invoke_string(data, "toString"); Returns current date as a string. ToUpper(val:string):string { string_lib:=external load_class("odra.sbql.external.lib.StringLib"); external
new_object(string_lib); init_string(); result:string; external
init_parameters(string_lib); external
add_parameters(string_lib, val); result:=external invoke_string(string_lib,
"ToUpper"); return
result; } The method ToUpper takes string as a parameter and returns this string in
uppercase. It uses ToUpper method
defined in odra.sbql.external.lib.StringLib. SBQL code creating standard SBQL
library can be found in res/standard_library
folder. |
|
15.2 Java Object Base Connectivity (JOBC)ODRA JOBC (Java Object Base
Connectivity) is defined and implemented according to the idea, syntax and
semantics of JDBC (Java Data Base Connectivity). The differences concerns:
In this section we introduce the
ideas and usage of the ODRA JOBC API. In particular, we explain how to
configure and connect an application to an ODRA database, how to prepare a
query and how to execute it and how to process its result. The implemented ODRA JOBC API
driver is compatible with Java 1.4+. 15.2.1 Configuring connection
Connection configuration is
accomplished during instantiating an JOBC class instance. Available
constructor signatures are the following: public JOBC(String user, String
password, String host, int port); public JOBC(String user, String
password, String host); where: user – the name of the database
user password – the user password host – the IP/DNS address of the
ODRA server. port – an optional ODRA database instance port number (if not
provided the default is assumed which is 1521). Example Connection to local host on the default port
as user ‘admin’ with password ‘admin’. JOBC db
= new JOBC("admin", "admin", "localhost"); 15.2.2 Connecting to and
disconnecting from an ODRA database
Connection to a database is performed by the connect method. If connection cannot
be established, an JOBC exception is thrown. The connect method signature is as follows: void connect() throws IOException; An opened connection can be explicitly closed
by calling the close() method. Example db.connect(); . . . . . . //execute queries db.close(); 15.2.3 Setting
up a working module
An ODRA data is stored in a
hierarchical structure of modules. Each user has an own root environment that
is named with the username. A root module can contain any number of
sub-modules storing programs and data. After connecting to the database the
current module indicator is set to the user root module. To switch the
current module the programmer can call the following method in the JOBC
class: void setCurrentModule(String moduleGlobalName) throws
JOBCException; where: moduleGlobalName – the global name of the
requested module. The global name starts with the username and contains zero
or more sub-module names separated by dots. Example Switch to the module ‘reports’
that is a sub-module of ‘current’ module owns by the user
‘admin’: db.setCurrentModule(“admin.current.reports”); 15.2.4 Executing
SBQL queries
Executing SBQL queries requires the following
actions:
public Result execute(String query) throws
JOBCException;
SBQLQuery getSBQLQuery(String query); where
‘query’ is a string containing the SBQL query. A query stored in an SBQLQuery class instance can be performed
through a call to the overloaded execute method of the JOBC class instance. public Result execute(SBQLQuery
query) throws JOBCException The execute method returns an instance of the
Result class,
as described below. The same method is used to read and update the data
stored in the database. Examples Result
result = db.execute(“2+ Result result
= db.execute(“startService()”); Result
result = db.execute(“(Employee where
lName=\”York\” and
worksIn.Dept.name = \“IT\”).salary := SBQLQuery
query = db.getSBQLQuery(“Person where
lName=\”York\””); Result
result = db.execute(query); 15.2.5 Queries
with parameters
Parameters in a query string are
represented by names enclosed in curly brackets. To set a parameter value,
first the SBQLQuery class instance has to be obtained from a JOBC instance (as described
in the previous sub-section). Setting actual values for query parameters of
different types can be performed through the following SBQLQuery class instance methods: void addIntegerParam(String name, int
param) throws JOBCException; void addBooleanParam(String name, boolean
param) throws JOBCException; void addStringParam(String name, String
param) throws JOBCException; void addRealParam(String name, double
param) throws JOBCException; where: name – the parameter name param – the actual value for the parameter A parameter with a given name can
occur in a query more than once. All the parameter occurrences will be
substituted by the actual value set for it. An instance of JOBCException is thrown if the name does not
match the parameter name in the target query. Examples SBQLQuery
query = db.getSBQLQuery(“Person where name = {pname}”); query.addString(“pname”,
“Smith”); SBQLQuery
query = db.getSBQLQuery(“Person where
name = {pname} and age >
{page}”); query.addStringParam(“pname”,
“Smith”); query.addIntegerParam(“page”,
30); SBQLQuery
query = db.getSBQLQuery(“Book where
(pagesNo >= {pnumber} – {range} and
pagesNo <= {pnumber} + {range}).(title, genre)”); query.addIntegerParam(“pnumber”,
150); query.addIntegerParam(“range”,
20); 15.2.6 Processing query results
Results of SBQL queries are
represented by the Result class instance. It can represent different types of SBQL query
results. The following types of results are supported: 1. primitive value of the following
types: a.
integer b.
real c.
string d.
boolean e.
date 2. object reference 3. structure of results 4. bag of results 5. named result (i.e. a binder)
– any of the result kinds equipped with a name. Getting a primitive value
To check if the result is primitive the
following method is to be used: boolean
isPrimitive(); If the result is primitive, its
value can be obtained with the use of the following methods in the Result class that maps an ODRA result to
a value of a Java equivalent type: int getInteger() throws JOBCException; double getReal() throws JOBCException; String getString() throws JOBCException; boolean getBoolean() throws JOBCException; Date getDate() throws JOBCException; Examples Result
result = db.execute(“2+ int value = result.getInteger(); Result
result = db.execute(“avg(Employee.salary)”); double value = result.getReal(); Result
result = db.execute(“forall(Person) address.city =
\“Warsaw\””); boolean result = result.getBoolean(); Object references
SBQL queries can return object references. Because
in the context of JOBC calls object references are currently not supported,
they are represented as string constant values - “object
reference”. Complex results
An SBQL query can return a structure, which
can be the result of operators used in a query or can be returned by the
dereference acting on an identifier of a complex object. Fields of a
structure can be named or unnamed. To check if the result is complex the
following method is to be used: boolean
isComplex(); Fields of a complex result can be accessed in
the following ways:
Example The query returns two elements structure ( {integer, string} ) containing the Smith’s age and home address. We
assume that there is only one Person with that name and the cardinality of
fields ‘age’ and ‘address’ is [1..1]. Result
result = db.execute(“(Person where
lName = \“Smith\”).(deref(age),
deref(address))”); Result
fields = result.fields(); System.out.println(fields.get(0) + “,” + fields.get(1)); Bag of results
and empty results
SBQL queries can return a bag of
results (primitive, structures, object references or named). The JOBC API
allows for iteration over the results in a bag result. The Result class
implements the Iterable<Result> interface. To check if the result is complex the
following method is to be used: boolean
isBag(); An empty result has the following properties: result.isBag(); // returns true result.size();
// returns 0 result.isEmpty();
// returns true Examples Result
result = db.execute(“(Person where
age > 20).lName”); //java
1.5+ foreach loop (in java1.4 use result.iterator()) for(Result
res : result.toArray()){ System.out.println(res.getString()); } Named results
Results returns by SBQL queries
can be named. A name can be given explicitly with the SBQL auxiliary names
operators (‘as’ and ‘groupas’) or can be a
consequence of dereference operation on an identifier of a complex object.
The difference between ‘as’ and ‘groupas’ SBQL
operators is that the first names each element in the result bag and the
latter names the entire bag. In JOBC the names are to be used
to navigate in the result searching for a sub-result with a given name. In
other situations the result name is transparent. Example: Result
result = db.execute(“2 + 2 as
result”); int ires = result.getInteger(); Result
result = db.execute(“(Person where
age > 21).(name as personName,
age as personAge) as adult”); Filtering results
by name
A result name can be used to
search for a sub-result with a particular name. The search can be performed
with the use of Result class instance method getByName taking the string representing a result name
as a parameter: Result
getByName(String name); Result returned by the getByName
method call is a Result class instance representing a sub-result that was named and the name
is equal to the parameter. If nothing was found the empty result is returned.
The search is performed to the first occurrence of a named result, thus if
the searched named result is a part of the other named result it will not be
returned. Examples: Result
result = db.execute(“(2 + 2) as
result”); Result
intresult = result.getByName(“result”); Result namedAdults
= db.execute(“(Person where
age > 21).name as personName,
age as personAge) as adult”); //we assume that there is more than one
person //satisfying the condition namedAdults.isNamed();
//returns true namedAdults.isBag();
//returns true (the names are transparent) //java
1.5+ foreach loop for(Result adult :
namedAdults.toArray()) { adult.isNamed(); //returns true (due to
operator as used in //the query) adult.isComplex(); //return true (names
are transparent) } //java
1.4 for(Iterator iter =
unnamedResult.iterator();iter.hasNext();) { Result adult = (Result)iter.next(); adult.isNamed(); //returns true (due to
operator as used in //the query) adult.isComplex(); //returns true (names
are transparent) } Result
unnamedAdults = namedAdults.getByName(“adult”); unnamedAdults.isNamed();
//returns false unnamedAdults.isBag();
//returns true //java 1.5+ foreach
loop for(Result adult : unnamedAdults.toArray()) {
adult.isNamed(); //returns false
adult.isComplex(); //returns true } //java
1.4 for(Iterator iter =
unnamedResult.iterator();iter.hasNext();) { Result adult = (Result)iter.next(); adult.isNamed(); //returns false adult.isComplex(); //return true } Result
names = result.getByName(“adult”).getByName(“name”); names.isNamed();
//returns false names.isBag();returns
true Result
ages = result.getByName(“adult”).getByName(“age”); ages.isNamed();
//returns false ages.isBag();
//returns true |
|
|
Last modified: July 9, 2008 |