Move the playlist model into the database. Prime the database from a YAML file.
This is the Playlist application as completed in Lab 07b:
Below as solutions to the exercises from Lab07b
Extend your version of the app to contain a new playlist - make up some songs. Verify that they are rendered as expected.
//...
Song s7 = new Song ("Opus 34: Six variations on a theme in F major", "Beethoven");
Song s8 = new Song ("Opus 120: Thirty-three variations on a waltz by Diabelli in C major", "Beethoven");
Playlist p3 = new Playlist("Beethoven Variations");
p3.songs.add (s7);
p3.songs.add (s8);
//...
playlists.add(p3);
//...
Extend the song model to also have a duration
field. Change the constructor to initialise this field. Modify the dashboard view to display it. Also include a duration field in the Playlist.
package models;
public class Song
{
public String title;
public String artist;
public int duration;
public Song(String title, String artist, int duration)
{
this.title = title;
this.artist = artist;
this.duration = duration;
}
}
package models;
import java.util.ArrayList;
import java.util.List;
public class Playlist
{
public String title;
public List<Song> songs = new ArrayList<Song>();
public int duration;
public Playlist(String title, int duration)
{
this.title = title;
this.duration = duration;
}
}
app/controllers/Dashboard.java
Song s1 = new Song("Piano Sonata No. 3", "Beethoven", 5);
Song s2 = new Song("Piano Sonata No. 7", "Beethoven", 6);
Song s3 = new Song("Piano Sonata No. 10", "Beethoven",8 );
Playlist p1 = new Playlist("Beethoven Sonatas", 19);
p1.songs.add (s1);
p1.songs.add (s2);
p1.songs.add (s3);
Song s4 = new Song("Piano Concerto No. 1", "Beethoven", 8);
Song s5 = new Song("Piano Concerto No. 12", "Beethoven", 12);
Song s6 = new Song("Piano Concerto No. 23", "Beethoven", 23);
Playlist p2 = new Playlist("Beethoven Concertos", 43);
p2.songs.add (s4);
p2.songs.add (s5);
p2.songs.add (s6);
Song s7 = new Song ("Opus 34: Six variations on a theme in F major", "Beethoven", 12);
Song s8 = new Song ("Opus 120: Thirty-three variations on a waltz by Diabelli in C major", "Beethoven", 14);
Playlist p3 = new Playlist("Beethoven Variations", 26);
p3.songs.add (s7);
p3.songs.add (s8);
Modify the dashboard view to display these new fields.
#{extends 'main.html' /}
#{set title:'Dashboard' /}
#{menu id:"dashboard"/}
#{list items:playlists, as:'playlist'}
<section class="ui segment">
<h2 class="ui header">
${playlist.title}
</h2>
Total Duration: ${playlist.duration}
#{listsongs playlist:playlist/}
</section>
#{/list}
<table class="ui fixed table">
<thead>
<tr>
<th>Song</th>
<th>Artist</th>
<th>Duration</th>
</tr>
</thead>
<tbody>
#{list items:_playlist.songs, as:'song'}
<tr>
<td>
${song.title}
</td>
<td>
${song.artist}
</td>
<td>
${song.duration}
</td>
</tr>
#{/list}
</tbody>
</tabl
Extend the following views:
#{extends 'main.html' /}
#{set title:'Start' /}
#{menu id:"start"/}
<section class="ui center aligned middle aligned segment">
<h1 class="ui header">
Welcome to Playlist 1
</h1>
<p>
A small app to let you compose playlists. This app will allow you to create, manage and share your playlists. Simple enter the playlist details one the dashboard.
</p>
</section>
#{extends 'main.html' /}
#{set title:'About' /}
#{menu id:"about"/}
<section class="ui center aligned middle aligned segment">
<p>
A Little Playlist Maker - Version 1. Prepared by Playlist Solutions, Playlist Mansions, PlaylistTown.
</p>
</section>
Create a new file in the conf
folder of the Playlist project called data.yml
. Here are its contents:
Song(s1):
title: Piano Sonata No. 3
artist: Beethoven
duration: 5
Song(s2):
title: Piano Sonata No. 7
artist: Beethoven
duration: 6
Song(s3):
title: Piano Sonata No. 10
artist: Beethoven
duration: 8
Song(s4):
title: Piano Concerto No. 27
artist: Beethoven
duration: 8
Song(s5):
title: Piano Concertos No. 17
artist: Beethoven
Song(s6):
title: Piano Concerto No. 10
artist: Beethoven
duration: 12
Playlist(p1):
title: Bethoven Sonatas
duration: 19
songs:
- s1
- s2
- s3
Playlist(p2):
title: Bethoven Concertos
duration: 23
songs:
- s4
- s5
- s6
This is a YAML file - a notation for describing configuration data and structure information:
We will use this to describe initial values for our model objects.
Revise the Model classes, by changing the structure of both model classes:
package models;
import javax.persistence.Entity;
import play.db.jpa.Model;
@Entity
public class Song extends Model
{
public String title;
public String artist;
public int duration;
public Song(String title, String artist, int duration)
{
this.title = title;
this.artist = artist;
this.duration = duration;
}
}
package models;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.CascadeType;
import javax.persistence.Entity;
import javax.persistence.OneToMany;
import play.db.jpa.Model;
@Entity
public class Playlist extends Model
{
public String title;
@OneToMany(cascade = CascadeType.ALL)
public List<Song> songs = new ArrayList<Song>();
public int duration;
public Playlist(String title, int duration)
{
this.title = title;
this.duration = duration;
}
}
These versions are largely the same as the current version, except that there are a number of additional 'annotations':
...
@Entity
...
@OneToMany(cascade = CascadeType.ALL)
...
The purpose of these is to associated these classes with a Database.
Now modify Dashboard.java - removing all of the playlists + songs:
...
// Song s1 = new Song("Piano Sonata No. 3", "Beethoven", 5);
// Song s2 = new Song("Piano Sonata No. 7", "Beethoven", 6);
// Song s3 = new Song("Piano Sonata No. 10", "Beethoven",8 );
// Playlist p1 = new Playlist("Beethoven Sonatas", 19);
// p1.songs.add (s1);
// p1.songs.add (s2);
// p1.songs.add (s3);
//
// Song s4 = new Song("Piano Concerto No. 1", "Beethoven", 8);
// Song s5 = new Song("Piano Concerto No. 12", "Beethoven", 12);
// Song s6 = new Song("Piano Concerto No. 23", "Beethoven", 23);
// Playlist p2 = new Playlist("Beethoven Concertos", 43);
// p2.songs.add (s4);
// p2.songs.add (s5);
// p2.songs.add (s6);
//
// Song s7 = new Song ("Opus 34: Six variations on a theme in F major", "Beethoven", 12);
// Song s8 = new Song ("Opus 120: Thirty-three variations on a waltz by Diabelli in C major", "Beethoven", 14);
// Playlist p3 = new Playlist("Beethoven Variations", 26);
// p3.songs.add (s7);
// p3.songs.add (s8);
//
// List<Playlist> playlists = new ArrayList<Playlist>();
// playlists.add(p1);
// playlists.add(p2);
// playlists.add(p3);
render ("dashboard.html");
...
Make sure the app is compiling and running without any erors - you may need to restart to make sure. Remember, to restart you enter Ctrl-C in the console use used to start the application.
Create a new class in the app
package containing this code here:
import java.util.List;
import play.*;
import play.jobs.*;
import play.test.*;
import models.*;
@OnApplicationStart
public class Bootstrap extends Job
{
public void doJob()
{
Fixtures.loadModels("data.yml");
}
}
Do this be selecting the app
package, and then selecting New->Java Class
Creating the class:
The class should be in the app
package:
Restart the app and keep a close eye on the console as you do this - you should see something like this:
~ _ _
~ _ __ | | __ _ _ _| |
~ | '_ \| |/ _' | || |_|
~ | __/|_|\____|\__ (_)
~ |_| |__/
~
~ play! 1.5.2, https://www.playframework.com
~
~ Ctrl+C to stop
~
~ using java version "1.8.0_162"
Listening for transport dt_socket at address: 8000
17:53:49,806 INFO ~ Starting /Users/edeleastar/dev/playlist-2
17:53:49,906 WARN ~ You're running Play! in DEV mode
17:53:50,007 INFO ~ Listening for HTTP on port 9000 (Waiting a first request to start) ...
~ Server is up and running
17:53:59,610 INFO ~ HikariPool-1 - Starting...
17:53:59,648 INFO ~ HikariPool-1 - Start completed.
17:53:59,662 INFO ~ Connected to jdbc:h2:mem:play for default
17:54:00,703 INFO ~ Application 'playlist-2' is now started !
17:54:01,593 INFO ~ Rendering Start
The 'Rendering' messages are familiar - but we have some new messages relating to the database.
To display the playlist from the database - here is a revised version of the dashboard controller:
package controllers;
import java.util.ArrayList;
import java.util.List;
import models.Playlist;
import models.Song;
import play.Logger;
import play.mvc.Controller;
public class Dashboard extends Controller
{
public static void index()
{
Logger.info("Rendering Admin");
List<Playlist> playlists = Playlist.findAll();
render ("dashboard.html", playlists);
}
}
Restart the project - and visit the Dashboard view. You should see the playlists in data.yml rendered to the view.
Notice in the above we are requesting all playlists from the database:
List<Playlist> playlists = Playlist.findAll();
Before sending them to the view.
With your application still running - visit the following url:
http://localhost:9000/@db
This should display this panel:
Please note - the first time your run this JDBC URL
field may be:
jdbc:h2:~/test
. If this is the case, change it to
jdbc:h2:mem:play
as shown above. You should only need to do this once.
Press Connect
This is a control panel for the database our application is using. This databases is 'transient' - only containing values while it is running. When the application is terminated, the database is wiped.
However, it is a complete Relational Database - you will be exploring this type of system in semester 2.
Select 'song' in the panel in the left - and the press the green 'play' button:
This has queried the play table - and displayed the full contents.
Try inspecting the other tables:
Can you make sense of these tables - and relate them to the Java model classes? In particular, can you see the role of the playlist_song table? Inspect the contents closely, paying attention to the IDs.
A complete version of the app as it should be at the end of this lab:
However, if you already have a project called 'playlist' in Idea, then it might be confusing So, first rename the project to playlist-2
(covered in step 1 of lab07b), and then run this command:
play idealize
... and then open with Idea.
Working only in the data.yml, create a new Playlist. If this works correctly, you should see the playlist in the dashboard just by restarting the application.
This is a challenging exercise - but the hints provide most of what you need.
Problem: We would like an 'admin' interface to the application, which displays all the songs in the database - ignoring the playlists they belong to for the moment. The view should also display the IDs of the songs:
This is a 'hidden' view, that does not appear in the menu. It is accessed by browsing directly to:
You will need a new route:
GET /admin Admin.index
... and a matching controller + view:
package controllers;
import java.util.ArrayList;
import java.util.List;
import models.Playlist;
import models.Song;
import play.Logger;
import play.mvc.Controller;
public class Admin extends Controller
{
public static void index()
{
Logger.info("Rendering Admin");
// TODO - get list of all songs, and send them to the view
render ("admin.html");
}
}
#{extends 'main.html' /}
#{set title:'Dashboard' /}
#{menu id:"admin"/}
<section class="ui segment">
<h2 class="ui header">
All Known Songs in the Database:
</h2>
<table class="ui fixed table">
<thead>
<tr>
<th>ID </th>
<th>Song</th>
<th>Artist</th>
<th>Duration</th>
</tr>
</thead>
<tbody>
// TODO Iterate through a list of songs and populate rows in the table.
</tbody>
</table>
</section>
How to read all songs form the Songs table:
List<Song> songs = Song.findAll();
How to iterate through a list of songs:
...
#{list items:songs, as:'song'}
...
...
#{/list}
...
How to get the songs from the controller to the view:
...
render("admin.html", songs);
...
The solution to this exercise is provided in the next lab - but try to have a go first before working through the solution.