JDBC: operazioni CRUD e transazioni

28 agosto 2017

Le operazioni di inserimento, cancellazione e aggiornamento di record sono note con il nome di operazioni CRUD (Create, Read, Update, Delete). In linea di principio è lecito seguire quanto fatto nei capitoli precedenti per le normali query: creare ed eseguire uno Statement (o PreparedStatement) facendo uso di una query SQL di tipo CRUD in luogo di una query di interrogazione.

Il risultato è l’apertura automatica di una transazione verso il database per l’esecuzione del nostro statement. Nella situazione di default una connessione aperta ha il flag di COMMIT impostato sul valore AUTO, questo significa che ogni operazione CRUD viene eseguita con un COMMIT automatico all’interno di una propria singola transazione.

Tale comportamento non è accettabile nel caso in cui una transazione coinvolga più statement. L’obiettivo del presente capitolo è quindi quello di modificare la classe AbstractDAO in modo tale che la gestione di una transazione sia centralizzata nella classe stessa, ed ereditata dalle varie sottoclassi DAO.

Per incapsulare la logica di gestione di una transazione, possiamo realizzare una classe membro all’interno di AbstractDAO contenente il seguente codice:

     protected abstract class Transaction {

		private List<Statement> statements;
		private Connection conn = null;

		public void execute() throws DAOException {
			try {
				conn = getConnection();
				boolean autocommit = conn.getAutoCommit();
				int isolation = conn.getTransactionIsolation();
				
				conn.setAutoCommit(false);
				conn.setTransactionIsolation(Connection.TRANSACTION_REPEATABLE_READ);
				
				buildStatements(conn);

				conn.commit();
				conn.setAutoCommit(autocommit);
				conn.setTransactionIsolation(isolation);

			} catch (SQLException e) {
				e.printStackTrace();
				try {
					if (conn != null)
						conn.rollback();
					throw new DAOException(e);
				} catch (SQLException e1) {
					e1.printStackTrace();
					throw new DAOException(e1);
				}
			} finally {

				try {
					for (Statement st : statements) {
						st.close();
					}
					if (conn != null) conn.close();
				} catch (SQLException e) {
					e.printStackTrace();
				}

				if (statements != null) statements.clear();
			}
		}
		
		public PreparedStatement getPreparedStatement(String query) throws DAOException {
			if (statements == null)
				statements = new ArrayList<PreparedStatement>();
			PreparedStatement statement;

			try {
				statement = conn.prepareStatement(query);
				statements.add(statement);
			} catch (SQLException e) {
				e.printStackTrace();
				throw new DAOException(e);
			}
			return statement;
		}

		public abstract void buildStatements(Connection conn) throws DAOException;
	}
	

La classe è dotata di un campo Connection e di un campo List per il mantenimento dei PreparedStatement creati ed utilizzati all’interno della transazione. execute() esegue la transazione attraverso i seguenti passaggi:

  • Recupero di una connessione.
  • Salvataggio dei valori correnti di autocommit e isolamento.
  • Impostazione dell’autocommit su false.
  • Impostazione di un livello di isolamento adeguato.
  • Invocazione del metodo buildStatement() contenente il corpo della transazione.
  • Esecuzione del commit.
  • Ripristino dell’isolamento e dell’autocommit al valore originario.

Una transazione può non concludersi con successo, i blocchi catch e finally gestiscono quindi rollback e rilascio delle risorse. getPreparedStatement() è utilizzato all’interno delle sottoclassi per ottenere un statement sotto la gestione automatica della classe AbstractDAO, mentre buildStatements() viene implementato dalla specifica sottoclasse.

L’architettura permette di non riscrivere la logica comune di gestione di una transazione, favorendo il riuso e la stesura di un quantitativo minore di linee di codice.

Esaminiamo il codice per l’operazione CRUD di inserimento di una persona della classe PersonaDAO:

	public void inserisciPersona(Persona persona) throws DAOException {
		Transaction transaction = new Transaction() {
			@Override
			public void buildStatements(Connection conn) throws DAOException {
				try {
					PreparedStatement pstmt = getPreparedStatement(getQuery("inserisci_persona"));
					pstmt.setInt(1, persona.getId());
					pstmt.setString(2, persona.getNome());
					pstmt.setString(3, persona.getCognome());
					pstmt.setString(4, persona.getProfessione());
					pstmt.setInt(5, persona.getEta());
					pstmt.executeUpdate();
				} catch (SQLException e) {
					e.printStackTrace();
					throw new DAOException(e);
				}
			}
		};
		transaction.execute();
	}
	

Una classe DAO deve quindi istanziare un oggetto Transaction, implementare buildStatements() ed eseguire execute(). All’interno di buildStatements() inseriamo tutta la logica di utilizzo delle operazioni CRUD eseguite nel contesto di una sola transazione.

In allegato trovate il codice completo con le query inserite nel file properties.

Tutte le lezioni

1 ... 67 68 69 ... 122

Se vuoi aggiornamenti su JDBC: operazioni CRUD e transazioni inserisci la tua e-mail nel box qui sotto:
Tags:
 
X
Se vuoi aggiornamenti su JDBC: operazioni CRUD e transazioni

inserisci la tua e-mail nel box qui sotto:

Ho letto e acconsento l'informativa sulla privacy

Acconsento al trattamento di cui al punto 3 dell'informativa sulla privacy