1
0
mirror of https://github.com/Rogiel/l2jserver2 synced 2025-12-05 23:22:47 +00:00

Implements OrientDB as internal plug-and-play database

This commit is contained in:
2011-12-25 21:13:00 -02:00
parent a55a1d55f6
commit 1a4a4b0fcf
57 changed files with 43925 additions and 41789 deletions

View File

@@ -124,6 +124,16 @@
<type>jar</type>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>com.orientechnologies</groupId>
<artifactId>orient-commons</artifactId>
<version>1.0rc7</version>
</dependency>
<dependency>
<groupId>com.orientechnologies</groupId>
<artifactId>orientdb-core</artifactId>
<version>1.0rc7</version>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>

View File

@@ -16,12 +16,16 @@
*/
package com.l2jserver.service.database;
import java.io.IOException;
import java.nio.file.Path;
import com.l2jserver.model.Model;
import com.l2jserver.model.id.ID;
import com.l2jserver.service.Service;
import com.l2jserver.service.ServiceConfiguration;
import com.l2jserver.service.configuration.Configuration;
import com.l2jserver.service.core.threading.AsyncFuture;
import com.mysema.query.sql.RelationalPathBase;
/**
* This service provides access to an database implementation. Each
@@ -101,6 +105,26 @@ public interface DatabaseService extends Service {
int perform();
}
/**
* Imports an static data file into the database. File must be an CSV file
* with the first row as column names.
*
* @param <M>
* the model type
* @param <T>
* the table type
*
* @param path
* the path
* @param entity
* the table
* @throws IOException
* if any error occur while reading or parsing the file
*/
<M extends Model<?>, T extends RelationalPathBase<?>> void importData(
Path path, T entity)
throws IOException;
/**
* Checks for the cached version of the object
*

View File

@@ -0,0 +1,59 @@
/*
* This file is part of l2jserver2 <l2jserver2.com>.
*
* l2jserver2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver2 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database;
import java.util.Map;
import com.l2jserver.service.database.dao.DatabaseRow;
import com.l2jserver.util.transformer.Transformer;
import com.l2jserver.util.transformer.TransformerFactory;
import com.mysema.query.types.Path;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public class StaticDatabaseRow implements DatabaseRow {
/**
* The static data
*/
private final Map<String, String> data;
/**
* @param data
* the data
*/
public StaticDatabaseRow(Map<String, String> data) {
this.data = data;
}
@Override
public <T> T get(Path<T> path) {
final String value = data.get(path.getMetadata().getExpression()
.toString());
@SuppressWarnings("unchecked")
Transformer<T> transformer = (Transformer<T>) TransformerFactory
.getTransfromer(path.getType());
return transformer.untransform(path.getType(), value);
}
@Override
public <T> boolean isNull(Path<T> path) {
// TODO Auto-generated method stub
return false;
}
}

View File

