mirror of
https://github.com/Rogiel/l2jserver2
synced 2025-12-06 07:32:46 +00:00
Implements better schema management
This commit is contained in:
@@ -24,7 +24,9 @@ import com.l2jserver.model.id.ID;
|
|||||||
import com.l2jserver.service.Service;
|
import com.l2jserver.service.Service;
|
||||||
import com.l2jserver.service.ServiceConfiguration;
|
import com.l2jserver.service.ServiceConfiguration;
|
||||||
import com.l2jserver.service.configuration.Configuration;
|
import com.l2jserver.service.configuration.Configuration;
|
||||||
|
import com.l2jserver.service.configuration.XMLConfigurationService.ConfigurationXPath;
|
||||||
import com.l2jserver.service.core.threading.AsyncFuture;
|
import com.l2jserver.service.core.threading.AsyncFuture;
|
||||||
|
import com.mysema.query.sql.RelationalPath;
|
||||||
import com.mysema.query.sql.RelationalPathBase;
|
import com.mysema.query.sql.RelationalPathBase;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -50,6 +52,20 @@ public interface DatabaseService extends Service {
|
|||||||
* @see Configuration
|
* @see Configuration
|
||||||
*/
|
*/
|
||||||
public interface DatabaseConfiguration extends ServiceConfiguration {
|
public interface DatabaseConfiguration extends ServiceConfiguration {
|
||||||
|
/**
|
||||||
|
* @return the update schema state
|
||||||
|
*/
|
||||||
|
@ConfigurationPropertyGetter(defaultValue = "true")
|
||||||
|
@ConfigurationXPath("/configuration/services/database/automaticSchemaUpdate")
|
||||||
|
boolean isAutomaticSchemaUpdateEnabled();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param updateSchema
|
||||||
|
* the new uodate schema state
|
||||||
|
*/
|
||||||
|
@ConfigurationPropertySetter
|
||||||
|
@ConfigurationXPath("/configuration/services/database/automaticSchemaUpdate")
|
||||||
|
void setUpdateSchema(boolean updateSchema);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -122,8 +138,22 @@ public interface DatabaseService extends Service {
|
|||||||
* if any error occur while reading or parsing the file
|
* if any error occur while reading or parsing the file
|
||||||
*/
|
*/
|
||||||
<M extends Model<?>, T extends RelationalPathBase<?>> void importData(
|
<M extends Model<?>, T extends RelationalPathBase<?>> void importData(
|
||||||
Path path, T entity)
|
Path path, T entity) throws IOException;
|
||||||
throws IOException;
|
|
||||||
|
/**
|
||||||
|
* Updates the given <code>schema</code> in the underlying storage engine.
|
||||||
|
*
|
||||||
|
* @param schema
|
||||||
|
* the schema specification
|
||||||
|
* @return true if the schema did not existed and was created (i.e. is empty
|
||||||
|
* and ready to receive default data)
|
||||||
|
*/
|
||||||
|
boolean updateSchema(RelationalPath<?> schema);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates all schemas in the underlying storage engine.
|
||||||
|
*/
|
||||||
|
void updateSchemas();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks for the cached version of the object
|
* Checks for the cached version of the object
|
||||||
|
|||||||
@@ -32,13 +32,13 @@ import com.l2jserver.service.database.ddl.annotation.ColumnDefault;
|
|||||||
import com.l2jserver.service.database.ddl.annotation.ColumnNullable;
|
import com.l2jserver.service.database.ddl.annotation.ColumnNullable;
|
||||||
import com.l2jserver.service.database.ddl.annotation.ColumnSize;
|
import com.l2jserver.service.database.ddl.annotation.ColumnSize;
|
||||||
import com.l2jserver.service.database.ddl.struct.Column;
|
import com.l2jserver.service.database.ddl.struct.Column;
|
||||||
|
import com.l2jserver.service.database.ddl.struct.Column.ColumnType;
|
||||||
import com.l2jserver.service.database.ddl.struct.ForeignKey;
|
import com.l2jserver.service.database.ddl.struct.ForeignKey;
|
||||||
import com.l2jserver.service.database.ddl.struct.PrimaryKey;
|
import com.l2jserver.service.database.ddl.struct.PrimaryKey;
|
||||||
import com.l2jserver.service.database.ddl.struct.Table;
|
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.ClassUtils;
|
||||||
import com.l2jserver.util.factory.CollectionFactory;
|
import com.l2jserver.util.factory.CollectionFactory;
|
||||||
import com.mysema.query.sql.RelationalPathBase;
|
import com.mysema.query.sql.RelationalPath;
|
||||||
import com.mysema.query.types.Path;
|
import com.mysema.query.types.Path;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -47,15 +47,15 @@ import com.mysema.query.types.Path;
|
|||||||
*/
|
*/
|
||||||
public class TableFactory {
|
public class TableFactory {
|
||||||
/**
|
/**
|
||||||
* Creates an {@link Table} object from an {@link RelationalPathBase}
|
* Creates an {@link Table} object from an {@link RelationalPath}
|
||||||
*
|
*
|
||||||
* @param tablePath
|
* @param tablePath
|
||||||
* the table path
|
* the table path
|
||||||
* @return the {@link Table} object
|
* @return the {@link Table} object
|
||||||
*/
|
*/
|
||||||
public static Table createTable(RelationalPathBase<?> tablePath) {
|
public static Table createTable(RelationalPath<?> tablePath) {
|
||||||
final Map<String, Column> columns = CollectionFactory.newMap();
|
final Map<String, Column> columns = CollectionFactory.newMap();
|
||||||
for (final Path<?> path : tablePath.all()) {
|
for (final Path<?> path : tablePath.getColumns()) {
|
||||||
final Column col = createColumn(tablePath, path);
|
final Column col = createColumn(tablePath, path);
|
||||||
columns.put(col.getName(), col);
|
columns.put(col.getName(), col);
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,8 @@ public class TableFactory {
|
|||||||
*
|
*
|
||||||
* @param conn
|
* @param conn
|
||||||
* the JDBC {@link Connection}
|
* the JDBC {@link Connection}
|
||||||
* @param template the query template
|
* @param template
|
||||||
|
* the query template
|
||||||
* @param tableName
|
* @param tableName
|
||||||
* the table name
|
* the table name
|
||||||
* @return the parsed table
|
* @return the parsed table
|
||||||
@@ -112,7 +113,7 @@ public class TableFactory {
|
|||||||
* the columns
|
* the columns
|
||||||
* @return the foreign key list
|
* @return the foreign key list
|
||||||
*/
|
*/
|
||||||
private static List<ForeignKey> createFKs(RelationalPathBase<?> tablePath,
|
private static List<ForeignKey> createFKs(RelationalPath<?> tablePath,
|
||||||
Map<String, Column> columns) {
|
Map<String, Column> columns) {
|
||||||
final List<ForeignKey> fks = CollectionFactory.newList();
|
final List<ForeignKey> fks = CollectionFactory.newList();
|
||||||
for (final com.mysema.query.sql.ForeignKey<?> fk : tablePath
|
for (final com.mysema.query.sql.ForeignKey<?> fk : tablePath
|
||||||
@@ -130,15 +131,14 @@ public class TableFactory {
|
|||||||
return fks;
|
return fks;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static PrimaryKey createPK(RelationalPathBase<?> tablePath,
|
private static PrimaryKey createPK(RelationalPath<?> tablePath,
|
||||||
Map<String, Column> columns) {
|
Map<String, Column> columns) {
|
||||||
return new PrimaryKey(columns.get(tablePath.getPrimaryKey()
|
return new PrimaryKey(columns.get(tablePath.getPrimaryKey()
|
||||||
.getLocalColumns().get(0).getMetadata().getExpression()
|
.getLocalColumns().get(0).getMetadata().getExpression()
|
||||||
.toString()));
|
.toString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private static Column createColumn(RelationalPathBase<?> tablePath,
|
private static Column createColumn(RelationalPath<?> tablePath, Path<?> path) {
|
||||||
Path<?> path) {
|
|
||||||
final String columnName = path.getMetadata().getExpression().toString();
|
final String columnName = path.getMetadata().getExpression().toString();
|
||||||
final ColumnType columnType = getColumnType(path.getType());
|
final ColumnType columnType = getColumnType(path.getType());
|
||||||
final Field field = ClassUtils.getFieldWithValue(tablePath, path);
|
final Field field = ClassUtils.getFieldWithValue(tablePath, path);
|
||||||
|
|||||||
@@ -16,12 +16,11 @@
|
|||||||
*/
|
*/
|
||||||
package com.l2jserver.service.database.orientdb;
|
package com.l2jserver.service.database.orientdb;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import org.apache.commons.lang3.StringUtils;
|
import org.apache.commons.lang3.StringUtils;
|
||||||
@@ -48,13 +47,18 @@ import com.l2jserver.service.core.threading.ScheduledAsyncFuture;
|
|||||||
import com.l2jserver.service.core.threading.ThreadService;
|
import com.l2jserver.service.core.threading.ThreadService;
|
||||||
import com.l2jserver.service.database.DAOResolver;
|
import com.l2jserver.service.database.DAOResolver;
|
||||||
import com.l2jserver.service.database.DataAccessObject;
|
import com.l2jserver.service.database.DataAccessObject;
|
||||||
|
import com.l2jserver.service.database.DatabaseException;
|
||||||
import com.l2jserver.service.database.DatabaseService;
|
import com.l2jserver.service.database.DatabaseService;
|
||||||
import com.l2jserver.service.database.dao.DatabaseRow;
|
import com.l2jserver.service.database.dao.DatabaseRow;
|
||||||
import com.l2jserver.service.database.dao.InsertMapper;
|
import com.l2jserver.service.database.dao.InsertMapper;
|
||||||
import com.l2jserver.service.database.dao.SelectMapper;
|
import com.l2jserver.service.database.dao.SelectMapper;
|
||||||
import com.l2jserver.service.database.dao.UpdateMapper;
|
import com.l2jserver.service.database.dao.UpdateMapper;
|
||||||
|
import com.l2jserver.util.CSVUtils;
|
||||||
|
import com.l2jserver.util.CSVUtils.CSVMapProcessor;
|
||||||
|
import com.l2jserver.util.QPathUtils;
|
||||||
import com.l2jserver.util.factory.CollectionFactory;
|
import com.l2jserver.util.factory.CollectionFactory;
|
||||||
import com.mysema.query.sql.ForeignKey;
|
import com.mysema.query.sql.ForeignKey;
|
||||||
|
import com.mysema.query.sql.RelationalPath;
|
||||||
import com.mysema.query.sql.RelationalPathBase;
|
import com.mysema.query.sql.RelationalPathBase;
|
||||||
import com.mysema.query.types.Path;
|
import com.mysema.query.types.Path;
|
||||||
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentPool;
|
import com.orientechnologies.orient.core.db.document.ODatabaseDocumentPool;
|
||||||
@@ -67,6 +71,7 @@ import com.orientechnologies.orient.core.metadata.schema.OType;
|
|||||||
import com.orientechnologies.orient.core.query.nativ.ONativeSynchQuery;
|
import com.orientechnologies.orient.core.query.nativ.ONativeSynchQuery;
|
||||||
import com.orientechnologies.orient.core.query.nativ.OQueryContextNative;
|
import com.orientechnologies.orient.core.query.nativ.OQueryContextNative;
|
||||||
import com.orientechnologies.orient.core.record.impl.ODocument;
|
import com.orientechnologies.orient.core.record.impl.ODocument;
|
||||||
|
import com.orientechnologies.orient.core.tx.OTransaction;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is an implementation of {@link DatabaseService} that provides an layer
|
* This is an implementation of {@link DatabaseService} that provides an layer
|
||||||
@@ -122,6 +127,10 @@ public abstract class AbstractOrientDatabaseService extends AbstractService
|
|||||||
* every 1 minute.
|
* every 1 minute.
|
||||||
*/
|
*/
|
||||||
private ScheduledAsyncFuture autoSaveFuture;
|
private ScheduledAsyncFuture autoSaveFuture;
|
||||||
|
/**
|
||||||
|
* The transactioned database connection, if any.
|
||||||
|
*/
|
||||||
|
private final ThreadLocal<ODatabaseDocumentTx> transaction = new ThreadLocal<ODatabaseDocumentTx>();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Configuration interface for {@link AbstractOrientDatabaseService}.
|
* Configuration interface for {@link AbstractOrientDatabaseService}.
|
||||||
@@ -208,11 +217,13 @@ public abstract class AbstractOrientDatabaseService extends AbstractService
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ensureDatabaseSchema();
|
|
||||||
|
|
||||||
database.close();
|
database.close();
|
||||||
|
|
||||||
|
// check if automatic schema update is enabled
|
||||||
|
if (config.isAutomaticSchemaUpdateEnabled()) {
|
||||||
|
updateSchemas();
|
||||||
|
}
|
||||||
|
|
||||||
// cache must be large enough for all world objects, to avoid
|
// cache must be large enough for all world objects, to avoid
|
||||||
// duplication... this would endanger non-persistent states
|
// duplication... this would endanger non-persistent states
|
||||||
objectCache = cacheService.createEternalCache("database-service",
|
objectCache = cacheService.createEternalCache("database-service",
|
||||||
@@ -242,7 +253,25 @@ public abstract class AbstractOrientDatabaseService extends AbstractService
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int transaction(TransactionExecutor executor) {
|
public int transaction(TransactionExecutor executor) {
|
||||||
return executor.perform();
|
final ODatabaseDocumentTx database = ODatabaseDocumentPool.global()
|
||||||
|
.acquire(config.getUrl(), config.getUsername(),
|
||||||
|
config.getPassword());
|
||||||
|
transaction.set(database);
|
||||||
|
try {
|
||||||
|
database.begin(OTransaction.TXTYPE.OPTIMISTIC);
|
||||||
|
int returnValue = executor.perform();
|
||||||
|
database.commit();
|
||||||
|
return returnValue;
|
||||||
|
} catch (DatabaseException e) {
|
||||||
|
database.rollback();
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
database.rollback();
|
||||||
|
throw new DatabaseException(e);
|
||||||
|
} finally {
|
||||||
|
transaction.set(null);
|
||||||
|
database.close();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -267,56 +296,62 @@ public abstract class AbstractOrientDatabaseService extends AbstractService
|
|||||||
*/
|
*/
|
||||||
public <T> T query(Query<T> query) {
|
public <T> T query(Query<T> query) {
|
||||||
Preconditions.checkNotNull(query, "query");
|
Preconditions.checkNotNull(query, "query");
|
||||||
final ODatabaseDocumentTx database = ODatabaseDocumentPool.global()
|
ODatabaseDocumentTx database = transaction.get();
|
||||||
.acquire(config.getUrl(), config.getUsername(),
|
if (database == null)
|
||||||
config.getPassword());
|
database = ODatabaseDocumentPool.global().acquire(config.getUrl(),
|
||||||
|
config.getUsername(), config.getPassword());
|
||||||
log.debug("Executing query {} with {}", query, database);
|
log.debug("Executing query {} with {}", query, database);
|
||||||
try {
|
try {
|
||||||
return query.query(database, this);
|
return query.query(database, this);
|
||||||
} finally {
|
} finally {
|
||||||
database.commit();
|
if (transaction.get() == null)
|
||||||
|
database.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <M extends Model<?>, T extends RelationalPathBase<?>> void importData(
|
public <M extends Model<?>, T extends RelationalPathBase<?>> void importData(
|
||||||
java.nio.file.Path path, T entity) throws IOException {
|
java.nio.file.Path path, final T entity) throws IOException {
|
||||||
final ODatabaseDocumentTx database = ODatabaseDocumentPool.global()
|
final ODatabaseDocumentTx database = ODatabaseDocumentPool.global()
|
||||||
.acquire(config.getUrl(), config.getUsername(),
|
.acquire(config.getUrl(), config.getUsername(),
|
||||||
config.getPassword());
|
config.getPassword());
|
||||||
|
|
||||||
log.info("Importing {} to {}", path, entity);
|
log.info("Importing {} to {}", path, entity);
|
||||||
|
|
||||||
BufferedReader reader = Files.newBufferedReader(path,
|
try {
|
||||||
Charset.defaultCharset());
|
database.begin(OTransaction.TXTYPE.OPTIMISTIC);
|
||||||
final String header[] = reader.readLine().split(",");
|
CSVUtils.parseCSV(path, new CSVMapProcessor<Object>() {
|
||||||
String line;
|
@Override
|
||||||
while ((line = reader.readLine()) != null) {
|
public Object process(Map<String, String> map) {
|
||||||
final String data[] = line.split(",");
|
final ODocument document = new ODocument(database, entity
|
||||||
final ODocument document = new ODocument(database,
|
.getTableName());
|
||||||
entity.getTableName());
|
for (final Entry<String, String> entry : map.entrySet()) {
|
||||||
for (int i = 0; i < data.length; i++) {
|
document.field(entry.getKey(), entry.getValue());
|
||||||
document.field(header[i], data[i]);
|
|
||||||
}
|
}
|
||||||
database.save(document);
|
database.save(document);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
database.commit();
|
||||||
|
} catch (IOException | RuntimeException e) {
|
||||||
|
database.rollback();
|
||||||
|
throw e;
|
||||||
|
} catch (Exception e) {
|
||||||
|
database.rollback();
|
||||||
|
throw new DatabaseException(e);
|
||||||
|
} finally {
|
||||||
|
transaction.set(null);
|
||||||
|
database.close();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
@Override
|
||||||
* Makes sure the database schema is up-to-date with the external database
|
public boolean updateSchema(RelationalPath<?> table) {
|
||||||
*/
|
|
||||||
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()
|
final ODatabaseDocumentTx database = ODatabaseDocumentPool.global()
|
||||||
.acquire(config.getUrl(), config.getUsername(),
|
.acquire(config.getUrl(), config.getUsername(),
|
||||||
config.getPassword());
|
config.getPassword());
|
||||||
|
|
||||||
|
log.info("Updating {} schema definition", table);
|
||||||
|
|
||||||
boolean newSchema = false;
|
boolean newSchema = false;
|
||||||
try {
|
try {
|
||||||
final OSchema schemas = database.getMetadata().getSchema();
|
final OSchema schemas = database.getMetadata().getSchema();
|
||||||
@@ -325,7 +360,7 @@ public abstract class AbstractOrientDatabaseService extends AbstractService
|
|||||||
schema = schemas.createClass(table.getTableName());
|
schema = schemas.createClass(table.getTableName());
|
||||||
newSchema = true;
|
newSchema = true;
|
||||||
}
|
}
|
||||||
for (final Path<?> path : table.all()) {
|
for (final Path<?> path : table.getColumns()) {
|
||||||
final String name = path.getMetadata().getExpression()
|
final String name = path.getMetadata().getExpression()
|
||||||
.toString();
|
.toString();
|
||||||
OProperty property = schema.getProperty(name);
|
OProperty property = schema.getProperty(name);
|
||||||
@@ -335,10 +370,18 @@ public abstract class AbstractOrientDatabaseService extends AbstractService
|
|||||||
(path.getType().isEnum() ? OType.STRING : OType
|
(path.getType().isEnum() ? OType.STRING : OType
|
||||||
.getTypeByClass(path.getType())));
|
.getTypeByClass(path.getType())));
|
||||||
if (path.getType().isEnum()) {
|
if (path.getType().isEnum()) {
|
||||||
|
if (property.getType() != OType.STRING)
|
||||||
property.setType(OType.STRING);
|
property.setType(OType.STRING);
|
||||||
} else {
|
} else {
|
||||||
|
if (property.getType() != OType.getTypeByClass(path
|
||||||
|
.getType()))
|
||||||
property.setType(OType.getTypeByClass(path.getType()));
|
property.setType(OType.getTypeByClass(path.getType()));
|
||||||
}
|
}
|
||||||
|
final boolean nullable = QPathUtils.isNullable(path);
|
||||||
|
if (property.isNotNull() != !nullable)
|
||||||
|
property.setNotNull(!nullable);
|
||||||
|
if (property.isMandatory() != !nullable)
|
||||||
|
property.setMandatory(!nullable);
|
||||||
}
|
}
|
||||||
for (final ForeignKey<?> fk : table.getForeignKeys()) {
|
for (final ForeignKey<?> fk : table.getForeignKeys()) {
|
||||||
final String[] columns = new String[fk.getLocalColumns().size()];
|
final String[] columns = new String[fk.getLocalColumns().size()];
|
||||||
@@ -347,6 +390,7 @@ public abstract class AbstractOrientDatabaseService extends AbstractService
|
|||||||
columns[i++] = keyPath.getMetadata().getExpression()
|
columns[i++] = keyPath.getMetadata().getExpression()
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
if (!schema.areIndexed(columns))
|
||||||
schema.createIndex(StringUtils.join(columns, "-"),
|
schema.createIndex(StringUtils.join(columns, "-"),
|
||||||
INDEX_TYPE.NOTUNIQUE, columns);
|
INDEX_TYPE.NOTUNIQUE, columns);
|
||||||
}
|
}
|
||||||
@@ -358,6 +402,7 @@ public abstract class AbstractOrientDatabaseService extends AbstractService
|
|||||||
pkColumns[i++] = keyPath.getMetadata().getExpression()
|
pkColumns[i++] = keyPath.getMetadata().getExpression()
|
||||||
.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
|
if (!schema.areIndexed(pkColumns))
|
||||||
schema.createIndex("PRIMARY", INDEX_TYPE.UNIQUE, pkColumns);
|
schema.createIndex("PRIMARY", INDEX_TYPE.UNIQUE, pkColumns);
|
||||||
schemas.save();
|
schemas.save();
|
||||||
} finally {
|
} finally {
|
||||||
@@ -446,7 +491,6 @@ public abstract class AbstractOrientDatabaseService extends AbstractService
|
|||||||
private final InsertMapper<O, RI, I, E> mapper;
|
private final InsertMapper<O, RI, I, E> mapper;
|
||||||
private final Iterator<O> iterator;
|
private final Iterator<O> iterator;
|
||||||
@SuppressWarnings("unused")
|
@SuppressWarnings("unused")
|
||||||
// FIXME implement id generation
|
|
||||||
private final Path<RI> primaryKey;
|
private final Path<RI> primaryKey;
|
||||||
|
|
||||||
protected final E e;
|
protected final E e;
|
||||||
@@ -525,7 +569,7 @@ public abstract class AbstractOrientDatabaseService extends AbstractService
|
|||||||
|
|
||||||
mapper.insert(e, object, row);
|
mapper.insert(e, object, row);
|
||||||
|
|
||||||
// TODO generate ids
|
// TODO generate unique id
|
||||||
row.getDocument().save();
|
row.getDocument().save();
|
||||||
rows++;
|
rows++;
|
||||||
|
|
||||||
|
|||||||
@@ -16,16 +16,14 @@
|
|||||||
*/
|
*/
|
||||||
package com.l2jserver.service.database.sql;
|
package com.l2jserver.service.database.sql;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.nio.charset.Charset;
|
|
||||||
import java.nio.file.Files;
|
|
||||||
import java.sql.Connection;
|
import java.sql.Connection;
|
||||||
import java.sql.SQLException;
|
import java.sql.SQLException;
|
||||||
import java.sql.Statement;
|
import java.sql.Statement;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.sql.DataSource;
|
import javax.sql.DataSource;
|
||||||
@@ -68,6 +66,9 @@ import com.l2jserver.service.database.dao.UpdateMapper;
|
|||||||
import com.l2jserver.service.database.ddl.QueryFactory;
|
import com.l2jserver.service.database.ddl.QueryFactory;
|
||||||
import com.l2jserver.service.database.ddl.TableFactory;
|
import com.l2jserver.service.database.ddl.TableFactory;
|
||||||
import com.l2jserver.service.database.ddl.struct.Table;
|
import com.l2jserver.service.database.ddl.struct.Table;
|
||||||
|
import com.l2jserver.util.CSVUtils;
|
||||||
|
import com.l2jserver.util.CSVUtils.CSVMapProcessor;
|
||||||
|
import com.l2jserver.util.QPathUtils;
|
||||||
import com.l2jserver.util.factory.CollectionFactory;
|
import com.l2jserver.util.factory.CollectionFactory;
|
||||||
import com.mysema.query.sql.AbstractSQLQuery;
|
import com.mysema.query.sql.AbstractSQLQuery;
|
||||||
import com.mysema.query.sql.RelationalPath;
|
import com.mysema.query.sql.RelationalPath;
|
||||||
@@ -237,21 +238,6 @@ public abstract class AbstractSQLDatabaseService extends AbstractService
|
|||||||
@ConfigurationXPath("/configuration/services/database/jdbc/password")
|
@ConfigurationXPath("/configuration/services/database/jdbc/password")
|
||||||
void setPassword(String password);
|
void setPassword(String password);
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the update schema state
|
|
||||||
*/
|
|
||||||
@ConfigurationPropertyGetter(defaultValue = "true")
|
|
||||||
@ConfigurationXPath("/configuration/services/database/jdbc/updateSchema")
|
|
||||||
boolean getUpdateSchema();
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param updateSchema
|
|
||||||
* the new uodate schema state
|
|
||||||
*/
|
|
||||||
@ConfigurationPropertySetter
|
|
||||||
@ConfigurationXPath("/configuration/services/database/jdbc/updateSchema")
|
|
||||||
void setUpdateSchema(boolean updateSchema);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the maximum number of active connections
|
* @return the maximum number of active connections
|
||||||
*/
|
*/
|
||||||
@@ -350,18 +336,8 @@ public abstract class AbstractSQLDatabaseService extends AbstractService
|
|||||||
true);
|
true);
|
||||||
dataSource = new PoolingDataSource(connectionPool);
|
dataSource = new PoolingDataSource(connectionPool);
|
||||||
|
|
||||||
if (config.getUpdateSchema()) {
|
if (config.isAutomaticSchemaUpdateEnabled()) {
|
||||||
try {
|
updateSchemas();
|
||||||
final Connection conn = dataSource.getConnection();
|
|
||||||
try {
|
|
||||||
ensureDatabaseSchema(conn);
|
|
||||||
} finally {
|
|
||||||
conn.close();
|
|
||||||
}
|
|
||||||
} catch (Exception e) {
|
|
||||||
throw new ServiceStartException(
|
|
||||||
"Couldn't update database schema", e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (final Type<?> type : sqlTypes) {
|
for (final Type<?> type : sqlTypes) {
|
||||||
@@ -401,51 +377,6 @@ public abstract class AbstractSQLDatabaseService extends AbstractService
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Makes sure the database schema is up-to-date with the external database
|
|
||||||
*
|
|
||||||
* @param conn
|
|
||||||
* the connection to be used
|
|
||||||
* @throws SQLException
|
|
||||||
* if any {@link SQLException} occur
|
|
||||||
* @throws IOException
|
|
||||||
* if any {@link IOException} occur
|
|
||||||
*/
|
|
||||||
protected abstract void ensureDatabaseSchema(Connection conn)
|
|
||||||
throws SQLException, IOException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
*
|
|
||||||
* @param conn
|
|
||||||
* the connection to be used
|
|
||||||
* @param table
|
|
||||||
* the {@link RelationalPathBase} table
|
|
||||||
* @return <code>true</code> if a new table was created, <code>false</code>
|
|
||||||
* otherwise.
|
|
||||||
* @throws SQLException
|
|
||||||
* if any {@link SQLException} occur
|
|
||||||
*/
|
|
||||||
protected boolean updateSchema(Connection conn, RelationalPathBase<?> table)
|
|
||||||
throws SQLException {
|
|
||||||
final Table expected = TableFactory.createTable(table);
|
|
||||||
String query = null;
|
|
||||||
boolean create = false;
|
|
||||||
try {
|
|
||||||
final Table current = TableFactory.createTable(conn,
|
|
||||||
engine.getTemplate(), table.getTableName());
|
|
||||||
query = QueryFactory.alterTableQueryUpdate(expected, current,
|
|
||||||
engine.getTemplate());
|
|
||||||
} catch (SQLException e) {
|
|
||||||
// table may not exist
|
|
||||||
query = QueryFactory.createTableQuery(expected,
|
|
||||||
engine.getTemplate());
|
|
||||||
create = true;
|
|
||||||
}
|
|
||||||
if ((engine.getTemplate().supportsAlterTable() && !create) || create)
|
|
||||||
executeSQL(conn, query);
|
|
||||||
return create;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Executes the SQL code in the databases
|
* Executes the SQL code in the databases
|
||||||
*
|
*
|
||||||
@@ -462,9 +393,6 @@ public abstract class AbstractSQLDatabaseService extends AbstractService
|
|||||||
final Statement st = conn.createStatement();
|
final Statement st = conn.createStatement();
|
||||||
try {
|
try {
|
||||||
return st.execute(sql);
|
return st.execute(sql);
|
||||||
} catch (SQLException e) {
|
|
||||||
log.warn("Error exectuing query {}", sql);
|
|
||||||
throw e;
|
|
||||||
} finally {
|
} finally {
|
||||||
st.close();
|
st.close();
|
||||||
}
|
}
|
||||||
@@ -472,8 +400,8 @@ public abstract class AbstractSQLDatabaseService extends AbstractService
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <M extends Model<?>, T extends RelationalPathBase<?>> void importData(
|
public <M extends Model<?>, T extends RelationalPathBase<?>> void importData(
|
||||||
java.nio.file.Path path, T entity) throws IOException {
|
java.nio.file.Path path, final T entity) throws IOException {
|
||||||
Connection conn;
|
final Connection conn;
|
||||||
try {
|
try {
|
||||||
conn = dataSource.getConnection();
|
conn = dataSource.getConnection();
|
||||||
} catch (SQLException e) {
|
} catch (SQLException e) {
|
||||||
@@ -481,37 +409,30 @@ public abstract class AbstractSQLDatabaseService extends AbstractService
|
|||||||
}
|
}
|
||||||
log.info("Importing {} to {}", path, entity);
|
log.info("Importing {} to {}", path, entity);
|
||||||
try {
|
try {
|
||||||
BufferedReader reader = Files.newBufferedReader(path,
|
CSVUtils.parseCSV(path, new CSVMapProcessor<Long>() {
|
||||||
Charset.defaultCharset());
|
@Override
|
||||||
final String header[] = reader.readLine().split(",");
|
public Long process(final Map<String, String> map) {
|
||||||
String line;
|
|
||||||
while ((line = reader.readLine()) != null) {
|
|
||||||
final String data[] = line.split(",");
|
|
||||||
SQLInsertClause insert = engine.createSQLQueryFactory(conn)
|
SQLInsertClause insert = engine.createSQLQueryFactory(conn)
|
||||||
.insert(entity);
|
.insert(entity);
|
||||||
insert.populate(data, new Mapper<Object[]>() {
|
insert.populate(map, new Mapper<Map<String, String>>() {
|
||||||
@Override
|
@Override
|
||||||
public Map<Path<?>, Object> createMap(
|
public Map<Path<?>, Object> createMap(
|
||||||
RelationalPath<?> relationalPath, Object[] object) {
|
RelationalPath<?> relationalPath,
|
||||||
|
Map<String, String> map) {
|
||||||
final Map<Path<?>, Object> values = CollectionFactory
|
final Map<Path<?>, Object> values = CollectionFactory
|
||||||
.newMap();
|
.newMap();
|
||||||
pathFor: for (final Path<?> path : relationalPath
|
for (final Entry<String, String> entry : map
|
||||||
.getColumns()) {
|
.entrySet()) {
|
||||||
int i = 0;
|
final Path<?> path = QPathUtils.getPath(entity,
|
||||||
for (final String headerName : header) {
|
entry.getKey());
|
||||||
if (path.getMetadata().getExpression()
|
values.put(path, entry.getValue());
|
||||||
.toString().equals(headerName)) {
|
|
||||||
values.put(path, object[i]);
|
|
||||||
continue pathFor;
|
|
||||||
}
|
|
||||||
i++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return values;
|
return values;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
insert.execute();
|
return insert.execute();
|
||||||
}
|
}
|
||||||
|
});
|
||||||
} finally {
|
} finally {
|
||||||
try {
|
try {
|
||||||
conn.close();
|
conn.close();
|
||||||
@@ -520,6 +441,46 @@ public abstract class AbstractSQLDatabaseService extends AbstractService
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean updateSchema(RelationalPath<?> table) {
|
||||||
|
final Table expected = TableFactory.createTable(table);
|
||||||
|
|
||||||
|
log.info("Updating {} schema definition", table);
|
||||||
|
|
||||||
|
try {
|
||||||
|
final Connection conn = dataSource.getConnection();
|
||||||
|
try {
|
||||||
|
String query = null;
|
||||||
|
boolean create = false;
|
||||||
|
try {
|
||||||
|
final Table current = TableFactory.createTable(conn,
|
||||||
|
engine.getTemplate(), table.getTableName());
|
||||||
|
query = QueryFactory.alterTableQueryUpdate(expected,
|
||||||
|
current, engine.getTemplate());
|
||||||
|
} catch (SQLException e) {
|
||||||
|
// table may not exist
|
||||||
|
query = QueryFactory.createTableQuery(expected,
|
||||||
|
engine.getTemplate());
|
||||||
|
create = true;
|
||||||
|
}
|
||||||
|
if ((engine.getTemplate().supportsAlterTable() && !create)
|
||||||
|
|| create)
|
||||||
|
executeSQL(conn, query);
|
||||||
|
return create;
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DatabaseException(e);
|
||||||
|
} finally {
|
||||||
|
try {
|
||||||
|
conn.close();
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DatabaseException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (SQLException e) {
|
||||||
|
throw new DatabaseException(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int transaction(TransactionExecutor executor) {
|
public int transaction(TransactionExecutor executor) {
|
||||||
Preconditions.checkNotNull(executor, "executor");
|
Preconditions.checkNotNull(executor, "executor");
|
||||||
|
|||||||
202
l2jserver2-common/src/main/java/com/l2jserver/util/CSVUtils.java
Normal file
202
l2jserver2-common/src/main/java/com/l2jserver/util/CSVUtils.java
Normal file
@@ -0,0 +1,202 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import java.io.BufferedReader;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
import java.io.InputStreamReader;
|
||||||
|
import java.io.Reader;
|
||||||
|
import java.nio.charset.Charset;
|
||||||
|
import java.nio.file.Files;
|
||||||
|
import java.nio.file.Path;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.l2jserver.util.factory.CollectionFactory;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This class provices CSV parsing and other utilities
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
||||||
|
*/
|
||||||
|
public class CSVUtils {
|
||||||
|
/**
|
||||||
|
* Parses an CSV files and sends every row to be processed by the
|
||||||
|
* <code>processor</code>.
|
||||||
|
* <p>
|
||||||
|
* <h1>The parsing process</h1>
|
||||||
|
* <p>
|
||||||
|
* The parser reads the first line of the CSV and interprets it as an header
|
||||||
|
* that will be subsequently be passed to the {@link CSVProcessor} as the
|
||||||
|
* <code>header</code> {@link String String[]}.
|
||||||
|
* <p>
|
||||||
|
* Following rows are data rows and are read and passed to the
|
||||||
|
* {@link CSVProcessor} one by one until the end of file.
|
||||||
|
*
|
||||||
|
* @param <R>
|
||||||
|
* the processor return type
|
||||||
|
*
|
||||||
|
* @param reader
|
||||||
|
* the CSV {@link BufferedReader}
|
||||||
|
* @param processor
|
||||||
|
* the processor
|
||||||
|
* @return all processed rows
|
||||||
|
* @throws IOException
|
||||||
|
* if any exception occur while parsing
|
||||||
|
*/
|
||||||
|
public static <R> List<R> parseCSV(BufferedReader reader,
|
||||||
|
CSVProcessor<R> processor) throws IOException {
|
||||||
|
final List<R> results = CollectionFactory.newList();
|
||||||
|
final String header[] = reader.readLine().split(",");
|
||||||
|
String line;
|
||||||
|
while ((line = reader.readLine()) != null) {
|
||||||
|
results.add(processor.process(header, line.split(",")));
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an CSV files and sends every row to be processed by the
|
||||||
|
* <code>processor</code>
|
||||||
|
*
|
||||||
|
* @param <R>
|
||||||
|
* the processor return type
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* the CSV file path
|
||||||
|
* @param processor
|
||||||
|
* the processor
|
||||||
|
* @return all processed rows
|
||||||
|
* @throws IOException
|
||||||
|
* if any exception occur while parsing
|
||||||
|
* @see #parseCSV(BufferedReader, CSVProcessor)
|
||||||
|
* <code>parseCSV(BufferedReader, CSVProcessor)</code> for more details
|
||||||
|
* about how the parsing works
|
||||||
|
*/
|
||||||
|
public static <R> List<R> parseCSV(Path path, CSVProcessor<R> processor)
|
||||||
|
throws IOException {
|
||||||
|
return parseCSV(
|
||||||
|
Files.newBufferedReader(path, Charset.defaultCharset()),
|
||||||
|
processor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an CSV files and sends every row to be processed by the
|
||||||
|
* <code>processor</code>
|
||||||
|
*
|
||||||
|
* @param <R>
|
||||||
|
* the processor return type
|
||||||
|
*
|
||||||
|
* @param file
|
||||||
|
* the CSV file
|
||||||
|
* @param processor
|
||||||
|
* the processor
|
||||||
|
* @return all processed rows
|
||||||
|
* @throws IOException
|
||||||
|
* if any exception occur while parsing
|
||||||
|
*/
|
||||||
|
public static <R> List<R> parseCSV(File file, CSVProcessor<R> processor)
|
||||||
|
throws IOException {
|
||||||
|
return parseCSV(file.toPath(), processor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an CSV files and sends every row to be processed by the
|
||||||
|
* <code>processor</code>
|
||||||
|
*
|
||||||
|
* @param <R>
|
||||||
|
* the processor return type
|
||||||
|
*
|
||||||
|
* @param in
|
||||||
|
* the CSV {@link InputStream}
|
||||||
|
* @param processor
|
||||||
|
* the processor
|
||||||
|
* @return all processed rows
|
||||||
|
* @throws IOException
|
||||||
|
* if any exception occur while parsing
|
||||||
|
*/
|
||||||
|
public static <R> List<R> parseCSV(InputStream in, CSVProcessor<R> processor)
|
||||||
|
throws IOException {
|
||||||
|
return parseCSV(new BufferedReader(new InputStreamReader(in)),
|
||||||
|
processor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses an CSV files and sends every row to be processed by the
|
||||||
|
* <code>processor</code>
|
||||||
|
*
|
||||||
|
* @param <R>
|
||||||
|
* the processor return type
|
||||||
|
*
|
||||||
|
* @param reader
|
||||||
|
* the CSV {@link Reader}
|
||||||
|
* @param processor
|
||||||
|
* the processor
|
||||||
|
* @return all processed rows
|
||||||
|
* @throws IOException
|
||||||
|
* if any exception occur while parsing
|
||||||
|
*/
|
||||||
|
public static <R> List<R> parseCSV(Reader reader, CSVProcessor<R> processor)
|
||||||
|
throws IOException {
|
||||||
|
return parseCSV(new BufferedReader(reader), processor);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Processes an single row in an CSV files and returns an processed object.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
||||||
|
* @param <R>
|
||||||
|
* the processed object type
|
||||||
|
*/
|
||||||
|
public interface CSVProcessor<R> {
|
||||||
|
/**
|
||||||
|
* Processes the current row and returns the processed object
|
||||||
|
*
|
||||||
|
* @param header
|
||||||
|
* the CSV header (first row)
|
||||||
|
* @param data
|
||||||
|
* the CSV data
|
||||||
|
* @return the processed object
|
||||||
|
*/
|
||||||
|
R process(String[] header, String[] data);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This {@link CSVProcessor} implementations act as an adapter to the String
|
||||||
|
* arrays, providing access to the rows in a more object oriented fashing,
|
||||||
|
* through a {@link Map}.
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
||||||
|
*
|
||||||
|
* @param <R>
|
||||||
|
* the processed object type
|
||||||
|
*/
|
||||||
|
public static abstract class CSVMapProcessor<R> implements CSVProcessor<R> {
|
||||||
|
@Override
|
||||||
|
public final R process(String[] header, String[] data) {
|
||||||
|
final Map<String, String> map = CollectionFactory.newMap();
|
||||||
|
for (int i = 0; i < header.length; i++) {
|
||||||
|
map.put(header[i], data[i]);
|
||||||
|
}
|
||||||
|
return process(map);
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract R process(Map<String, String> map);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,173 @@
|
|||||||
|
/*
|
||||||
|
* 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.util;
|
||||||
|
|
||||||
|
import java.lang.reflect.Field;
|
||||||
|
|
||||||
|
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.util.transformer.Transformer;
|
||||||
|
import com.l2jserver.util.transformer.TransformerFactory;
|
||||||
|
import com.mysema.query.sql.RelationalPath;
|
||||||
|
import com.mysema.query.types.Path;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Class serving several utilities for {@link Path} and {@link RelationalPath}
|
||||||
|
*
|
||||||
|
* @author <a href="http://www.rogiel.com">Rogiel</a>
|
||||||
|
*/
|
||||||
|
public class QPathUtils {
|
||||||
|
/**
|
||||||
|
* Returns the {@link Path} represented by <code>name</code> within the
|
||||||
|
* given {@link RelationalPath}
|
||||||
|
*
|
||||||
|
* @param relationalPath
|
||||||
|
* the {@link RelationalPath}
|
||||||
|
* @param name
|
||||||
|
* the {@link Path} name
|
||||||
|
* @return the {@link Path} instance, if existent
|
||||||
|
*/
|
||||||
|
public static Path<?> getPath(RelationalPath<?> relationalPath, String name) {
|
||||||
|
for (final Path<?> path : relationalPath.getColumns()) {
|
||||||
|
if (path.getMetadata().getExpression().toString().equals(name))
|
||||||
|
return path;
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param path
|
||||||
|
* the {@link Path} to be searched in its root entity
|
||||||
|
* @return the {@link Field} holding <code>path</code>
|
||||||
|
*/
|
||||||
|
public static Field getReflectionField(Path<?> path) {
|
||||||
|
return ClassUtils.getFieldWithValue(path.getMetadata().getParent(), path);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param relationalPath
|
||||||
|
* the {@link RelationalPath}
|
||||||
|
* @param pathName
|
||||||
|
* the {@link Path} name
|
||||||
|
* @return the {@link Field} holding <code>pathName</code>
|
||||||
|
* @see #getPath(RelationalPath, String)
|
||||||
|
*/
|
||||||
|
public static Field getRefelctionField(RelationalPath<?> relationalPath,
|
||||||
|
String pathName) {
|
||||||
|
return ClassUtils.getFieldWithValue(relationalPath,
|
||||||
|
getPath(relationalPath, pathName));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param path
|
||||||
|
* the {@link Path}
|
||||||
|
* @return the column maximum size
|
||||||
|
*/
|
||||||
|
public static int getColumnSize(Path<?> path) {
|
||||||
|
final Field field = getReflectionField(path);
|
||||||
|
if(field == null)
|
||||||
|
return 0;
|
||||||
|
final ColumnSize size = field.getAnnotation(ColumnSize.class);
|
||||||
|
if (size != null)
|
||||||
|
return size.value();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param path
|
||||||
|
* the {@link Path}
|
||||||
|
* @return whether the column is auto incrementable or not
|
||||||
|
*/
|
||||||
|
public static boolean isAutoIncrementable(Path<?> path) {
|
||||||
|
final Field field = getReflectionField(path);
|
||||||
|
if(field == null)
|
||||||
|
return false;
|
||||||
|
final ColumnAutoIncrement autoInc = field
|
||||||
|
.getAnnotation(ColumnAutoIncrement.class);
|
||||||
|
if (autoInc != null)
|
||||||
|
return true;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param path
|
||||||
|
* the {@link Path}
|
||||||
|
* @return whether the column can be <code>null</code>
|
||||||
|
*/
|
||||||
|
public static boolean isNullable(Path<?> path) {
|
||||||
|
final Field field = getReflectionField(path);
|
||||||
|
if(field == null)
|
||||||
|
return false;
|
||||||
|
final ColumnNullable nullable = field
|
||||||
|
.getAnnotation(ColumnNullable.class);
|
||||||
|
if (nullable == null) {
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param path
|
||||||
|
* the {@link Path}
|
||||||
|
* @return <code>true</code> if the column has an default value
|
||||||
|
*/
|
||||||
|
public static boolean hasDefaultValue(Path<?> path) {
|
||||||
|
final Field field = getReflectionField(path);
|
||||||
|
if(field == null)
|
||||||
|
return false;
|
||||||
|
final ColumnDefault def = field.getAnnotation(ColumnDefault.class);
|
||||||
|
if (def == null) {
|
||||||
|
return false;
|
||||||
|
} else {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param path
|
||||||
|
* the {@link Path}
|
||||||
|
* @return the column default value
|
||||||
|
*/
|
||||||
|
public static String getDefaultUntransformedValue(Path<?> path) {
|
||||||
|
final Field field = getReflectionField(path);
|
||||||
|
if(field == null)
|
||||||
|
return null;
|
||||||
|
final ColumnDefault def = field.getAnnotation(ColumnDefault.class);
|
||||||
|
if (def != null)
|
||||||
|
return def.value();
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param <T>
|
||||||
|
* the column return type
|
||||||
|
* @param path
|
||||||
|
* the {@link Path}
|
||||||
|
* @return the column default value (already transformed)
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <T> T getDefaultValue(Path<T> path) {
|
||||||
|
@SuppressWarnings("rawtypes")
|
||||||
|
final Transformer transformer = TransformerFactory.getTransfromer(path
|
||||||
|
.getType());
|
||||||
|
return (T) transformer.untransform(path.getType(),
|
||||||
|
getDefaultUntransformedValue(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -19,11 +19,22 @@
|
|||||||
not sure on the usage of any parameter, read the "Configuration" section
|
not sure on the usage of any parameter, read the "Configuration" section
|
||||||
in wiki article about DatabaseService. -->
|
in wiki article about DatabaseService. -->
|
||||||
<database>
|
<database>
|
||||||
|
<!-- Whether or not the service should try to update and create missing
|
||||||
|
tables at startup. This should be disabled after the first server start as
|
||||||
|
it could cause data corruption. -->
|
||||||
|
<automaticSchemaUpdate>true</automaticSchemaUpdate>
|
||||||
|
|
||||||
|
<!-- Configures OrientDB engine - plug-and-play -->
|
||||||
<orientdb>
|
<orientdb>
|
||||||
|
<!-- The OrientDB storage location -->
|
||||||
<url>local:data/database</url>
|
<url>local:data/database</url>
|
||||||
|
<!-- The OrientDB username (local storage must use "admin") -->
|
||||||
<username>admin</username>
|
<username>admin</username>
|
||||||
|
<!-- The OrientDB password (local storage must use "admin") -->
|
||||||
<password>admin</password>
|
<password>admin</password>
|
||||||
</orientdb>
|
</orientdb>
|
||||||
|
|
||||||
|
<!-- Configures JDBC engine - requires database server configuration -->
|
||||||
<jdbc>
|
<jdbc>
|
||||||
<!-- Defines the connection URL used by JDBC to connect to the database. -->
|
<!-- Defines the connection URL used by JDBC to connect to the database. -->
|
||||||
<url>jdbc:mysql://localhost/l2jserver2</url>
|
<url>jdbc:mysql://localhost/l2jserver2</url>
|
||||||
@@ -32,11 +43,6 @@
|
|||||||
<engine>com.l2jserver.service.database.sql.MySQLDatabaseEngine
|
<engine>com.l2jserver.service.database.sql.MySQLDatabaseEngine
|
||||||
</engine>
|
</engine>
|
||||||
|
|
||||||
<!-- Whether or not the service should try to update and create missing
|
|
||||||
tables at startup. This should be disabled after the first server start as
|
|
||||||
it could cause data corruption. -->
|
|
||||||
<updateSchema>true</updateSchema>
|
|
||||||
|
|
||||||
<!-- The username used to login into the database. NOTE: Try not use
|
<!-- The username used to login into the database. NOTE: Try not use
|
||||||
"root" in production servers for security reasons. -->
|
"root" in production servers for security reasons. -->
|
||||||
<username>l2j</username>
|
<username>l2j</username>
|
||||||
|
|||||||
@@ -19,6 +19,22 @@
|
|||||||
not sure on the usage of any parameter, read the "Configuration" section
|
not sure on the usage of any parameter, read the "Configuration" section
|
||||||
in wiki article about DatabaseService. -->
|
in wiki article about DatabaseService. -->
|
||||||
<database>
|
<database>
|
||||||
|
<!-- Whether or not the service should try to update and create missing
|
||||||
|
tables at startup. This should be disabled after the first server start as
|
||||||
|
it could cause data corruption. -->
|
||||||
|
<automaticSchemaUpdate>true</automaticSchemaUpdate>
|
||||||
|
|
||||||
|
<!-- Configures OrientDB engine - plug-and-play -->
|
||||||
|
<orientdb>
|
||||||
|
<!-- The OrientDB storage location -->
|
||||||
|
<url>local:data/database</url>
|
||||||
|
<!-- The OrientDB username (local storage must use "admin") -->
|
||||||
|
<username>admin</username>
|
||||||
|
<!-- The OrientDB password (local storage must use "admin") -->
|
||||||
|
<password>admin</password>
|
||||||
|
</orientdb>
|
||||||
|
|
||||||
|
<!-- Configures JDBC engine - requires database server configuration -->
|
||||||
<jdbc>
|
<jdbc>
|
||||||
<!-- Defines the connection URL used by JDBC to connect to the database. -->
|
<!-- Defines the connection URL used by JDBC to connect to the database. -->
|
||||||
<url>jdbc:mysql://localhost/l2jserver2</url>
|
<url>jdbc:mysql://localhost/l2jserver2</url>
|
||||||
@@ -27,11 +43,6 @@
|
|||||||
<engine>com.l2jserver.service.database.sql.MySQLDatabaseEngine
|
<engine>com.l2jserver.service.database.sql.MySQLDatabaseEngine
|
||||||
</engine>
|
</engine>
|
||||||
|
|
||||||
<!-- Whether or not the service should try to update and create missing
|
|
||||||
tables at startup. This should be disabled after the first server start as
|
|
||||||
it could cause data corruption. -->
|
|
||||||
<updateSchema>true</updateSchema>
|
|
||||||
|
|
||||||
<!-- The username used to login into the database. NOTE: Try not use
|
<!-- The username used to login into the database. NOTE: Try not use
|
||||||
"root" in production servers for security reasons. -->
|
"root" in production servers for security reasons. -->
|
||||||
<username>l2j</username>
|
<username>l2j</username>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ import com.google.inject.AbstractModule;
|
|||||||
import com.google.inject.Module;
|
import com.google.inject.Module;
|
||||||
import com.l2jserver.model.id.provider.IDProviderModule;
|
import com.l2jserver.model.id.provider.IDProviderModule;
|
||||||
import com.l2jserver.service.ServiceModule;
|
import com.l2jserver.service.ServiceModule;
|
||||||
import com.l2jserver.service.database.JDBCDAOModule;
|
import com.l2jserver.service.database.OrientDBDAOModule;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The game server Google Guice {@link Module}.
|
* The game server Google Guice {@link Module}.
|
||||||
@@ -32,6 +32,6 @@ public class GameServerModule extends AbstractModule {
|
|||||||
protected void configure() {
|
protected void configure() {
|
||||||
install(new ServiceModule());
|
install(new ServiceModule());
|
||||||
install(new IDProviderModule());
|
install(new IDProviderModule());
|
||||||
install(new JDBCDAOModule());
|
install(new OrientDBDAOModule());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import com.l2jserver.service.core.threading.ThreadServiceImpl;
|
|||||||
import com.l2jserver.service.core.vfs.Java7VFSService;
|
import com.l2jserver.service.core.vfs.Java7VFSService;
|
||||||
import com.l2jserver.service.core.vfs.VFSService;
|
import com.l2jserver.service.core.vfs.VFSService;
|
||||||
import com.l2jserver.service.database.DatabaseService;
|
import com.l2jserver.service.database.DatabaseService;
|
||||||
import com.l2jserver.service.database.GameServerJDBCDatabaseService;
|
import com.l2jserver.service.database.GameServerOrientDatabaseService;
|
||||||
import com.l2jserver.service.game.AttackService;
|
import com.l2jserver.service.game.AttackService;
|
||||||
import com.l2jserver.service.game.AttackServiceImpl;
|
import com.l2jserver.service.game.AttackServiceImpl;
|
||||||
import com.l2jserver.service.game.admin.AdministratorService;
|
import com.l2jserver.service.game.admin.AdministratorService;
|
||||||
@@ -89,7 +89,7 @@ public class ServiceModule extends AbstractModule {
|
|||||||
bind(CacheService.class).to(SoftCacheService.class)
|
bind(CacheService.class).to(SoftCacheService.class)
|
||||||
.in(Scopes.SINGLETON);
|
.in(Scopes.SINGLETON);
|
||||||
|
|
||||||
bind(DatabaseService.class).to(GameServerJDBCDatabaseService.class)
|
bind(DatabaseService.class).to(GameServerOrientDatabaseService.class)
|
||||||
.in(Scopes.SINGLETON);
|
.in(Scopes.SINGLETON);
|
||||||
bind(WorldIDService.class).to(CachedWorldIDService.class).in(
|
bind(WorldIDService.class).to(CachedWorldIDService.class).in(
|
||||||
Scopes.SINGLETON);
|
Scopes.SINGLETON);
|
||||||
|
|||||||
@@ -17,8 +17,6 @@
|
|||||||
package com.l2jserver.service.database;
|
package com.l2jserver.service.database;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import com.google.inject.Inject;
|
import com.google.inject.Inject;
|
||||||
import com.l2jserver.model.game.CharacterShortcut.ShortcutType;
|
import com.l2jserver.model.game.CharacterShortcut.ShortcutType;
|
||||||
@@ -112,17 +110,20 @@ public class GameServerJDBCDatabaseService extends AbstractSQLDatabaseService
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void ensureDatabaseSchema(Connection conn) throws SQLException,
|
public void updateSchemas() {
|
||||||
IOException {
|
updateSchema(QActorSkill.actorSkill);
|
||||||
updateSchema(conn, QActorSkill.actorSkill);
|
updateSchema(QCharacter.character);
|
||||||
updateSchema(conn, QCharacter.character);
|
updateSchema(QCharacterFriend.characterFriend);
|
||||||
updateSchema(conn, QCharacterFriend.characterFriend);
|
updateSchema(QCharacterShortcut.characterShortcut);
|
||||||
updateSchema(conn, QCharacterShortcut.characterShortcut);
|
updateSchema(QClan.clan);
|
||||||
updateSchema(conn, QClan.clan);
|
updateSchema(QItem.item);
|
||||||
updateSchema(conn, QItem.item);
|
updateSchema(QLogChat.logChat);
|
||||||
updateSchema(conn, QLogChat.logChat);
|
if (updateSchema(QNPC.npc)) {
|
||||||
if (updateSchema(conn, QNPC.npc)) {
|
try {
|
||||||
importData(vfsService.resolve("data/static/npc.csv"), QNPC.npc);
|
importData(vfsService.resolve("data/static/npc.csv"), QNPC.npc);
|
||||||
|
} catch (IOException e) {
|
||||||
|
throw new DatabaseException(e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -82,20 +82,19 @@ public class GameServerOrientDatabaseService extends
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void ensureDatabaseSchema() {
|
public void updateSchemas() {
|
||||||
createSchema(QActorSkill.actorSkill);
|
updateSchema(QActorSkill.actorSkill);
|
||||||
createSchema(QCharacter.character);
|
updateSchema(QCharacter.character);
|
||||||
createSchema(QCharacterFriend.characterFriend);
|
updateSchema(QCharacterFriend.characterFriend);
|
||||||
createSchema(QCharacterShortcut.characterShortcut);
|
updateSchema(QCharacterShortcut.characterShortcut);
|
||||||
createSchema(QClan.clan);
|
updateSchema(QClan.clan);
|
||||||
createSchema(QItem.item);
|
updateSchema(QItem.item);
|
||||||
createSchema(QLogChat.logChat);
|
updateSchema(QLogChat.logChat);
|
||||||
if (createSchema(QNPC.npc)) {
|
if (updateSchema(QNPC.npc)) {
|
||||||
try {
|
try {
|
||||||
importData(Paths.get("data/static/npc.csv"), QNPC.npc);
|
importData(Paths.get("data/static/npc.csv"), QNPC.npc);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
// TODO Auto-generated catch block
|
throw new DatabaseException(e);
|
||||||
e.printStackTrace();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,15 +23,14 @@ public class QActorSkill extends RelationalPathBase<Skill> {
|
|||||||
@ColumnSize(10)
|
@ColumnSize(10)
|
||||||
public final NumberPath<Integer> actorId = createNumber("actor_id",
|
public final NumberPath<Integer> actorId = createNumber("actor_id",
|
||||||
Integer.class);
|
Integer.class);
|
||||||
|
@ColumnSize(6)
|
||||||
|
public final NumberPath<Integer> skillId = createNumber("skill_id",
|
||||||
|
Integer.class);
|
||||||
|
|
||||||
@ColumnSize(4)
|
@ColumnSize(4)
|
||||||
public final NumberPath<Integer> level = createNumber("level",
|
public final NumberPath<Integer> level = createNumber("level",
|
||||||
Integer.class);
|
Integer.class);
|
||||||
|
|
||||||
@ColumnSize(6)
|
|
||||||
public final NumberPath<Integer> skillId = createNumber("skill_id",
|
|
||||||
Integer.class);
|
|
||||||
|
|
||||||
public final PrimaryKey<Skill> primary = createPrimaryKey(actorId, skillId);
|
public final PrimaryKey<Skill> primary = createPrimaryKey(actorId, skillId);
|
||||||
|
|
||||||
public QActorSkill(String variable) {
|
public QActorSkill(String variable) {
|
||||||
|
|||||||
@@ -16,21 +16,13 @@
|
|||||||
*/
|
*/
|
||||||
package com.l2jserver.service.database;
|
package com.l2jserver.service.database;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.nio.file.Path;
|
|
||||||
import java.sql.Connection;
|
|
||||||
import java.sql.SQLException;
|
|
||||||
|
|
||||||
import com.l2jserver.model.Model;
|
|
||||||
import com.l2jserver.service.AbstractService.Depends;
|
import com.l2jserver.service.AbstractService.Depends;
|
||||||
import com.l2jserver.service.cache.CacheService;
|
import com.l2jserver.service.cache.CacheService;
|
||||||
import com.l2jserver.service.configuration.ConfigurationService;
|
import com.l2jserver.service.configuration.ConfigurationService;
|
||||||
import com.l2jserver.service.core.LoggingService;
|
import com.l2jserver.service.core.LoggingService;
|
||||||
import com.l2jserver.service.core.threading.ThreadService;
|
import com.l2jserver.service.core.threading.ThreadService;
|
||||||
import com.l2jserver.service.core.vfs.VFSService;
|
import com.l2jserver.service.core.vfs.VFSService;
|
||||||
import com.l2jserver.service.database.dao.SelectMapper;
|
|
||||||
import com.l2jserver.service.database.sql.AbstractSQLDatabaseService;
|
import com.l2jserver.service.database.sql.AbstractSQLDatabaseService;
|
||||||
import com.mysema.query.sql.RelationalPathBase;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This is an implementation of {@link DatabaseService} that provides an layer
|
* This is an implementation of {@link DatabaseService} that provides an layer
|
||||||
@@ -80,6 +72,6 @@ public class LoginServerSQLDatabaseService extends AbstractSQLDatabaseService
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void ensureDatabaseSchema(Connection conn) throws SQLException {
|
public void updateSchemas() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user