轻量级嵌入式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. </ul> </li>
-
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. </ul> </li> -
可以利用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. </ul> </li> -
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. </ul> </li> -
事务支持
- 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. </ul> </li> </ul>
-
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. </ul> </li> -
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. </ul> </li> </ul> Add via maven:
<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 </ul> </li>
- Apache Commons Logging 1.1
- commons-logging-1.1.1.jar </ul> </li> </ul>
- Jaxen-1.1.4 - for compiling XPath strings into expressions.
- jaxen-1.1.4.jar
- JAXB reference implementation 1.0 - for automatic POJO mapping </ul>
Optional dependencies:
以下是使用示例:
增加一个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.4XpathQuery 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.open-open.com/lib/view/home/1389228727421
- 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
- Engines can take advantage of indexes that are based on any arbitrary XPath expression. The expression selects a part of the
将来会提供的特性:
- A table can be sharded across multiple files by its ID. The ID must be a
- Tables can be queried by any arbitrary XPath expression using the table row as the context. The expression selects a part of the
- XFlat maps POJOs to JDOM