1<!-- $Id$ --> 2<html> 3<body> 4The Direct Persistence Layer (DPL) adds a persistent object model to the 5Berkeley DB transactional engine. 6 7<h1>Package Specification</h1> 8 9<ul> 10<li><a href="#intro">Introduction</a></li> 11<li><a href="#model">The Entity Model</a></li> 12<li><a href="#example">A brief example</a></li> 13<li><a href="#whichAPI">Which API to use?</a></li> 14<li><a href="#java14and15">Java 1.5 dependencies</a> 15 <ul> 16 <li><a href="#genericTypes">Generic Types</a></li> 17 <li><a href="#annotations">Annotations</a></li> 18 </ul> 19</li> 20<li><a href="#bytecode">Bytecode Enhancement</a></li> 21</ul> 22 23<a name="intro"><h2>Introduction</h2></a> 24 25<p>The Direct Persistence Layer (DPL) was designed to meet the following 26requirements.</p> 27<ul> 28<li>A type safe and convenient API is provided for accessing persistent 29objects. The use of Java generic types, although optional, is fully exploited 30to provide type safety. For example: 31<pre class="code"> 32{@literal PrimaryIndex<Long,Employer> employerById = ...;} 33long employerId = ...; 34Employer employer = employerById.get(employerId);</pre> 35</li> 36<li>All Java types are allowed to be persistent without requiring that they 37implement special interfaces. Persistent fields may be {@code private}, 38package-private (default access), {@code protected}, or {@code public}. No 39hand-coding of bindings is required. However, each persistent class must have 40a default constructor. For example: 41<pre class="code"> 42{@literal @Persistent} 43class Address { 44 String street; 45 String city; 46 String state; 47 int zipCode; 48 private Address() {} 49}</pre> 50</li> 51<li>Bytecode enhancement provides fully optimized bindings that do not use Java 52reflection.</li> 53<li>It is easy to define primary and secondary keys. No external schema is 54required and Java annotations may be used for defining all metadata. 55Extensions may derive metadata from other sources. For example, the following 56Employer class is defined as a persistent entity with a primary key field 57{@code id} and the secondary key field {@code name}:</li> 58<pre class="code"> 59{@literal @Entity} 60class Employer { 61 62 {@literal @PrimaryKey(sequence="ID")} 63 long id; 64 65 {@literal @SecondaryKey(relate=ONE_TO_ONE)} 66 String name; 67 68 Address address; 69 70 private Employer() {} 71}</pre> 72<li>Interoperability with external components is supported via the Java 73collections framework. Any primary or secondary index can be accessed using a 74standard <code>java.util</code> collection. For example: 75<pre class="code">{@literal java.util.SortedMap<String,Employer> map = employerByName.sortedMap();}</pre> 76</li> 77<li>Class evolution is explicitly supported. Compatible changes (adding fields 78and type widening) are performed automatically and transparently. For example, 79without any special configuration a {@code street2} field may be added to the 80{@code Address} class and the type of the {@code zipCode} field may be changed 81from {@code int} to {@code long}: 82<pre class="code"> 83{@literal @Persistent} 84class Address { 85 String street; 86 String street2; 87 String city; 88 String state; 89 long zipCode; 90 private Address() {} 91}</pre> 92Many incompatible class changes, such as renaming fields or refactoring a 93single class, can be performed using {@link 94com.sleepycat.persist.evolve.Mutations Mutations}. Mutations are automatically 95applied lazily as data is accessed, avoiding downtime to convert large 96databases during a software upgrade. 97<p>Complex refactoring involving multiple classes may be performed using the a 98<a href="package-summary.html#storeConversion">store conversion</a>. The DPL 99always provides access to your data via a {@code RawStore}, no matter what 100changes have been made to persistent classes.</p> 101</li> 102<br> 103<li>The performance of the Berkeley DB transactional engine is not compromised. 104Operations are internally mapped directly to the engine API, object bindings 105are lightweight, and all engine tuning parameters are available. For example, 106a "dirty read" may be performed using an optional {@link 107com.sleepycat.db.LockMode LockMode} parameter: 108<pre class="code">Employer employer = employerByName.get(null, "Gizmo Inc", LockMode.READ_UNCOMMITTED);</pre> 109For high performance applications, {@link com.sleepycat.db.DatabaseConfig 110DatabaseConfig} parameters may be used to tune the performance of the Berkeley 111DB engine. For example, the size of an internal Btree node can be specified 112as follows: 113<pre class="code"> 114DatabaseConfig config = store.getPrimaryConfig(Employer.class); 115config.setNodeMaxEntries(64); 116store.setPrimaryConfig(config);</pre> 117</li> 118</ul> 119 120<a name="model"><h2>The Entity Model</h2></a> 121 122<p>The DPL is intended for applications that represent persistent domain 123objects using Java classes. An <em>entity class</em> is an ordinary Java class 124that has a primary key and is stored and accessed using a primary index. It 125may also have any number of secondary keys, and entities may be accessed by 126secondary key using a secondary index.</p> 127 128<p>An entity class may be defined with the {@link 129com.sleepycat.persist.model.Entity Entity} annotation. For each entity class, 130its primary key may be defined using the {@link 131com.sleepycat.persist.model.PrimaryKey PrimaryKey} annotation and any number of 132secondary keys may be defined using the {@link 133com.sleepycat.persist.model.SecondaryKey SecondaryKey} annotation.</p> 134 135<p>In the following example, the {@code Person.ssn} (social security number) 136field is the primary key and the {@code Person.employerIds} field is a 137many-to-many secondary key.</p> 138<pre class="code"> 139{@literal @Entity} 140class Person { 141 142 {@literal @PrimaryKey} 143 String ssn; 144 145 String name; 146 Address address; 147 148 {@literal @SecondaryKey(relate=MANY_TO_MANY, relatedEntity=Employer.class)} 149 {@literal Set<Long> employerIds = new HashSet<Long>();} 150 151 private Person() {} // For bindings 152}</pre> 153 154<p>A set of entity classes constitutes an <em>entity model</em>. In addition 155to isolated entity classes, an entity model may contain relationships between 156entities. Relationships may be defined using the {@link 157com.sleepycat.persist.model.SecondaryKey SecondaryKey} annotation. 158Many-to-one, one-to-many, many-to-many and one-to-one relationships are 159supported, as well as foreign key constraints.</p> 160 161<p>In the example above, a relationship between the {@code Person} and {@code 162Employer} entities is defined via the {@code Person.employerIds} field. The 163{@code relatedEntity=Employer.class} annotation property establishes foreign 164key constraints to guarantee that every element of the {@code employerIds} set 165is a valid {@code Employer} primary key.</p> 166 167<p>For more information on the entity model, see the {@link 168com.sleepycat.persist.model.AnnotationModel AnnotationModel} and the {@link 169com.sleepycat.persist.model.Entity Entity} annotation.</p> 170 171<p>The root object in the DPL is the {@link com.sleepycat.persist.EntityStore 172EntityStore}. An entity store manages any number of objects for each entity 173class defined in the model. The store provides access to the primary and 174secondary indices for each entity class, for example:</p> 175 176<pre class="code"> 177EntityStore store = new EntityStore(...); 178 179{@literal PrimaryIndex<String,Person> personBySsn} = 180 store.getPrimaryIndex(String.class, Person.class);</pre> 181 182<a name="example"><h2>A brief example</h2></a> 183 184<p>The following example shows how to define an entity model and how to store 185and access persistent objects. Exception handling is omitted for brevity.</p> 186 187<pre class="code"> 188import java.io.File; 189import java.util.HashSet; 190import java.util.Set; 191 192import com.sleepycat.db.DatabaseException; 193import com.sleepycat.db.Environment; 194import com.sleepycat.db.EnvironmentConfig; 195import com.sleepycat.persist.EntityCursor; 196import com.sleepycat.persist.EntityIndex; 197import com.sleepycat.persist.EntityStore; 198import com.sleepycat.persist.PrimaryIndex; 199import com.sleepycat.persist.SecondaryIndex; 200import com.sleepycat.persist.StoreConfig; 201import com.sleepycat.persist.model.Entity; 202import com.sleepycat.persist.model.Persistent; 203import com.sleepycat.persist.model.PrimaryKey; 204import com.sleepycat.persist.model.SecondaryKey; 205import static com.sleepycat.persist.model.DeleteAction.NULLIFY; 206import static com.sleepycat.persist.model.Relationship.ONE_TO_ONE; 207import static com.sleepycat.persist.model.Relationship.ONE_TO_MANY; 208import static com.sleepycat.persist.model.Relationship.MANY_TO_ONE; 209import static com.sleepycat.persist.model.Relationship.MANY_TO_MANY; 210 211// An entity class. 212// 213{@literal @Entity} 214class Person { 215 216 {@literal @PrimaryKey} 217 String ssn; 218 219 String name; 220 Address address; 221 222 {@literal @SecondaryKey(relate=MANY_TO_ONE, relatedEntity=Person.class)} 223 String parentSsn; 224 225 {@literal @SecondaryKey(relate=ONE_TO_MANY)} 226 {@literal Set<String> emailAddresses = new HashSet<String>();} 227 228 {@code @SecondaryKey(relate=MANY_TO_MANY, relatedEntity=Employer.class, 229 onRelatedEntityDelete=NULLIFY)} 230 {@code Set<Long> employerIds = new HashSet<Long>();} 231 232 Person(String name, String ssn, String parentSsn) { 233 this.name = name; 234 this.ssn = ssn; 235 this.parentSsn = parentSsn; 236 } 237 238 private Person() {} // For bindings 239} 240 241// Another entity class. 242// 243{@literal @Entity} 244class Employer { 245 246 {@literal @PrimaryKey(sequence="ID")} 247 long id; 248 249 {@literal @SecondaryKey(relate=ONE_TO_ONE)} 250 String name; 251 252 Address address; 253 254 Employer(String name) { 255 this.name = name; 256 } 257 258 private Employer() {} // For bindings 259} 260 261// A persistent class used in other classes. 262// 263{@literal @Persistent} 264class Address { 265 String street; 266 String city; 267 String state; 268 int zipCode; 269 private Address() {} // For bindings 270} 271 272// The data accessor class for the entity model. 273// 274class PersonAccessor { 275 276 // Person accessors 277 // 278 {@literal PrimaryIndex<String,Person> personBySsn;} 279 {@literal SecondaryIndex<String,String,Person> personByParentSsn;} 280 {@literal SecondaryIndex<String,String,Person> personByEmailAddresses;} 281 {@literal SecondaryIndex<Long,String,Person> personByEmployerIds;} 282 283 // Employer accessors 284 // 285 {@literal PrimaryIndex<Long,Employer> employerById;} 286 {@literal SecondaryIndex<String,Long,Employer> employerByName;} 287 288 // Opens all primary and secondary indices. 289 // 290 public PersonAccessor(EntityStore store) 291 throws DatabaseException { 292 293 personBySsn = store.getPrimaryIndex( 294 String.class, Person.class); 295 296 personByParentSsn = store.getSecondaryIndex( 297 personBySsn, String.class, "parentSsn"); 298 299 personByEmailAddresses = store.getSecondaryIndex( 300 personBySsn, String.class, "emailAddresses"); 301 302 personByEmployerIds = store.getSecondaryIndex( 303 personBySsn, Long.class, "employerIds"); 304 305 employerById = store.getPrimaryIndex( 306 Long.class, Employer.class); 307 308 employerByName = store.getSecondaryIndex( 309 employerById, String.class, "name"); 310 } 311} 312 313// Open a transactional Berkeley DB engine environment. 314// 315EnvironmentConfig envConfig = new EnvironmentConfig(); 316envConfig.setAllowCreate(true); 317envConfig.setTransactional(true); 318Environment env = new Environment(new File("/my/data"), envConfig); 319 320// Open a transactional entity store. 321// 322StoreConfig storeConfig = new StoreConfig(); 323storeConfig.setAllowCreate(true); 324storeConfig.setTransactional(true); 325EntityStore store = new EntityStore(env, "PersonStore", storeConfig); 326 327// Initialize the data access object. 328// 329PersonAccessor dao = new PersonAccessor(store); 330 331// Add a parent and two children using the Person primary index. Specifying a 332// non-null parentSsn adds the child Person to the sub-index of children for 333// that parent key. 334// 335dao.personBySsn.put(new Person("Bob Smith", "111-11-1111", null)); 336dao.personBySsn.put(new Person("Mary Smith", "333-33-3333", "111-11-1111")); 337dao.personBySsn.put(new Person("Jack Smith", "222-22-2222", "111-11-1111")); 338 339// Print the children of a parent using a sub-index and a cursor. 340// 341{@literal EntityCursor<Person> children =} 342 dao.personByParentSsn.subIndex("111-11-1111").entities(); 343try { 344 for (Person child : children) { 345 System.out.println(child.ssn + ' ' + child.name); 346 } 347} finally { 348 children.close(); 349} 350 351// Get Bob by primary key using the primary index. 352// 353Person bob = dao.personBySsn.get("111-11-1111"); 354assert bob != null; 355 356// Create two employers. Their primary keys are assigned from a sequence. 357// 358Employer gizmoInc = new Employer("Gizmo Inc"); 359Employer gadgetInc = new Employer("Gadget Inc"); 360dao.employerById.put(gizmoInc); 361dao.employerById.put(gadgetInc); 362 363// Bob has two jobs and two email addresses. 364// 365bob.employerIds.add(gizmoInc.id); 366bob.employerIds.add(gadgetInc.id); 367bob.emailAddresses.add("bob@bob.com"); 368bob.emailAddresses.add("bob@gmail.com"); 369 370// Update Bob's record. 371// 372dao.personBySsn.put(bob); 373 374// Bob can now be found by both email addresses. 375// 376bob = dao.personByEmailAddresses.get("bob@bob.com"); 377assert bob != null; 378bob = dao.personByEmailAddresses.get("bob@gmail.com"); 379assert bob != null; 380 381// Bob can also be found as an employee of both employers. 382// 383{@literal EntityIndex<String,Person> employees;} 384employees = dao.personByEmployerIds.subIndex(gizmoInc.id); 385assert employees.contains("111-11-1111"); 386employees = dao.personByEmployerIds.subIndex(gadgetInc.id); 387assert employees.contains("111-11-1111"); 388 389// When an employer is deleted, the onRelatedEntityDelete=NULLIFY for the 390// employerIds key causes the deleted ID to be removed from Bob's employerIds. 391// 392dao.employerById.delete(gizmoInc.id); 393bob = dao.personBySsn.get("111-11-1111"); 394assert !bob.employerIds.contains(gizmoInc.id); 395 396store.close(); 397env.close(); 398</pre> 399<p>The example illustrates several characteristics of the DPL:</p> 400<ul> 401<li>Persistent data and keys are defined in terms of instance fields. For 402brevity the example does not show getter and setter methods, although these 403would normally exist to provide encapsulation. The DPL accesses fields during 404object serialization and deserialization, rather than calling getter/setter 405methods, leaving business methods free to enforce arbitrary validation rules. 406For example: 407<pre class="code"> 408{@literal @Persistent} 409public class ConstrainedValue { 410 411 private int min; 412 private int max; 413 private int value; 414 415 private ConstrainedValue() {} // For bindings 416 417 public ConstrainedValue(int min, int max) { 418 this.min = min; 419 this.max = max; 420 value = min; 421 } 422 423 public setValue(int value) { 424 if (value < min || value > max) { 425 throw new IllegalArgumentException("out of range"); 426 } 427 this.value = value; 428 } 429} 430</pre> 431The above {@code setValue} method would not work if it were called during 432object deserialization, since the order of setting fields is arbitrary. The 433{@code min} and {@code max} fields may not be set before the {@code value} is 434set. 435</li> 436<br> 437<li>The example creates a transactional store and therefore all operations are 438transaction protected. Because no explicit transactions are used, auto-commit 439is used implicitly. 440 441<p>Explicit transactions may also be used to group multiple operations in a 442single transaction, and all access methods have optional transaction 443parameters. For example, the following two operations are performed atomically 444in a transaction: 445<pre class="code"> 446Transaction txn = env.beginTransaction(null, null); 447dao.employerById.put(txn, gizmoInc); 448dao.employerById.put(txn, gadgetInc); 449txn.commit(); 450</pre> 451</li> 452<li>To provide maximum performance, the DPL operations map directly to the 453Btree operations of the Berkeley DB engine. Unlike other persistence 454approaches, keys and indices are exposed for direct access and performance 455tuning. 456<p>Queries are implemented by calling methods of the primary and secondary 457indices. An {@link com.sleepycat.persist.EntityJoin EntityJoin} class is also 458available for performing equality joins. For example, the following code 459queries all of Bob's children that work for Gizmo Inc: 460<pre class="code"> 461{@literal EntityJoin<String,Person> join = new EntityJoin(dao.personBySsn);} 462 463join.addCondition(dao.personByParentSsn, "111-11-1111"); 464join.addCondition(dao.personByEmployerIds, gizmoInc.id); 465 466{@literal ForwardCursor<Person> results = join.entities();} 467try { 468 for (Person person : results) { 469 System.out.println(person.ssn + ' ' + person.name); 470 } 471} finally { 472 results.close(); 473} 474</li> 475<li>Object relationships are based on keys. When a {@code Person} with a given 476employer ID in its {@code employerIds} set is stored, the {@code Person} object 477becomes part of the collection of employees for that employer. This collection 478of employees is accessed using a {@link 479com.sleepycat.persist.SecondaryIndex#subIndex SecondaryIndex.subIndex} for the 480employer ID, as shown below: 481<pre class="code"> 482{@literal EntityCursor<Person> employees =} 483 dao.personByEmployerIds.subIndex(gizmoInc.id).entities(); 484try { 485 for (Person employee : employees) { 486 System.out.println(employee.ssn + ' ' + employee.name); 487 } 488} finally { 489 employees.close(); 490} 491</pre></li> 492<li>Note that when Bob's employer is deleted in the example, the {@code Person} 493object for Bob is refetched to see the change to its {@code employerIds}. This 494is because objects are accessed by value, not by reference. In other words, no 495object cache or "persistence context" is maintained by the DPL. The low level 496caching of the embedded Berkeley DB engine, combined with lightweight object 497bindings, provides maximum performance.</li> 498</ul> 499 500<a name="whichAPI"><h2>Which API to use?</h2></a> 501 502<p>The Berkeley DB engine has a {@link com.sleepycat.db Base API}, a {@link 503com.sleepycat.collections Collections API} and a {@link com.sleepycat.persist 504Direct Persistence Layer (DPL)}. Follow these guidelines if you are not sure 505which API to use:</p> 506<ul> 507<li>When Java classes are used to represent domain objects in an application, 508the DPL is recommended. The more domain classes, the more value there is in 509using annotations to define your schema.</li> 510<br> 511<li>When porting an application between Berkeley DB and Berkeley DB Java 512Edition, or when you've chosen not to use Java classes to represent domain 513objects, then the Base API is recommended. You may also prefer to use this API 514if you have very few domain classes.</li> 515<br> 516<li>The Collections API is useful for interoperating with external components 517because it conforms to the standard Java Collections Framework. It is 518therefore useful in combination with both the Base API and the DPL. You may 519prefer this API because it provides the familiar Java Collections 520interface.</li> 521</ul> 522 523<a name="java14and15"><h2>Java 1.5 dependencies</h2></a> 524 525<p>The DPL uses two features of Java 1.5: generic types and annotations. If 526you wish to avoid using these two Java 1.5 features, the DPL provides options 527for doing so.</p> 528 529<a name="genericTypes"><h3>Generic Types</h3></a> 530 531<p>Generic types are used to provide type safety, especially for the {@link 532com.sleepycat.persist.PrimaryIndex PrimaryIndex}, {@link 533com.sleepycat.persist.SecondaryIndex SecondaryIndex}, and {@link 534com.sleepycat.persist.EntityCursor EntityCursor} classes. If you don't wish to 535use generic types, you can simply not declare your index and cursor objects 536using generic type parameters. This is the same as using the Java 1.5 537Collections Framework without using generic types.</p> 538 539<a name="annotations"><h3>Annotations</h3></a> 540 541<p>If you don't wish to use annotations, you can provide another source of 542metadata by implementing an {@link com.sleepycat.persist.model.EntityModel 543EntityModel} class. For example, naming conventions, static members, or an XML 544configuration file might be used as a source of metadata. However, if you 545don't use annotations then you won't be able to use bytecode enhancement, which 546is described next.</p> 547 548<a name="bytecode"><h2>Bytecode Enhancement</h2></a> 549 550<p>The persistent fields of a class may be private, package-private, protected 551or public. The DPL can access persistent fields either by bytecode enhancement 552or by reflection.</p> 553 554<p>Bytecode enhancement may be used to fully optimize binding performance and 555to avoid the use of Java reflection. In applications that are CPU bound, 556avoiding Java reflection can have a significant performance impact.</p> 557 558<p>Bytecode enhancement may be performed either at runtime or at build time 559(offline). When enhancement is performed at runtime, persistent classes are 560enhanced as they are loaded. When enhancement is performed offline, class 561files are enhanced during a post-compilation step. 562Enhanced classes are used to efficiently access all fields and default 563constructors, including non-public members.</p> 564 565<p>See {@link com.sleepycat.persist.model.ClassEnhancer ClassEnhancer} for 566bytecode enhancement configuration details.</p> 567 568<p>If bytecode enhancement is not used as described above, the DPL will use 569reflection for accessing persistent fields and the default constructor. The 570{@link java.lang.reflect.AccessibleObject#setAccessible 571AccessibleObject.setAccessible} method is called by the DPL to enable access to 572non-public fields and constructors. If you are running under a Java security 573manager you must configure your security policy to allow the following 574permission:</p> 575 576<p>{@code permission java.lang.reflect.ReflectPermission "suppressAccessChecks";} 577 578<p>There are three cases where setting the above permission is <em>not</em> 579required:</p> 580<ol> 581<li>If you are not running under a Java Security Manager, then access to 582non-public members via reflection is not restricted. This is the default for 583J2SE.</li> 584<br> 585<li>If all persistent fields and default constructors are {@code public} then 586they can be accessed via reflection without special permissions, even when 587running under a Java Security Manager. However, declaring {@code public} 588instance fields is not recommended because it discourages encapsulation.</li> 589<br> 590<li>If bytecode enhancement is used as described above, then reflection will 591not be used.</li> 592</ol> 593 594<p>It is well known that executing generated code is faster than reflection. 595However, this performance difference may or may not impact a given application 596since it may be overshadowed by other factors. Performance testing in a 597realistic usage scenario is the best way to determine the impact. If you are 598determined to avoid the use of reflection then option 3 above is 599recommended.</p> 600 601</body> 602</html> 603