|
Companion Notes on
|
Similarly to the decision I took on re-inventing a tutorial on Eclipse Data Tools Platform (DTP), i.e.: not duplicating someone else's already capable efforts, I decided to follow one on this topic that Lars Vogel has done. There are very few comments to be made. Pick up and begin Lars' tutorial at http://www.vogella.de/articles/JavaPersistenceAPI/article.html.
I make notes by section number only where I have a comment to make. However, I do make some rather important contributions to the original tutorial in my comments.
First, simply put, persistence is the storage of state between application instances. If you run an application, state mostly in the form of data exists, modified by what the application may do, and sometimes by user interaction. When that application ceases to run, that state is lost unless persisted. The Java Persistence API (JPA) is a light framework for accomplishing this Java.
JPA handles data persistence without having by hand to write code to instantiate (and initialize) a (schema) bean or dredge the bean for creating SQL UPDATE statements.
(Another possible way of doing this, lighter than JPA, is to use XStream and the filesystem, but that's a topic for another day.)
The JPA solution consists primarily of four components:
Getter/setter annotations |
The fields of the "entity" (the bean class that will become a table)
are saved in the database. JPA can use either the instance variables
(fields) themselves or their corresponding getters and setters for
access the fields, but not both. Note: Hibernate uses the getters and setters; we're going to do that too here. It's probably a "best practice". To use the setter and getter methods the class must follow Java bean naming convention. By default, JPA persists all fields of an entity. If fields are not to be saved (i.e.: are transient, of unreliable or irrelevant value) they must be marked so. |
---|---|
@Id | Identifies the unique ID of the database entry |
@GeneratedValue | Together with ID defines that this value is generated automatically |
@Transient | Field will not be saved in database |
Relationship mapping |
JPA allows the definition of relationships between classes, e.g. a
class can be defined as being part of another (containment). Classes
can have one-to-one, one-to-many, many-to-one and many-to-mant
relationships with other classes. Relationships can be bi- or uni-directional. In a bi-directional relationship both classes store a (foreign table) reference to each other. In a uni-directional relationship only one class has a such a reference (to the other class). Within a bi-directional relationship the owning side of this relationship must be specified using the "mappedBy" attribute (as seen in the code of this project). |
@OneToOne | |
@OneToMany | |
@ManyToOne | |
@ManyToMany |
If you're paying attention, you've already figured out that elements of this solution are cast in concrete when it comes to making schema changes subsequent to releasing any version of your application. Be therefore wary! Solutions to this problem include versioning POJOs, maybe under different names. This isn't covered in this tutorial. In fact, as nearly as I can tell, this isn't provided for in JPA at all.
Now, you may stumble upon JPA 2.0 annotation, @Version.
Sadly, this will not magically obviate the need to handle modifications made to a POJO subsequent to releasing your application. It has nothing to do with that at all, but it's a locking mechanism.
The SQL (underneath in JPA) generated to deal with the field thus marked is as
below. Assume that a new field,
@Version private int version;
has been added just after
private String id;
in this tutorial's Person.java. This will generate:
UPDATE Person SET ..., version = version + 1 WHERE id = ? AND version = readVersion
This bumps the version field at the time of update. The code is generated based on the presences of the annotation in your code. So, what this does is allow your application to version or count instances, but not in the sense that the different instances correspond or not to substantial schema additions to your POJO code (added, subtracted or renamed fields).
What does this accomplish you ask? It is a locking mechanism by which your code can sort out problems arising from concurrent access to your application's persistence solution. It allows persistence transactions to proceed simultaneously, but detects and prevents collisions. Any thread can read and update a persisted entity (the contents of a POJO), but an exception is thrown if the version field has been updated since the entity was last read.
I installed EclipseLink 2.1.0 and Apache Derby 10.6.1.0. The exact JARs I ended up installing were:
Oddly, I didn't seem to have to do anything to get Derby working. I did not configure it, so what's in persistence.xml magically works for JpaTest.java: username and password are null-length. Like Lars, I leave this configuration (and the instantiation of an actual file in the filesystem) as an exercise to the user. The tutorial links to a Derby tutorial to help accomplish this.
If you're actually planning on using JPA, EclipseLink and Derby in any real project, you'll want to learn to set up a User Library instead of creating the lib subdirectory the way the tutorial shows. See JARs and Eclipse Build Path: Appendix D: Setting up a third-party library: a practical example.
I added some comments to the persistence configuration file that I think are helpful. A big difference between what I did and what he did is perhaps the version of EclipseLink use. The later one I used seemed to encourage using javax.persistence; there was no persistence.jar in the distro.
<?xml version="1.0" encoding="UTF-8"?> <persistence version="1.0" xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_1_0.xsd"> <persistence-unit name="people"> <provider>org.eclipse.persistence.jpa.PersistenceProvider</provider> <class>de.vogella.jpa.eclipselink.model.Family</class> <class>de.vogella.jpa.eclipselink.model.Person</class> <class>de.vogella.jpa.eclipselink.model.Job</class> <properties> <!-- Note: The tutorial said to use "eclipselink.jdbc.driver (user, password, etc.), but EclipseLink now ships with javax.persistence_1.0.0.jar (instead of persistence.jar?) and you get complaints in Console about it. Use "javax.persistence" in place of "eclipselink". --> <property name="javax.persistence.jdbc.driver" value="org.apache.derby.jdbc.EmbeddedDriver" /> <property name="javax.persistence.jdbc.url" value="jdbc:derby:C:/DerbyDatabases/hellojpa-database8;create=true" /> <!-- I work in this example without user / password --> <property name="javax.persistence.jdbc.user" value="" /> <property name="javax.persistence.jdbc.password" value="" /> <!-- EclipseLink should create the database schema automatically --> <property name="eclipselink.ddl-generation" value="drop-and-create-tables" /> <!-- To see the SQL generated for the the databases set eclipselink.ddl-generation.output-mode value from "database" to "sql-script" or "both". Two files will get generated "createDDL.jdbc" and "dropDDL.jdbc". These are very useful. <property name="eclipselink.ddl-generation.output-mode" value="database" /> --> <property name="eclipselink.ddl-generation.output-mode" value="both" /> </properties> <!-- I got output saying: "[EL Warning]: 2010-09-27 10:21:52.521--ServerSession(9688764) --Exception [EclipseLink-4002] (Eclipse Persistence Services - 2.1.1.v20100817-r8050): org.eclipse.persistence.exceptions.DatabaseException Internal Exception: java.sql.SQLException: Table/View 'SEQUENCE' already exists in Schema 'APP'. Error Code: 30000" I noted that a) table SEQUENCE isn't dropped, probably because it's frequently used in the same database by others and not just this project, and b) it's a row that's inserted this table that belongs to this project and that's deleted--not the whole table. Hence the non-fatal error (that doesn't prohibit the example from working). --> </persistence-unit> </persistence>
Somewhat unstated, you'll want to add the JUnit 4 JARs (via Build Path) to your project (rather than JUnit 3 if you're only familiar with that).
The utility of this discussion is to show you how to to use JPA in Java 1.4—before annotations were introduced (in Java 5). This is done using an XML file, orm.xml to specify all the information that was otherwise specified in the POJOs via annotation. Confusingly, the tutorial leaves annotations in the Java code, but you should not (indeed you cannot) have to do that if you're using Java 1.4.
XML files is how how complication configuration was done for JPA, JAX, Spring and many other framework technologies prior to the introdution of annotations in Java 5.