Como gerenciar a transação JPA com Hibernate via Filtro em projeto Web

Como gerenciar a transação JPA com Hibernate via Filtro em projeto Web

Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /home/feltexco/public_html/felix/wp-content/plugins/wp-syntax/wp-syntax.php on line 380

Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /home/feltexco/public_html/felix/wp-content/plugins/wp-syntax/wp-syntax.php on line 380

Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /home/feltexco/public_html/felix/wp-content/plugins/wp-syntax/wp-syntax.php on line 380

Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /home/feltexco/public_html/felix/wp-content/plugins/wp-syntax/wp-syntax.php on line 380

Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /home/feltexco/public_html/felix/wp-content/plugins/wp-syntax/wp-syntax.php on line 380

Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /home/feltexco/public_html/felix/wp-content/plugins/wp-syntax/wp-syntax.php on line 380

Warning: WP_Syntax::substituteToken(): Argument #1 ($match) must be passed by reference, value given in /home/feltexco/public_html/felix/wp-content/plugins/wp-syntax/wp-syntax.php on line 380

Como gerenciar a transação JPA com Hibernate via Filtro em projeto Web

Olá amigos, muitos vezes precisamos gerenciar a transação JPA em nosso servidor e não temos a opção de utilizar EJB ou Spring. Dessa forma, ficar a cargo do programador gerenciar o início e fim da transação jpa como o banco dados. Entre a várias alternativas

Então vamos mostrar uma das formas de se fazer isso.
Transação JPA


Tecnologias utilizadas

  1. Java 7
  2. Eclipse Luna
  3. PrimeFaces 4
  4. Tomcat 8
  5. Hibernate 4

Essa abordagem é muito comum e utilizada quando nossa aplicação fica disponível no Apache Tomcat. Ele é um servidor WEB e não possui suporte a tecnologia EJB, por exemplo.
Em aplicações desktop(JSE), quando utilizamos JPA precisamos controlar a transação jpa manualmente. Veja o exemplo abaixo disponível em nosso tutorial sobre JPA com Hibernate.

1
2
3
4
5
6
7
8
9
10
11
12
13
public void incluir(Aluno aluno) {
		try {
			session = HibernateUtil.getSession();
			session.beginTransaction();
			session.save(aluno);
			session.getTransaction().commit();
		} catch (Exception e) {
			session.getTransaction().rollback();
			e.printStackTrace();
		} finally {
			session.close();
		}
	}

Veja que o código acima precisamos fazer a chamada aos métodos “session.beginTransaction();” e “session.getTransaction().commit();” isto é necessário, pois estamos utilizando o padrão BTM (Bean-Managed Transactions), ou seja, nossa aplicação tem que iniciar, executar a operação desejada e em seguida concluir a operação com um “commit” e caso ocorra um erro precisamos enviar o comando “session.getTransaction().rollback();” para desfazer as operação que foram executadas.

Para o post de hoje utilizaremos o Exemplo completo com JSF Primefaces + Hibernate + MySQL criado aqui no Blog para auxiliar no nosso aprendizado.

Faça download o WAR e importe no seu eclipse. Para lembrar como isso funciona acesse Importando um projeto Web no Eclipse Então criaremos um filtro, para isso após a importação no eclipse selecione o projeto com o botão direito “New ==> Filter” no campo pacote adicione:
“br.com.feltex.academicnet.filter” e no campo Class Name digite: “FiltroJPA”. Selecione Finish.

O código do seu filtro ficará dessa maneira:

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
package br.com.feltex.academicnet.filter;
 
import java.io.IOException;
 
import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
 
import org.hibernate.Session;
 
import br.com.feltex.hibernate.util.HibernateUtil;
 
// Atenção que apenas as chamadas JSF serão filtradas por este filtro
@WebFilter("*.xhtml")
public class FiltroJPA implements Filter {
 
	public FiltroJPA() {}
 
	public void destroy() {}
 
