> createQuery(
+ ODatabaseDocumentTx database);
+
+ /**
+ * Return the mapper that will bind {@link ResultSet} objects into an
+ * T object instance. The mapper will need to create the object
+ * instance.
+ *
+ * Note: This method will be called for each row, an thus is a
+ * good idea to create a new instance on each call!
+ *
+ * @return the mapper instance
+ */
+ protected abstract Mapper mapper();
+ }
+
+ /**
+ * An select query that returns a single object of type T
+ *
+ * @author Rogiel
+ *
+ * @param
+ * the query return type
+ */
+ public static abstract class SelectSingleQuery implements Query {
+ /**
+ * The logger
+ */
+ private final Logger log = LoggerFactory
+ .getLogger(SelectSingleQuery.class);
+
+ @Override
+ public T query(ODatabaseDocumentTx database) throws SQLException {
+ Preconditions.checkNotNull(database, "database");
+
+ log.debug("Starting SELECT single query execution");
+
+ List result = database.query(createQuery(database));
+ final Mapper mapper = mapper();
+ log.debug("Database returned {}", result);
+ for (final ODocument document : result) {
+ log.debug("Mapping row with {}", mapper);
+ final T obj = mapper.map(document);
+ if (obj == null) {
+ log.debug("Mapper {} returned a null row", mapper);
+ continue;
+ }
+ if (obj instanceof Model)
+ ((Model>) obj).setObjectDesire(ObjectDesire.NONE);
+ log.debug("Mapper {} returned {}", mapper, obj);
+ return obj;
+ }
+ return null;
+ }
+
+ /**
+ * Creates the prepared query for execution
+ *
+ * @param database
+ * the database
+ *
+ * @return the prepared query
+ */
+ protected abstract ONativeSynchQuery> createQuery(
+ ODatabaseDocumentTx database);
+
+ /**
+ * Return the mapper that will bind {@link ResultSet} objects into an
+ * T object instance. The mapper will need to create the object
+ * instance.
+ *
+ * Note: This method will be called for each row, an thus is a
+ * good idea to create a new instance on each call!
+ *
+ * @return the mapper instance
+ */
+ protected abstract Mapper mapper();
+ }
+
+ /**
+ * The {@link Mapper} maps an {@link ResultSet} into an object T
+ *
+ * @author Rogiel
+ *
+ * @param
+ * the object type
+ */
+ public interface Mapper {
+ /**
+ * Map the result set value into an object.
+ *
+ * Note: it is required to call {@link ResultSet#next()}, since
+ * it is called by the {@link Query}.
+ *
+ * @param document
+ * the resulted document
+ * @return the created instance
+ * @throws SQLException
+ * if any SQL error occur
+ */
+ T map(ODocument document) throws SQLException;
+ }
+
+ /**
+ * The cached mapper will try to lookup the result in the cache, before
+ * create a new instance. If the instance is not found in the cache, then
+ * the {@link Mapper} implementation is called to create the object. Note
+ * that the ID, used for the cache lookup, will be reused. After creation,
+ * the cache is updated.
+ *
+ * @author Rogiel
+ *
+ * @param
+ * the object type
+ * @param
+ * the id type
+ */
+ public abstract static class CachedMapper, I extends ID>>
+ implements Mapper {
+ /**
+ * The logger
+ */
+ private final Logger log = LoggerFactory
+ .getLogger(SelectSingleQuery.class);
+
+ /**
+ * The database service instance
+ */
+ private final OrientDatabaseService database;
+
+ /**
+ * The {@link ID} mapper
+ */
+ private final Mapper idMapper;
+
+ /**
+ * Creates a new instance
+ *
+ * @param database
+ * the database service
+ * @param idMapper
+ * the {@link ID} {@link Mapper}
+ */
+ public CachedMapper(OrientDatabaseService database, Mapper idMapper) {
+ this.database = database;
+ this.idMapper = idMapper;
+ }
+
+ @Override
+ @SuppressWarnings("unchecked")
+ public final T map(ODocument document) throws SQLException {
+ log.debug("Mapping row {} ID with {}", document, idMapper);
+ final I id = idMapper.map(document);
+ Preconditions.checkNotNull(id, "id");
+
+ log.debug("ID={}, locating cached object", id);
+
+ if (database.hasCachedObject(id))
+ return (T) database.getCachedObject(id);
+
+ log.debug("Cached object not found, creating...");
+
+ final T object = map(id, document);
+ if (object != null)
+ database.updateCache(id, object);
+ log.debug("Object {} created", object);
+ return object;
+ }
+
+ /**
+ * Maps an uncached object. Once mapping is complete, it will be added
+ * to the cache.
+ *
+ * @param id
+ * the object id
+ * @param document
+ * the document result
+ * @return the created object
+ * @throws SQLException
+ * if any SQL error occur
+ */
+ protected abstract T map(I id, ODocument document) throws SQLException;
+ }
+}
diff --git a/src/main/java/com/l2jserver/service/database/orientdb/AbstractOrientDBDAO.java b/src/main/java/com/l2jserver/service/database/orientdb/AbstractOrientDBDAO.java
new file mode 100644
index 000000000..9d3f76979
--- /dev/null
+++ b/src/main/java/com/l2jserver/service/database/orientdb/AbstractOrientDBDAO.java
@@ -0,0 +1,44 @@
+/*
+ * This file is part of l2jserver .
+ *
+ * l2jserver 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.
+ *
+ * l2jserver 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 l2jserver. If not, see .
+ */
+package com.l2jserver.service.database.orientdb;
+
+import com.l2jserver.model.Model;
+import com.l2jserver.model.id.ID;
+import com.l2jserver.service.database.AbstractDAO;
+import com.l2jserver.service.database.DatabaseService;
+import com.l2jserver.service.database.OrientDatabaseService;
+
+/**
+ * @author Rogiel
+ * @param
+ * the model type
+ * @param
+ * the id type
+ */
+public abstract class AbstractOrientDBDAO, I extends ID>>
+ extends AbstractDAO {
+ protected final OrientDatabaseService database;
+
+ /**
+ * @param database
+ * the database instance
+ */
+ protected AbstractOrientDBDAO(DatabaseService database) {
+ super(database);
+ this.database = (OrientDatabaseService) database;
+ }
+}
diff --git a/src/main/java/com/l2jserver/service/database/orientdb/OrientDBCharacterDAO.java b/src/main/java/com/l2jserver/service/database/orientdb/OrientDBCharacterDAO.java
new file mode 100644
index 000000000..e4ba5eedc
--- /dev/null
+++ b/src/main/java/com/l2jserver/service/database/orientdb/OrientDBCharacterDAO.java
@@ -0,0 +1,465 @@
+/*
+ * This file is part of l2jserver .
+ *
+ * l2jserver 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.
+ *
+ * l2jserver 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 l2jserver. If not, see .
+ */
+package com.l2jserver.service.database.orientdb;
+
+import java.sql.SQLException;
+import java.util.Collection;
+import java.util.List;
+
+import com.google.inject.Inject;
+import com.l2jserver.model.dao.CharacterDAO;
+import com.l2jserver.model.id.AccountID;
+import com.l2jserver.model.id.object.CharacterID;
+import com.l2jserver.model.id.object.ClanID;
+import com.l2jserver.model.id.object.provider.CharacterIDProvider;
+import com.l2jserver.model.id.object.provider.ClanIDProvider;
+import com.l2jserver.model.id.provider.AccountIDProvider;
+import com.l2jserver.model.id.template.CharacterTemplateID;
+import com.l2jserver.model.id.template.provider.CharacterTemplateIDProvider;
+import com.l2jserver.model.template.CharacterTemplate;
+import com.l2jserver.model.template.actor.ActorSex;
+import com.l2jserver.model.template.character.CharacterClass;
+import com.l2jserver.model.template.character.CharacterRace;
+import com.l2jserver.model.world.Clan;
+import com.l2jserver.model.world.L2Character;
+import com.l2jserver.model.world.character.CharacterAppearance;
+import com.l2jserver.model.world.character.CharacterAppearance.CharacterFace;
+import com.l2jserver.model.world.character.CharacterAppearance.CharacterHairColor;
+import com.l2jserver.model.world.character.CharacterAppearance.CharacterHairStyle;
+import com.l2jserver.service.database.DatabaseService;
+import com.l2jserver.service.database.OrientDatabaseService.CachedMapper;
+import com.l2jserver.service.database.OrientDatabaseService.InsertUpdateQuery;
+import com.l2jserver.service.database.OrientDatabaseService.Mapper;
+import com.l2jserver.service.database.OrientDatabaseService.SelectListQuery;
+import com.l2jserver.service.database.OrientDatabaseService.SelectSingleQuery;
+import com.l2jserver.util.geometry.Point3D;
+import com.orientechnologies.orient.core.db.document.ODatabaseDocumentTx;
+import com.orientechnologies.orient.core.query.nativ.ONativeSynchQuery;
+import com.orientechnologies.orient.core.query.nativ.OQueryContextNativeSchema;
+import com.orientechnologies.orient.core.record.impl.ODocument;
+
+/**
+ * @author Rogiel
+ *
+ */
+public class OrientDBCharacterDAO extends
+ AbstractOrientDBDAO implements CharacterDAO {
+ /**
+ * The {@link CharacterID} factory
+ */
+ private final CharacterIDProvider idFactory;
+ /**
+ * The {@link CharacterTemplateID} factory
+ */
+ private final CharacterTemplateIDProvider templateIdFactory;
+ /**
+ * The {@link AccountID} factory
+ */
+ private final AccountIDProvider accountIdFactory;
+ /**
+ * The {@link ClanID} factory
+ */
+ private final ClanIDProvider clanIdFactory;
+
+ /**
+ * Character table name
+ */
+ public static final String CLASS_NAME = L2Character.class.getSimpleName();
+ // FIELDS
+ public static final String CHAR_ID = "character_id";
+ public static final String ACCOUNT_ID = "account_id";
+ public static final String CLAN_ID = "clan_id";
+ public static final String NAME = "name";
+
+ public static final String RACE = "race";
+ public static final String CLASS = "class";
+ public static final String SEX = "sex";
+
+ public static final String LEVEL = "level";
+ public static final String EXPERIENCE = "experience";
+ public static final String SP = "sp";
+
+ public static final String HP = "hp";
+ public static final String MP = "mp";
+ public static final String CP = "cp";
+
+ public static final String POINT_X = "point_x";
+ public static final String POINT_Y = "point_y";
+ public static final String POINT_Z = "point_z";
+ public static final String POINT_ANGLE = "point_angle";
+
+ public static final String APPEARANCE_HAIR_STYLE = "appearance_hair_style";
+ public static final String APPEARANCE_HAIR_COLOR = "appearance_hair_color";
+ public static final String APPEARANCE_FACE = "apperance_face";
+
+ /**
+ * The mapper for {@link CharacterID}
+ */
+ private final Mapper idMapper = new Mapper() {
+ @Override
+ public CharacterID map(ODocument document) throws SQLException {
+ return idFactory.resolveID((Integer) document.field(CHAR_ID));
+ }
+ };
+
+ /**
+ * The {@link Mapper} for {@link L2Character}
+ */
+ private final Mapper mapper = new CachedMapper(
+ database, idMapper) {
+ @Override
+ protected L2Character map(CharacterID id, ODocument document)
+ throws SQLException {
+ final CharacterClass charClass = (CharacterClass) document
+ .field(CLASS);
+ final CharacterTemplateID templateId = templateIdFactory
+ .resolveID(charClass.id);
+ final CharacterTemplate template = templateId.getTemplate();
+
+ final L2Character character = template.create();
+
+ character.setID(id);
+ character.setAccountID(accountIdFactory.resolveID((String) document
+ .field(ACCOUNT_ID)));
+ if (document.containsField(CLAN_ID))
+ character.setClanID(clanIdFactory.resolveID((Integer) document
+ .field(CLAN_ID)));
+
+ character.setName((String) document.field(NAME));
+
+ character.setRace((CharacterRace) document.field(RACE));
+ character.setCharacterClass((CharacterClass) document.field(CLASS));
+ character.setSex((ActorSex) document.field(SEX));
+
+ character.setLevel((Integer) document.field(LEVEL));
+ character.setExperience((Long) document.field(EXPERIENCE));
+ character.setSP((Integer) document.field(SP));
+
+ character.setHP((Double) document.field(HP));
+ character.setMP((Double) document.field(MP));
+ character.setCP((Double) document.field(CP));
+
+ character.setPoint(Point3D.fromXYZA(
+ (Integer) document.field(POINT_X),
+ (Integer) document.field(POINT_Y),
+ (Integer) document.field(POINT_Z),
+ (Double) document.field(POINT_ANGLE)));
+
+ // appearance
+ character.getAppearance().setHairStyle(
+ (CharacterHairStyle) document.field(APPEARANCE_HAIR_STYLE));
+ character.getAppearance().setHairColor(
+ (CharacterHairColor) document.field(APPEARANCE_HAIR_COLOR));
+ character.getAppearance().setFace(
+ (CharacterFace) document.field(APPEARANCE_FACE));
+
+ return character;
+ }
+ };
+
+ /**
+ * @param database
+ * the database service
+ * @param idFactory
+ * the character id provider
+ * @param templateIdFactory
+ * the template id provider
+ * @param accountIdFactory
+ * the account id provider
+ * @param clanIdFactory
+ * the clan id provider
+ */
+ @Inject
+ protected OrientDBCharacterDAO(DatabaseService database,
+ final CharacterIDProvider idFactory,
+ CharacterTemplateIDProvider templateIdFactory,
+ AccountIDProvider accountIdFactory, ClanIDProvider clanIdFactory) {
+ super(database);
+ this.idFactory = idFactory;
+ this.templateIdFactory = templateIdFactory;
+ this.accountIdFactory = accountIdFactory;
+ this.clanIdFactory = clanIdFactory;
+ }
+
+ @Override
+ public L2Character select(final CharacterID id) {
+ return database.query(new SelectSingleQuery() {
+ @Override
+ protected ONativeSynchQuery> createQuery(
+ ODatabaseDocumentTx database) {
+ return new ONativeSynchQuery>(
+ database, CLASS_NAME,
+ new OQueryContextNativeSchema()) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public boolean filter(
+ OQueryContextNativeSchema criteria) {
+ return criteria.field(CHAR_ID).eq(id.getID()).go();
+ };
+ };
+ }
+
+ @Override
+ protected Mapper mapper() {
+ return mapper;
+ }
+ });
+ }
+
+ @Override
+ public Collection selectIDs() {
+ return database.query(new SelectListQuery() {
+ @Override
+ protected ONativeSynchQuery> createQuery(
+ ODatabaseDocumentTx database) {
+ return new ONativeSynchQuery>(
+ database, CLASS_NAME,
+ new OQueryContextNativeSchema()) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public boolean filter(
+ OQueryContextNativeSchema criteria) {
+ return true;
+ };
+ };
+ }
+
+ @Override
+ protected Mapper mapper() {
+ return idMapper;
+ }
+ });
+ }
+
+ @Override
+ public boolean insert(L2Character object) {
+ return database.query(new InsertUpdateQuery(object) {
+ @Override
+ protected ONativeSynchQuery> createQuery(
+ ODatabaseDocumentTx database, L2Character object) {
+ return null;
+ }
+
+ @Override
+ protected ODocument update(ODocument document, L2Character character)
+ throws SQLException {
+ return null;
+ }
+
+ @Override
+ protected ODocument insert(ODocument document, L2Character character)
+ throws SQLException {
+ final CharacterAppearance appearance = character
+ .getAppearance();
+
+ document.field(CHAR_ID, character.getID().getID());
+ document.field(ACCOUNT_ID, character.getAccountID().getID());
+ if (character.getClanID() != null)
+ document.field(CLAN_ID, character.getClanID().getID());
+
+ document.field(NAME, character.getName());
+
+ document.field(RACE, character.getRace().name());
+ document.field(CLASS, character.getCharacterClass().name());
+ document.field(SEX, character.getSex().name());
+
+ document.field(LEVEL, character.getLevel());
+ document.field(EXPERIENCE, character.getExperience());
+ document.field(SP, character.getSP());
+
+ document.field(HP, character.getHP());
+ document.field(MP, character.getMP());
+ document.field(CP, character.getCP());
+
+ document.field(POINT_X, character.getPoint().getX());
+ document.field(POINT_Y, character.getPoint().getY());
+ document.field(POINT_Z, character.getPoint().getZ());
+ document.field(POINT_ANGLE, character.getPoint().getAngle());
+
+ // appearance
+ document.field(APPEARANCE_HAIR_STYLE, appearance.getHairStyle()
+ .name());
+ document.field(APPEARANCE_HAIR_COLOR, appearance.getHairColor()
+ .name());
+ document.field(APPEARANCE_FACE, appearance.getFace().name());
+
+ return document;
+ }
+ }) != 0;
+ }
+
+ @Override
+ public boolean update(final L2Character character) {
+ return database.query(new InsertUpdateQuery(character) {
+ @Override
+ protected ONativeSynchQuery> createQuery(
+ ODatabaseDocumentTx database, final L2Character character) {
+ return new ONativeSynchQuery>(
+ database, CLASS_NAME,
+ new OQueryContextNativeSchema()) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public boolean filter(
+ OQueryContextNativeSchema criteria) {
+ return criteria.field(CHAR_ID)
+ .eq(character.getID().getID()).go();
+ };
+ };
+ }
+
+ @Override
+ protected ODocument update(ODocument document, L2Character character)
+ throws SQLException {
+ final CharacterAppearance appearance = character
+ .getAppearance();
+
+ document.field(ACCOUNT_ID, character.getAccountID().getID());
+ if (character.getClanID() != null)
+ document.field(CLAN_ID, character.getClanID().getID());
+
+ document.field(NAME, character.getName());
+
+ document.field(RACE, character.getRace().name());
+ document.field(CLASS, character.getCharacterClass().name());
+ document.field(SEX, character.getSex().name());
+
+ document.field(LEVEL, character.getLevel());
+ document.field(EXPERIENCE, character.getExperience());
+ document.field(SP, character.getSP());
+
+ document.field(HP, character.getHP());
+ document.field(MP, character.getMP());
+ document.field(CP, character.getCP());
+
+ document.field(POINT_X, character.getPoint().getX());
+ document.field(POINT_Y, character.getPoint().getY());
+ document.field(POINT_Z, character.getPoint().getZ());
+ document.field(POINT_ANGLE, character.getPoint().getAngle());
+
+ // appearance
+ document.field(APPEARANCE_HAIR_STYLE, appearance.getHairStyle()
+ .name());
+ document.field(APPEARANCE_HAIR_COLOR, appearance.getHairColor()
+ .name());
+ document.field(APPEARANCE_FACE, appearance.getFace().name());
+
+ return document;
+ }
+
+ @Override
+ protected ODocument insert(ODocument document, L2Character character)
+ throws SQLException {
+ return null;
+ }
+ }) != 0;
+ }
+
+ @Override
+ public boolean delete(L2Character object) {
+ return database.query(new InsertUpdateQuery(object) {
+ @Override
+ protected ONativeSynchQuery> createQuery(
+ ODatabaseDocumentTx database, final L2Character character) {
+ return new ONativeSynchQuery>(
+ database, CLASS_NAME,
+ new OQueryContextNativeSchema()) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public boolean filter(
+ OQueryContextNativeSchema criteria) {
+ return criteria.field(CHAR_ID)
+ .eq(character.getID().getID()).go();
+ };
+ };
+ }
+
+ @Override
+ protected ODocument update(ODocument document, L2Character character)
+ throws SQLException {
+ document.delete();
+ return null;
+ }
+
+ @Override
+ protected ODocument insert(ODocument document, L2Character character)
+ throws SQLException {
+ return null;
+ }
+ }) != 0;
+ }
+
+ @Override
+ public void load(Clan clan) {
+
+ }
+
+ @Override
+ public L2Character selectByName(final String name) {
+ return database.query(new SelectSingleQuery() {
+ @Override
+ protected ONativeSynchQuery> createQuery(
+ ODatabaseDocumentTx database) {
+ return new ONativeSynchQuery>(
+ database, CLASS_NAME,
+ new OQueryContextNativeSchema()) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public boolean filter(
+ OQueryContextNativeSchema criteria) {
+ return criteria.field(NAME).eq(name).go();
+ };
+ };
+ }
+
+ @Override
+ protected Mapper mapper() {
+ return mapper;
+ }
+ });
+ }
+
+ @Override
+ public List selectByAccount(final AccountID account) {
+ return database.query(new SelectListQuery() {
+ @Override
+ protected ONativeSynchQuery> createQuery(
+ ODatabaseDocumentTx database) {
+ return new ONativeSynchQuery>(
+ database, CLASS_NAME,
+ new OQueryContextNativeSchema()) {
+ private static final long serialVersionUID = 1L;
+
+ @Override
+ public boolean filter(
+ OQueryContextNativeSchema criteria) {
+ return criteria.field(ACCOUNT_ID).eq(account.getID()).go();
+ };
+ };
+ }
+
+ @Override
+ protected Mapper mapper() {
+ return mapper;
+ }
+ });
+ }
+}