In the previous parts we've covered the basic elements of persisting with JPA in Java SE. Now, we'll look at an example application and expand on how you can use JPA in your development.

First though, let's look at the requirements for the example application, downloadable here. It's all about managing licences. There are a number of applications, each application has several versions, each version in turn has one or more licences associated with it. There's also a set of users, who can be associated with any of those licences. We want to create an application which can manage all of those elements.

Let's start with the entities. They are in their own package rather than mixed with the application code. It is worth applying this discipline; in a larger project you could handle entities as a project in their own right making them easier to reuse in other projects. We have created four entities, Application, Version, Licence and User, so let's look at the highlights of each entity.

In the Application class, we have a one to many relationship with the Version class. Here's part of the Application method; we've skipped the id and name properties for these as they are similar to what we've previously covered.

@Entity
public class Application {
    ...
    private List<Version> versions=new ArrayList<Version>();
    ...
    @OneToMany(mappedBy="application",cascade=CascadeType.ALL)
    public List<Version> getVersions() {
      return versions;
    }
    ...
}

We covered the mappedBy parameter last month. What is new is the cascade parameter. The cascade controls the ability of the persistence engine to let operations flow down affecting other tables in the database. By default, there is no cascading so changes to a collection require you to manage the contents of the collection explicitly. Examining the other CascadeType values reveals the covered operations: ALL, PERSIST, MERGE, REMOVE, REFRESH. A setting of CascadeType.PERSIST for example, would cascade only persisting objects, so if a new Version instance were added to the version list, then updating the Application instance would cascade to save the new Version in the underlying database. CascadeType.MERGE applies the same rule to updates and CascadeType.REMOVE does the same for removals from the collection. CascadeType.REFRESH cascades the rereading of entities from the database; we'll cover refreshing later.

Moving into the Version class, we have the inverse side of the @OneToMany to Application and another cascading collection, this time for a set of licences.

@Entity
public class Version {
    ...
    private Application application;
    private Set<Licence> licences=new HashSet<Licence>();
    ...
    @ManyToOne
    public Application getApplication() {
       return application;
    }
    ...
    @OneToMany(mappedBy="version",cascade=CascadeType.ALL)
    public Set<Licence> getLicences() {
       return licences;
    }
    ...
}

Moving down the chain, we get to the Licence class. It's here that the cascade ends.

@Entity
public class Licence {
    ...
    private Version version;
    private Set<User> users=new HashSet<User>();
    ...

We still have a collection to represent the set of users. The mapping to the version is handled with a @ManyToOne annotation.

@ManyToOne
public Version getVersion() {
    return version;
}

Now we get to the most important mapping in this example; many licences can refer to many users, so we use the @ManyToMany annotation to denote this.

@ManyToMany
@JoinTable(name="LicenceUsers",
    joinColumns={@JoinColumn(name="licence_id")},
    inverseJoinColumns={@JoinColumn(name="user_id")})
public Set<User> getUsers() {
    return users;
  }
}

The @JoinTable annotation allows us to take control of the @ManyToMany and override its defaults. The name parameter is the name of the table we want to be created to hold the many to many mapping. The joinColumns parameter allows us to then set one side of the join. You may wonder why the braces around the argument we are giving it; the argument type is an array, so despite there being only one value, we still need the braces. The value within that is an @JoinColumn annotation which sets the name for the column in the database. The same syntax applies in the inverseJoinColumns parameter, this time though for the other side of the many-to-many relationship.

Finally we get to the User class. For this example, we are assuming that users are uniquely identified through their user name. We can therefore use that as an identifier rather than using @GeneratedId to generate a unique ID value which we have used in the other entity classes.

@Entity
public class User {
    private String userName;
    private List<Licence> licences=new ArrayList<Licence>();
    public User() {}
    @Id
    public String getUserName() {
       return userName;
    }
    ...

Now we can look at creating a mapping to the licences, the other side of the ManyToMany relationship, which is of course an @ManyToMany.

@ManyToMany(mappedBy="users")
public List<Licence> getLicences() {
return licences;
}
...
}

