Moderne Webentwicklung mit Java EE 7: Ein Experiment

Seite 4: Datenablage

Inhaltsverzeichnis

Das folgende Beispiel zeigt das Speichern eines Kommentars zu einem Post. In diesem kurzen Quelltextausschnitt sind bereits einige zentrale Features von Java EE 7 zu sehen, die Entwicklern das Programmieren erleichtern. Der Application Server instanziiert das CommentRepository automatisch, sobald er es benötigt.

Für jeden Request gegen die Anwendung erzeugt er eine neue Instanz (@RequestScoped). Alternativen dazu wären unter anderem @SessionScoped (ein Objekt für die gesamte Benutzersitzung) oder @ApplicationScoped (quasi ein Singleton für die gesamte Anwendungslaufzeit):

@RequestScoped
public class CommentRepository {
@PersistenceContext
private EntityManager em;

@Transactional
public void persist(final Long postId, final Comment c) {
final Post post = em.find(Post.class, postId);
final Comment comment = em.merge(c);
post.addComment(comment);
em.persist(post);
}
}

Die automatisch erzeugten Instanzen können dank CDI mit Annotationen in andere Klassen injiziert werden. Das Beispiel bindet einen EntityManager mit der speziellen Annotation @PersistenceContext ein. Er stellt die zentrale Schnittstelle zur Datenbank dar und bietet einen typsicheren Zugriff auf die Entitäten (em.find(Post.class, postId)). Das Speichern von Entitäten erfolgt über seine Methode persist(). Wenn der EntityManager das zu persistierende Objekt noch nicht verwaltet, müssen Entwickler es ihm zunächst bekanntmachen. Das Beispiel liest den Post bereits mit find(), während das Erstellen des Comment durch den Aufrufer erfolgt. merge() bringt ihn unter die Kontrolle des EntityManager und persistiert ihn dank des kaskadierenden Speicherns gemeinsam mit dem Post.

Die gesamte Datenbankoperation läuft durch die Annotation @Transactional in einer Transaktion ab. Der Application Server übernimmt dabei die komplette Transaktionssteuerung und erzeugt bei Bedarf methodenübergreifende Klammern.

In Rails müssen alle Model-Klassen von ActiveRecord erben und sind dann über sprechende Methoden direkt persistierbar, wie das folgende Beispiel zeigt:

def persist(postId, comment)
post = Post.find(postId)
post.addComment(comment)
post.save!
end

Beide Ansätze haben Vor- und Nachteile. Der Zwang zur Vererbung verhindert oder erschwert potenziell eigene Klassenhierarchien, während die Arbeit mit einem externen EntityManager verwandten Code gegebenenfalls auf mehrere Klassen verstreut. Dennoch ist der notwendige Code für die Datenbankoperationen in beiden Frameworks übersichtlich und gut verständlich, auch wenn er in Java – bedingt durch die statische Typisierung – etwas geschwätziger ausfällt.

Das folgende verkürzte Quelltextbeispiel zeigt den Controller für Kommentare, der mit dem injizierten CommentRepository arbeitet und dessen Verwaltung durch den Application Server erfolgt (@RequestScoped):

@Named
@RequestScoped
public class CommentController extends Controller {
@Inject
private CommentRepository repo;

private String content;

// [...] Getter und Setter

public String save(final Post p)
{
repo.persist(p.getId(), new Comment(getContent()));
addMessage(FacesMessage.SEVERITY_INFO,
"Your comment has beed saved");
return redirect(PostController.POST_PAGE +
"?id=" + p.getId());
}
}

Eine Besonderheit des Controllers ist die Annotation @Named, die dafür sorgt, dass die JSF-Oberflächen einfach über den kleingeschriebenen Klassennamen auf den Controller und dessen Attribute und Methoden zugreifen können, wie das folgende (verkürzte) Beispiel zeigt:

<h:form id="addComment">
<div class="#{yourComment.valid ? '' : 'has-error'}">
<label for="yourComment">Your comment</label>
<h:inputText value="#{commentController.content}"
id="yourComment" />
</div>
<h:commandButton value="Add comment"
action="#{commentController.save(
postController.currentPost)}" />
</h:form>

Die Gestaltung der Oberflächen in JSF geschieht in Form von XHTML. Zahlreiche verfügbare Tags erzeugen beispielsweise Formularelemente oder bedingte Ausgaben. Im Beispiel stammt das Element h:form aus JSF, während darin durchaus native HTML-Elemente wie div erlaubt sind. Für die Logik kommt die sogenannte Expression Language (JSF-EL) zum Einsatz. Der obige Code bindet den Inhalt des input-Elements yourComment mittels value="#{commentController.content}" an das Attribut content des CommentController. Ein Klick auf den Button ruft die Methode save() mit dem aktuellen Post als Parameter auf. Die Übertragung der Daten zwischen Front- und Backend erledigt anschließend das Framework.

Eine Umsetzung in Rails sähe sehr ähnlich aus, wie das folgende Beispiel zeigt. In Rails sind die View und der Controller automatisch verdrahtet und können auf die gleichen Instanzvariablen wie @comment zugreifen. Der Ansatz, Ruby-Code in HTML-Dateien einzubetten (ERB) ("Layouts and Rendering in Rails") ist fast der gleiche wie bei JSF.

<%= form_for(@comment) do |f| %>
<%= f.label :content %>
<%= f.text_field :content %>
<%= f.submit "Add comment" %>
<% end %>