Unsere Webapplikation erweitern Um mit Play zu arbeiten: 1. Starten Sie zunächst den MySQL-Server. Führen Sie dazu auf dem USB-Stick mysql_start.cmd aus. 2. Sie brauchen eine Windows-Kommandozeile: Starten Sie auf dem USB Stick „get-ready-to-play.cmd“. 3. Wechseln Sie in das Verzeichnis mit der Play Applikation: cd workspace/play_imdb-database. cd steht Dabei für „change directory“, Verzeichnis wechseln. 4. Starten Sie nun den Play Server: play run. 5. Laden Sie im Browser die Seite http://localhost:9000. 6. Jetzt können Sie in Eclipse die Aufgaben lösen. 7. Mit Ctrl-D können Sie Play stoppen. Weitere Hinweise zu Play: Wenn Sie eine neue View hinzufügen (eine HTML-Datei im Verzeichnis app/views), müssen Sie diese View durch Play kompilieren lassen, damit sie auch in Eclipse sichtbar wird. Führen Sie dazu den Befehl play eclipsify aus. Machen Sie anschliessend in Eclipse auf Ihrem Projekt via Rechts-Klick auf Projektnamen ein Refresh. Jetzt sieht auch Eclipse die neue View. Aufgabe 1: Nach Filmen suchen Die Suche nach Schauspieler/innen funktioniert bereits vollständig. Bei der Suche nach Filmen fehlt noch die Abfrage auf der Datenbank in der Klasse Movie. Die View hingegen in movies.scala.html ist bereits programmiert, und die notwendigen Konfigurationen in conf/routes sind bereits vorhanden. 1 Wir betrachten dazu im Detail, wie die Suche nach Schauspieler/innen implementiert ist; implementieren Sie anschliessend die Suche nach Filmen analog: In der Klasse models.Actor ist die Methode findActorsByName zuständig für die Suche nach Schauspieler/innen: public static List<Actor> findActorsByName(String name) { if (name != null && !name.equals("")) { // Achtung, Abfrage ist HQL, nicht SQL! String hql = "SELECT a FROM actors a WHERE name LIKE :name"; TypedQuery<Actor> query = JPA.em().createQuery(hql, Actor.class); query = query.setParameter("name", "%" + name + "%"); List<Actor> actors = query.getResultList(); return actors; } return new ArrayList<Actor>(); } Zuerst prüft die Methode, ob der Parameter name gesetzt ist (if (name != null …). 1. Wir müssen eine HQL-Abfrage definieren: Die Hibernate Query Language HQL ist an SQL angelehnt, ist aber leicht anders; Details siehe http://docs.jboss.org/hibernate/orm/3.3/reference/en/html/queryhql.html. Wir müssen mit HQL arbeiten, damit uns Play einige Arbeiten abnehmen kann (mehr dazu in der folgenden Aufgabe). Die Abfrage sucht auf der Tabelle actors (FROM actors a) nach allen Einträgen, wo name dem Suchmuster entspricht, das wir als Platzhalter :name angeben (WHERE name LIKE :name). Wir selektieren alle Spalten (SELECT a – nicht SELECT * wie in SQL). 2. Wir bereiten eine Abfrage in Java vor, eine sogenannte TypedQuery<Actor>. Damit weiss Play, dass wir als Resultat unserer Suche Actor-Objekte erwarten. 3. Wir müssen noch den Wert des Platzhalters :name definieren. Dazu setzen wir auf der vorbereiteten Abfrage query den Parameter "name" auf "%" + name + "%". 4. Anschliessend führen wir die Abfrage aus (query.getResultList()) und erhalten eine Liste von ActorObjekten (List<Actor> actors). Diese Liste gibt unsere Methode zurück (return actors). Falls der Parameter name nicht gesetzt ist, gibt unsere Methode eine leere Liste von Actor-Objekten zurück (return new ArrayList<Actor>();). 1. Implementieren Sie analog die Methode findMoviesByTitle in der Klasse Movie. Im Moment gibt diese Methode immer nur eine leere Liste von Filmen zurück. 2. Letzte Teilaufgabe: Erklären Sie sich selbst in eigenen Worten, wie die Methode findMoviesByTitle funktioniert. Es kommt dabei nicht auf die genaue Schreibweise der Java-Befehle an, sondern auf die Schritte, die notwendig sind, damit eine Abfrage nach Filmen möglich ist. 2 Aufgabe 2: Zitate zu einem Film anzeigen Erweitern Sie die Ansicht zu einem Film, so dass auch Zitate angezeigt werden: Wir betrachten dazu im Detail, wie die Taglines implementiert sind; implementieren Sie anschliessend die Zitate analog: 1. Zuerst müssen wir das Datenbank-Modell in Java definieren, damit Play weiss, welche Daten es aus der Datenbank laden muss. Die Tabelle taglines in der Datenbank enthält die Taglines der Filme. Das Datenmodell der IMDBDatenbank ist an dieser Stelle etwas speziell: Zu jedem Film gibt es genau einen Datensatz in der Tabelle taglines; dieser Datensatz enthält alle Taglines zu einem Film, pro Tagline eine Zeile. In Heidi SQL betrachtet sieht die Tabelle taglines wie folgt aus: In Java sieht die Klasse models.Taglines wie folgt aus: @Entity(name = "taglines") public class Tagline implements Serializable { private static final long serialVersionUID = 1L; @Id public Long movieid; public String taglinetext; @OneToOne @JoinColumn(name = "movieid") public Movie movie; } Wichtig sind hier die Annotationen: @Entity gibt den Namen der Tabelle in der Datenbank an. @Id bedeutet, dieses Feld ist der Primärschlüssel. Und für uns hier am wichtigsten: @OneToOne definiert, dass es eine 1:1 Beziehung zwischen Movies und Taglines gibt. Mit @JoinColumn wird angegeben, wie in der Tabelle Taglines das Feld heisst, das als Fremdschlüssel auf die Tabelle Movies verweist. Zudem müssen wir die Datenbank-Spalte angeben, welche eigentlichen Tagline-Texte enthält: Dazu definieren wir einfach das Feld „taglinetext“ vom Typ String. 3 2. Zusätzlich muss in der Klasse models.Movie auf die Klasse Tagline verwiesen werden, damit zu einem Film die Taglines angegeben werden können: @Entity(name = "movies") public class Movie implements Serializable { // ... @OneToOne @JoinColumn(name = "movieid") public Tagline tagline; // ... } Damit ist das Datenbank-Modell beschrieben. Wenn Play jetzt einen Movie lädt, lädt es automatisch auch die zugehörige Tagline. Wir müssen uns nicht um die notwendige Abfrage mit JOIN von Movie und Tagline kümmern. a. b. Erstellen Sie analog eine Klasse models.Quote, welche die Tabelle quotes in Java abbildet. Verknüpfen Sie analog die Klasse Movie mit der Klasse Quote. 3. Jetzt müssen die Taglines noch anzeigt werden. Dafür ist die View views/movie.scala.html zuständig, sie zeigt alle Informationen zu einem Film in den verschiedenen Tabs an: <div id="tabs-2"> @if(movie.tagline != null) { <ul> @for(tagline <- movie.tagline.taglinetext.split("\n")) { <li>@tagline</li> } </ul> } </div> Zuerst prüft der Code, ob eine Tagline für den darzustellenden Film vorhanden ist (nicht alle Filme haben eine Tagline). Das geschieht mit dem Scala-Ausdruck @if(movie.tagline != null). Die Variable movie vom Typ Movie enthält den darzustellenden Film; das Feld tagline in der Klasse Movie haben wir oben definiert. Mit <ul> wird eine „unsortierte Liste“ in HTML gestartet. Da ein Tagline-Text wie oben erwähnt alle Taglines zu einem Film enthält, pro Zeile eine Tagline, splitten wir den Inhalt des Feldes „taglinetext“ nach Zeile (movie.tagline.taglinetext.split("\n"), \n steht für „New Line“). So erhalten wir einen Array von Strings. In Scala können wir mit for (tagline <- …) eine Schleife über alle Elemente dieses Arrays laufen lassen, wobei das aktuelle Element jeweils in der Variable tagline abgelegt ist. So können wir dann über <li>@tagline</li> eine einzelne Tagline als „List Item“ (li) in HTML ausgeben. Zuletzt schliessen wir die unsortierte Liste mit </ul>. Erstellen Sie analog die Ansicht für die Quotes. Sie können dazu das noch leere HTML-Element <div id="tabs-2"> verwenden. Hinweis: Die Zitate in der IMDB-Datenbank sind im Gegensatz zu den Taglines durch jeweils eine leere Zeile getrennt. Sie müssen daher splitten nach zwei New Lines: split("\n\n"). Letzte Teilaufgabe: Erklären Sie sich selbst in eigenen Worten, welche Schritte warum notwendig waren, damit wir die Zitate zu einem Film anzeigen konnten. 4 Aufgabe 3: Zusatzaufgabe für die Schnellen: Nach Regisseur suchen Die Suche nach Regisseuren ist noch nicht vollständig implementiert: Es sind folgende Schritte notwendig: 1. Das Datenbank-Modell für Regisseure (Tabelle directors in der IMDB-Datenbank) muss in Java abgebildet werden. Ergänzen Sie dazu das Gerüst der Klasse models.Director in Analogie zur Klasse models.Actor: Ergänzen Sie die Felder der Tabelle (directorid, name) sowie die M:N-Beziehung zur Tabelle actors. Ergänzen Sie die die Implementationen der Methoden findDirectorsByName und findDirectorById. 2. Die View views/directors.scala.html für die Liste der Regisseure ist noch unvollständig: Ergänzen Sie diese View in Analogie zur View views/actors.scala.html. 3. Die View views/director.scala.html für die Filme eines Regisseurs ist noch unvollständig: Ergänzen Sie diese View in Analogie zur View views/actor.scala.html. 4. Die Controller-Methoden in controllers.Application sind bereits vorhanden: @Transactional(readOnly = true) public static Result directors(String name) { List<Director> director = Director.findDirectorsByName(name); return ok(views.html.directors.render(director)); } @Transactional(readOnly = true) public static Result director(Long id) { Director directors = Director.findDirectorById(id); return ok(views.html.director.render(directors)); } 5. Ebenso ist die Konfiguration des Play-Controllers in conf/routes bereits erledigt. Mit den oben beschriebenen Ergänzungen sollten Sie jetzt nach Regisseuren suchen können; Sie sollten eine Liste von Regisseuren angezeigt erhalten, in dieser Liste sollten Sie auf einen einzelnen Regisseur klicken können und anschliessend eine Liste aller Filme dieses Regisseurs angezeigt erhalten. Letzte Teilaufgabe: Erklären Sie sich selbst in eigenen Worten, wozu welche der oben genannten Elemente notwendig sind, damit wir nach Regisseuren suchen können. 5