	public void doFilter(ServletRequest request, ServletResponse response,
			FilterChain chain) throws IOException, ServletException {
		Session session = HibernateUtil.getSession();
		try {
			session.beginTransaction();
			chain.doFilter(request, response);
			session.getTransaction().commit();
		} catch (Exception e) {
			session.getTransaction().rollback();
			e.printStackTrace();
		} finally {
			if (session != null && session.isOpen()) {
				session.close();
			}
		}
	}
 
	public void init(FilterConfig fConfig) throws ServletException {}
}

Nesse filtro estamos fazendo o controle de transação jpa de todas as operações no nosso banco de dados. Antes de fazer qualquer ação no banco de dados as chamadas da nossa aplicação passam no nosso filtro que cria uma sessão e inicia uma transação jpa “beginTransaction”. Em seguida é executado o método “chain.doFilter(request, response);” que permite a execução do método desejado no DAO. Em seguida a execução volta para o nosso FiltroJPA que conclui a transação jpa executando um commit.

Agora vamos analisar a nossa classe “AlunoHibernateDAO” que ficou simplificada em relação à versão que está no Post Exemplo completo com JSF Primefaces + Hibernate + MySQL.

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
package br.com.feltex.academicnet.dao.hibernate;
 
import java.util.List;
 
import org.hibernate.Criteria;
import org.hibernate.Session;
import org.hibernate.criterion.Order;
import org.hibernate.criterion.Restrictions;
 
import br.com.feltex.academicnet.dao.AlunoDAO;
import br.com.feltex.academicnet.entidade.Aluno;
import br.com.feltex.hibernate.util.HibernateUtil;
 
public class AlunoHibernateDAO implements AlunoDAO {
 
	private Session session;
 
	public void alterar(Aluno p) {
		session = HibernateUtil.getSession();
		session.merge(p);
	}
 
	public Aluno consultar(Aluno aluno) {
		session = HibernateUtil.getSession();
		return (Aluno) session.get(Aluno.class, aluno.getMatricula());
	}
 
	public void excluir(Aluno p) {
		session = HibernateUtil.getSession();
		Aluno alunoPesquisado = consultar(p);
		session.delete(alunoPesquisado);
	}
 
	public boolean existe(Aluno aluno) {
		Aluno a = consultar(aluno);
		return (a.getMatricula() != null);
	}
 
	public void inserir(Aluno p) {
		session = HibernateUtil.getSession();
		session.save(p);
	}
 
	@SuppressWarnings("unchecked")
	public List<Aluno> listar() {
		session = HibernateUtil.getSession();
		return (List<Aluno>) session.createCriteria(Aluno.class).list();
	}
 
	@SuppressWarnings("unchecked")
	public List<Aluno> listar(Aluno aluno) {
		Criteria c = session.createCriteria(Aluno.class);
		if (aluno.getNome().length() > 0) {
			c.add(Restrictions.like("nome", aluno.getNome() + "%"));
		}
		c.addOrder(Order.asc("nome"));
		return (List<Aluno>) c.list();
	}
}

Vejamos como foram as mudanças, para isso compare o método incluir

Antes:

1
2
3
4
5
6
7
8
9
10
11
12
13
public void incluir(Aluno aluno) {
		try {
			session = HibernateUtil.getSession();
			session.beginTransaction();
			session.save(aluno);
			session.getTransaction().commit();
		} catch (Exception e) {
			session.getTransaction().rollback();
			e.printStackTrace();
		} finally {
			session.close();
		}
	}

E depois!

1
2
3
4
public void inserir(Aluno p) {
		session = HibernateUtil.getSession();
		session.save(p);
	}

Isto não é MAGIA e sim TECNOLOGIA!
O que fizemos neste caso foi a passagem do controle de transação jpa para o filtro que irá interceptar todas as chamadas (xhtml) antes de ocorrerem as operações no DAO.

Também fizemos a alteração da Classe Hibernate Util para pegar sempre a mesma sessão:

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
 
package br.com.feltex.hibernate.util;
 
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;
import org.hibernate.service.ServiceRegistryBuilder;
import org.hibernate.tool.hbm2ddl.SchemaExport;
 