Here we use the mappedBy parameter to point at the users property in the Licence class. This allows the persistence engine to use the @JoinTable we specified in Licence.

That's the entities; now we move on to how we are going to manipulate them. We are writing this example to be targeted at a Java Standard Edition platform, so as in the previous articles we can create a manager class and instantiate the EJB/JPA layer from that class. Here, it's the LicManStore.java class which we'll create as a singleton.

public class LicManStore {
private EntityManagerFactory emf;
private EntityManager em;
private static LicManStore myInstance;
public static LicManStore getStore() {
    if(myInstance==null) {
      myInstance=new LicManStore();
       }
      return myInstance;
}
private LicManStore() {
    emf=Persistence.createEntityManagerFactory("appman",
      new Properties());
    em=emf.createEntityManager(PersistenceContextType.EXTENDED);
}
...

The constructor has one obvious difference from previous examples; the use of PersistenceContextType.EXTENDED. By default, EntityManagers are created with a PersistenceContextType of TRANSACTION. What this means is that entities are managed only when there's an active transaction in progress. As soon as the transaction ends, the entities become detached from the entity manager which we can then discard. The EXTENDED context type says that that detaching doesn't happen, keeping the entities managed beyond the end of a transaction. This means for example, you don't need to worry if a collection is lazily fetched, because the entity manager is available to do the required fetching. We still need to get the EntityTransaction when we want to persist and update/merge entities or remove entities from the database, for example when we want to save a new Application instance;

public void saveApplication(Application a) {
   EntityTransaction tx=em.getTransaction();
   tx.begin();
   em.persist(a);
   tx.commit();
}

If you look in the LicManStore source, you'll find only save, update and delete operations for Application and only save and delete for User, Licence and Version have no methods. They haven't been forgotten, but aren't needed. Remember that we set the cascade on the relationships between Application and Version and Licence. This means all we have to do is update the owning Application and all the required operations for Version and Licence are done. If you look in the Controller.java code, you'll see this in action. First let's look at creating an Application:

void addApplication(String appname) throws UpdateException {
if(licmanStore.getApplication(appname)!=null)
    throw new UpdateException("An application "
      + appname + " already exists");
Application app=new Application();
app.setApplicationname(appname);
licmanStore.saveApplication(app);
populateApplications();
ui.setSelectedApplication(app);
}

Do you need help with Java, C, or C++? Gain advice from Builder AU forums

Related links

Comments

1

Ole Martin S&Atilde;&cedil;rli - 21/02/08

about @ManyToMany:

I have a relational database, with a table 'user'.
this 'user' needs to relate to many other 'user'-entities.

I have searched the web for days now without finding one example where ONE table has a many to many relation to itself.

Is this not covered in EJB persitence?

This is the type of relation I use the most, and I cannot think of any other way to model it. It is fairly simple to solve with native sql, but a combination of the two becomes rather hairy..

» Report offensive content

Leave a comment

You must read and type the 6 chars within 0..9 and A..F

* indicates mandatory fields.

1

Ole Martin S&Atilde;&cedil;rli - 21/02/08

about @ManyToMany: I have a relational database, with a table 'user'. this 'user' needs to relate to many other 'user'-entities. I have searched the ... more

Log in


Sign up | Forgot your password?

  • Staff Aussies to pay more for Win 7

    If you are looking to make some money in these troubled times, perhaps importing copies of Windows 7 could be for you. Read more »

    -- posted by Staff

  • Staff Firefox: Greens want it, 3.5rc2 not up to par

    This week's roundup looks at the situation surrounding a campaign to change Outlook HTML renderer, a Greens MP wants to install Firefox but is restricted and all the photos from the iPhone 3GS launch. Read more »

    -- posted by Staff

  • Chris Duckett Microsoft misses the Outlook point

    Ask designers which mail program is the bane of their existence, and you'll find that Outlook tops the list. The reason why the most popular email reader is also the most painful is simple: it uses Word to render HTML emails. Read more »

    -- posted by Chris Duckett

What's on?