Table of contents

0. Classpath

To use GDBMS in a project it's necessary to include GDBMS in its classpath. If a third party driver is going to be used, it's needed its jar to be in the classpath too. GDBMS depends on other projects and a list of those dependencies can be found here and the actual jars can be found here.

1. Configuration and data source access.

We can perform read/write operations in a data set through the DataSource interface. Since the 2.0 version it's quite easy to get a DataSource instance, we have to follow this steps:

–Create a DataSourceFactory.

–Get the DataSource instance.

1.1 Create a DataSourceFactory.

This step is common to any data source. We need to create a DataSourceFactory instance:

DataSourceFactory factory = new DataSourceFactory();

1.2 Get a DataSource instance.

There are several ways to obtain a DataSource instance to access the data. First, the data source can be registered by name in the DataSourceFactory and the instance can be retrieved by that name. It's also possible to execute a SQL instruction over the registered data sources. Even it's possible to store to disk the information of an existing instance and create the instance from that information later. But the easiest way to get a DataSource instance is by calling the getAlphanumericDataSource() or getSpatialDataSource(). There are several methods with that name, the one we'll choose will depend on the type of source we want to access.

No matter the way, the DataSource is registered by a name in the DataSource factory. It's possible to remove the DataSource registration by calling its remove method. It has no efect on the DataSource but its name is no longer registered in the DataSourceFactory so no more instances can be obtained.

DataSource source = factory.getAlphanumericDataSource("file.csv");
source.remove();

It's even possible to execute SQL queries against registered DataSources:

DataSource source = factory.getAlphanumericDataSource("file.csv");
DataSource filtered = factory.executeSQL("select * from " + source.getName() + " where name='fernando'");

1.2.1 Files.

If the file is an alphanumeric one we will use the getAlphanumericDataSource(File file) method:

