📄 database.java
字号:
package org.garret.perst;
import java.util.*;
import org.garret.perst.impl.ClassDescriptor;
/**
* This class emulates relational database on top of Perst storage
* It maintain class extends, associated indices, prepare queries.
*/
public class Database {
/**
* Constructor of database. This method initialize database if it not initialized yet.
* @param storage opened storage. Storage should be either empty (non-initialized, either
* previously initialized by the this method. It is not possible to open storage with
* root object other than table index created by this constructor.
* @param multithreaded <code>true</code> if database should support concurrent access
* to the data from multiple threads.
*/
public Database(Storage storage, boolean multithreaded) {
this.storage = storage;
this.multithreaded = multithreaded;
metadata = (Index)storage.getRoot();
if (metadata == null) {
metadata = storage.createIndex(String.class, true);
storage.setRoot(metadata);
}
Iterator iterator = metadata.entryIterator();
tables = new HashMap();
while (iterator.hasNext()) {
Map.Entry map = (Map.Entry)iterator.next();
Class table = ClassDescriptor.loadClass(storage, (String)map.getKey());
tables.put(table, map.getValue());
}
}
/**
* Constructor of single threaded database. This method initialize database if it not initialized yet.
* @param storage opened storage. Storage should be either empty (non-initialized, either
* previously initialized by the this method. It is not possible to open storage with
* root object other than table index created by this constructor.
*/
public Database(Storage storage) {
this(storage, false);
}
/**
* Create table for the specified class.
* This function does nothing if table for such class already exists
* @param table class corresponding to the table
* @return <code>true</code> if table is created, <code>false</code> if table
* alreay exists
*/
public boolean createTable(Class table) {
if (multithreaded) {
metadata.exclusiveLock();
}
if (tables.get(table) == null) {
Table t = new Table();
t.extent = storage.createSet();
t.indices = storage.createLink();
t.indicesMap = new HashMap();
tables.put(table, t);
metadata.put(table.getName(), t);
return true;
}
return false;
}
/**
* Drop table associated with this class. Do nothing if there is no such table in the database.
* @param table class corresponding to the table
* @return <code>true</code> if table is deleted, <code>false</code> if table
* is not found
*/
public boolean dropTable(Class table) {
if (multithreaded) {
metadata.exclusiveLock();
}
if (tables.remove(table) != null) {
metadata.remove(table.getName());
return true;
}
return false;
}
/**
* Add new record to the table. Record is inserted in table corresponding to the class of the object.
* Record will be automatically added to all indices existed for this table.
* If there is not table associated with class of this object, then
* database will search for table associated with superclass and so on...
* @param record object to be inserted in the table
* @return <code>true</code> if record was successfully added to the table, <code>false</code>
* if there is already such record (object with the same ID) in the table
* @exception StorageError(CLASS_NOT_FOUND) exception is thrown if there is no table corresponding to
* record class
*/
public <T extends IPersistent> boolean addRecord(T record) {
return addRecord(record.getClass(), record);
}
private Table locateTable(Class cls, boolean exclusive) {
Table table = null;
if (multithreaded) {
metadata.sharedLock();
}
for (Class c = cls; c != null && (table = (Table)tables.get(c)) == null; c = c.getSuperclass());
if (table == null) {
throw new StorageError(StorageError.CLASS_NOT_FOUND, cls.getName());
}
if (exclusive) {
table.extent.exclusiveLock();
} else {
table.extent.sharedLock();
}
return table;
}
/**
* Add new record to the specified table. Record is inserted in table corresponding to the specified class.
* Record will be automatically added to all indices existed for this table.
* @param table class corresponding to the table
* @param record object to be inserted in the table
* @return <code>true</code> if record was successfully added to the table, <code>false</code>
* if there is already such record (object with the same ID) in the table
* @exception StorageError(CLASS_NOT_FOUND) exception is thrown if there is no table corresponding to
* record class
*/
public <T extends IPersistent> boolean addRecord(Class table, T record) {
boolean added = false;
boolean found = false;
if (multithreaded) {
metadata.sharedLock();
}
for (Class c = table; c != null; c = c.getSuperclass()) {
Table t = (Table)tables.get(c);
if (t != null) {
found = true;
if (multithreaded) {
t.extent.exclusiveLock();
}
if (t.extent.add(record)) {
Iterator iterator = t.indicesMap.values().iterator();
while (iterator.hasNext()) {
FieldIndex index = (FieldIndex)iterator.next();
index.put(record);
}
added = true;
}
}
}
if (!found) {
throw new StorageError(StorageError.CLASS_NOT_FOUND, table.getName());
}
return added;
}
/**
* Delete record from the table. Record is removed from the table corresponding to the class
* of the object. Record will be automatically added to all indices existed for this table.
* If there is not table associated with class of this object, then
* database will search for table associated with superclass and so on...
* Object represented the record will be also deleted from the storage.
* @param record object to be deleted from the table
* @return <code>true</code> if record was successfully deleted from the table, <code>false</code>
* if there is not such record (object with the same ID) in the table
* @exception StorageError(CLASS_NOT_FOUND) exception is thrown if there is no table corresponding to
* record class
*/
public <T extends IPersistent> boolean deleteRecord(T record) {
return deleteRecord(record.getClass(), record);
}
/**
* Delete record from the specified table. Record is removed from the table corresponding to the
* specified class. Record will be automatically added to all indices existed for this table.
* Object represented the record will be also deleted from the storage.
* @param table class corresponding to the table
* @param record object to be deleted from the table
* @return <code>true</code> if record was successfully deleted from the table, <code>false</code>
* if there is not such record (object with the same ID) in the table
* @exception StorageError(CLASS_NOT_FOUND) exception is thrown if there is no table corresponding to
* specified class
*/
public <T extends IPersistent> boolean deleteRecord(Class table, T record) {
boolean removed = false;
boolean found = false;
if (multithreaded) {
metadata.sharedLock();
}
for (Class c = table; c != null; c = c.getSuperclass()) {
Table t = (Table)tables.get(c);
if (t != null) {
found = true;
if (multithreaded) {
t.extent.exclusiveLock();
}
if (t.extent.remove(record)) {
Iterator iterator = t.indicesMap.values().iterator();
while (iterator.hasNext()) {
FieldIndex index = (FieldIndex)iterator.next();
index.remove(record);
}
removed = true;
}
}
}
if (!found) {
throw new StorageError(StorageError.CLASS_NOT_FOUND, table.getName());
}
if (removed) {
record.deallocate();
}
return removed;
}
/**
* Add new index to the table. If such index already exists this method does nothing.
* @param table class corresponding to the table
* @param key field of the class to be indexed
* @param unique if index is unique or not
* @exception StorageError(CLASS_NOT_FOUND) exception is thrown if there is no table corresponding to
* the specified class
* @return <code>true</code> if index is created, <code>false</code> if index
* already exists
*/
public boolean createIndex(Class table, String key, boolean unique) {
Table t = locateTable(table, true);
if (t.indicesMap.get(key) == null) {
FieldIndex index = storage.createFieldIndex(table, key, unique);
t.indicesMap.put(key, index);
t.indices.add(index);
return true;
}
return false;
}
/**
* Drop index for the specified table and key.
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -