|
| 1 | +--- |
| 2 | +layout: default |
| 3 | +title: examples |
| 4 | +headtitle: Examples - |
| 5 | +--- |
| 6 | + |
| 7 | + |
| 8 | +<script type="syntaxhighlighter" class="brush: scala"><![CDATA[ |
| 9 | + |
| 10 | + |
| 11 | +package org.squeryl.demos; |
| 12 | + |
| 13 | +import org.squeryl.PrimitiveTypeMode._ |
| 14 | +import org.squeryl.adapters.H2Adapter |
| 15 | +import org.squeryl.{Session, KeyedEntity, Schema} |
| 16 | + |
| 17 | + |
| 18 | +// The root object of the schema. Inheriting KeyedEntity[T] is not mandatory |
| 19 | +// it just makes primary key methods available (delete and lookup) on tables. |
| 20 | +class MusicDbObject extends KeyedEntity[Long] { |
| 21 | + var id: Long = 0 |
| 22 | +} |
| 23 | + |
| 24 | +class Artist(var name:String) extends MusicDbObject { |
| 25 | + |
| 26 | + // this returns a Query[Song] which is also an Iterable[Song] : |
| 27 | + def songs = from(MusicDb.songs)(s => where(s.artistId === id) select(s)) |
| 28 | + |
| 29 | + def newSong(title: String, filePath: Option[String]) = |
| 30 | + MusicDb.songs.insert(new Song(title, id, filePath)) |
| 31 | +} |
| 32 | + |
| 33 | +// Option[] members are mapped to nullable database columns, |
| 34 | +// otherwise they have a NOT NULL constraint. |
| 35 | +class Song(var title: String, var artistId: Long, var filePath: Option[String]) extends MusicDbObject { |
| 36 | + |
| 37 | + // IMPORTANT : currently classes with Option[] members *must* provide a zero arg |
| 38 | + // constructor where every Option[T] member gets initialized with Some(t:T). |
| 39 | + // or else Squeryl will not be able to reflect the type of the field, and an exception will |
| 40 | + // be thrown at table instantiation time. |
| 41 | + def this() = this("", 0, Some("")) |
| 42 | + |
| 43 | + // the schema can be imported in the scope, to lighten the syntax : |
| 44 | + import MusicDb._ |
| 45 | + |
| 46 | + // An alternative (shorter) syntax for single table queries : |
| 47 | + def artist = artists.where(a => a.id === artistId).single |
| 48 | + |
| 49 | + // Another alternative for lookup by primary key, since Artist is a |
| 50 | + // KeyedEntity[Long], it's table has a lookup[Long](k: Long) |
| 51 | + // method available : |
| 52 | + def lookupArtist = artists.lookup(artistId) |
| 53 | +} |
| 54 | + |
| 55 | +class Playlist(var name: String, var path: String) extends MusicDbObject { |
| 56 | + |
| 57 | + import MusicDb._ |
| 58 | + |
| 59 | + // a two table join : |
| 60 | + def songsInPlaylistOrder = |
| 61 | + from(playlistElements, songs)((ple, s) => |
| 62 | + where(ple.playlistId === id and ple.songId === s.id) |
| 63 | + select(s) |
| 64 | + orderBy(ple.songNumber asc) |
| 65 | + ) |
| 66 | + |
| 67 | + def addSong(s: Song) = { |
| 68 | + |
| 69 | + // Note how this query can be implicitly converted to an Int since it returns |
| 70 | + // at most one row, this applies to all single column aggregate queries with no groupBy clause. |
| 71 | + // The nvl function in this example changed the return type to Int, from |
| 72 | + // Option[Int], since the 'max' function (like all aggregates, 'count' being the only exception). |
| 73 | + val nextSongNumber: Int = |
| 74 | + from(playlistElements)(ple => |
| 75 | + where(ple.playlistId === id) |
| 76 | + compute(nvl(max(ple.songNumber), 0)) |
| 77 | + ) |
| 78 | + |
| 79 | + playlistElements.insert(new PlaylistElement(nextSongNumber, id, s.id)) |
| 80 | + } |
| 81 | + |
| 82 | + // New concept : a group query with aggregate functions return GroupWithMeasures[K,M] |
| 83 | + // where K and M are tuples whose members correspond to the group by list and compute list |
| 84 | + // respectively. |
| 85 | + private def _songCountByArtistId = |
| 86 | + from(artists, songs)((a,s) => |
| 87 | + where(a.id === s.artistId) |
| 88 | + groupBy(a.id) |
| 89 | + compute(count) |
| 90 | + ) |
| 91 | + |
| 92 | + // Queries are nestable just as they would in SQL |
| 93 | + def songCountForAllArtists = |
| 94 | + from(_songCountByArtistId, artists)((sca,a) => |
| 95 | + where(sca.key === a.id) |
| 96 | + select((a, sca.measures)) |
| 97 | + ) |
| 98 | + |
| 99 | + // Unlike SQL, a function that returns a query can be nested |
| 100 | + // as if it were a query, notice the nesting of 'songsOf' |
| 101 | + // allowing DRY persistence layers as reuse is enhanced. |
| 102 | + def latestSongFrom(artistId: Long) = |
| 103 | + from(songsOf(artistId))(s => |
| 104 | + select(s) |
| 105 | + orderBy(s.id desc) |
| 106 | + ).headOption |
| 107 | + |
| 108 | + def songsOf(artistId: Long) = |
| 109 | + from(playlistElements, songs)((ple,s) => |
| 110 | + where(id === ple.playlistId and ple.songId === s.id and s.artistId === artistId) |
| 111 | + select(s) |
| 112 | + ) |
| 113 | +} |
| 114 | + |
| 115 | + |
| 116 | +class PlaylistElement(var songNumber: Int, var playlistId: Long, var songId: Long) |
| 117 | + |
| 118 | + |
| 119 | +object MusicDb extends Schema { |
| 120 | + |
| 121 | + val songs = table[Song] |
| 122 | + val artists = table[Artist] |
| 123 | + val playlists = table[Playlist] |
| 124 | + val playlistElements = table[PlaylistElement] |
| 125 | +} |
| 126 | + |
| 127 | + |
| 128 | + |
| 129 | +object KickTheTires { |
| 130 | + |
| 131 | + import MusicDb._ |
| 132 | + |
| 133 | + //A Squeryl session is a thin wrapper over a JDBC connection : |
| 134 | + Class.forName("org.h2.Driver"); |
| 135 | + val session = Session.create( |
| 136 | + java.sql.DriverManager.getConnection("jdbc:h2:~/test", "sa", ""), |
| 137 | + //Currently there are adapters for Oracle, Postgres, MySQL and H2 : |
| 138 | + new H2Adapter |
| 139 | + ) |
| 140 | + |
| 141 | + try { |
| 142 | + session.work { |
| 143 | + // database access code goes here |
| 144 | + test |
| 145 | + session.connection.commit |
| 146 | + } |
| 147 | + } |
| 148 | + catch { |
| 149 | + case e:Exception => session.connection.rollback |
| 150 | + } |
| 151 | + |
| 152 | + def test = { |
| 153 | + |
| 154 | + val herbyHancock = artists.insert(new Artist("Herby Hancock")) |
| 155 | + val ponchoSanchez = artists.insert(new Artist("Poncho Sanchez")) |
| 156 | + val mongoSantaMaria = artists.insert(new Artist("Mongo Santa Maria")) |
| 157 | + |
| 158 | + val watermelonMan = herbyHancock.newSong("Watermelon Man", None) |
| 159 | + val besameMama = mongoSantaMaria.newSong("Besame Mama", Some("c:/MyMusic/besameMama.flac")) |
| 160 | + val freedomSound = ponchoSanchez.newSong("Freedom Sound", None) |
| 161 | + } |
| 162 | +} |
| 163 | + |
| 164 | +]]></script> |
0 commit comments