@@ -14,15 +14,15 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.sql.ddl;
package com.l2jserver.service.database.ddl;
import java.util.List;
import com.l2jserver.service.database.sql.ddl.struct.Column;
import com.l2jserver.service.database.sql.ddl.struct.Column.ColumnType;
import com.l2jserver.service.database.sql.ddl.struct.ForeignKey;
import com.l2jserver.service.database.sql.ddl.struct.PrimaryKey;
import com.l2jserver.service.database.sql.ddl.struct.Table;
import com.l2jserver.service.database.ddl.struct.Column;
import com.l2jserver.service.database.ddl.struct.ForeignKey;
import com.l2jserver.service.database.ddl.struct.PrimaryKey;
import com.l2jserver.service.database.ddl.struct.Table;
import com.l2jserver.service.database.ddl.struct.Column.ColumnType;
import com.l2jserver.util.factory.CollectionFactory;
/**

View File

@@ -14,9 +14,9 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.sql.ddl;
package com.l2jserver.service.database.ddl;
import com.l2jserver.service.database.sql.ddl.struct.Column.ColumnType;
import com.l2jserver.service.database.ddl.struct.Column.ColumnType;
import com.mysema.query.sql.SQLTemplates;
/**

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.sql.ddl;
package com.l2jserver.service.database.ddl;
import java.lang.reflect.Field;
import java.sql.Connection;
@@ -27,15 +27,15 @@ import java.util.Date;
import java.util.List;
import java.util.Map;
import com.l2jserver.service.database.sql.ddl.annotation.ColumnAutoIncrement;
import com.l2jserver.service.database.sql.ddl.annotation.ColumnDefault;
import com.l2jserver.service.database.sql.ddl.annotation.ColumnNullable;
import com.l2jserver.service.database.sql.ddl.annotation.ColumnSize;
import com.l2jserver.service.database.sql.ddl.struct.Column;
import com.l2jserver.service.database.sql.ddl.struct.Column.ColumnType;
import com.l2jserver.service.database.sql.ddl.struct.ForeignKey;
import com.l2jserver.service.database.sql.ddl.struct.PrimaryKey;
import com.l2jserver.service.database.sql.ddl.struct.Table;
import com.l2jserver.service.database.ddl.annotation.ColumnAutoIncrement;
import com.l2jserver.service.database.ddl.annotation.ColumnDefault;
import com.l2jserver.service.database.ddl.annotation.ColumnNullable;
import com.l2jserver.service.database.ddl.annotation.ColumnSize;
import com.l2jserver.service.database.ddl.struct.Column;
import com.l2jserver.service.database.ddl.struct.ForeignKey;
import com.l2jserver.service.database.ddl.struct.PrimaryKey;
import com.l2jserver.service.database.ddl.struct.Table;
import com.l2jserver.service.database.ddl.struct.Column.ColumnType;
import com.l2jserver.util.ClassUtils;
import com.l2jserver.util.factory.CollectionFactory;
import com.mysema.query.sql.RelationalPathBase;

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.sql.ddl.annotation;
package com.l2jserver.service.database.ddl.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.sql.ddl.annotation;
package com.l2jserver.service.database.ddl.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.sql.ddl.annotation;
package com.l2jserver.service.database.ddl.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.sql.ddl.annotation;
package com.l2jserver.service.database.ddl.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.sql.ddl.struct;
package com.l2jserver.service.database.ddl.struct;
import java.util.List;

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.sql.ddl.struct;
package com.l2jserver.service.database.ddl.struct;
import java.util.Collections;
import java.util.List;

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.sql.ddl.struct;
package com.l2jserver.service.database.ddl.struct;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>

View File

@@ -14,7 +14,7 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.sql.ddl.struct;
package com.l2jserver.service.database.ddl.struct;
import java.util.Collection;
import java.util.Collections;

View File

@@ -14,10 +14,10 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.sql.ddl.template;
package com.l2jserver.service.database.ddl.template;
import com.l2jserver.service.database.sql.ddl.QueryTemplate;
import com.l2jserver.service.database.sql.ddl.struct.Column.ColumnType;
import com.l2jserver.service.database.ddl.QueryTemplate;
import com.l2jserver.service.database.ddl.struct.Column.ColumnType;
import com.mysema.query.QueryMetadata;
import com.mysema.query.QueryModifiers;
import com.mysema.query.sql.support.SerializationContext;

View File

@@ -14,10 +14,10 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.sql.ddl.template;
package com.l2jserver.service.database.ddl.template;
import com.l2jserver.service.database.sql.ddl.QueryTemplate;
import com.l2jserver.service.database.sql.ddl.struct.Column.ColumnType;
import com.l2jserver.service.database.ddl.QueryTemplate;
import com.l2jserver.service.database.ddl.struct.Column.ColumnType;
import com.mysema.query.types.Ops;
/**

View File

@@ -14,12 +14,12 @@
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.sql.ddl.template;
package com.l2jserver.service.database.ddl.template;
import java.math.BigDecimal;
import com.l2jserver.service.database.sql.ddl.QueryTemplate;
import com.l2jserver.service.database.sql.ddl.struct.Column.ColumnType;
import com.l2jserver.service.database.ddl.QueryTemplate;
import com.l2jserver.service.database.ddl.struct.Column.ColumnType;
import com.mysema.query.types.Ops;
/**

View File

@@ -0,0 +1,51 @@
/*
* This file is part of l2jserver2 <l2jserver2.com>.
*
* l2jserver2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver2 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.orientdb;
import com.google.inject.Inject;
import com.l2jserver.model.Model;
import com.l2jserver.model.id.ID;
import com.l2jserver.service.database.AbstractDAO;
import com.l2jserver.service.database.DatabaseService;
/**
* {@link AbstractDAO} for OrientDB DAO implementation
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <T>
* the object for the DAO
* @param <I>
* the object ID type
*/
public abstract class AbstractOrientDBDAO<T extends Model<?>, I extends ID<?>>
extends AbstractDAO<T, I> {
/**
* The JDBC Database Service
*/
protected final AbstractOrientDatabaseService database;
/**
* @param database
* the database service
*/
@Inject
protected AbstractOrientDBDAO(DatabaseService database) {
super(database);
this.database = (AbstractOrientDatabaseService) database;
}
}

View File

