Initial revision of music repository code.
git-svn-id: https://samskivert.googlecode.com/svn/trunk@14 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
@@ -0,0 +1,17 @@
|
|||||||
|
//
|
||||||
|
// $Id: Category.java,v 1.1 2000/11/08 06:42:57 mdb Exp $
|
||||||
|
|
||||||
|
package robodj.repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The category class represents an entry in the category table which
|
||||||
|
* simply maps category ids to category names.
|
||||||
|
*/
|
||||||
|
public class Category
|
||||||
|
{
|
||||||
|
/** This category's unique identifier. */
|
||||||
|
public int categoryid;
|
||||||
|
|
||||||
|
/** The name of this category. */
|
||||||
|
public String name;
|
||||||
|
}
|
||||||
@@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// $Id: CategoryMapping.java,v 1.1 2000/11/08 06:42:57 mdb Exp $
|
||||||
|
|
||||||
|
package robodj.repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is a convenience class that allows us to easily manipulate rows in
|
||||||
|
* the category_map table.
|
||||||
|
*/
|
||||||
|
public class CategoryMapping
|
||||||
|
{
|
||||||
|
/** The id of the category to which the entry is mapped. */
|
||||||
|
public int categoryid;
|
||||||
|
|
||||||
|
/** The entry that is mapped to the specified category. */
|
||||||
|
public int entryid;
|
||||||
|
|
||||||
|
public CategoryMapping ()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public CategoryMapping (int catid, int entid)
|
||||||
|
{
|
||||||
|
categoryid = catid;
|
||||||
|
entryid = entid;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
//
|
||||||
|
// $Id: Entry.java,v 1.1 2000/11/08 06:42:57 mdb Exp $
|
||||||
|
|
||||||
|
package robodj.repository;
|
||||||
|
|
||||||
|
import com.samskivert.util.StringUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The repository contains entries which represent music taken from any of
|
||||||
|
* a variety of sources (CDs, CD singles, downloaded from the Internet,
|
||||||
|
* stored remotely on the net, etc.). An entry is a collection of songs
|
||||||
|
* with a title and some other metadata.
|
||||||
|
*/
|
||||||
|
public class Entry
|
||||||
|
{
|
||||||
|
/** The unique identifier for this entry. */
|
||||||
|
public int entryid;
|
||||||
|
|
||||||
|
/** The title of this entry. */
|
||||||
|
public String title;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The artist that created the music for this entry or "Various" if it
|
||||||
|
* is not a single-artist work.
|
||||||
|
*/
|
||||||
|
public String artist;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The source media for this entry (CD, record, tape, Internet, etc.).
|
||||||
|
*/
|
||||||
|
public String source;
|
||||||
|
|
||||||
|
/** The songs associated with this entry. */
|
||||||
|
public transient Song[] songs;
|
||||||
|
|
||||||
|
public String toString ()
|
||||||
|
{
|
||||||
|
return "[entryid=" + entryid + ", title=" + title +
|
||||||
|
", artist=" + artist + ", source=" + source +
|
||||||
|
", songs=" + StringUtil.toString(songs) + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
#
|
||||||
|
# $Id: Makefile,v 1.1 2000/11/08 06:42:57 mdb Exp $
|
||||||
|
|
||||||
|
ROOT = ../..
|
||||||
|
|
||||||
|
SRCS = \
|
||||||
|
Category.java \
|
||||||
|
CategoryMapping.java \
|
||||||
|
Entry.java \
|
||||||
|
Repository.java \
|
||||||
|
RepositoryTest.java \
|
||||||
|
Song.java \
|
||||||
|
|
||||||
|
include $(ROOT)/build/Makefile.java
|
||||||
@@ -0,0 +1,394 @@
|
|||||||
|
//
|
||||||
|
// $Id: Repository.java,v 1.1 2000/11/08 06:42:57 mdb Exp $
|
||||||
|
|
||||||
|
package robodj.repository;
|
||||||
|
|
||||||
|
import java.sql.*;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
import com.samskivert.util.*;
|
||||||
|
import jora.*;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The repository class provides access to the music information
|
||||||
|
* repository which contains information on all of the music registered
|
||||||
|
* with the system (album names, track names, paths to the actual media,
|
||||||
|
* etc.).
|
||||||
|
*
|
||||||
|
* <p> Entries are stored in the repository according to genre. An entry
|
||||||
|
* may be associated with multiple genres.
|
||||||
|
*/
|
||||||
|
public class Repository
|
||||||
|
{
|
||||||
|
/**
|
||||||
|
* Creates the repository and opens the music database. A properties
|
||||||
|
* object should be supplied with the following fields:
|
||||||
|
*
|
||||||
|
* <pre>
|
||||||
|
* driver=[jdbc driver class]
|
||||||
|
* url=[jdbc driver url]
|
||||||
|
* username=[jdbc username]
|
||||||
|
* password=[jdbc password]
|
||||||
|
* </pre>
|
||||||
|
*
|
||||||
|
* @param props a properties object containing the configuration
|
||||||
|
* parameters for the repository.
|
||||||
|
*/
|
||||||
|
public Repository (Properties props)
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
// create our JORA session
|
||||||
|
String dclass =
|
||||||
|
requireProp(props, "driver", "No driver class specified.");
|
||||||
|
_session = new Session(dclass);
|
||||||
|
|
||||||
|
// connect the session to the database
|
||||||
|
String url =
|
||||||
|
requireProp(props, "url", "No driver url specified.");
|
||||||
|
String username =
|
||||||
|
requireProp(props, "username", "No driver username specified.");
|
||||||
|
String password =
|
||||||
|
requireProp(props, "password", "No driver password specified.");
|
||||||
|
_session.open(url, username, password);
|
||||||
|
|
||||||
|
// set auto-commit to false
|
||||||
|
_session.connection.setAutoCommit(false);
|
||||||
|
|
||||||
|
// create our table objects
|
||||||
|
_etable = new Table(Entry.class.getName(), "entries", _session,
|
||||||
|
"entryid");
|
||||||
|
_stable = new Table(Song.class.getName(), "songs", _session,
|
||||||
|
"songid");
|
||||||
|
_ctable = new Table(Category.class.getName(), "categories", _session,
|
||||||
|
"categoryid");
|
||||||
|
_cmtable = new Table(CategoryMapping.class.getName(), "category_map",
|
||||||
|
_session, new String[] {"categoryid", "entryid"});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static String requireProp (Properties props,
|
||||||
|
String name, String errmsg)
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
String value = props.getProperty(name);
|
||||||
|
if (StringUtil.blank(value)) {
|
||||||
|
throw new SQLException(errmsg);
|
||||||
|
}
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A repository should be shutdown before the calling code disposes of
|
||||||
|
* it. This allows the repository to cleanly terminate the underlying
|
||||||
|
* database services.
|
||||||
|
*/
|
||||||
|
public void shutdown ()
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
_session.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the entry with the specified entry id or null if no entry
|
||||||
|
* with that id exists.
|
||||||
|
*/
|
||||||
|
public Entry getEntry (int entryid)
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
// look up the entry
|
||||||
|
Cursor ec = _etable.select("where entryid = " + entryid);
|
||||||
|
|
||||||
|
// fetch the entry from the cursor
|
||||||
|
Entry entry = (Entry)ec.next();
|
||||||
|
|
||||||
|
if (entry != null) {
|
||||||
|
// and call next() again to cause the cursor to close itself
|
||||||
|
ec.next();
|
||||||
|
|
||||||
|
// load up the songs for this entry
|
||||||
|
Cursor sc = _stable.select("where entryid = " + entryid);
|
||||||
|
List slist = sc.toArrayList();
|
||||||
|
entry.songs = new Song[slist.size()];
|
||||||
|
slist.toArray(entry.songs);
|
||||||
|
}
|
||||||
|
|
||||||
|
return entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inserts a new entry into the table. All fields except the entryid
|
||||||
|
* should contain valid values. The entryid field should be zero. The
|
||||||
|
* songs array should contain song objects for all of the songs
|
||||||
|
* associated with this entry. The entryid field (in the entry object
|
||||||
|
* and the song objects) will be filled in with the entryid of the
|
||||||
|
* newly created entry.
|
||||||
|
*/
|
||||||
|
public void insertEntry (Entry entry)
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// insert the entry into the entry table
|
||||||
|
_etable.insert(entry);
|
||||||
|
|
||||||
|
// update the entryid now that it's known
|
||||||
|
entry.entryid = lastInsertedId();
|
||||||
|
|
||||||
|
// and insert all of it's songs into the songs table
|
||||||
|
if (entry.songs != null) {
|
||||||
|
for (int i = 0; i < entry.songs.length; i++) {
|
||||||
|
// insert the proper entryid
|
||||||
|
entry.songs[i].entryid = entry.entryid;
|
||||||
|
_stable.insert(entry.songs[i]);
|
||||||
|
// find out what songid was assigned
|
||||||
|
entry.songs[i].songid = lastInsertedId();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_session.commit();
|
||||||
|
|
||||||
|
} catch (SQLException sqe) {
|
||||||
|
// back out our changes if something got hosed
|
||||||
|
_session.rollback();
|
||||||
|
throw sqe;
|
||||||
|
|
||||||
|
} catch (RuntimeException rte) {
|
||||||
|
// back out our changes if something got hosed
|
||||||
|
_session.rollback();
|
||||||
|
throw rte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected int lastInsertedId ()
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
// we have to do this by hand. alas all is not roses.
|
||||||
|
Statement stmt = _session.connection.createStatement();
|
||||||
|
ResultSet rs = stmt.executeQuery("select LAST_INSERT_ID()");
|
||||||
|
if (rs.next()) {
|
||||||
|
return rs.getInt(1);
|
||||||
|
} else {
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates an entry that was previously fetched from the database. The
|
||||||
|
* number of songs in the songs array <em>must not</em> have changed
|
||||||
|
* (the fields of those songs can have changed, however). If you need
|
||||||
|
* to add or remove songs, you should use the specific member
|
||||||
|
* functions for doing that.
|
||||||
|
*
|
||||||
|
* @see addSongToEntry
|
||||||
|
* @see removeSongFromEntry
|
||||||
|
*/
|
||||||
|
public void updateEntry (Entry entry)
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// update the entry
|
||||||
|
_etable.update(entry);
|
||||||
|
|
||||||
|
// and the entry's songs
|
||||||
|
if (entry.songs != null) {
|
||||||
|
for (int i = 0; i < entry.songs.length; i++) {
|
||||||
|
_stable.update(entry.songs[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_session.commit();
|
||||||
|
|
||||||
|
} catch (SQLException sqe) {
|
||||||
|
// back out our changes if something got hosed
|
||||||
|
_session.rollback();
|
||||||
|
throw sqe;
|
||||||
|
|
||||||
|
} catch (RuntimeException rte) {
|
||||||
|
// back out our changes if something got hosed
|
||||||
|
_session.rollback();
|
||||||
|
throw rte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the entry (and all associated songs) from the database.
|
||||||
|
*/
|
||||||
|
public void deleteEntry (Entry entry)
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// remove the entry from the entry table and the foreign key
|
||||||
|
// constraints will automatically remove all of the
|
||||||
|
// corresponding songs
|
||||||
|
_etable.delete(entry);
|
||||||
|
_session.commit();
|
||||||
|
|
||||||
|
} catch (SQLException sqe) {
|
||||||
|
// back out our changes if something got hosed
|
||||||
|
_session.rollback();
|
||||||
|
throw sqe;
|
||||||
|
|
||||||
|
} catch (RuntimeException rte) {
|
||||||
|
// back out our changes if something got hosed
|
||||||
|
_session.rollback();
|
||||||
|
throw rte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the specified song to the specified entry (both in the
|
||||||
|
* database and in the current Java object that represents the
|
||||||
|
* database information). The song should not have a value supplied
|
||||||
|
* for the songid field because it is expected that this song will be
|
||||||
|
* newly added to the database (use updateSong to update an existing
|
||||||
|
* song's information). Upon successful return from this function, the
|
||||||
|
* songid field will be filled in with the songid value assigned to
|
||||||
|
* the newly created song.
|
||||||
|
*
|
||||||
|
* @see updateSong
|
||||||
|
*/
|
||||||
|
public void addSongToEntry (Entry entry, Song song)
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
// fill in the appropriate entry id value
|
||||||
|
song.entryid = entry.entryid;
|
||||||
|
|
||||||
|
// and stick the song into the database
|
||||||
|
_stable.insert(song);
|
||||||
|
// communicate the songid back to the caller
|
||||||
|
song.songid = lastInsertedId();
|
||||||
|
_session.commit();
|
||||||
|
|
||||||
|
} catch (SQLException sqe) {
|
||||||
|
// back out our changes if something got hosed
|
||||||
|
_session.rollback();
|
||||||
|
throw sqe;
|
||||||
|
|
||||||
|
} catch (RuntimeException rte) {
|
||||||
|
// back out our changes if something got hosed
|
||||||
|
_session.rollback();
|
||||||
|
throw rte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Updates the specified song individually. The songid and entryid
|
||||||
|
* parameters should already be set to the appropriate values.
|
||||||
|
*/
|
||||||
|
public void updateSong (Song song)
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
_stable.update(song);
|
||||||
|
_session.commit();
|
||||||
|
|
||||||
|
} catch (SQLException sqe) {
|
||||||
|
// back out our changes if something got hosed
|
||||||
|
_session.rollback();
|
||||||
|
throw sqe;
|
||||||
|
|
||||||
|
} catch (RuntimeException rte) {
|
||||||
|
// back out our changes if something got hosed
|
||||||
|
_session.rollback();
|
||||||
|
throw rte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a category with the specified name and returns the id of
|
||||||
|
* that new category.
|
||||||
|
*/
|
||||||
|
public int createCategory (String name)
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Category cat = new Category();
|
||||||
|
cat.name = name;
|
||||||
|
_ctable.insert(cat);
|
||||||
|
|
||||||
|
int categoryid = lastInsertedId();
|
||||||
|
_session.commit();
|
||||||
|
|
||||||
|
return categoryid;
|
||||||
|
|
||||||
|
} catch (SQLException sqe) {
|
||||||
|
// back out our changes if something got hosed
|
||||||
|
_session.rollback();
|
||||||
|
throw sqe;
|
||||||
|
|
||||||
|
} catch (RuntimeException rte) {
|
||||||
|
// back out our changes if something got hosed
|
||||||
|
_session.rollback();
|
||||||
|
throw rte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return an array of all of the categories that currently exist in
|
||||||
|
* the category table.
|
||||||
|
*/
|
||||||
|
public Category[] getCategories ()
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
Cursor ccur = _ctable.select("");
|
||||||
|
List clist = ccur.toArrayList();
|
||||||
|
Category[] cats = new Category[clist.size()];
|
||||||
|
clist.toArray(cats);
|
||||||
|
return cats;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Associates the specified entry with the specified category. An
|
||||||
|
* entry can be mapped into multiple categories.
|
||||||
|
*/
|
||||||
|
public void associateEntry (Entry entry, int categoryid)
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
CategoryMapping map =
|
||||||
|
new CategoryMapping(categoryid, entry.entryid);
|
||||||
|
_cmtable.insert(map);
|
||||||
|
_session.commit();
|
||||||
|
|
||||||
|
} catch (SQLException sqe) {
|
||||||
|
// back out our changes if something got hosed
|
||||||
|
_session.rollback();
|
||||||
|
throw sqe;
|
||||||
|
|
||||||
|
} catch (RuntimeException rte) {
|
||||||
|
// back out our changes if something got hosed
|
||||||
|
_session.rollback();
|
||||||
|
throw rte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Disassociates the specified entry from the specified category.
|
||||||
|
*/
|
||||||
|
public void disassociateEntry (Entry entry, int categoryid)
|
||||||
|
throws SQLException
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
CategoryMapping map =
|
||||||
|
new CategoryMapping(categoryid, entry.entryid);
|
||||||
|
_cmtable.delete(map);
|
||||||
|
_session.commit();
|
||||||
|
|
||||||
|
} catch (SQLException sqe) {
|
||||||
|
// back out our changes if something got hosed
|
||||||
|
_session.rollback();
|
||||||
|
throw sqe;
|
||||||
|
|
||||||
|
} catch (RuntimeException rte) {
|
||||||
|
// back out our changes if something got hosed
|
||||||
|
_session.rollback();
|
||||||
|
throw rte;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Session _session;
|
||||||
|
protected Table _etable;
|
||||||
|
protected Table _stable;
|
||||||
|
protected Table _ctable;
|
||||||
|
protected Table _cmtable;
|
||||||
|
}
|
||||||
@@ -0,0 +1,43 @@
|
|||||||
|
//
|
||||||
|
// $Id: RepositoryTest.java,v 1.1 2000/11/08 06:42:57 mdb Exp $
|
||||||
|
|
||||||
|
package robodj.repository;
|
||||||
|
|
||||||
|
import java.util.Properties;
|
||||||
|
|
||||||
|
public class RepositoryTest
|
||||||
|
{
|
||||||
|
public static void main (String[] args)
|
||||||
|
{
|
||||||
|
try {
|
||||||
|
Properties props = new Properties();
|
||||||
|
props.put("driver", "org.gjt.mm.mysql.Driver");
|
||||||
|
props.put("url", "jdbc:mysql://localhost:3306/robodj");
|
||||||
|
props.put("username", "www");
|
||||||
|
props.put("password", "Il0ve2PL@Y");
|
||||||
|
|
||||||
|
Repository rep = new Repository(props);
|
||||||
|
|
||||||
|
Entry ient = new Entry();
|
||||||
|
ient.title = "Test entry";
|
||||||
|
ient.artist = "Test artist";
|
||||||
|
ient.source = "Test source";
|
||||||
|
ient.songs = new Song[1];
|
||||||
|
ient.songs[0] = new Song();
|
||||||
|
ient.songs[0].position = 1;
|
||||||
|
ient.songs[0].title = "Test song one";
|
||||||
|
ient.songs[0].location = "test_song_one.mp3";
|
||||||
|
ient.songs[0].duration = 99;
|
||||||
|
rep.insertEntry(ient);
|
||||||
|
System.out.println("--> " + ient);
|
||||||
|
|
||||||
|
Entry ent = rep.getEntry(ient.entryid);
|
||||||
|
System.out.println("<-- " + ent);
|
||||||
|
|
||||||
|
rep.shutdown();
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,47 @@
|
|||||||
|
//
|
||||||
|
// $Id: Song.java,v 1.1 2000/11/08 06:42:57 mdb Exp $
|
||||||
|
|
||||||
|
package robodj.repository;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A song maps approximately to an individual piece of music. In most
|
||||||
|
* cases repository entries are created from albums which contain some
|
||||||
|
* number of songs. Some special cases involve mixed CDs with one big song
|
||||||
|
* or streaming Internet music sources. In these cases, the entry tends to
|
||||||
|
* be comprised of a single song which represents the entire contents of
|
||||||
|
* the entry.
|
||||||
|
*/
|
||||||
|
public class Song
|
||||||
|
{
|
||||||
|
/** The unique identifier for this song. */
|
||||||
|
public int songid;
|
||||||
|
|
||||||
|
/** The unique identifier of the entry to which this song maps. */
|
||||||
|
public int entryid;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The position of this song on the media (counting from 1 since
|
||||||
|
* that's what people do on the actual media). I'd call it index but
|
||||||
|
* that's a reserved word in SQL.
|
||||||
|
*/
|
||||||
|
public int position;
|
||||||
|
|
||||||
|
/** The title of this song. */
|
||||||
|
public String title;
|
||||||
|
|
||||||
|
/** The location of the media for this song. */
|
||||||
|
public String location;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The duration of this song (in seconds) or -1 if the duration is
|
||||||
|
* unknown.
|
||||||
|
*/
|
||||||
|
public int duration;
|
||||||
|
|
||||||
|
public String toString ()
|
||||||
|
{
|
||||||
|
return "[songid=" + songid + ", entryid=" + entryid +
|
||||||
|
", position=" + position + ", title=" + title +
|
||||||
|
", location=" + location + ", duration=" + duration + "]";
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user