AlphanumericDataSource ds = factory.getAlphanumericDataSource(“person.csv");

The following code opens a shapefile called points.shp:

SpatialDataSource sds = factory.getSpatialDataSource(“points.shp");

1.2.2 Objects.

Objects can also be a data source if they implement either the AlphanumericObjectDriver or SpatialObjectDriver interface. To get a DataSource instance that accesses the information of an object we proceed in the same way than before:

FakeObjectDriver driver = new FakeObjectDriver();
AlphanumericDataSource ds = factory.createDataSource(driver);

This one registers an object with a spatial field:

FakeSpatialObjectDriver driver = new FakeSpatialObjectDriver();
SpatialDataSource ds = factory.createDataSource(driver);

FakeObjectDriver y FakeSpatialObjectDriver are object drivers located in the com.hardcode.gdbms.engine.data package. They are only suitable to examples, unit and functional tests and so on.

1.2.3 Databases.

There are two methods to get a DataSource instance that accesses a database.

This code registers an alphanumeric database table:

DBSource source = new DBSource(null, 0, basedir + "/src/test/resources/testdb", "sa", "", "persona", "jdbc:hsqldb:file");
AlphanumericDataSource ds = factory.createDataSource(source);

This one is for spatial database tables:

DBSource source = new DBSource(“127.0.0.1”, 5432, “testdb", "root", "", "polygons", "jdbc:postgresql");
AlphanumericDataSource ds = factory.createDataSource(source);

2 Reading from a DataSource

The read capabilities for a DataSource are:

WARNING!!: To perform any of these operations, the DataSource.start() method must be called before. Otherwise, the DataSource will have an undefined behaviour. Once all read operations are done, DataSource.stop() have to be called to release resources.

2.1 Get a field value.

To get the value of a specified field at a specified row the getFieldValue() method has to be used.

source.start();
String v = source.getString(1, "name");
int i = source.getInt(1, "age");
...
source.stop();

This gets the value of field called "name" at the second row.

2.2 Get the names of the fields.

To perform this operation the DataSource.getFieldNames() method is used.

source.start();
String[] nombres = source.getFieldNames();
source.stop();

2.3 Get a field index

The method DataSource.getFieldIndexByName(String name) returns the index of the specified field.

source.start();
int index = source.getFieldIndexByName(“campo”);
source.stop();

2.4 Get the row number.

It's possible to obtain the row number by calling the DataSource.getRowCount() method.

source.start();
long rows = source.getRowCount();
source.stop();

2.5 Get all row values.

The DataSource.getRow() method returns a Value array with the values of all fields at the specified row. Value is an interface implemented by several wrappers over basic data types (see javadoc for more information).

source.start();
Value[] valores = source.getRow(0);
source.stop();

2.6 Get the geometry type.

With spatial DataSources it is possible to get the geometry type by using the SpatialDataSource.getGeometryType() method. This method returns an integer that can be checked against the SpatialDataSource constants.

SpatialDataSource spatial = (SpatialDataSource) source;
int type = spatial.getGeometryType();

2.7 Get the index of the spatial field.

It's also possible for spatial DataSources to get the spatial field index. This is done by calling the getSpatialFieldIndex() method.

SpatialDataSource spatial = (SpatialDataSource) source;
int spatialIndex = spatial.getSpatialFieldIndex();

3 Writing in a DataSource

These are the writing operations available to through the DataSource interface:

WARNING!!: To perform any of these operations the method DataSource.beginTrans() must be called before. The operations don't modify the data source until the DataSource.commit() method is called. To cancel the operations the DataSource.rollback() method must be called instead. Anyway, it's necessary to call one of the two methods to free resources. These operations are possible only if the driver provides writing support.

These operations can be observed by adding an EditionListener to the DataSource by calling the DataSource.addEditionListener() and DataSource.removeEditionListener(). When a listener is added to a DataSource it receives notifications of the edition operations until it's removed. If a large amount of operations have to be done it may be slow or wrong to notify the listeners after each single operation. To avoid this there have been implemented three notification modes: DISPATCH, STORE and IGNORE. The first one notifies the listeners after every single operation, the sencond stores the single notifications and delivers them when the mode changes and the third doesn't notify at all. This modes can be changed and retrieved by the DataSource.getDispatchingMode() and DataSource.setDispatchingMode() methods.

3.1 Set a field value

It's possible to set a field value by calling the setFieldValue() method. To create the value to set, the ValueFactory.createValue() have to be used. Though concrete implementations of Value can be directly instatiated by calling its constructor, it's highly discouraged because this constructors may be hidden in next versions. The first two instructions are equivalent:

source.setFieldValue(0, 0, ValueFactory.createValue("valor"));
source.setString(0, 0, "valor");
source.setInt(0, "age", 25);

3.2 Insert an empty row.

It's possible to insert a row at the end of the DataSource by calling DataSource.insertEmptyRow().

source.insertEmptyRow();

In order to insert a row at a specific position a casting to either AlphanumericDataSource or SpatialDataSource has to be done. Both interfaces has a method called insertEmptyRowAt but the behaviour is not the same.

AlphanumericDataSource alpha = (AlphanumericDataSource) source;
alpha.insertEmptyRowAt(0);

3.3 Insert a filled row.

This case is the same than the previous with the only difference of the arguments the methods take. insertFilledRow takes a Value array with the values to be inserted.

Value[] values = new Value[] {ValueFactory.createValue("valor1"), ValueFactory.createValue(1) };
source.insertFilledRow(values);

As the previous case to insert a row at a specified position a casting has to be done:

SpatialDataSource spatial = (SpatialDataSource) source;
Value[] valores = new Value[] {ValueFactory.createValue("valor1"), ValueFactory.createValue(1)};
spatial.insertFilledRowAt(0, valores);

3.4 Row deletion.

To delete a row it's neccesary to cast too. Either AlphanumericDataSource or SpatialDataSource have a method called deleteRow() but their behaviours differs:

SpatialDataSource spatial = (SpatialDataSource) source;
spatial.deleteRow(0);

3.5 Undo/Redo.

To enable undo capabilities to a DataSource it has to be retrieved this way:

DataSource source = factory.createRandomDataSource(nombre, DataSourceFactory.UNDOABLE);
After it, the DataSource.undo() and DataSource.redo() methods can be called only when there is an operation to undo or redo. It can be known by calling canUndo() and canRedo().

4 SQL Statement execution.

GDBMS allows the execution of SQL statements against the registered data sources. This is done through the DataSourceFactory.executeSQL() method.

As a result of the execution, a DataSource instance is returned which is registered too so it can be used in next instructions:

DataSource source = factory.executeSQL("select name from persona;");
DataSource source2 = factory.executeSQL("select * from " + source.getName() + " order by name asc;");

It's possible to create custom functions to be used in SQL statements. To do it, the Function interface has to be implemented. It has four methods:

factory.executeSQL(“select max(age) from person; ”);
factory.executeSQL(“select sum(lenght(name)) from persona; ”);

To be able to use the implemented function in a SQL statement it has to be registered in the FunctionManager:

FunctionManager.addFunction(new MyFunction());

Where MyFunction is an implementation of the Function interfaz.

Once registered it can be executed instructions like this one:

factory.executeSQL(“select myFunction(name) from persona;”);

where the implementation of the getName() method returns "myFunction" and the implementation of evaluate will be called with each name of the data source.

It's possible to delegate SQL statements to the server if the data source is stored in a database management system capable of execute SQL. It's also possible to create custom queries different from the already defined: "select" and "union"

5 Metadata access.

To retrieve information about the DataSource schema the DataSource.getDataSourceMetadata() method it can be used. This method returns an implementation of the Metadata interface which gives access to the field names, field types, read only property and more.

Metadata m = ds.getDataSourceMetadata();
for (int i = 0; i < m.getFieldCount(); i++) {
System.out.println(m.getFieldName(i) + ": " + m.getFieldType(i));
}

The getFieldType method return value is one of the Value constants and that value is the type in the GDBMS type model. To know the driver specific schema there is the DataSource.getDriverMetadata() method. It returns a DriverMetadata interface implementation and it's possible to know the driver specific field type through this interface.

It's also possible the metadata edition in an already created source by removing or adding fields with the DataSource.addField() y DataSource.removeField() methods.

6 Source creation.

GDBMS gives the possibility of creating data sources for those drivers that support it. The DataSourceFactory.createDataSource() method takes an implementation of the DataSourceCreation interface and creates the source specified by the concrete implementation. The FileSourceCreation and DBSourceCreation are the concrete implementations to create files and database tables respectively. The constructor from these implementations take a DriverMetadata argument and we can use an implementation of SpatialDriverMetadata since SpatialDriverMetadata extends DriverMetadata. There are two implementations of these interfaces that can be used to create a data source from scratch: DefaultDriverMetadata and DefaultSpatialDriverMetadata.

DefaultDriverMetadata ddm = new DefaultDriverMetadata();
ddm.addField("text", "VARCHAR", new String[]{"LENGTH"}, new String[]{"5"});
ddm.addField("int", "TINYINT");
ddm.addField("decimal", "DECIMAL", new String[]{"SCALE", "PRECISION"}, new String[]{"5", "3"});
DBSource dbsd = new DBSource("www.myhost.com", 1234, "testdb", "user", "password", "new_table");
SourceCreation sc = new DBSourceCreation("GDBMS HSQLDB driver", dbsd, ddm);
dsf.createDataSource(sc);
dsf.addDataSource(new DBTableSourceDefinition("newDataSource", dbsd, "GDBMS HSQLDB driver"));
DataSource d = dsf.createRandomDataSource("newDataSource");
d.start();
...
d.stop();

To create a data source with the same schema as another we can use the DriverMetadata of the first one to build the SourceCreation object. Nevertheless, this can only be done if the two sources are acceded with the same driver since the operation deals with driver specific information.

DataSource d1 = dsf.createRandomDataSource("existingDataSource");
d1.start();
DefaultDriverMetadata ddm = d1.getDriverMetadata();
d1.stop();
DBSource dbsd = new DBSource("www.myhost.com", 1234, "testdb", "user", "password", "new_table");
SourceCreation sc = new DBSourceCreation("GDBMS HSQLDB driver", dbsd, ddm);
dsf.createDataSource(sc);
dsf.addDataSource(new DBTableSourceDefinition("newDataSource", dbsd, "GDBMS HSQLDB driver"));
DataSource d = dsf.createRandomDataSource("newDataSource");
d.start();
...
d.stop();

7 Spatial data sources.

To access spatial data the interface SpatialDataSource is used. Through it it's possible to know which is the spatial field index, the type of the geometries it stores, the full extent of the data it contains, ...

It's possible to create a spatial index over the SpatialDataSource and query that index to access the data faster. Build a spatial index is as easy as call the SpatialDataSource.buildIndex(). After such a call the rows in the DataSource cannot be removed and all calls to deleteRow() will set all its fields to null but won't delete any record. What if we are editing a SpatialDataSource and want to index it? No problem, the rows are set to null and not deleted but when the DataSource.commitTrans() is called, first of all, all the deleted rows are actually deleted. To query the spatial index the SpatialDataSource.queryIndex() method has to be used. The index can be removed explicitly by calling the SpatialDataSource.removeIndex() or implicitly by closing source with SpatialDataSource.commitTrans() or SpatialDataSource.rollBackTrans().