@@ -0,0 +1,795 @@
/*
* This file is part of l2jserver2 <l2jserver2.com>.
*
* l2jserver2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver2 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.orientdb;
import java.io.BufferedReader;
import java.io.IOException;
import java.nio.charset.Charset;
import java.nio.file.Files;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.TimeUnit;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.google.common.base.Preconditions;
import com.google.common.collect.Iterators;
import com.google.inject.Inject;
import com.l2jserver.model.Model;
import com.l2jserver.model.Model.ObjectDesire;
import com.l2jserver.model.id.ID;
import com.l2jserver.model.id.object.allocator.IDAllocator;
import com.l2jserver.service.AbstractService;
import com.l2jserver.service.ServiceStartException;
import com.l2jserver.service.ServiceStopException;
import com.l2jserver.service.cache.Cache;
import com.l2jserver.service.cache.CacheService;
import com.l2jserver.service.configuration.ConfigurationService;
import com.l2jserver.service.configuration.XMLConfigurationService.ConfigurationXPath;
import com.l2jserver.service.core.threading.AbstractTask;
import com.l2jserver.service.core.threading.AsyncFuture;
import com.l2jserver.service.core.threading.ScheduledAsyncFuture;
import com.l2jserver.service.core.threading.ThreadService;
import com.l2jserver.service.database.DAOResolver;
import com.l2jserver.service.database.DataAccessObject;
import com.l2jserver.service.database.DatabaseService;
import com.l2jserver.service.database.dao.DatabaseRow;
import com.l2jserver.service.database.dao.InsertMapper;
import com.l2jserver.service.database.dao.SelectMapper;
import com.l2jserver.service.database.dao.UpdateMapper;
import com.l2jserver.util.factory.CollectionFactory;
import com.mysema.query.sql.ForeignKey;
import com.mysema.query.sql.RelationalPathBase;
import com.mysema.query.types.Path;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentPool;
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import com.orientechnologies.orient.core.metadata.schema.OClass.INDEX_TYPE;
import com.orientechnologies.orient.core.metadata.schema.OProperty;
import com.orientechnologies.orient.core.metadata.schema.OSchema;
import com.orientechnologies.orient.core.metadata.schema.OType;
import com.orientechnologies.orient.core.query.nativ.ONativeSynchQuery;
import com.orientechnologies.orient.core.query.nativ.OQueryContextNative;
import com.orientechnologies.orient.core.record.impl.ODocument;
/**
* This is an implementation of {@link DatabaseService} that provides an layer
* to OrientDB Document Database.
*
* <h1>Internal specification</h1> <h2>The {@link Query} object</h2>
*
* If you wish to implement a new {@link DataAccessObject} you should try not
* use {@link Query} object directly because it only provides low level access
* to the JDBC architecture. Instead, you could use an specialized class, like
* {@link InsertQuery}, {@link SelectListQuery} or {@link SelectSingleQuery}. If
* you do need low level access, feel free to use the {@link Query} class
* directly.
*
* <h2>The {@link SelectMapper} object</h2>
*
* The {@link SelectMapper} object maps an OrientDB {@link ODocument} into an
* Java {@link Object}.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public abstract class AbstractOrientDatabaseService extends AbstractService
implements DatabaseService {
/**
* The configuration object
*/
private final OrientDatabaseConfiguration config;
/**
* The logger
*/
private final Logger log = LoggerFactory
.getLogger(AbstractOrientDatabaseService.class);
/**
* The cache service
*/
private final CacheService cacheService;
/**
* The thread service
*/
private final ThreadService threadService;
/**
* The {@link DAOResolver} instance
*/
private final DAOResolver daoResolver;
/**
* An cache object
*/
private Cache<Object, Model<?>> objectCache;
/**
* Future for the auto-save task. Each object that has changed is auto saved
* every 1 minute.
*/
private ScheduledAsyncFuture autoSaveFuture;
/**
* Configuration interface for {@link AbstractOrientDatabaseService}.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*/
public interface OrientDatabaseConfiguration extends DatabaseConfiguration {
/**
* @return the orientdb url
*/
@ConfigurationPropertyGetter(defaultValue = "local:data/database")
@ConfigurationXPath("/configuration/services/database/orientdb/url")
String getUrl();
/**
* @param url
* the new orientdb url
*/
@ConfigurationPropertySetter
@ConfigurationXPath("/configuration/services/database/orientdb/url")
void setUrl(String url);
/**
* @return the orientdb database username
*/
@ConfigurationPropertyGetter(defaultValue = "admin")
@ConfigurationXPath("/configuration/services/database/orientdb/username")
String getUsername();
/**
* @param username
* the orientdb database username
*/
@ConfigurationPropertySetter
@ConfigurationXPath("/configuration/services/database/orientdb/username")
void setUsername(String username);
/**
* @return the orientdb database password
*/
@ConfigurationPropertyGetter(defaultValue = "admin")
@ConfigurationXPath("/configuration/services/database/orientdb/password")
String getPassword();
/**
* @param password
* the jdbc database password
*/
@ConfigurationPropertySetter
@ConfigurationXPath("/configuration/services/database/jdbc/password")
void setPassword(String password);
}
/**
* @param configService
* the configuration service
* @param cacheService
* the cache service
* @param threadService
* the thread service
* @param daoResolver
* the {@link DataAccessObject DAO} resolver
*/
@Inject
public AbstractOrientDatabaseService(ConfigurationService configService,
CacheService cacheService, ThreadService threadService,
DAOResolver daoResolver) {
config = configService.get(OrientDatabaseConfiguration.class);
this.cacheService = cacheService;
this.threadService = threadService;
this.daoResolver = daoResolver;
}
@Override
protected void doStart() throws ServiceStartException {
ODatabaseDocumentTx database;
try {
database = ODatabaseDocumentPool.global().acquire(config.getUrl(),
config.getUsername(), config.getPassword());
} catch (Exception e) {
database = new ODatabaseDocumentTx(config.getUrl());
if (!database.exists()) {
database.create();
}
}
ensureDatabaseSchema();
database.close();
// cache must be large enough for all world objects, to avoid
// duplication... this would endanger non-persistent states
objectCache = cacheService.createEternalCache("database-service",
IDAllocator.ALLOCABLE_IDS);
// start the auto save task
autoSaveFuture = threadService.async(60, TimeUnit.SECONDS, 60,
new Runnable() {
@Override
public void run() {
log.debug("Auto save task started");
int objects = 0;
for (final Model<?> object : objectCache) {
@SuppressWarnings("unchecked")
final DataAccessObject<Model<?>, ?> dao = daoResolver
.getDAO(object.getClass());
if (dao.save(object) > 0) {
objects++;
}
}
log.info(
"{} objects have been saved by the auto save task",
objects);
}
});
}
@Override
public int transaction(TransactionExecutor executor) {
return executor.perform();
}
@Override
public AsyncFuture<Integer> transactionAsync(
final TransactionExecutor executor) {
return threadService.async(new AbstractTask<Integer>() {
@Override
public Integer call() throws Exception {
return transaction(executor);
}
});
}
/**
* Executes an <tt>query</tt> in the database.
*
* @param <T>
* the query return type
* @param query
* the query
* @return an instance of <tt>T</tt>
*/
public <T> T query(Query<T> query) {
Preconditions.checkNotNull(query, "query");
final ODatabaseDocumentTx database = ODatabaseDocumentPool.global()
.acquire(config.getUrl(), config.getUsername(),
config.getPassword());
log.debug("Executing query {} with {}", query, database);
try {
return query.query(database, this);
} finally {
database.commit();
}
}
@Override
public <M extends Model<?>, T extends RelationalPathBase<?>> void importData(
java.nio.file.Path path, T entity) throws IOException {
final ODatabaseDocumentTx database = ODatabaseDocumentPool.global()
.acquire(config.getUrl(), config.getUsername(),
config.getPassword());
log.info("Importing {} to {}", path, entity);
BufferedReader reader = Files.newBufferedReader(path,
Charset.defaultCharset());
final String header[] = reader.readLine().split(",");
String line;
while ((line = reader.readLine()) != null) {
final String data[] = line.split(",");
final ODocument document = new ODocument(database,
entity.getTableName());
for (int i = 0; i < data.length; i++) {
document.field(header[i], data[i]);
}
database.save(document);
}
}
/**
* Makes sure the database schema is up-to-date with the external database
*/
protected abstract void ensureDatabaseSchema();
/**
* @param table
* the {@link RelationalPathBase} table
* @return true if a new schema was created
*/
protected boolean createSchema(RelationalPathBase<?> table) {
final ODatabaseDocumentTx database = ODatabaseDocumentPool.global()
.acquire(config.getUrl(), config.getUsername(),
config.getPassword());
boolean newSchema = false;
try {
final OSchema schemas = database.getMetadata().getSchema();
OClass schema = schemas.getClass(table.getTableName());
if (schema == null) {
schema = schemas.createClass(table.getTableName());
newSchema = true;
}
for (final Path<?> path : table.all()) {
final String name = path.getMetadata().getExpression()
.toString();
OProperty property = schema.getProperty(name);
if (property == null)
property = schema.createProperty(
path.getMetadata().getExpression().toString(),
(path.getType().isEnum() ? OType.STRING : OType
.getTypeByClass(path.getType())));
if (path.getType().isEnum()) {
property.setType(OType.STRING);
} else {
property.setType(OType.getTypeByClass(path.getType()));
}
}
for (final ForeignKey<?> fk : table.getForeignKeys()) {
final String[] columns = new String[fk.getLocalColumns().size()];
int i = 0;
for (final Path<?> keyPath : fk.getLocalColumns()) {
columns[i++] = keyPath.getMetadata().getExpression()
.toString();
}
schema.createIndex(StringUtils.join(columns, "-"),
INDEX_TYPE.NOTUNIQUE, columns);
}
final String[] pkColumns = new String[table.getPrimaryKey()
.getLocalColumns().size()];
int i = 0;
for (final Path<?> keyPath : table.getPrimaryKey()
.getLocalColumns()) {
pkColumns[i++] = keyPath.getMetadata().getExpression()
.toString();
}
schema.createIndex("PRIMARY", INDEX_TYPE.UNIQUE, pkColumns);
schemas.save();
} finally {
database.close();
}
return newSchema;
}
@Override
@SuppressWarnings("unchecked")
public <I extends ID<?>, O extends Model<?>> O getCachedObject(I id) {
Preconditions.checkNotNull(id, "id");
log.debug("Fetching cached object {}", id);
return (O) objectCache.get(id);
}
@Override
public <I extends ID<?>, O extends Model<?>> boolean hasCachedObject(I id) {
Preconditions.checkNotNull(id, "id");
log.debug("Locating cached object {}", id);
return objectCache.contains(id);
}
@Override
public <I extends ID<?>, O extends Model<?>> void updateCache(I id, O value) {
Preconditions.checkNotNull(id, "key");
Preconditions.checkNotNull(value, "value");
log.debug("Updating cached object {} with {}", id, value);
objectCache.put(id, value);
}
@Override
public <I extends ID<?>, O extends Model<?>> void removeCache(I id) {
Preconditions.checkNotNull(id, "key");
log.debug("Removing cached object {}", id);
objectCache.remove(id);
}
@Override
protected void doStop() throws ServiceStopException {
autoSaveFuture.cancel(true);
autoSaveFuture = null;
cacheService.dispose(objectCache);
objectCache = null;
ODatabaseDocumentPool.global().close();
}
/**
* The query interface. The query will receive an connection an will be
* executed. The can return return a value if required.
*
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
* @param <R>
* the return type
*/
public interface Query<R> {
/**
* Execute the query in <tt>conn</tt>
*
* @param database
* the database instance
* @param service
* the database service instance
* @return the query return value
*/
R query(ODatabaseDocumentTx database, DatabaseService service);
}
public static abstract class AbstractQuery<R> implements Query<R> {
protected void updateDesire(Object object, ObjectDesire expected) {
if (object instanceof Model) {
if (((Model<?>) object).getObjectDesire() == expected) {
((Model<?>) object).setObjectDesire(ObjectDesire.NONE);
}
}
}
protected String name(Path<?> path) {
return path.getMetadata().getExpression().toString();
}
}
public static class InsertQuery<O, RI, I extends ID<? super RI>, E extends RelationalPathBase<?>>
extends AbstractQuery<Integer> {
private final InsertMapper<O, RI, I, E> mapper;
private final Iterator<O> iterator;
@SuppressWarnings("unused")
// FIXME implement id generation
private final Path<RI> primaryKey;
protected final E e;
/**
* @param entity
* the entity type
* @param mapper
* the insert mapper
* @param iterator
* the objects to be inserted
* @param primaryKey
* the primary key, if any. Only required if the ID is
* generated by the database engine.
*/
public InsertQuery(E entity, InsertMapper<O, RI, I, E> mapper,
Path<RI> primaryKey, Iterator<O> iterator) {
this.iterator = iterator;
this.mapper = mapper;
this.e = entity;
this.primaryKey = primaryKey;
}
/**
* @param entity
* the entity type
* @param mapper
* the insert mapper
* @param iterator
* the objects to be inserted
*/
public InsertQuery(E entity, InsertMapper<O, RI, I, E> mapper,
Iterator<O> iterator) {
this(entity, mapper, null, iterator);
}
/**
* @param entity
* the entity type
* @param mapper
* the insert mapper
* @param objects
* the objects to be inserted
*/
@SafeVarargs
public InsertQuery(E entity, InsertMapper<O, RI, I, E> mapper,
O... objects) {
this(entity, mapper, null, Iterators.forArray(objects));
}
/**
* @param entity
* the entity type
* @param mapper
* the insert mapper
* @param objects
* the objects to be inserted
* @param primaryKey
* the primary key, if any. Only required if the ID is
* generated by the database engine.
*/
@SafeVarargs
public InsertQuery(E entity, InsertMapper<O, RI, I, E> mapper,
Path<RI> primaryKey, O... objects) {
this(entity, mapper, primaryKey, Iterators.forArray(objects));
}
@Override
public final Integer query(ODatabaseDocumentTx database,
DatabaseService service) {
int rows = 0;
final DocumentDatabaseRow row = new DocumentDatabaseRow();
while (iterator.hasNext()) {
final O object = iterator.next();
row.setDocument(new ODocument(database, e.getTableName()));
mapper.insert(e, object, row);
// TODO generate ids
row.getDocument().save();
rows++;
updateDesire(object, ObjectDesire.INSERT);
}
return rows;
}
}
public static abstract class UpdateQuery<O, E extends RelationalPathBase<?>>
extends AbstractQuery<Integer> {
private final UpdateMapper<O, E> mapper;
private final Iterator<O> iterator;
protected final E e;
/**
* @param entity
* the entity type
* @param mapper
* the update mapper
* @param iterator
* the objects to be inserted
*/
public UpdateQuery(E entity, UpdateMapper<O, E> mapper,
Iterator<O> iterator) {
this.iterator = iterator;
this.mapper = mapper;
this.e = entity;
}
/**
* @param entity
* the entity type
* @param mapper
* the update mapper
* @param objects
* the objects to be inserted
*/
@SafeVarargs
public UpdateQuery(E entity, UpdateMapper<O, E> mapper, O... objects) {
this(entity, mapper, Iterators.forArray(objects));
}
@Override
public final Integer query(ODatabaseDocumentTx database,
DatabaseService service) {
int rows = 0;
final DocumentDatabaseRow row = new DocumentDatabaseRow();
while (iterator.hasNext()) {
final O object = iterator.next();
List<ODocument> documents = database
.query(new ONativeSynchQuery<OQueryContextNative>(
database, e.getTableName(),
new OQueryContextNative()) {
private static final long serialVersionUID = 1L;
@Override
public boolean filter(OQueryContextNative record) {
return query(record, object).go();
};
});
if (documents.size() < 1)
continue;
row.setDocument(documents.get(0));
mapper.update(e, object, row);
row.getDocument().save();
rows++;
updateDesire(object, ObjectDesire.UPDATE);
}
return rows;
}
protected abstract OQueryContextNative query(
OQueryContextNative record, O o);
}
public static abstract class DeleteQuery<O, E extends RelationalPathBase<?>>
extends AbstractQuery<Integer> {
private final Iterator<O> iterator;
protected final E e;
/**
* @param entity
* the entity type
* @param iterator
* the objects to be inserted
*/
public DeleteQuery(E entity, Iterator<O> iterator) {
this.iterator = iterator;
this.e = entity;
}
/**
* @param entity
* the entity type
* @param objects
* the objects to be inserted
*/
@SafeVarargs
public DeleteQuery(E entity, O... objects) {
this(entity, Iterators.forArray(objects));
}
@Override
public final Integer query(ODatabaseDocumentTx database,
DatabaseService service) {
int rows = 0;
while (iterator.hasNext()) {
final O object = iterator.next();
List<ODocument> documents = database
.query(new ONativeSynchQuery<OQueryContextNative>(
database, e.getTableName(),
new OQueryContextNative()) {
private static final long serialVersionUID = 1L;
@Override
public boolean filter(OQueryContextNative record) {
return query(record, object).go();
};
});
for (final ODocument document : documents) {
document.delete();
rows++;
}
updateDesire(object, ObjectDesire.DELETE);
}
return rows;
}
protected abstract OQueryContextNative query(
OQueryContextNative record, O o);
}
public static abstract class AbstractSelectQuery<R, O, RI, I extends ID<? super RI>, E extends RelationalPathBase<RI>>
extends AbstractQuery<R> {
protected final E entity;
protected final SelectMapper<O, RI, I, E> mapper;
/**
* @param entity
* the entity type
* @param mapper
* the object mapper
*/
public AbstractSelectQuery(E entity, SelectMapper<O, RI, I, E> mapper) {
this.entity = entity;
this.mapper = mapper;
}
@Override
public final R query(ODatabaseDocumentTx database,
DatabaseService service) {
List<ODocument> documents = database
.query(new ONativeSynchQuery<OQueryContextNative>(database,
entity.getTableName(), new OQueryContextNative()) {
private static final long serialVersionUID = 1L;
@Override
public boolean filter(OQueryContextNative record) {
record = query(record, entity);
if (record == null)
return true;
return record.go();
};
});
return perform(documents, service);
}
protected abstract OQueryContextNative query(
OQueryContextNative record, E e);
protected abstract R perform(List<ODocument> documents,
DatabaseService service);
@SuppressWarnings("unchecked")
protected O lookupCache(DatabaseRow row, DatabaseService database) {
final I id = mapper.getPrimaryKeyMapper().createID(
(RI) row.get(entity.getPrimaryKey().getLocalColumns()
.get(0)));
if (id != null) {
if (database.hasCachedObject(id))
return (O) database.getCachedObject(id);
}
return null;
}
protected void updateCache(O instance, DatabaseService database) {
if (instance == null)
return;
if (instance instanceof Model)
database.updateCache(((Model<?>) instance).getID(),
(Model<?>) instance);
}
}
public static abstract class SelectSingleQuery<O, RI, I extends ID<? super RI>, E extends RelationalPathBase<RI>>
extends AbstractSelectQuery<O, O, RI, I, E> {
/**
* @param entity
* the entity
* @param mapper
* the mapper
*/
public SelectSingleQuery(E entity, SelectMapper<O, RI, I, E> mapper) {
super(entity, mapper);
}
@Override
protected final O perform(List<ODocument> documents,
DatabaseService service) {
final ODocument document = (!documents.isEmpty() ? documents.get(0)
: null);
if (document == null)
return null;
final DocumentDatabaseRow row = new DocumentDatabaseRow(document);
O object = lookupCache(row, service);
if (object == null) {
object = mapper.select(entity, row);
updateCache(object, service);
if (object instanceof Model) {
((Model<?>) object).setObjectDesire(ObjectDesire.NONE);
}
}
return object;
}
}
public static abstract class SelectListQuery<O, RI, I extends ID<? super RI>, E extends RelationalPathBase<RI>>
extends AbstractSelectQuery<List<O>, O, RI, I, E> {
/**
* @param entity
* the entity
* @param mapper
* the mapper
*/
public SelectListQuery(E entity, SelectMapper<O, RI, I, E> mapper) {
super(entity, mapper);
}
@Override
protected final List<O> perform(List<ODocument> documents,
DatabaseService service) {
final List<O> results = CollectionFactory.newList();
final DocumentDatabaseRow row = new DocumentDatabaseRow();
for (final ODocument document : documents) {
row.setDocument(document);
O object = lookupCache(row, service);
if (object == null) {
object = mapper.select(entity, row);
updateCache(object, service);
if (object instanceof Model) {
((Model<?>) object).setObjectDesire(ObjectDesire.NONE);
}
}
if (object != null)
results.add(object);
}
return results;
}
}
}

