Files
samskivert/src/main/java/com/samskivert/jdbc/TransitionRepository.java
T

211 lines
7.4 KiB
Java

//
// samskivert library - useful routines for java programs
// Copyright (C) 2001-2011 Michael Bayne, et al.
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.jdbc;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import com.samskivert.io.PersistenceException;
import static com.samskivert.Log.log;
/**
* Used to note that transitionary code has been run to migrate persistent data. This is especially
* useful for data that one cannot examine to determine if it's been transitioned.
*/
public class TransitionRepository extends SimpleRepository
{
/** The identifier of this connection. */
public static final String TRANSITION_DB_IDENT = "transitiondb";
/**
* An interface for the transition.
*/
public static interface Transition
{
/**
* Do the transition.
*/
public void run () throws PersistenceException;
}
/**
* Construct a TransitionRepository for the server.
*/
public TransitionRepository (ConnectionProvider conprov)
throws PersistenceException
{
super(conprov, TRANSITION_DB_IDENT);
}
/**
* Perform a transition if it has not already been applied, and record that it was applied.
*/
public void transition (Class<?> clazz, String name, Transition trans)
throws PersistenceException
{
if (!isTransitionApplied(clazz, name) && noteTransition(clazz, name)) {
try {
trans.run();
} catch (PersistenceException e) {
try {
clearTransition(clazz, name);
} catch (PersistenceException pe) {
log.warning("Failed to clear failed transition", "class", clazz, "name", name,
pe);
}
throw e;
} catch (RuntimeException rte) {
try {
clearTransition(clazz, name);
} catch (PersistenceException pe) {
log.warning("Failed to clear failed transition", "class", clazz, "name", name,
pe);
}
throw rte;
}
}
}
/**
* Returns whether the specified name transition been applied.
*/
public boolean isTransitionApplied (Class<?> clazz, final String name)
throws PersistenceException
{
final String cname = clazz.getName();
return execute(new Operation<Boolean>() {
public Boolean invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement(
" select " + liaison.columnSQL("NAME") +
" from " + liaison.tableSQL("TRANSITIONS") +
" where " + liaison.columnSQL("CLASS") + "=?" +
" and " + liaison.columnSQL("NAME") + "=?");
stmt.setString(1, cname);
stmt.setString(2, name);
ResultSet rs = stmt.executeQuery();
if (rs.next()) {
return true;
}
} finally {
JDBCUtil.close(stmt);
}
return false;
}
});
}
/**
* Note in the database that a particular transition has been applied.
*
* @return true if the transition was noted, false if it could not be noted because another
* process noted it first.
*/
public boolean noteTransition (Class<?> clazz, final String name)
throws PersistenceException
{
final String cname = clazz.getName();
return executeUpdate(new Operation<Boolean>() {
public Boolean invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement(
"insert into " + liaison.tableSQL("TRANSITIONS") + " (" +
liaison.columnSQL("CLASS") + ", " + liaison.columnSQL("NAME") +
", " + liaison.columnSQL("APPLIED") +
") values (?, ?, ?)");
stmt.setString(1, cname);
stmt.setString(2, name);
stmt.setTimestamp(3, new Timestamp(System.currentTimeMillis()));
JDBCUtil.checkedUpdate(stmt, 1);
return true;
} catch (SQLException sqe) {
if (liaison.isDuplicateRowException(sqe)) {
return false;
} else {
throw sqe;
}
} finally {
JDBCUtil.close(stmt);
}
}
});
}
/**
* Clear the transition.
*/
public void clearTransition (Class<?> clazz, final String name)
throws PersistenceException
{
final String cname = clazz.getName();
executeUpdate(new Operation<Void>() {
public Void invoke (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
PreparedStatement stmt = null;
try {
stmt = conn.prepareStatement(
" delete from " + liaison.tableSQL("TRANSITIONS") +
" where " + liaison.columnSQL("CLASS") + "=? " +
" and " + liaison.columnSQL("NAME") + "=?");
stmt.setString(1, cname);
stmt.setString(2, name);
stmt.executeUpdate(); // we don't care if it worked or not
} finally {
JDBCUtil.close(stmt);
}
return null;
}
});
}
@Override
protected void migrateSchema (Connection conn, DatabaseLiaison liaison)
throws SQLException, PersistenceException
{
liaison.createTableIfMissing(
conn,
"TRANSITIONS",
new String[] { "CLASS", "NAME", "APPLIED" },
new ColumnDefinition[] {
new ColumnDefinition("VARCHAR(200)", true, false, null),
new ColumnDefinition("VARCHAR(50)", true, false, null),
new ColumnDefinition("TIMESTAMP")
},
null,
new String[] { "CLASS", "NAME" });
}
}