001 package fj.control.db; 002 003 import fj.Unit; 004 005 import java.sql.Connection; 006 import java.sql.DriverManager; 007 import java.sql.SQLException; 008 009 /** 010 * Performs database I/O, in order to read or write the database state. 011 */ 012 public final class DbState { 013 private final Connector pc; 014 private final DB<Unit> terminal; 015 016 private DbState(final Connector pc, final DB<Unit> terminal) { 017 this.pc = pc; 018 this.terminal = terminal; 019 } 020 021 /** 022 * A simple connector (the default) that gets connections to the given database URL from the driver manager. 023 * 024 * @param url The database URL to connect to. 025 * @return A connector that generates connections to the given database. 026 */ 027 public static Connector driverManager(final String url) { 028 return new Connector() { 029 public Connection connect() throws SQLException { 030 return DriverManager.getConnection(url); 031 } 032 }; 033 } 034 035 /** 036 * Creates a database state reader given a connection URL. 037 * 038 * @param url The connection URL to the database. 039 * @return A database state reader that reads the given database. 040 */ 041 public static DbState reader(final String url) { 042 return new DbState(driverManager(url), rollback); 043 } 044 045 /** 046 * Creates a database state writer given a connection URL. 047 * 048 * @param url The connection URL to the database. 049 * @return A database state writer that writes the given database. 050 */ 051 public static DbState writer(final String url) { 052 return new DbState(driverManager(url), commit); 053 } 054 055 /** 056 * Returns a new reader that reads the database via the given Connector. 057 * 058 * @param pc A connector with which to generate database connections. 059 * @return A new reader that reads the database via the given Connector. 060 */ 061 public static DbState reader(final Connector pc) { 062 return new DbState(pc, rollback); 063 } 064 065 /** 066 * Returns a new writer that writes the database via the given Connector. 067 * 068 * @param pc A connector with which to generate database connections. 069 * @return A new writer that writes the database via the given Connector. 070 */ 071 public static DbState writer(final Connector pc) { 072 return new DbState(pc, commit); 073 } 074 075 private static final DB<Unit> rollback = new DB<Unit>() { 076 public Unit run(final Connection c) throws SQLException { 077 c.rollback(); 078 return Unit.unit(); 079 } 080 }; 081 082 private static final DB<Unit> commit = new DB<Unit>() { 083 public Unit run(final Connection c) throws SQLException { 084 c.commit(); 085 return Unit.unit(); 086 } 087 }; 088 089 /** 090 * Runs the given database action as a single transaction. 091 * 092 * @param dba A database action to run. 093 * @return The result of running the action against the database. 094 * @throws SQLException in case of a database error. 095 */ 096 public <A> A run(final DB<A> dba) throws SQLException { 097 final Connection c = pc.connect(); 098 c.setAutoCommit(false); 099 try { 100 final A a = dba.run(c); 101 terminal.run(c); 102 return a; 103 } catch (SQLException e) { 104 c.rollback(); 105 throw e; 106 } 107 finally { 108 c.close(); 109 } 110 } 111 }