Pmemkv is a key-value data store written in C and C++, however, it also opens up a way to leverage persistent memory by developers who prefer high-level languages - such as Java. For more information about other bindings please read Language bindings for pmemkv article and pmemkv README
We built an API for pmemkv-java binding on top of libpmemkv 1.0 API, but java binding is also compatible with newer versions of libpmemkv. You don’t have to worry about the dependencies, because packages from the OS vendor’s repositories suffice.
Examples directory in pmemkv-java repository is a good resource, where you may learn how to create pmemkv database and use it with different types of data; starting from simple ByteBuffer, up to little bit more real-life-application, which shows you how to store and display pictures.
Since pmemkv-java binding is the java wrapper for C++ library, it internally relies on java native interface, so it can only store ByteBuffer objects. Because it wouldn’t be very convenient to operate on ByteBuffers inside a high level application, we introduced an additional layer of types converters. It makes it possible to store instance of any class in pmemkv, as long as an appropriate converter is available.
You have to implement Converter generic interface, with two methods:
class ImageConverter implements Converter<BufferedImage> {
public ByteBuffer toByteBuffer(BufferedImage entry) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
try {
ImageIO.write(entry, "png", out);
} catch (IOException e) {
return null;
}
return ByteBuffer.wrap(out.toByteArray());
}
public BufferedImage fromByteBuffer(ByteBuffer entry) {
BufferedImage out = null;
try {
out = ImageIO.read(new ByteBufferBackedInputStream(entry));
} catch (IOException e) {
return null;
}
return out;
}
}
Database is the generic class, which needs to be specialized by key and value types.
If you want to create and configure your database, you have to call setValueConverter()
and setKeyConverter()
methods, and make sure their types correspond with Database key and value types.
db = new Database.Builder<String, BufferedImage>(engine)
.setSize(size)
.setPath(Path)
.setKeyConverter(new StringConverter())
.setValueConverter(new ImageConverter())
.setForceCreate(true)
.build();
For persistent engines, database may be created by your application during a previous run or e.g. by pmempool tool. In such case, you have to specify only a path and converter objects.
db = new Database.Builder<String, BufferedImage>(engine)
.setPath(Path)
.setKeyConverter(new StringConverter())
.setValueConverter(new ImageConverter())
.build();
To store an object of the appropriate type, you have to call a put()
method.
BufferedImage image_buffer = null;
try {
image_buffer = ImageIO.read(image);
} catch (IOException e) {
System.exit(1);
}
db.put(image.getName(), image_buffer);
You can use group of get*()
methods, which gets lambda expressions,
and can directly read data stored in the database.
public void paint(Graphics g) {
System.out.println("Draw images from pmemkv database");
AtomicInteger yPosition = new AtomicInteger(0);
db.getAll((k, v) -> {
System.out.println("\tDraw" + k);
Graphics2D g2 = (Graphics2D) g;
g.drawImage(v, 0, yPosition.getAndAdd(v.getHeight()), null);
});
}
Currently pmemkv-java 1.0 API is compatibile with pmemkv 1.0 API. In upcoming releases we are going to implement features available in new pmemkv versions.