Bem-vindos ao blog Preciso Estudar Sempre. Meu nome é João Paulo Maida e minha paixão é estudar.
Começo este post com a seguinte pergunta: você já parou para pensar sobre controle transacional ? Já tirou cinco minutos do seu dia para pensar no assunto ?
Minha pergunta pode parecer descabida ou até um pouco sentimental pois não fiz nenhuma apresentação sobre o tema. Contudo, faço essa pergunta porque em minha vivência de mercado já me deparei com muitos programadores que não tem a mínima idéia sobre o assunto. Obviamente eles sabem o que é uma transação, um commit ou um rollback. Porém pouquíssimos sabem implementar estruturas de controle transacionais do zero.
Interessante !
Tal fato além de ser extremamente preocupante na minha opinião, ocorre porque este tipo de questionamento não é mais necessário no mercado de trabalho atualmente. A maioria senão todos os projetos utilizam tecnologias que através da inversão de controle (IoC) implementam seu próprio controle transacional. Como resultado, oferecem interfaces bem definidas e limpas para seus usuários, tornando assim trabalho deles mais fácil e ágil. Aí está o ponto que me causa preocupação, o excesso de facilidade. Excesso o qual tende a tornar as pessoas, no caso desenvolvedores, mais preguiçosos causando um esquecimento de conceitos importantes.
Se você acabou de ler o parágrafo acima está pensando que estou falando de tecnologias como Spring, EJB 3 ou Hibernate. Acertou, estou falando exatamente disso ! Mas não vá pensar que estou condenando o uso de tais ferramentas. Pelo contrário, acho super correto utilizá-las e se você não as conhece, recomendo que estude-as. Entretanto, nunca se esqueça disso: não fique preso somente ao funcionamento das ferramentas existentes, entenda como elas funcionam ! O ponto central do meu raciocínio é justamente este, é necessário entender como “por detrás dos panos” o Spring, por exemplo, consegue executar rollback em uma transação inteira, onde nesta várias tabelas sofreram operações. Obviamente não podemos esquecer que ele não somente consegue fazer isso, mas como ainda disponibilizar uma forma que você escreva sua camada DAO (Data Access Object) de forma não acoplada a sua fachada de negócio, ou serviço.
A esta altura do campeonato você já deve ter notado que esta não é uma discussão trivial. Estamos lidando com uma situação arquitetural complexa com vários padrões de projeto, conceitos e preocupações. Logo algumas amostras de código e um diagrama de classes poderiam ajudar a trazer para a realidade o que estou propondo.
Figura 1 - Diagrama de classes proposto |
1 2 3 4 5 6 7 | package src.model.dao; import java.sql.Connection; public interface ConnectionInjector { void injectConnection(Connection connection); } |
Classe HelperDAO.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 | package src.model.dao.helper; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class HelperDAO { public void closeStatement(Statement statement) throws SQLException { if (statement == null) { throw new IllegalArgumentException( "Errorcode: 102 - Statement nulo."); } try { statement.close(); } catch (SQLException e) { throw new SQLException("Errorcode: 102 - Erro no fechamento do objeto Statement.", e); } } public void closeStatementAndResultSet(Statement statement, ResultSet resultSet) throws SQLException { this.closeStatement(statement); if (resultSet == null) { throw new IllegalArgumentException( "Errorcode: 102 - ResultSet nulo."); } try { resultSet.close(); } catch (SQLException e) { throw new SQLException("Errorcode: 102 - Erro no fechamento do objeto ResultSet.", e); } } } |
DAO.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | package src.model.dao; import src.model.dao.helper.HelperDAO; import java.util.List; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; public class DAO implements ConnectionInjector{ private Connection connection; private HelperDAO helperDAO; public DAO(){ this.helperDAO = new HelperDAO(); } @Override public void injectConnection(Connection connection) { this.connection = connection; } public List findAll() throws IntegrationException { PreparedStatement preparedStatement = null; ResultSet resultSet = null; List results = new ArrayList<>(); try{ String sql = "SELECT COLUMN1, COLUMN2, COLUMN3 FROM TABLE"; preparedStatement = this.connection.prepareStatement(sql); resultSet = preparedStatement.executeQuery(sql); while (resultSet.next()) { MyObject myObject = new MyObject(); myObject.setMyProperty1(resultSet.getLong("COLUMN1")); myObject.setMyProperty2(resultSet.getString("COLUMN2")); myObject.setMyProperty3(resultSet.getString("COLUMN3")); results.add(myObject); } return results; } catch (SQLException e){ throw new SQLException("Errorcode: 104 - Erro de SQL na listagem de todos os objetos.", e); } finally { helperDAO.closeStatementAndResultSet(preparedStatement, resultSet); } } private void insert(MyObject myObject) throws SQLException { PreparedStatement preparedStatement = null; try { int paramPos = 0; String sql = "INSER INTO TABLE (COLUMN1, COLUMN2, COLUMN3) VALUES (?,?,?)"); preparedStatement = this.connection.prepareStatement(sql); preparedStatement.setLong(paramPos++, myObject.getProperty1()); preparedStatement.setString(paramPos++, myObject.getProperty2()); preparedStatement.setString(paramPos++, myObject.getProperty3()); preparedStatement.execute(); } catch (SQLException e) { throw new SQLException( "Errorcode: 104 - Erro de SQL no cadastro do objeto.", e); } finally { helperDAO.closeStatement(preparedStatement); } } } |
HelperTransactionalControl.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | package src.model.business.helper; import java.sql.Connection; import java.sql.DriverManager; import java.sql.SQLException; import src.model.dao.ConnectionInjector; import src.model.exception.IntegrationException; public class HelperTransactionalControl { private static final String USER_DATABASE = "USER"; private static final String PASSWORD_DATABASE = "PASSWORD"; private Connection connection; public void openConnection() throws IntegrationException { try { Class.forName("com.jdbc.mysql.Driver"); this.connection = DriverManager.getConnection( "jdbc:mysql://localhost:3306/DATABASE", USER_DATABASE, PASSWORD_DATABASE); } catch (SQLException | ClassNotFoundException e) { throw new IntegrationException("Errorcode: 100 - Erro na criação de uma conexão ao banco de dados.", e); } } public void injectConnection(ConnectionInjector... daosObject){ for (ConnectionInjector daoObject : daosObject) { daoObject.injectConnection(connection); } } public void endConnection() throws IntegrationException { if (connection == null) { throw new IllegalArgumentException( "Errorcode: 102 - Connection nula."); } try { connection.close(); } catch (SQLException e) { throw new IntegrationException("Errorcode: 102 - Erro no fechamento do objeto Connection.", e); } } public void beginTransaction() throws IntegrationException{ try { connection.setAutoCommit(false); } catch (SQLException e) { throw new IntegrationException("Errorcode: 103 - Erro na abertura da transação de banco.", e); } } public void commitTransaction() throws IntegrationException{ try { connection.commit(); } catch (SQLException e) { throw new IntegrationException("Errorcode: 103 - Erro na commit da transação de banco.", e); } } public void rollbackTransaction() throws IntegrationException{ try { connection.rollback(); } catch (SQLException e) { throw new IntegrationException("Errorcode: 103 - Erro no rollback da transação de banco.", e); } } } |
BusinessFacade.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 | package src.model.business; import java.sql.SQLException; import java.util.List; import src.model.dao.DAO; import src.model.exception.IntegrationException; import src.model.business.helper.HelperTransactionalControl; public class BusinessFacade implements IBusinessFacade{ private HelperTransactionalControl helper; public BusinessFacade() { helper = new HelperTransactionalControl(); } @Override public MyObject insert(MyObject myObject) throws IntegrationException { try { DAO dao = new DAO(); helper.openConnection(); helper.beginTransaction(); helper.injectConnection(dao); dao.insert(myObject); helper.commitTransaction(); return myObject; } catch (SQLException e) { helper.rollbackTransaction(); throw new IntegrationException(e); } finally { helper.endConnection(); } } @Override public List findAll() throws IntegrationException{ try { DAO dao = new DAO(); this.helper.openConnection(); this.helper.injectConnection(dao); return dao.findAll(); } catch (SQLException e) { throw new IntegrationException(e); } finally { this.helper.endConnection(); } } } |
Note que como consequência da implementação do projeto de software proposto, a classe de tratamento do negócio ficou bem enxuta e com responsabilidades bem definidas. Não achei necessário expor aqui o código-fonte da exceção especializada (IntegrationException) e da interface IBusinessFacade porque seus conteúdos são bastante triviais e iriam causar mais volume na postagem, talvez tornando a leitura mais cansativa.
Como dito anteriormente, sinta-se livre para criticar, existem várias formas de se fazer o mesmo e é isso que engrandece um projeto de software, mas se você quiser elogiar não se acanhe 😋.
Chegamos ao fim de mais um post e neste aqui devo grandes agradecimentos a um amigo e mentor, o imenso Luís Marcelo Bruckner. Muito obrigado meu amigo.
Nenhum comentário:
Postar um comentário