輕量級嵌入式NoSQL文檔數據庫:XFlat
XFlat是一個輕量級嵌入式NoSQL對象數據庫,它將對象持久化至XML文件中。XFlat是db4o的一個完全免費替代,用于作為一個嵌入式對象數據庫。
XFlat是一個非常小的JAR文件,將XML DOM元素持久化至文件中。并提供一個CRUD接口來操作XML元素,可以通過ID或任意XPath表達式進行查詢。
特性:
-
輕量級
- The XFlat Jar is less than 250kb compressed. With required dependencies only, it is still below 750k. With all optional dependencies, XFlat weighs in at about 4 MB. This includes the JAXB dependency for automatic POJO mapping, which is over 3 MB.
- At runtime, XFlat uses a scheduled thread pool executor to manage recurring tasks. By default this spawns 4 threads. If you have a light-usage scenario, you can get away with configuring XFlat to use only 2 threads. You can even pass it a
ScheduledThreadPoolExecutor
in the constructor, for even finer control of the database's concurrent tasks.
-
純XML數據文數據文件
- XFlat's data files are pure XML. This means they can be inspected, queried, transformed and manipulated by common XML tools like XSLT and XQuery. It is trivial to create a process to export your entire database, or import data from another process into your database by directly manipulating the XML (provided the database is not running during the import, which is simple to control). XFlat will automatically re-index the data files on startup if they have changed.
-
POJO 映射成XML
- XFlat maps POJOs to JDOM
Element
objects using JAXB. The underlying implementation can be swapped if necessary. The JAXB context is only loaded if it is used, so you can avoid it completely by specifying custom converters, or using only the JDOM CRUD interface.
- XFlat maps POJOs to JDOM
-
可以利用XPath表達式進行查詢
- Tables can be queried by any arbitrary XPath expression using the table row as the context. The expression selects a part of the
Element
that is convertible to the value which is being matched, then the matching is performed using Hamcrest Matchers. Breaking the query into XPath expressions and values allows the engine to leverage indexes effectively, much more easily than if we used XQuery. Future versions may support XQuery.
- Tables can be queried by any arbitrary XPath expression using the table row as the context. The expression selects a part of the
-
Sharding by ID
- A table can be sharded across multiple files by its ID. The ID must be a
Comparable
. A sharded table is spread across as many files as is necessary, and each row is stored in the appropriate file based on its ID. ARangeProvider
determines which values go into which shards.
- A table can be sharded across multiple files by its ID. The ID must be a
-
事務支持
- XFlat supports Transactions that by default span all tables in the database. Currently XFlat only implements snapshot-isolation transactions, serializable transactions are planned for a future version.
將來會提供的特性:
-
Multiple swappable Engines (to be implemented)
- The management of each table is handled by an Engine. As a table grows or shrinks, the appropriate Engine for managing the data is swapped in behind the scenes. For example, very small tables can use an engine that loads the whole XML DOM in-memory, while very large tables can use an engine that manipulates a memory-mapped file. Only one engine is implemented for version 1.
-
Indexing on XPath expressions (to be implemented)
- Engines can take advantage of indexes that are based on any arbitrary XPath expression. The expression selects a part of the
Element
that is converted to aComparable
(such as an Integer), then the engine can map thatComparable
to the row and binary search indexes to improve performance.
- Engines can take advantage of indexes that are based on any arbitrary XPath expression. The expression selects a part of the
-
Sharding by arbitrary XPath expressions (to be implemented)
- A table can be sharded across multiple files based on a sharding key selected by an XPath expression. The expression selects a part of the
Element
that is converted to aComparable
, then aRangeProvider
determines which file to store the Element in.
- A table can be sharded across multiple files based on a sharding key selected by an XPath expression. The expression selects a part of the
<dependency> <groupId>org.xflatdb</groupId> <artifactId>xflat</artifactId> <version>0.9</version> <classifier>sources</classifier> </dependency>
View on Maven Central
要求和依賴:
- Java 7
- JDOM 2
- jdom-2.0.4.jar
- Hamcrest matchers 1.3
- hamcrest-core-1.3.jar
- hamcrest-library-1.3.jar
- Apache Commons Logging 1.1
- commons-logging-1.1.1.jar
Optional dependencies:
- Jaxen-1.1.4 - for compiling XPath strings into expressions.
- jaxen-1.1.4.jar
- JAXB reference implementation 1.0 - for automatic POJO mapping
以下是使用示例:
增加一個Foo實例至存儲在
"myDataDirectory/Foo.xml"的 "Foo"表格中。
//initialize with default config
Database myDatabase = XFlatDatabase.Build(dir).create();
Foo myFoo = new Foo();
Table<Foo> fooTable = db.getTable(Foo.class);
//inserts with unique automatically-generated ID
fooTable.insert(myFoo);
System.out.println("Stored foo in table Foo with ID " + myFoo.getId());
Foo myFoo2 = fooTable.find(myFoo.getId());
//myFoo2 is a new instance with the same data as myFoo
Insert an instance of Foo
with a pre-set ID
//initialize with default config
Database myDatabase = XFlatDatabase.Build(dir).create();
Foo myFoo = new Foo();
myFoo.setId("SomeUniqueId");
Table<Foo> fooTable = db.getTable(Foo.class);
fooTable.insert(myFoo);
//beware DuplicateKeyException if fooTable already has a row with that ID
Foo myFoo2 = fooTable.find("someUniqueId");
//myFoo2 is a new instance with the same data as myFoo
Insert an instance of Bar
into the "Foo" table - this works as long as their ID types are both compatible with the ID generator assigned to "Foo".
Foo myFoo = new Foo();
Table<Foo> fooTable = db.getTable(Foo.class);
fooTable.insert(myFoo);
Bar myBar = new Bar();
Table<Bar> barTable = db.getTable(Bar.class, "Foo");
barTable.insert(myBar);
Bar myBar2 = barTable.find(myBar.getId());
Bar thisIsFooData = barTable.find(myFoo.getId());
//attempts to deserialize myFoo's data as a Bar, might throw ConversionException.
Element myFooData = db.getTable(Element.class, "Foo").find(myFoo.getId());
//myFooData is the XML serialized representation of myFoo.
Use a custom ID generator for the "Foo" table - the custom class must have a no-args constructor and extend IdGenerator
.
Database myDatabase = XFlatDatabase.Build(dir)
.withTableConfig("timestampedData",
TableConfig.DEFAULT.withIdGenerator(TimestampIdGenerator.class)
.create();
Foo myFoo = new Foo();
Table<Foo> fooTable = db.getTable(Foo.class);
fooTable.insert(myFoo);
//myFoo now has a timestamp for it's ID
Use custom converters to map Foo
objects to XML. If this is done for every POJO that is a root of a row, then the JAXB mapper will never be invoked and the JAXB jars do not need to be on the classpath.
//initialize with default config
Database myDatabase = XFlatDatabase.Build(dir).create();
db.getConversionService().addConverter(Foo.class, Element.class, new FooToElementConverter());
db.getConversionService().addConverter(Element.class, Foo.class, new ElementToFooConverter());
Foo myFoo = new Foo();
Table<Foo> fooTable = db.getTable(Foo.class);
fooTable.insert(myFoo);
//myFoo was converted to XML using the FooToElementConverter
Foo myFoo2 = fooTable.find(myFoo.getId());
//myFoo2 was converted from XML using the ElementToFooConverter
Query the table for the first Foo
which has fooInt > 17
//XPath expressions must be compiled, consider caching often-used expressions
XPathExpression<Object> expression = XPathFactory.instance().compile("foo/fooInt");
XpathQuery query = XpathQuery.gte(expression, 17);
Foo found = fooTable.findOne(query);
if(found == null)
System.out.println("Could not find a Foo with fooInt > 17");
else
System.out.println("Found foo " + found + " with fooInt " + found.getFooInt());
Query the table for all Bar
with barStr == "Some String" (Bar has the barStr property mapped to an attribute using @XmlAttribute
).
XPathExpression<Object> expression = XPathFactory.instance().compile("bar/@barStr");
XpathQuery query = XpathQuery.eq(expression, "Some String");
Cursor<Bar> barCursor = barTable.find(query);
try{
int i = 0;
for(Bar bar : barCursor){
System.out.println("Found bar " + bar);
i++;
}
System.out.println("Found " + i + " Bars");
}finally{
//Always close cursors!
barCursor.close();
}
Atomically update each Bar
with barStr == "Some String" to set barDouble to 17.4
XpathQuery query = XpathQuery.eq(XPathFactory.instance().compile("bar/@barStr"), "Some String");
XpathUpdate update = XpathUpdate.set(XPathFactory.instance().compile("bar/barDouble"), 17.4);
int rowsUpdated = barTable.update(query, update);
System.out.println(rowsUpdated + " rows were updated");
Bar bar = barTable.findOne(query);
System.out.println("updated bar has barDouble " + bar.getBarDouble());
An example of a table persisted to an XML file:
<?xml version="1.0" encoding="UTF-8"?>
<db:table xmlns:db="http://www.xflatdb.org/xflat/db" db:name="Foo">
<db:row db:id="627070d8-0b9b-4154-aa01-eafd0a388c54" db:tx="89085479315505152" db:commit="89085479315505152">
<foo db:id="627070d8-0b9b-4154-aa01-eafd0a388c54">
<fooInt>26</fooInt>
</foo>
</db:row>
</db:table>
Transactions
Open a transaction and commit multiple updates atomically
Table<Foo> fooTable = db.getTable(Foo.class);
try(Transaction tx = db.getTransactionManager().openTransaction()){
Foo newFoo = new Foo();
newFoo.setFooInt(17);
fooTable.insert("1", newFoo);
XpathQuery query = XpathQuery.eq(XPathFactory.instance().compile("foo/fooInt"), 34);
XpathUpdate update = XpathUpdate.set(XPathFactory.instance().compile("foo/fooString"), "updated text");
fooTable.update(query, update);
tx.commit(); //can throw TransactionException
}
Open a read-only transaction that is automatically reverted when closed; the transaction reads a snapshot of the committed data at the time the transaction was opened.
Table<Foo> fooTable = db.getTable(Foo.class);
try(Transaction tx = db.getTransactionManager().openTransaction(new TransactionOptions().withReadOnly(true))){
Foo foo1 = fooTable.find("1");
XpathQuery query = XpathQuery.gt(XPathFactory.instance().compile("foo/fooInt"), 21);
List<Foo> moreFoos = fooTable.findAll(query);
}
Open and commit a transaction spanning multiple tables
Table<Foo> fooTable = db.getTable(Foo.class, "Table_1");
Table<Bar> barTable = db.getTable(Bar.class, "Table_2");
try(Transaction tx = db.getTransactionManager().openTransaction()){
Foo newFoo = new Foo();
newFoo.setFooInt(17);
fooTable.insert("1", newFoo);
XpathQuery query = XpathQuery.lte(XPathFactory.instance().compile("bar/barDouble"), 34.1);
Baz newBaz = new Baz();
newBaz.setData("some data");
XpathUpdate update = XpathUpdate.set(XPathFactory.instance().compile("bar/barBaz"), newBaz);
barTable.update(query, update);
//commits to both Table_1 and Table_2
tx.commit(); //can throw TransactionException
}
項目主頁:http://www.baiduhome.net/lib/view/home/1389228727421