From 9c007b3de5a0a90b17eb61bec9dbfab8369ffaf1 Mon Sep 17 00:00:00 2001 From: Jonathan Bernard Date: Wed, 30 Mar 2016 01:08:27 -0500 Subject: [PATCH] Changed the core schema to use UUIDs instead of SERIALs for all record IDs. --- .../com/jdbernard/wdiwtlt/MediaLibrary.groovy | 14 ++- .../com/jdbernard/wdiwtlt/db/DbApi.groovy | 87 ++++++++++++------- .../jdbernard/wdiwtlt/db/models/Bookmark.java | 5 +- .../wdiwtlt/db/models/MediaFile.java | 1 + .../jdbernard/wdiwtlt/db/models/Model.java | 5 +- .../jdbernard/wdiwtlt/db/models/Playlist.java | 3 +- .../20151209054632-initial-schema-up.sql | 49 ++++++----- 7 files changed, 101 insertions(+), 63 deletions(-) diff --git a/core/src/main/groovy/com/jdbernard/wdiwtlt/MediaLibrary.groovy b/core/src/main/groovy/com/jdbernard/wdiwtlt/MediaLibrary.groovy index 99cd9e1..1be2f27 100644 --- a/core/src/main/groovy/com/jdbernard/wdiwtlt/MediaLibrary.groovy +++ b/core/src/main/groovy/com/jdbernard/wdiwtlt/MediaLibrary.groovy @@ -133,11 +133,14 @@ public class MediaLibrary { public def getByIdOrName(Class modelClass, String input) { def match - if (safeToInteger(input)) { - match = dbapi.getById(modelClass, safeToInteger(input)) + + if (safeToUUID(input)) { + match = dbapi.getById(modelClass, safeToUUID(input)) if (match) match = [match] } + else { - match = dbapi.getByName(modelClass, input) + match = dbapi.getByIdLike(modelClass, input) + if (!match) match = dbapi.getByName(modelClass, input) if (!match) match = dbapi.getLike(modelClass, ["name"], [input]) } return match } @@ -252,6 +255,11 @@ public class MediaLibrary { if (b != parentPath.length) return "" return (['.'] + childPath[b.. getAllIds(Class modelClass) { + String query = 'SELECT id FROM ' + + pluralize(nameFromModel(modelClass.simpleName)) + + logger.debug('Selecting model ids.\n\tSQL: {}', query) + return sql.rows(query).collect { it.id } } + + public Model getById(Class modelClass, UUID id) { def model = modelClass.newInstance() model.id = id return refresh(model) } + public List getByIdLike(Class modelClass, + String partialId) { + String likeVal = partialId + '%' + String query = new StringBuilder() + .append('SELECT * FROM ') + .append(pluralize(nameFromModel(modelClass.simpleName))) + .append(' WHERE id LIKE ?') + .toString() + + logger.debug('Selectin models like id.\n\tSQL: {}\n\tPARAMS: {}', + query, likeVal) + return sql.rows(query, likeVal) + .collect { recordToModel(modelClass, it) } } + public def refresh(def model) { def query = new StringBuilder() .append('SELECT * FROM ') @@ -110,7 +131,7 @@ public class DbApi { case Tag: return getTagsWhere(criteria) } } public def save(def model) { - if (model.id > 0) return update(model) + if (model.id) return update(model) else return create(model) } public def update(def model) { @@ -142,9 +163,8 @@ public class DbApi { def columns = [] def params = [] - getInstanceFields(model.class) - .findAll { it.name != 'id' } - .each { field -> + if (!model.id) model.id = UUID.randomUUID() + getInstanceFields(model.class).each { field -> //if (field.class.getAnnotation(Entity)) // check to see if we // have nested models columns << '"' + nameFromModel(field.name) + '"' @@ -160,7 +180,7 @@ public class DbApi { .append(')').toString() logger.debug('Creating model.\n\tSQL: {}\n\tPARAMS: {}', query, params) - model.id = sql.executeInsert(query, params)[0][0] + sql.executeInsert(query, params) return model } public def delete(def model) { @@ -176,7 +196,7 @@ public class DbApi { return sql.updateCount } public def associate(Class modelClass1, Class modelClass2 , - Integer firstId, Integer secondId) { + UUID firstId, UUID secondId) { String linkTable = pluralize(nameFromModel(modelClass1.simpleName)) + '_' + pluralize(nameFromModel(modelClass2.simpleName)) String col1 = nameFromModel(modelClass1.simpleName) + '_id' @@ -203,7 +223,7 @@ public class DbApi { /// ### Album-specific methods // ======================================================================= - public Album getAlbumById(int id) { return getById(Album, id) } + public Album getAlbumById(UUID id) { return getById(Album, id) } public List getAlbums() { return getAll(Album) } @@ -268,7 +288,7 @@ public class DbApi { /// ### Artist-specific methods public List getArtists() { return getAll(Artist) } - public Artist getArtistById(int id) { return getById(Artist, id) } + public Artist getArtistById(UUID id) { return getById(Artist, id) } public Artist getArtistByName(String name) { def artists = getByName(Artist, name) @@ -314,7 +334,7 @@ public class DbApi { public List getArtistsLikeName(String name) { return getLike(Artist, ['name'], [name]) } - public def addAlbumArtist(int albumId, int artistId) { + public def addAlbumArtist(UUID albumId, UUID artistId) { return associate(Artist, Album, artistId, albumId) } public void removeEmptyArtists() { @@ -330,7 +350,7 @@ public class DbApi { sql.execute(query) } /// ### Bookmark-specific methods - public Bookmark getBookmarkById(int id) { return getById(Bookmark, id) } + public Bookmark getBookmarkById(UUID id) { return getById(Bookmark, id) } public List getBookmarkByName(String name) { return getByName(Bookmark, name) } @@ -394,10 +414,10 @@ public class DbApi { .collect { recordToModel(Bookmark, it) } } /// ### Image-specific methods - public Image getImageById(int id) { return getById(Image, id) } + public Image getImageById(UUID id) { return getById(Image, id) } /// ### MediaFile-specific methods - public MediaFile getMediaFileById(int id) { return getById(MediaFile, id) } + public MediaFile getMediaFileById(UUID id) { return getById(MediaFile, id) } public List getMediaFileByName(String name) { return getByName(MediaFile, name) } @@ -460,21 +480,25 @@ public class DbApi { sqlParams << params.name } if (params.discNumber) { - query.append('mf.disc_number = ?') + whereClauses << 'mf.disc_number = ?' sqlParams << params.discNumber } if (params.trackNumber) { - query.append('mf.track_number = ?') + whereClauses << 'mf.track_number = ?' sqlParams << params.trackNumber } if (params.filePath) { - query.append('mf.file_path = ?') + whereClauses << 'mf.file_path = ?' sqlParams << params.filePath } if (params.fileHash) { - query.append('mf.file_hash = ?') + whereClauses << 'mf.file_hash = ?' sqlParams << params.fileHash } + if (params.containsKey('presentLocally')) { + whereClauses << 'mf.present_locally = ?' + sqlParams << params.presentLocally } + if (params.tags) { params.tags.eachWithIndex { tag, idx -> String L = "mft${idx}"; @@ -518,13 +542,13 @@ public class DbApi { logger.debug('Finding untagged media files.\n\tSQL: {}', query) return sql.rows(query).collect { recordToModel(MediaFile, it) } } - public def associateMediaFileWithAlbum(int mediaFileId, int albumId) { + public def associateMediaFileWithAlbum(UUID mediaFileId, UUID albumId) { return associate(Album, MediaFile, albumId, mediaFileId) } - public def associateMediaFileWithArtist(int mediaFileId, int artistId) { + public def associateMediaFileWithArtist(UUID mediaFileId, UUID artistId) { return associate(Album, MediaFile, artistId, mediaFileId) } - public def incrementPlayCount(int mediaFileId) { + public def incrementPlayCount(UUID mediaFileId) { def query = 'UPDATE media_files SET play_count = play_count + 1 WHERE ID = ?' def params = [mediaFileId] @@ -541,7 +565,7 @@ public class DbApi { /// ### Playlist-specific methods public List getPlaylists() { return getAll(Playlist) } - public Playlist getPlaylistById(int id) { return getById(Playlist, id) } + public Playlist getPlaylistById(UUID id) { return getById(Playlist, id) } public List getPlaylistByName(String name) { return getByName(Playlist, name) } @@ -613,7 +637,7 @@ public class DbApi { return sql.rows(query, sqlParams) .collect { recordToModel(Playlist, it) } } - public int getNextPlaylistPosition(int playlistId) { + public int getNextPlaylistPosition(UUID playlistId) { String query = """\ SELECT COALESCE(MAX(position), 0) + 1 FROM playlists_media_files @@ -624,7 +648,7 @@ public class DbApi { return sql.firstRow(query, playlistId)[0] } - private int incrementPlaylistPositions(int playlistId, int startPosition, + private int incrementPlaylistPositions(UUID playlistId, int startPosition, int incrementAmount) { String query = """\ UPDATE playlists_media_files @@ -636,8 +660,8 @@ public class DbApi { query, params) return sql.executeUpdate(query, params) } - public Playlist addToPlaylist(int playlistId, - List mediaFileIds, int startPosition = -1) { + public Playlist addToPlaylist(UUID playlistId, + List mediaFileIds, int startPosition = -1) { String query def params @@ -665,7 +689,7 @@ public class DbApi { p.lastUsed = new Timestamp(new Date().time) return update(p) } } - public Playlist addToPlaylist(int playlistId, int mediaFileId, + public Playlist addToPlaylist(UUID playlistId, UUID mediaFileId, int position = -1) { String query @@ -690,7 +714,7 @@ public class DbApi { p.lastUsed = new Timestamp(new Date().time) return update(p) } } - public Playlist removeFromPlaylist(int playlistId, int mediaFileId) { + public Playlist removeFromPlaylist(UUID playlistId, UUID mediaFileId) { String getPositionQuery = """\ SELECT position FROM playlists_media_files WHERE playlist_id = ? AND media_file_id = ?""" @@ -735,7 +759,7 @@ public class DbApi { return getById(Playlist, playlistId) } } - public Playlist removeAllFromPlaylist(int playlistId) { + public Playlist removeAllFromPlaylist(UUID playlistId) { return withTransaction { def p = getById(Playlist, playlistId) if (!p) return null @@ -762,7 +786,7 @@ public class DbApi { sql.execute(query) } /// ### Tag-specific methods - public Tag getTagById(int id) { return getById(Tag, id) } + public Tag getTagById(UUID id) { return getById(Tag, id) } public Tag getTagByName(String name) { return getByName(Tag, name)[0] } public List getTags() { return getAll(Tag, 'name ASC') } @@ -807,7 +831,7 @@ public class DbApi { query, sqlParams) return sql.rows(query, sqlParams).collect { recordToModel(Tag, it) } } - public def tagMediaFiles(List mediaFileIds, List tagNames) { + public def tagMediaFiles(List mediaFileIds, List tagNames) { String insertQuery = 'INSERT INTO media_files_tags VALUES (?, ?)' String checkQuery = 'SELECT * FROM media_files_tags WHERE media_file_id = ? AND tag_id = ?' def params @@ -828,8 +852,7 @@ public class DbApi { params) sql.executeInsert(insertQuery, params) } } } } } - public def untagMediaFiles(List mediaFileIds, - List tagNames) { + public def untagMediaFiles(List mediaFileIds, List tagNames) { withTransaction { List tags = tagNames.collect(this.&getTagByName).findAll() diff --git a/core/src/main/java/com/jdbernard/wdiwtlt/db/models/Bookmark.java b/core/src/main/java/com/jdbernard/wdiwtlt/db/models/Bookmark.java index e35a68a..650e06f 100644 --- a/core/src/main/java/com/jdbernard/wdiwtlt/db/models/Bookmark.java +++ b/core/src/main/java/com/jdbernard/wdiwtlt/db/models/Bookmark.java @@ -2,11 +2,12 @@ package com.jdbernard.wdiwtlt.db.models; import java.sql.Timestamp; import java.util.Date; +import java.util.UUID; public class Bookmark extends Model { public String name; - public int playlistId; - public int mediaFileId; + public UUID playlistId; + public UUID mediaFileId; public int playIndex; public int playTimeMs = 0; public boolean userCreated; diff --git a/core/src/main/java/com/jdbernard/wdiwtlt/db/models/MediaFile.java b/core/src/main/java/com/jdbernard/wdiwtlt/db/models/MediaFile.java index 5212133..d30f0b4 100644 --- a/core/src/main/java/com/jdbernard/wdiwtlt/db/models/MediaFile.java +++ b/core/src/main/java/com/jdbernard/wdiwtlt/db/models/MediaFile.java @@ -16,6 +16,7 @@ public class MediaFile extends Model { public String metaInfoSource = TAG_INFO; public Timestamp dateAdded = new Timestamp(new Date().getTime()); public Timestamp lastPlayed; + public boolean presentLocally = true; public String comment; public String toString() { diff --git a/core/src/main/java/com/jdbernard/wdiwtlt/db/models/Model.java b/core/src/main/java/com/jdbernard/wdiwtlt/db/models/Model.java index f8b873a..a3821d6 100644 --- a/core/src/main/java/com/jdbernard/wdiwtlt/db/models/Model.java +++ b/core/src/main/java/com/jdbernard/wdiwtlt/db/models/Model.java @@ -1,16 +1,19 @@ package com.jdbernard.wdiwtlt.db.models; +import java.util.UUID; import javax.persistence.Entity; @Entity public class Model implements Comparable { - public Integer id; + public UUID id; public boolean equals(Object thatObj) { if (thatObj == null) return false; if (this.getClass() != thatObj.getClass()) return false; Model that = (Model) thatObj; + + if (this.id == null || that.id == null) return false; return this.id.equals(that.id); } public int compareTo(Model that) { diff --git a/core/src/main/java/com/jdbernard/wdiwtlt/db/models/Playlist.java b/core/src/main/java/com/jdbernard/wdiwtlt/db/models/Playlist.java index 7a43f1f..cabb53c 100644 --- a/core/src/main/java/com/jdbernard/wdiwtlt/db/models/Playlist.java +++ b/core/src/main/java/com/jdbernard/wdiwtlt/db/models/Playlist.java @@ -2,12 +2,13 @@ package com.jdbernard.wdiwtlt.db.models; import java.sql.Timestamp; import java.util.Date; +import java.util.UUID; public class Playlist extends Model { public boolean userCreated = false; public String name; public int mediaFileCount = 0; - public Integer copiedFromId = null; + public UUID copiedFromId = null; public Timestamp createdAt = new Timestamp(new Date().getTime()); public Timestamp lastUsed = new Timestamp(new Date().getTime()); diff --git a/core/src/main/sql/migrations/20151209054632-initial-schema-up.sql b/core/src/main/sql/migrations/20151209054632-initial-schema-up.sql index ce6ac57..dfd4211 100644 --- a/core/src/main/sql/migrations/20151209054632-initial-schema-up.sql +++ b/core/src/main/sql/migrations/20151209054632-initial-schema-up.sql @@ -1,12 +1,12 @@ CREATE TABLE artists ( - id SERIAL PRIMARY KEY, + id UUID PRIMARY KEY, name VARCHAR UNIQUE NOT NULL ); CREATE INDEX artists_name_idx ON artists(name); CREATE TABLE albums ( - id SERIAL PRIMARY KEY, + id UUID PRIMARY KEY, name VARCHAR NOT NULL, year INTEGER, track_total INTEGER @@ -15,7 +15,7 @@ CREATE TABLE albums ( CREATE INDEX albums_name_idx ON albums(name); CREATE TABLE media_files ( - id SERIAL PRIMARY KEY, + id UUID PRIMARY KEY, name VARCHAR NOT NULL, disc_number VARCHAR NOT NULL DEFAULT '1', track_number INTEGER, @@ -25,53 +25,54 @@ CREATE TABLE media_files ( meta_info_source VARCHAR NOT NULL, -- 'tag' or 'filesystem' date_added TIMESTAMP NOT NULL DEFAULT NOW(), last_played TIMESTAMP, + present_locally BOOLEAN NOT NULL DEFAULT TRUE, comment VARCHAR DEFAULT '' ); CREATE INDEX media_files_name_idx ON media_files(name); CREATE TABLE artists_media_files ( - artist_id INTEGER NOT NULL REFERENCES artists(id) ON DELETE CASCADE, - media_file_id INTEGER NOT NULL REFERENCES media_files(id) ON DELETE CASCADE, + artist_id UUID NOT NULL REFERENCES artists(id) ON DELETE CASCADE ON UPDATE CASCADE, + media_file_id UUID NOT NULL REFERENCES media_files(id) ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY (artist_id, media_file_id) ); CREATE TABLE albums_media_files ( - album_id INTEGER NOT NULL REFERENCES albums(id) ON DELETE CASCADE, - media_file_id INTEGER NOT NULL REFERENCES media_files(id) ON DELETE CASCADE, + album_id UUID NOT NULL REFERENCES albums(id) ON DELETE CASCADE ON UPDATE CASCADE, + media_file_id UUID NOT NULL REFERENCES media_files(id) ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY (album_id, media_file_id) ); CREATE TABLE tags ( - id SERIAL PRIMARY KEY, + id UUID PRIMARY KEY, name VARCHAR UNIQUE NOT NULL, description VARCHAR NOT NULL DEFAULT '' ); CREATE TABLE playlists ( - id SERIAL PRIMARY KEY, + id UUID PRIMARY KEY, user_created BOOLEAN NOT NULL DEFAULT FALSE, name VARCHAR NOT NULL, media_file_count INTEGER NOT NULL DEFAULT 0, - copied_from_id INTEGER DEFAULT NULL, + copied_from_id UUID DEFAULT NULL, created_at TIMESTAMP NOT NULL DEFAULT NOW(), last_used TIMESTAMP NOT NULL DEFAULT NOW() ); CREATE TABLE playlists_media_files ( - playlist_id INTEGER NOT NULL REFERENCES playlists(id) ON DELETE CASCADE, - media_file_id INTEGER NOT NULL REFERENCES media_files(id) ON DELETE CASCADE, + playlist_id UUID NOT NULL REFERENCES playlists(id) ON DELETE CASCADE ON UPDATE CASCADE, + media_file_id UUID NOT NULL REFERENCES media_files(id) ON DELETE CASCADE ON UPDATE CASCADE, position INTEGER NOT NULL DEFAULT 0, PRIMARY KEY (playlist_id, media_file_id, position), UNIQUE (playlist_id, position) ); CREATE TABLE bookmarks ( - id SERIAL PRIMARY KEY, + id UUID PRIMARY KEY, name VARCHAR, user_created BOOLEAN NOT NULL DEFAULT FALSE, - playlist_id INTEGER NOT NULL REFERENCES playlists(id) ON DELETE CASCADE, - media_file_id INTEGER NOT NULL REFERENCES media_files(id) ON DELETE CASCADE, + playlist_id UUID NOT NULL REFERENCES playlists(id) ON DELETE CASCADE ON UPDATE CASCADE, + media_file_id UUID NOT NULL REFERENCES media_files(id) ON DELETE CASCADE ON UPDATE CASCADE, play_index INTEGER NOT NULL, play_time_ms INTEGER NOT NULL DEFAULT 0, created_at TIMESTAMP NOT NULL DEFAULT NOW(), @@ -82,30 +83,30 @@ CREATE INDEX bookmarks_playlist_id_idx ON bookmarks (playlist_id); CREATE INDEX bookmarks_media_file_id_idx ON bookmarks (media_file_id); CREATE TABLE media_files_tags ( - media_file_id INTEGER REFERENCES media_files(id) ON DELETE CASCADE, - tag_id INTEGER REFERENCES tags(id) ON DELETE CASCADE, + media_file_id UUID REFERENCES media_files(id) ON DELETE CASCADE ON UPDATE CASCADE, + tag_id UUID REFERENCES tags(id) ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY (media_file_id, tag_id) ); CREATE TABLE images ( - id SERIAL PRIMARY KEY, + id UUID PRIMARY KEY, url VARCHAR ); CREATE TABLE artists_images ( - artist_id INTEGER REFERENCES artists (id) ON DELETE CASCADE, - image_id INTEGER REFERENCES images (id) ON DELETE CASCADE, + artist_id UUID REFERENCES artists (id) ON DELETE CASCADE ON UPDATE CASCADE, + image_id UUID REFERENCES images (id) ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY (artist_id, image_id) ); CREATE TABLE albums_images ( - album_id INTEGER REFERENCES albums (id) ON DELETE CASCADE, - image_id INTEGER REFERENCES images (id) ON DELETE CASCADE, + album_id UUID REFERENCES albums (id) ON DELETE CASCADE ON UPDATE CASCADE, + image_id UUID REFERENCES images (id) ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY (album_id, image_id) ); CREATE TABLE artists_albums ( - artist_id INTEGER NOT NULL REFERENCES artists (id) ON DELETE CASCADE, - album_id INTEGER NOT NULL REFERENCES albums (id) ON DELETE CASCADE, + artist_id UUID NOT NULL REFERENCES artists (id) ON DELETE CASCADE ON UPDATE CASCADE, + album_id UUID NOT NULL REFERENCES albums (id) ON DELETE CASCADE ON UPDATE CASCADE, PRIMARY KEY (artist_id, album_id) );