View File

@@ -0,0 +1,78 @@
/*
* This file is part of l2jserver2 <l2jserver2.com>.
*
* l2jserver2 is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* l2jserver2 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
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with l2jserver2. If not, see <http://www.gnu.org/licenses/>.
*/
package com.l2jserver.service.database.orientdb;
import com.l2jserver.service.database.dao.DatabaseRow;
import com.l2jserver.service.database.dao.WritableDatabaseRow;
import com.mysema.query.types.Path;
import com.orientechnologies.orient.core.record.impl.ODocument;
/**
* @author <a href="http://www.rogiel.com">Rogiel</a>
*
*/
public class DocumentDatabaseRow implements DatabaseRow, WritableDatabaseRow {
private ODocument document;
/**
* @param document
* the orientdb document
*/
public DocumentDatabaseRow(ODocument document) {
this.document = document;
}
public DocumentDatabaseRow() {
}
@Override
public <T> T get(Path<T> path) {
return document.field(path.getMetadata().getExpression().toString(),
path.getType());
}
@Override
public <T> boolean isNull(Path<T> path) {
return get(path) == null;
}
@Override
public <T> WritableDatabaseRow set(Path<T> path, T value) {
document.field(path.getMetadata().getExpression().toString(), value);
return this;
}
@Override
public <T> WritableDatabaseRow setNull(Path<T> path) {
return set(path, null);
}
/**
* @return the orientdb document
*/
public ODocument getDocument() {
return document;
}
/**
* @param document
* the orientdb document to set
*/
public void setDocument(ODocument document) {
this.document = document;
}
}

