использование в памяти orientdb в многопоточном приложении

Я пытаюсь использовать экземпляр orientdb в памяти в многопоточном приложении. Я просмотрел множество примеров кода, но ни один из них не охватывает этот случай. Вот мой тестовый код:

DbTest.java

public class DbTest {
    public static void main(String [] args) {
        ODatabaseDocumentTx db = new ODatabaseDocumentTx("memory:MyDb").create();

        Runnable myRunnable = new MyRunnable(db);

        Thread thread = new Thread(myRunnable);
        thread.start();
    }  
}  

MyRunnable.java

public class MyRunnable implements Runnable {
    private ODatabaseDocumentTx db;

    MyRunnable(ODatabaseDocumentTx db) {
        this.db = db;
    }

    public void run() {
        ODocument animal = new ODocument("Animal");
        animal.field( "name", "Gaudi" );
        animal.field( "location", "Madrid" );
        animal.save();
    }
}

Это дает ошибку

Исключение в потоке "Thread-3" com.orienttechnologies.orient.core.exception.ODatabaseException: экземпляр базы данных не установлен в текущем потоке. Обязательно установите его с помощью: ODatabaseRecordThreadLocal.INSTANCE.set(db);

Я читал, что я должен попытаться использовать пул соединений, но пул соединений, похоже, не работает с базами данных в памяти.

ODatabaseDocumentTx db = ODatabaseDocumentPool.global().acquire("memory:MyDb", "admin", "admin");

Исключение в потоке «основной» com.orienttechnologies.orient.core.exception.OStorageException: невозможно открыть локальное хранилище «MyDb» с режимом = rw

Любые идеи, как это на самом деле должно работать?


person LeventisV2    schedule 27.10.2014    source источник


Ответы (2)


Экземпляр ODatabaseDocumentPool является потокобезопасным и может использоваться повторно. ODatabaseDocumentTx не является потокобезопасным и должен использоваться только одним потоком. API OrientDB здесь немного уродлив, сначала необходимо создать базу данных в памяти, что невозможно при использовании API по умолчанию. Однако при использовании базы данных в памяти объединение в пулы не имеет никакого смысла.

Итак, в вашем примере наиболее эффективный способ сделать это:

public class OrientTest {

static class MyRunnable implements Runnable {

    @Override
    public void run() {
        // If using Java7+, use try-with-resources!
        try (ODatabaseDocumentTx tx = getDatabase("memory:Test", "admin", "admin")) {
            ODocument animal = tx.newInstance("Animal")
                .field("name", "Gaudi")
                .field("location", "Madrid");
            tx.save(animal);
        }
    }

    private ODatabaseDocumentTx getDatabase(String url, String userName, String password) {
        ODatabaseDocumentTx tx = new ODatabaseDocumentTx(url);
        // database is not open yet!
        if (!tx.exists()) {
            // this will create AND open the database! 
            // Default credentials are "admin"/"admin"
            tx.create();
            return tx;
        }
        return tx.open(userName, password);
    }

}

public static void main(String[] args) {
    new Thread(new MyRunnable()).start();
}
}
person rmuller    schedule 28.10.2014
comment
Я сделал это статическим методом, поэтому его можно вызывать для загрузки соединения с БД из любого места. Единственное странное поведение, когда getDatabase() вызывается из конструктора, похоже, ничего не делает, если только он не был ранее вызван в обычном методе (не имеет значения, в каком потоке). Я уверен, что этому есть вполне разумное объяснение, но, тем не менее, оно работает. - person LeventisV2; 29.10.2014

Попробуйте создать базу данных до

ODatabaseDocumentTx db = new ODatabaseDocumentTx("memory:MyDb").create();

а затем используйте пул в своем потоке

ODatabaseDocumentTx db = ODatabaseDocumentPool.global().acquire("memory:MyDb", "admin", "admin");
person wolf4ood    schedule 28.10.2014
comment
Это также отлично работает, и, оглядываясь назад, я должен был понять, как эти двое взаимодействовали, спасибо. - person LeventisV2; 29.10.2014