org.integratedmodelling.engine.kbox.sql.h2.H2Kbox Maven / Gradle / Ivy
The newest version!
/*******************************************************************************
* Copyright (C) 2007, 2015:
*
* - Ferdinando Villa - integratedmodelling.org - any
* other authors listed in @author annotations
*
* All rights reserved. This file is part of the k.LAB software suite, meant to enable
* modular, collaborative, integrated development of interoperable data and model
* components. For details, see http://integratedmodelling.org.
*
* This program is free software; you can redistribute it and/or modify it under the terms
* of the Affero General Public License Version 3 or any later version.
*
* This program is distributed in the hope that it will be useful, but without any
* warranty; without even the implied warranty of merchantability or fitness for a
* particular purpose. See the Affero General Public License for more details.
*
* You should have received a copy of the Affero General Public License along with this
* program; if not, write to the Free Software Foundation, Inc., 59 Temple Place - Suite
* 330, Boston, MA 02111-1307, USA. The license is also available at:
* https://www.gnu.org/licenses/agpl.html
*******************************************************************************/
package org.integratedmodelling.engine.kbox.sql.h2;
import java.io.File;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.integratedmodelling.api.monitoring.IMonitor;
import org.integratedmodelling.api.persistence.IKbox;
import org.integratedmodelling.api.persistence.IQuery;
import org.integratedmodelling.engine.kbox.sql.SQL;
import org.integratedmodelling.exceptions.KlabException;
import org.integratedmodelling.exceptions.KlabRuntimeException;
/**
* Poor-man hybernate with Thinklab-specialized query language, aware of semantics and
* time/space, using assumptions and conventions to allow IKbox clean model with little or
* no configuration.
*
* @author ferdinando.villa
*
*/
public abstract class H2Kbox implements IKbox {
protected H2Database database;
protected Map, Serializer> serializers = new HashMap<>();
protected Map, Deserializer> deserializers = new HashMap<>();
protected QueryBuilder queryBuilder;
protected IMonitor monitor;
private static Map kboxes = new HashMap<>();
protected static IKbox set(String name, H2Kbox kbox) {
kboxes.put(name, kbox);
return kbox;
}
protected static IKbox get(String name) {
if (kboxes.containsKey(name)) {
return kboxes.get(name);
}
return null;
};
public H2Kbox(String name, IMonitor monitor) {
database = H2Database.get(name);
this.monitor = monitor;
}
public H2Kbox(String name, File directory, IMonitor monitor) {
database = H2Database.get(directory, name);
this.monitor = monitor;
}
@Override
public List query(IQuery query, Class cls) throws KlabException {
return queryBuilder == null ? new ArrayList<>() : querySql(queryBuilder.getSQL(query), cls);
}
/**
* Call the H2 recover tool.
*
* @throws KlabException
*/
public void recover() throws KlabException {
database.recover();
}
public List querySql(String query, Class cls) throws KlabException {
Deserializer deserializer = getDeserializer(cls);
final List ret = deserializer instanceof DeferredDeserializer ? new H2Result(this, monitor)
: new ArrayList<>();
deserializer.setKbox(this);
database.query(query, new SQL.ResultHandler() {
@Override
public void onRow(ResultSet rs) {
if (deserializer instanceof DeferredDeserializer) {
try {
((DeferredDeserializer) deserializer).addId(rs.getLong(H2Schema.FIELD_PKEY));
} catch (SQLException e) {
throw new KlabRuntimeException(e);
}
} else {
ret.add(((DirectDeserializer) deserializer).deserialize(rs));
}
}
@Override
public void nResults(int nres) {
}
});
return ret;
}
private Deserializer getDeserializer(Class cls) {
Deserializer ret = null;
for (Class cl : deserializers.keySet()) {
if (cl.isAssignableFrom(cls)) {
ret = (Deserializer) deserializers.get(cl);
}
}
if (ret == null) {
throw new KlabRuntimeException("kbox: no deserializer for class " + cls.getCanonicalName());
}
return ret;
}
@Override
public long store(Object o) throws KlabException {
return database.storeObject(o, 0l, getSerializer(o.getClass()), monitor);
}
private Serializer getSerializer(Class cls) {
Serializer ret = null;
for (Class cl : serializers.keySet()) {
if (cl.isAssignableFrom(cls)) {
ret = serializers.get(cl);
}
}
if (ret == null) {
throw new KlabRuntimeException("kbox: no serializer for class " + cls.getCanonicalName());
}
return ret;
}
@Override
public T retrieve(long id, Class cls) throws KlabException {
// TODO Auto-generated method stub
return null;
}
@Override
public void remove(long id) throws KlabException {
// TODO Auto-generated method stub
}
@Override
public void remove(IQuery query) throws KlabException {
// TODO Auto-generated method stub
}
@Override
public void clear() throws KlabException {
// TODO Auto-generated method stub
}
protected void setSerializer(Class cls, Serializer serializer) {
this.serializers.put(cls, serializer);
}
protected void setDeserializer(Class cls, Deserializer deserializer) {
this.deserializers.put(cls, deserializer);
}
protected void setQueryBuilder(QueryBuilder querybuilder) {
this.queryBuilder = querybuilder;
}
protected void setSchema(Class cls, Schema schema) {
database.setSchema(cls, schema);
}
// TODO these interfaces should go in implementation.
/**
* Turns an object into SQL instructions to store it.
*
* @author ferdinando.villa
*
*/
public static interface Serializer {
/**
*
* @param o
* @param schema
* @param primaryKey
* @param foreignKey
* @return SQL instructions to serialize object
*/
String serialize(T o, Schema schema, long primaryKey, long foreignKey);
}
/**
* Turns a SQL result into an object. Only a tag interface: the actual working API is
* in the two derived ones.
*
* @author ferdinando.villa
*
*/
public static abstract interface Deserializer {
void setKbox(IKbox h2Kbox);
}
/**
* Builds the object directly in the query result list. Use when building the objects
* is fast and cheap.
*
* @author ferdinando.villa
*
*/
public static interface DirectDeserializer extends Deserializer {
/**
* Passed a result set localized on a valid match. Just get your things from it
* and return - its lifetime is controlled outside. Should return null in error.
*
* @param rs
* @return deserialized object
*/
T deserialize(ResultSet rs);
}
/**
* Turns a database unique ID into an object. Use when building objects is complex,
* requiring more than one query, or when performance is an issue when creating
* objects. In this case, the query strategy will return the OID field for all matches
* to it, and the serializer will have to behave like a list that lazily extracts the
* objects when get() is called.
*
* @author ferdinando.villa
*
*/
public static interface DeferredDeserializer extends Deserializer, List {
void addId(long id);
}
public static interface QueryBuilder {
String getSQL(IQuery query);
}
public static interface Schema {
/**
* Return non-null if this schema creates a whole table.
*
* @return SQL to create schema
*/
String getCreateSQL();
/*
* Return non-null if this schema is only for a field.
*
* @return field name if only a field
*/
String getFieldName();
/*
* Same
*
* @return type
*/
String getFieldType();
/**
* Return the name of the primary table created by this schema, if
* any, or null. Used to check for the need of creating the corresponding
* tableset.
*
* @return
*/
String getTableName();
}
}