View File

@@ -25,6 +25,7 @@ import java.sql.SQLException;
import java.sql.Statement;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import javax.sql.DataSource;
@@ -64,14 +65,17 @@ import com.l2jserver.service.database.dao.DatabaseRow;
import com.l2jserver.service.database.dao.InsertMapper;
import com.l2jserver.service.database.dao.SelectMapper;
import com.l2jserver.service.database.dao.UpdateMapper;
import com.l2jserver.service.database.sql.ddl.QueryFactory;
import com.l2jserver.service.database.sql.ddl.TableFactory;
import com.l2jserver.service.database.sql.ddl.struct.Table;
import com.l2jserver.service.database.ddl.QueryFactory;
import com.l2jserver.service.database.ddl.TableFactory;
import com.l2jserver.service.database.ddl.struct.Table;
import com.l2jserver.util.factory.CollectionFactory;
import com.mysema.query.sql.AbstractSQLQuery;
import com.mysema.query.sql.RelationalPath;
import com.mysema.query.sql.RelationalPathBase;
import com.mysema.query.sql.SQLQueryFactory;
import com.mysema.query.sql.dml.Mapper;
import com.mysema.query.sql.dml.SQLDeleteClause;
import com.mysema.query.sql.dml.SQLInsertClause;
import com.mysema.query.sql.dml.SQLUpdateClause;
import com.mysema.query.sql.types.Type;
import com.mysema.query.types.Path;
@@ -442,43 +446,6 @@ public abstract class AbstractSQLDatabaseService extends AbstractService
return create;
}
/**
* Imports an entire SQL file into the database. If the file consists of
* several SQL statements, they will be splitted and executed separated.
*
* @param conn
* the SQL connection
* @param sqlPath
* the path for the SQL file
* @throws IOException
* if any error occur while reading the file
* @throws SQLException
* if any error occur while executing the statements
*/
protected void importSQL(Connection conn, java.nio.file.Path sqlPath)
throws IOException, SQLException {
BufferedReader reader = Files.newBufferedReader(sqlPath,
Charset.defaultCharset());
final StringBuilder builder = new StringBuilder();
String line;
conn.setAutoCommit(false);
try {
while ((line = reader.readLine()) != null) {
builder.append(line).append("\n");
if (line.trim().endsWith(";")) {
executeSQL(conn, builder.substring(0, builder.length() - 2));
builder.setLength(0);
}
}
conn.commit();
} catch (SQLException | IOException e) {
conn.rollback();
throw e;
} finally {
conn.setAutoCommit(true);
}
}
/**
* Executes the SQL code in the databases
*
@@ -503,6 +470,56 @@ public abstract class AbstractSQLDatabaseService extends AbstractService
}
}
@Override
public <M extends Model<?>, T extends RelationalPathBase<?>> void importData(
java.nio.file.Path path, T entity) throws IOException {
Connection conn;
try {
conn = dataSource.getConnection();
} catch (SQLException e) {
return;
}
log.info("Importing {} to {}", path, entity);
try {
BufferedReader reader = Files.newBufferedReader(path,
Charset.defaultCharset());
final String header[] = reader.readLine().split(",");
String line;
while ((line = reader.readLine()) != null) {
final String data[] = line.split(",");
SQLInsertClause insert = engine.createSQLQueryFactory(conn)
.insert(entity);
insert.populate(data, new Mapper<Object[]>() {
@Override
public Map<Path<?>, Object> createMap(
RelationalPath<?> relationalPath, Object[] object) {
final Map<Path<?>, Object> values = CollectionFactory
.newMap();
pathFor: for (final Path<?> path : relationalPath
.getColumns()) {
int i = 0;
for (final String headerName : header) {
if (path.getMetadata().getExpression()
.toString().equals(headerName)) {
values.put(path, object[i]);
continue pathFor;
}
i++;
}
}
return values;
}
});
insert.execute();
}
} finally {
try {
conn.close();
} catch (SQLException e) {
}
}
}
@Override
public int transaction(TransactionExecutor executor) {
Preconditions.checkNotNull(executor, "executor");

View File

@@ -20,7 +20,7 @@ import java.sql.Connection;
import java.sql.Driver;
import java.sql.SQLException;
import com.l2jserver.service.database.sql.ddl.QueryTemplate;
import com.l2jserver.service.database.ddl.QueryTemplate;
import com.mysema.query.sql.AbstractSQLQuery;
import com.mysema.query.sql.SQLQueryFactory;
import com.mysema.query.sql.types.Type;

View File

@@ -24,8 +24,8 @@ import javax.inject.Provider;
import org.apache.derby.jdbc.EmbeddedDriver;
import com.l2jserver.service.database.sql.ddl.QueryTemplate;
import com.l2jserver.service.database.sql.ddl.template.DerbyTemplate;
import com.l2jserver.service.database.ddl.QueryTemplate;
import com.l2jserver.service.database.ddl.template.DerbyTemplate;
import com.mysema.query.sql.AbstractSQLQuery;
import com.mysema.query.sql.Configuration;
import com.mysema.query.sql.SQLQueryFactory;

View File

@@ -23,8 +23,8 @@ import javax.inject.Provider;
import org.h2.Driver;
import com.l2jserver.service.database.sql.ddl.QueryTemplate;
import com.l2jserver.service.database.sql.ddl.template.H2Template;
import com.l2jserver.service.database.ddl.QueryTemplate;
import com.l2jserver.service.database.ddl.template.H2Template;
import com.mysema.query.sql.AbstractSQLQuery;
import com.mysema.query.sql.Configuration;
import com.mysema.query.sql.SQLQueryFactory;

View File

@@ -22,8 +22,8 @@ import java.sql.SQLException;
import javax.inject.Provider;
import com.l2jserver.service.database.sql.ddl.QueryTemplate;
import com.l2jserver.service.database.sql.ddl.template.MySQLTemplate;
import com.l2jserver.service.database.ddl.QueryTemplate;
import com.l2jserver.service.database.ddl.template.MySQLTemplate;
import com.mysema.query.sql.AbstractSQLQuery;
import com.mysema.query.sql.Configuration;
import com.mysema.query.sql.SQLQueryFactory;

View File

@@ -38,7 +38,7 @@ public class ClassTransformer implements Transformer<Class<?>> {
@Override
public Class<?> untransform(Class<? extends Class<?>> type, String value) {
try {
return Class.forName(value);
return Class.forName(value.trim());
} catch (ClassNotFoundException e) {
throw new TransformException(e);
}