« Berkeley DB Java Edition (JE) and JRuby Interoperability | Main | Berkeley DB Java Edition in Oracle Magazine »

Berkeley DB Java Edition (JE) and JRuby Integration, Part 2: Using the Direct Persistence Layer (DPL)

In my previous post (Berkeley DB Java Edition in JRuby), I showed an example of calling JE's base API layer and mentioned that Mark and I had been thinking about how to use the DPL from JRuby.  Our ideal is to be able to define classes in Ruby, annotate those class definitions with DPL-like annotations, and have the JE DPL store them.  There are a number of technical hurdles to overcome before we can do this.  For instance, Ruby classes defined in JRuby do not map directly to underlying Java classes; instead they all appear as generic RubyObjects to a Java method.  Granted, it would be possible for the DPL to fish out all of the fields from these classes using reflection, but presently it's just not set up to do that (hence the modification to the DPL that I spoke about in my previous blog entry).  Furthermore, unlike Java, Ruby allows classes to change on the fly (add/remote new fields and methods) causing more heartburn for the DPL unless we required that only frozen Ruby classes could be stored persistently.

On thinking about this some more, we realized that there may be a way to use the DPL from JRuby, albeit with some compromises.  The key to this is that in JRuby, if a Java instance is passed back to the "Ruby side" (e.g. through a return value or by calling the constructor for a Java class), it  remains a Java instance, even when passed around in JRuby (and eventually passed back into the "Java side").  So what if we require all persistent classes to be defined (i.e. annotated) on the Java side?  That buys us the standard DPL annotations (effectively the DDL), freezes the classes that the DPL sees, and still lets us benefit from the POJO persistence of the DPL.  All of this can be done without modification to JE or the DPL using the currently available release.  I cooked up a quick example that builds on the standard "Person" example in the DPL doc and included the code below.

require 'java'

module DPL
  require 'date'

  # Include all the Java and JE classes that we need.

  include_class 'java.io.File'

  include_class 'com.sleepycat.je.Environment'
  include_class 'com.sleepycat.je.EnvironmentConfig'
  include_class 'com.sleepycat.persist.EntityCursor'
  include_class 'com.sleepycat.persist.EntityIndex'
  include_class 'com.sleepycat.persist.EntityStore'
  include_class 'com.sleepycat.persist.PrimaryIndex'
  include_class 'com.sleepycat.persist.SecondaryIndex'
  include_class 'com.sleepycat.persist.StoreConfig'
  include_class 'com.sleepycat.persist.model.Entity'
  include_class 'com.sleepycat.persist.model.Persistent'
  include_class 'com.sleepycat.persist.model.PrimaryKey'
  include_class 'com.sleepycat.persist.model.SecondaryKey'
  include_class 'com.sleepycat.persist.model.DeleteAction'
  include_class 'persist.Person'
  include_class 'persist.PersonExample'

  # Create a JE Environment and Database.  Make them transactional.

  envConf = EnvironmentConfig.new()
  envConf.setAllowCreate(true)
  envConf.setTransactional(true)

  f = File.new('/export/home/cwl/work-jruby/JE')
  env = Environment.new(f, envConf);

  # Open a transactional entity store.
  storeConfig = StoreConfig.new();
  storeConfig.setAllowCreate(true);
  storeConfig.setTransactional(true);
  store = EntityStore.new(env, "PersonStore", storeConfig);

  class PersonAccessor
    attr_accessor :personBySsn, :personByParentSsn

    def init(store)
      stringClass = java.lang.Class.forName('java.lang.String')
      personClass = java.lang.Class.forName('persist.Person')
      @personBySsn = store.getPrimaryIndex(stringClass, personClass)

      @personByParentSsn =
        store.getSecondaryIndex(@personBySsn, stringClass, "parentSsn");
    end
  end

  dao = PersonAccessor.new(store)
  dao.init(store)

  personBySsn = dao.personBySsn

  person = Person.new('Bob Smith', '111-11-1111', nil)
  personBySsn.put(person);

  person = Person.new('Mary Smith', '333-33-3333', '111-11-1111')
  personBySsn.put(person);

  person = Person.new('Jack Smith', '222-22-2222', '111-11-1111')
  personBySsn.put(person);

  # Get Bob by primary key using the primary index.
  bob = personBySsn.get("111-11-1111")
  puts "Lookup of Bob => #{bob.name}, #{bob.ssn}"

  children = dao.personByParentSsn.subIndex(bob.ssn).entities()

  puts "\nRetrieving children of Bob"

  while (true) do
    child = children.next()
    break if child == nil
    puts "#{child.name}, #{child.ssn}"
  end

  children.close()

  store.close
  env.close
end

Post a comment

(If you haven't left a comment here before, you may need to be approved by the site owner before your comment will appear. Until then, it won't appear on the entry. Thanks for waiting.)

About This Entry

This page contains a single entry from the blog posted on January 25, 2007 7:43 PM.

The previous post in this blog was Berkeley DB Java Edition (JE) and JRuby Interoperability.

The next post in this blog is Berkeley DB Java Edition in Oracle Magazine.

Many more can be found on the main index page or by looking through the archives.

Top Tags

Powered by
Movable Type and Oracle