Não entendi muito bem o seu TransactionalCommand, Louds. Pode pôr um trecho de exemplo do uso? (pra clarear).
[quote=louds]Gerenciar as transações fora dos DAOs permite você usar mais de um DAO sem muita dor de cabeça.
[/quote]
Por isso que eu adoro o controle declarativo do Spring
Maurício, spring é legal, mas quase sempre só rola usar em projetos greenfield e sem nenhum “legado” como frameworks proprietarios e políticas corporativas. Eu nem pensei em usar springframework no atual projeto por que o trabalho que daria para ter ele homologado não ia valer o beneficio.
Tem muitos cenarios em que não usar transações, ou usar isolamentos diferente, é indispensavel.
Primeiro tem performance, uma transação é cara pro RDBMS manter. Segundo tem questões de locking e tosquisses de cada banco. Por último tem algumas operações que são melhores executadas sem transação nenhuma (um delete de 500mil registros por exemplo).
Por exemplo, o mysql com InnoDB não permite “select count(*) from x” executar em paralelo com “insert into x values(1)” até mesmo em read commited.
Outro exemplo é o sqlserver, algumas queries mais complexas nunca completam e podem fundir o servidor quando você tem muitas transações simultâneas (lock escalation acaba fazendo tuas queries tentarem table lock em metade do banco).
Por fim, emissão de relatorios ou consultas a maioria pode ser executada fora de um contexto transacional, read uncommited mesmo, sem problema. Alguns bancos reconhecem essa barbada e são muito mais eficientes.
Daniel, o transactional command é bem simples:
class TransactionManager {
public void executeWithTransaction(Runnable r) { //
Transaction t = session.beginTransaction();
boolean commited = false;
try {
r.run();
t.commit();
commited = true;
} finally {
if(!commited)
t.rollback();
}
}
}
Dai você simplesmente envolve o teu código em um Runnable e pronto.
Coisas como transações aninhadas e junção de transação exigem um pouco mais de esforço, mas o básico é isso ae.
[quote=louds]Maurício, spring é legal, mas quase sempre só rola usar em projetos greenfield e sem nenhum “legado” como frameworks proprietarios e políticas corporativas. Eu nem pensei em usar springframework no atual projeto por que o trabalho que daria para ter ele homologado não ia valer o beneficio.
[/quote]
Cara, acho que quando eu começar a trabalhar vou me matar :shock:
èééééé amigo, como eu disse, você não viu metada ainda.
Pensou quando te empurrarem o TosquiceFramework ™? Ou então a SoluçãoIn-HouseQueÉUmDrogaEUmaBombaGrande ?
Sem contar que você vai ter que aprender ele por osmose do código existente. Afinal, ainda tá para ser criado o framework caseiro com documentação razoavel.
A maior bizarrice que eu já ví. “Framework de Mensagens XXX*” (XXX = nome da empresa).
O esquema “funciona” assim:
Um programa que eu fiz, estilo o Javalee (http://javalee.sf.net), gerou milhares de classes e stored procedures baseado nas tabelas do banco.
Esse Framwork de Mensagens (aka MI) recebe umas mensagens textuais para interpretar e executar certa lógica.
Explico. Para telas de cadastro simples, é bem fácil. Você tem uma tela com os campos (id e nome), para a tabela pessoa.
Aí, no request você manda os dados e mais a msg=“Pessoa;create|Pessoa;insert”. O MI insere a pessoa no BD.
Só que é só isso. Não tem ajuda pra jogar dados na tela e etc.
O único bem disso é em telas de cadastro BEM simples, pois você não programa em Java, apenas manda a menagem. Mas quando envolve processos mais complicados, FOD$U!!!
noel,
ThreadLocal não elimina o uso de pool, apenas elimina o passa-passa de objetos entre os varios layers da aplicação. Com TL, eu faço +/- assim:
try {
threadLocal.put(pool.getConnection());
} finaly{
((Connection)threadLocal.get()).close();
threadLocal.put(null);
}
Quanto a Runnable, você tá confundindo a interface Runnable com a classe Thread. Usar a interface não implica em criar threads.
Veja que se você usa uma conecção por DAO, tuas operações não são transacionais, afinal todos precisam todas usar a mesma conecção.
Normalmente uma ThreadLocal é uma variavel estática com getter público e setter não-público. Ou seja, todo mundo pode pegar.
Dentro da mesma thread.
Já me perguntaram em PM, como eu implementei meu controle transacional. Vou colocar o código aqui. É bem simples!
Transaction.java
[code]package my.trans;
import java.sql.*;
import java.util.Map;
import javax.sql.DataSource;
public class Transaction {
private Connection conn;
private boolean oldAutCommit;
public Transaction() {
}
public void end() {
TransactionManager.endTransaction();
if (this.conn != null) {
try { this.conn.setAutoCommit( this.oldAutoCommit ); }
catch (SQLException e) {}
try { this.conn.close(); }
catch (SQLException e) {}
}
}
public Connection getConnection(String dataSource) throws Exception {
if (this.conn == null) {
try {
synchronized (this) {
this.oldAutoCommit = this.conn.getAutoCommit();
this.conn = pegaAConexaoDeAlgumLugar();
//seja de um POOL, via JNDI etc...
this.conn.setAutoCommit(false);
}
}
}
return new TransactionConnection(this.conn);
}
public void rollback() throws Exception {
this.conn.rollback();
}
public void commit() throws Exception {
this.conn.commit();
}
}[/code]
TransactionConnection.java
[code]package my.trans;
import java.sql.*;
protected class TransactionConnection implements Connection {
private Connection conn;
protected TransactionConnection(Connection conn) {
if (conn == null) {
throw new IllegalArgumentException();
}
this.conn = conn;
}
public int getHoldability() throws SQLException {
return conn.getHoldability();
}
public int getTransactionIsolation() throws SQLException {
return conn.getTransactionIsolation();
}
public void clearWarnings() throws SQLException {
conn.clearWarnings();
}
public void close() throws SQLException {
//conn.close();
}
public void commit() throws SQLException {
//conn.commit();
}
public void rollback() throws SQLException {
//conn.rollback();
}
public boolean getAutoCommit() throws SQLException {
//return conn.getAutoCommit();
return false;
}
public boolean isClosed() throws SQLException {
return conn.isClosed();
}
public boolean isReadOnly() throws SQLException {
return conn.isReadOnly();
}
public void setHoldability(int arg0) throws SQLException {
conn.setHoldability(arg0);
}
public void setTransactionIsolation(int arg0) throws SQLException {
conn.setTransactionIsolation(arg0);
}
public void setAutoCommit(boolean arg0) throws SQLException {
//conn.setAutoCommit(arg0);
}
public void setReadOnly(boolean arg0) throws SQLException {
conn.setReadOnly(arg0);
}
public String getCatalog() throws SQLException {
return conn.getCatalog();
}
public void setCatalog(String arg0) throws SQLException {
conn.setCatalog(arg0);
}
public DatabaseMetaData getMetaData() throws SQLException {
return conn.getMetaData();
}
public SQLWarning getWarnings() throws SQLException {
return conn.getWarnings();
}
public Savepoint setSavepoint() throws SQLException {
return conn.setSavepoint();
}
public void releaseSavepoint(Savepoint arg0) throws SQLException {
conn.releaseSavepoint(arg0);
}
public void rollback(Savepoint arg0) throws SQLException {
//conn.rollback(arg0);
}
public Statement createStatement() throws SQLException {
return conn.createStatement();
}
public Statement createStatement(int arg0, int arg1) throws SQLException {
return conn.createStatement(arg0, arg1);
}
public Statement createStatement(int arg0, int arg1, int arg2) throws SQLException {
return conn.createStatement(arg0, arg1, arg2);
}
public Map getTypeMap() throws SQLException {
return conn.getTypeMap();
}
public void setTypeMap(Map arg0) throws SQLException {
conn.setTypeMap(arg0);
}
public String nativeSQL(String arg0) throws SQLException {
return conn.nativeSQL(arg0);
}
public CallableStatement prepareCall(String arg0) throws SQLException {
return conn.prepareCall(arg0);
}
public CallableStatement prepareCall(String arg0, int arg1, int arg2) throws SQLException {
return conn.prepareCall(arg0, arg1, arg2);
}
public CallableStatement prepareCall(String arg0, int arg1, int arg2, int arg3) throws SQLException {
return conn.prepareCall(arg0, arg1, arg2, arg3);
}
public PreparedStatement prepareStatement(String arg0) throws SQLException {
return conn.prepareStatement(arg0);
}
public PreparedStatement prepareStatement(String arg0, int arg1) throws SQLException {
return conn.prepareStatement(arg0, arg1);
}
public PreparedStatement prepareStatement(String arg0, int arg1, int arg2) throws SQLException {
return conn.prepareStatement(arg0, arg1, arg2);
}
public PreparedStatement prepareStatement(String arg0, int arg1, int arg2, int arg3) throws SQLException {
return conn.prepareStatement(arg0, arg1, arg2, arg3);
}
public PreparedStatement prepareStatement(String arg0, int[] arg1) throws SQLException {
return conn.prepareStatement(arg0, arg1);
}
public Savepoint setSavepoint(String arg0) throws SQLException {
return conn.setSavepoint(arg0);
}
public PreparedStatement prepareStatement(String arg0, String[] arg1) throws SQLException {
return conn.prepareStatement(arg0, arg1);
}
}[/code]
TransactionManager .java
[code]package my.trans;
public class TransactionManager {
private static final ThreadLocal threadLocal = new ThreadLocal();
private TransactionManager() {}
public static synchronized Transaction beginTransaction() {
Transaction t = (Transaction) threadLocal.get();
if (t != null) {
throw new RuntimeException("Já existe uma transação ativa");
}
t = new Transaction();
threadLocal.set(t);
return t;
}
public static synchronized Transaction getTransaction() {
return (Transaction) threadLocal.get();
}
protected static synchronized void endTransaction() {
if (threadLocal.get() == null) {
throw new RuntimeException("Não existe uma transação ativa");
}
threadLocal.set(null);
}
}[/code]
No DAO:
[code]public class MeuDAOLegal {
private Connection getConnection() {
if( TransactionManager.getTransaction() != null ) {
return TransactionManager.getTransaction().getConnection();
} else {
return pegarConexaoDeOutroLugar();
}
}
public void save( Object o ) {
conn = getConnection();
// catch the video mothafucker…
conn.close();
}
}[/code]
E para usar:
Transaction t = TransactionManager.beginTransaction();
try {
/* chamada(s) para o(s) Dao(s) */
t.commit();
} catch (Exception e) {
t.rollback();
} finally {
t.end();
}
Pode ser mais abstraído, mas basicamente é isso.
Louds, como vc faz com Exceptions que seu runnable tem que lançar? Tudo unchecked?
O esqueminha do Tiny Marbles é exatamente assim, mas ainda não consegui inventar um esquema decente de lançar exceções arbitrárias.
po… sinceramente…
codigo de infra-estrutura é chato pra caramba,
porque nao usar um EJB session bean ou uma fachada pojo com Spring?
na forma declarativa fica muito mais facil de resolver problemas mais complexos, pois voce seta 1 tipo de atributo de comportamento pra cada componente ou chamada (required, required new, supported etc,)
falow
[quote=dukejeffrie]Louds, como vc faz com Exceptions que seu runnable tem que lançar? Tudo unchecked?
O esqueminha do Tiny Marbles é exatamente assim, mas ainda não consegui inventar um esquema decente de lançar exceções arbitrárias.[/quote]
O runnable empacota todas exceptions como unchecked. Até pouco tempo a maioria dos casos eram “ou funcionou ou deu pau”, então não existia um tratamento mais apurado para as exceptions lançadas.
Recentemente tive que implementar um caso no qual eu precisava executar transações aninhadas, tinha que diferenciar o tipo de erro ocorrido por uma unit-of-work para tomar uma decisão abort/retry/change.
Nesse caso eu passei a usar EDU.oswego.cs.dl.util.concurrent.Callable e a tratar exceptions usando uma Chain Of Responsibility com filtros que ou assimilavam a exception ou convertiam ela para uma mais util.
Felipe, usar EJB só para ter gerênciamento de transações é loucura, muita pentelhação e tralha por pouca coisa. Spring é legal, use no lugar de fazer na mão sempre que possivel, mas infelizmente o framework de suporte a Hibernate e transações precisa ser alterado (leia editar os fontes do Spring) se você quer suportar coisas como replicação, particionamento, fail-over ou stop/resume. Além do fato que em vários projetos usar Spring simplesmente não é uma opção (cliente não quer, equipe não sabe usar, gerênte cagou regra sobre…).
[quote=louds]
Felipe, usar EJB só para ter gerênciamento de transações é loucura, muita pentelhação e tralha por pouca coisa. Spring é legal, use no lugar de fazer na mão sempre que possivel, mas infelizmente o framework de suporte a Hibernate e transações precisa ser alterado (leia editar os fontes do Spring) se você quer suportar coisas como replicação, particionamento, fail-over ou stop/resume. Além do fato que em vários projetos usar Spring simplesmente não é uma opção (cliente não quer, equipe não sabe usar, gerênte cagou regra sobre…).[/quote]
só para isso é exagero mas nao loucura 8)
e tb session beans sao moleza de implementar… diferente dos entity… e alem do controle de transação trazem outras facilidades, que as vezes sao uteis
qto ao particionamento, replicação… os DB´s deveriam cuidar disso pra nos de forma transparente não? a nao ser q vc replique ou fragmente na mao… mas eu nunca vi isso
oq eu quis dizer… é que escrevendo o codigo de transação (o controle dela), um caso um pouquinho mais elaborado, como usar + de 1 componente, ja pode ficar chato e com muito “codigozinho” pra implementar e dar manutencao…
tendo ferramentas que cuidam desse controle transacional pra nos… pra que ficar implementando codigo pra isso? hehehe
Sim, mas te obriga a usar um AS, eu não escrevo aplicações web/online a meses, entretanto tenho necessidades transacionais muito chatas. SLSB são uteis somente quando você tem um cenario que eles se encaixam, senão são um enorme entrave.
Replicação quem cuida de fazer é o banco mesmo, mas eu ainda tou para ver um driver JDBC que de forma transparente acesse as réplicas quando preciso fazer consultas apenas. Replicar o RDBMS faz, mas acessar as réplicas é problema da aplicação.
Particionamento em todos os casos que eu vi era gerenciado pela aplicação. Existem RDBMS que fazer isso, mas ou ainda são produtos beta (mysql), ou o custo é insanamente caro (imagine um deployment de 40 servidores oracle rac?), ou simplesmente não escalam (oracle rac tem um limite baixo no número de nós).
A maioria dos lugares que conheço que faz particionamento de dados, faz na camada de aplicação. Tou falando de sites como flickr e livejournal, por exemplo. A escalabilidade e os custos são muito boas como você pode verificar.
[quote=felipecruz]
oq eu quis dizer… é que escrevendo o codigo de transação (o controle dela), um caso um pouquinho mais elaborado, como usar + de 1 componente, ja pode ficar chato e com muito “codigozinho” pra implementar e dar manutencao…
tendo ferramentas que cuidam desse controle transacional pra nos… pra que ficar implementando codigo pra isso? hehehe
[/quote]
Como menCionei, nem todos os casos existem ferramentas que atendem, ou então simplesmente não é possivel usá-las. Fora isso, concordo com você, não faz sentido escrever código de infra, que é chato e dificil, se já existem implementações por ai.
concordo com voce… hehe mas oq vc descreveu são 20% dos casos no maximo
pra maioria das pessoas um spring ou EJB ja é suficiente!
o particionamento q vc menciono… é logico e nao fisico… por isso eles tao na camada de aplicação… o particionamento fisico TEM q ser transparente… se nao for tem algo de errado…
mas ai ja é fugir mto do topico ahuehua
Também acho que o gerenciamento de transações NÃO deve estar nos DAOs. Caso vc precise de dois ou mais DAOs para executar uma mesma transação, a implementação fica difícil.
Nesse caso, eu também optaria por usar um AS com EJBs. As vantagens são enormes!!!
O AS abstrai as funcionalidades de gerenciamento de transação. Além disso, a aderência aos padrões J2EE permite que vc escolha o AS que mais adeque-se às suas necessidades (hoje, os players fornecem o básico, descrito na especificação, e vão muito além quando o assunto é otimização).
Se vc precisar de segurança, ela está lá, é só ativar.
Se vc precisar de clusterização, ela está está lá, é só ativar.
Se vc precisa de load balancing, ele está lá, é só ativar.
Se vc precisar de transações distribuídas, sejam quão chatas elas forem, elas estão, é só ativar.
Se vc precisar trocar de AS, outros estarão lá, com suas vantagens e desvantagens.
Pq que reinventar a roda? Depois o pessoal fica reclamando das bombas que pegam de outros desenvolvedores que criam frameworks proprietários.
[quote=Taz]Também acho que o gerenciamento de transações NÃO deve estar nos DAOs. Caso vc precise de dois ou mais DAOs para executar uma mesma transação, a implementação fica difícil.
Nesse caso, eu também optaria por usar um AS com EJBs. As vantagens são enormes!!!
O AS abstrai as funcionalidades de gerenciamento de transação. Além disso, a aderência aos padrões J2EE permite que vc escolha o AS que mais adeque-se às suas necessidades (hoje, os players fornecem o básico, descrito na especificação, e vão muito além quando o assunto é otimização).
Se vc precisar de segurança, ela está lá, é só ativar.
Se vc precisar de clusterização, ela está está lá, é só ativar.
Se vc precisa de load balancing, ele está lá, é só ativar.
Se vc precisar de transações distribuídas, sejam quão chatas elas forem, elas estão, é só ativar.
Se vc precisar trocar de AS, outros estarão lá, com suas vantagens e desvantagens.
Pq que reinventar a roda? Depois o pessoal fica reclamando das bombas que pegam de outros desenvolvedores que criam frameworks proprietários.[/quote]
Cara, segurança definida no J2EE é muito, muito ruim. Qualquer coisa que não seja ‘área protegida + form de login’ não existe padrão, você é obrigado a utilizar extensões proprietárias. O clássico exemplo de implementar login programático na aplicação continua impossivel de forma padronizada.
Você não precisa de EJB ou um AS para usar clustering e load balancing, normalmente você está melhor usando outras soluções que não um application server para isso.
Se você precisar de transações distribuidas, usar um AS vai ser o menor dos teus problemas, então é melhor mesmo usar um.
Quanto a trocar de AS, esse teu argumento é brincadeira, nunca vi a migração de qualquer aplicação de media complexidade entre AS diferentes ser menos que um INFERNO. Normalmente já um problema enorme trocar a versão, de fornecedor então…
[quote=louds]
Cara, segurança definida no J2EE é muito, muito ruim. Qualquer coisa que não seja ‘área protegida + form de login’ não existe padrão, você é obrigado a utilizar extensões proprietárias. O clássico exemplo de implementar login programático na aplicação continua impossivel de forma padronizada.[/quote]
Acho que não. Autenticação é um dos aspectos de segurança (e talvez o mais simples!!!) tratados na J2EE.
[quote=louds]
Você não precisa de EJB ou um AS para usar clustering e load balancing, normalmente você está melhor usando outras soluções que não um application server para isso.[/quote]
Quais soluções? Como vc implementou?
[quote=louds]
Quanto a trocar de AS, esse teu argumento é brincadeira, nunca vi a migração de qualquer aplicação de media complexidade entre AS diferentes ser menos que um INFERNO. Normalmente já um problema enorme trocar a versão, de fornecedor então…[/quote]
Se foi um INFERNO é pq foram utilizados recursos proprietários. Se um arquiteto trabalha com o requisito de que a aplicação deve ser portável entre ASs, obviamente ele não vai deixar que o time de desenvolvimento utilize recursos proprietários. Além disso, a troca de AS tb não é muito frequente e, geralmente, as empresas estão contentes com os fornecedores que possuem atualmente.