public class HibernateUtil {
 
	public static void main(String[] args) {
		System.out.println("Inicio");
 
		Configuration cfg = new Configuration();
		cfg.configure();
		SchemaExport se = new SchemaExport(cfg);
		se.create(true, true);
 
		System.out.println("Fim");
	}
 
	private static SessionFactory sessionFactory = null;
	private static ServiceRegistry serviceRegistry;
 
	static {
		try {
			sessionFactory = getSessionFactory();
		} catch (Throwable ex) {
			System.err.println("Initial SessionFactory creation failed." + ex);
			throw new ExceptionInInitializerError(ex);
		}
	}
 
	public static SessionFactory getSessionFactory() {
		if (sessionFactory == null) {
			Configuration configuration = new Configuration();
			configuration.configure();
			serviceRegistry = new ServiceRegistryBuilder().applySettings(
					configuration.getProperties()).buildServiceRegistry();
			sessionFactory = configuration.buildSessionFactory(serviceRegistry);
			sessionFactory.openSession();
			return sessionFactory;
		}
		return sessionFactory;
	}
 
	public static Session getSession() {
		// return sessionFactory.openSession();
		return getSessionFactory().getCurrentSession();
	}
}

No método “getSession()” tivemos que mudar de openSession para “getCurrentSession()” isso faz com que nossa aplicação reutilize a sessão que está aberta.
Nosso arquivo de configuração do Hibernate (hibernate.cfg.xml) também foi alterado e ficou assim:

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
 
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://hibernate.sourceforge.net/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
	<session-factory>
		 <property name="hibernate.connection.driver_class">com.mysql.jdbc.Driver</property>
        <property name="hibernate.connection.url">jdbc:mysql://localhost:3306/bdacademicnet</property>
        <property name="hibernate.connection.username">root</property>
        <property name="hibernate.connection.password">teco01</property>
        <property name="hibernate.dialect">org.hibernate.dialect.MySQLDialect</property>
        <property name="hibernate.current_session_context_class">thread</property>
		<property name="hibernate.show_sql">true</property>
		<property name="hibernate.format_sql">true</property>
		<property name="hibernate.jdbc.batch_size">20</property>
 
		<property name="c3p0.acquire_increment">1</property>
		<property name="c3p0.idle_test_period">100</property>
		<property name="c3p0.max_size">100</property>
		<property name="c3p0.max_statements">0</property>
		<property name="c3p0.min_size">10</property>
		<property name="c3p0.timeout">100</property>
 
		<property name="hibernate.hbm2ddl.auto">update</property>
 
		<!-- validate | update | create | create-drop -->
		<mapping class="br.com.feltex.academicnet.entidade.Aluno" />
		<mapping class="br.com.feltex.academicnet.entidade.Turma" />		
	</session-factory>
</hibernate-configuration>

Adicionamos a linha: “thread” para garantir que o método “getCurrentSession()” da classe HibernateUtil funcione corretamente.

Conclusão

Esta forma de controle de transação jpa é muito interessante quando não podemos utilizar o CTM – Container Transaction Manager. Isso é comum em aplicações que só rodam em container Web, sistemas legados ou aplicações que tem como premissa o uso de tecnologias Servlet ou JSF.
Leia mais sobre EJB 3 que traz muitas facilidades no uso de CTM, mas tem que ser executado em um EJB container (JBoss/WildFly, Websphere, WebLogic).
Como alternativa leia também sobre o uso do Framework Spring que também faz o controle de transação jpa para nossa aplicação e tem a vantagem de rodar no Tomcat e nos EJB Containers.

Para pegar o código completo clique aqui

Veja como ficará a nossa aplicação:
Transação JPA

Então amigos por hoje é só e vida que segue.

Links relacionados


Configuração de acesso a console do servidor TOMCAT 8
Tomcat
Instalação e configuração do TOMCAT
Criar datasource no servidor Tomcat 8

Tutorial da Oracle

Não esqueça de curtir este post nas rede sociais. Dê a sua contribuição social e ajude o autor:

Deixe um comentário