Multi-Database

27 respostas
J

Pessoal, gostaria da ajuda de vocês.

Estou desenvolvendo uma aplicação que será distribuida na modalidade SaaS (Software as a Service), onde vários clientes utilizarão a mesma aplicação, sendo que uma vez feito o login, as chamadas do usuário passarão a ser realizadas a sua base de dados específica. Logo, minha aplicação deverá estabelecer a conexao com a base de dados em tempo de execução, de acordo com os métodos invocados pelo usuário.

As classes de entidades e serviços da minha aplicação está dividida em JARs lógicos dentro de um EAR (aplicacao.ear = [pessoa.jar, local.jar, catalogo.jar, etc.]).

Gostaria de saber de vocês qual a melhor forma de implementar a logica de conexão com a base de dados.

O ideal seria não ter que configurar as conexões com persistence.xml, porém não encontrei nenhuma forma de implementar isso.

Tentei usar o EntityManagerFactory, porém só consegui abrir uma conexão a cada sessão.

Após muito pesquisar na internet, li sobre a classe EJB3Configuration http://www.hibernate.org/386.html?cmd=comphist&histnode=2844, que talvez pudesse me ajudar, mas não estou conseguindo usar esse exemplo.

Será que alguém poderia me indicar uma forma de implementação? Valeu.

27 Respostas

GraveDigger

Olá,

Tenho exatamente o mesmo problema.

Estou pensando no seguinte:

Colocar o mapeamento em algum outro arquivo, criar um script para gerar meu persistence.xml e meu xml do datasource(estou usando jboss), já que tenho(e imagino q vc tb) todas as informações das conexões dos clientes em um banco de dados.

Essa é a parte de configuração, na parte de código, criei uma Factory que cria EntityManagerFactory de acordo com o cliente, ficou assim:

public EntityManagerFactory createEnityManagerFactory(Customer customer) {

		HashMap<String, String> prop = new HashMap<String, String>();
		prop.put("hibernate.dialect",
				"org.hibernate.dialect.MySQLDialect");
		prop.put("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
		prop.put("hibernate.show_sql", "true");
		prop.put("hibernate.format_sql", "true");
		prop.put("hibernate.connection.username", customer.getMysqlUser());
		prop.put("hibernate.connection.password", customer.getMysqlPassword());
		prop.put("hibernate.connection.url", "jdbc:mysql://localhost:3306/"
				+ customer.getMysqlDatabase());

		return Persistence.createEntityManagerFactory(customer.getMysqlDatabase(), prop);

	}

Nesse código percebe-se que:

Tenho um persistence-unit por cliente.

Procurei bastante a solução para isso na internet, infelizmente não achei nada que pudesse ajudar, se voce achar algo, por favor me avise tb, pq a “solução” que encontrei não me parece a mais adequada, embora funcione.

O principal ponto aqui é o atributo do persistence.xml, se ele pudesse ser informado via configuração, como os outros parâmetros que usei acima, poderia ter apenas um persistence-unit, assim não seria obrigado a alterar esse xml que força o redeploy de minha aplicação. Mas, pelo que vi e pesquisei, apenas os parâmetros referentes ao hibernate podem ser informados em tempo de execução.

Se houver uma forma de passar isso como parâmetro, resolvemos o problema, fazendo com que apenas o arquivo dos datasources tenha que conter todas as informações do cliente, não sendo assim necessário fazer um redeploy de toda a aplicação para cada novo cliente.

A única forma que encontrei foi fazer com que a forma de transação usada deixe de ser JTA para ser RESOURCE_LOCAL, assim não necessitante de um jta-data-source, porém, com isso, todo o gerenciamento de transações cai na mão do desenvolvedor, sendo assim mais propenso a falhas, e se não me engano(não fui mto a fundo nessa solução) o Jboss tem alguma limitação quanto a se uso.

Vejo cada vez mais essa situação se repetir para vários desenvolvedores, mas até agora não vi nenhuma solução satisfatória, se encontrar algo interessante, por favor não deixe de postar aqui

Abraço,

Pedro Sena

J

Ola,

Então, não consegui fugir de criar os arquivos de bd-ds.xml manualmente no diretorio do JBoss, porém consegui descartar o uso do persistence.xml. O que elimina a necessidade de um novo deploy para cada novo Database.

Estou tentando adotar a seguinte alternativa:

private static void criarEMF(String nomeDataSource)
	{
                log.debug("Criando novo EntityManagerFactory para o DataSource "
				+ nomeDataSource);
		
		Ejb3Configuration ejbConf = new Ejb3Configuration();
		
		log.debug("Definindo as propriedades da conexao");
		
		ejbConf.setProperty("hibernate.connection.datasource", "java:/"
				+ nomeDataSource);
		ejbConf.setProperty("hibernate.dialect",
			"org.hibernate.dialect.PostgreSQLDialect");
		ejbConf.setProperty("hibernate.archive.autodetection", "class");
		ejbConf.setProperty("hibernate.show_sql", "false");
		ejbConf.setProperty("hibernate.format_sql", "true");
		//ejbConf.setProperty("hibernate.hbm2ddl.auto", "update");
		
		log.debug("Construindo EntityManagerFactory");
		emf = ejbConf.createEntityManagerFactory();
	}
  • O método:

é deprecated, porém quanto tento usar o novo:

Tenho uma exception relacionada a segurança, diz que minha Entidade é somente leitura, coisa assim.

  • Como minhas classes de entidade ficam em JARs específicos, não estou conseguindo fazer com que o Ejb3Configuration faça o mapeamento das entidades automaticamente, então tenho que adicionar as classes ao Configuration… tipo:
  • Tambem não consigo usar a opção:

Dá um erro de transaction. Portanto, as tabelas precisam ser criadas manualmente também.

Agora estou resolvendo um problema de transaction, assim que resolver, acredito que essa seja a solução que mais se aproxime do que preciso. Se você encontrar uma solução melhor, por favor me avise.

Abraços

GraveDigger

O Ejb3Configuration tem um método addResource que aceita o arquivo de mapeamento do hibernate para as classes, o .hbm.xml.

Talvez se vc criasse um Wrapper que lesse todos os seus e passasse ele como parâmetro resolvesse.

Vou testar aqui esse hibernate.connection.datasource, era justamente o que eu precisava.

Vamos ver se funciona como o esperado.

Grato

Edit:

Cara, fiz um teste aqui, obtive o mesmo erro das transactions, dizendo que nenhuma está em andamento.

Testei num ambiente com o Jboss (embedded), isso acontece porque o tipo de transação associado é RESOURCE_LOCAL e não JTA.

Vamos buscar uma forma de fazer com que isso funciona com transações JTA, pq RESOURCE_LOCAL teríamos que manipular isso manualmente, o q definitivamente não é boa idéia.

[]'s

GraveDigger

Cara,

Resolvi o problema das transactions:

conf.setProperty("hibernate.transaction.factory_class", "org.hibernate.transaction.JTATransactionFactory");

Agora acho que consigo matar isso sem o persistence.xml !!!

Fiz uns testes CRUD aqui e funcionou !!

Qualquer progresso mantenha-me informado que vou fazendo o mesmo.

Um grande abraço,

Pedro Sena

GraveDigger

Bom,

Consegui fazer quase tudo, meu problema está apenas em usar algum arquivo de configuração para indicar as classes mapeadas.

Estou usando addResource do Ejb3Configuration, ele acha meu arquivo mas continua dando um erro de Unknown entity…

Estou buscando a solução para isso, se voce achar, por favor me avise.

Pedro Sena

J

Olha só,

No meu sistema o EntityManager está sendo instanciado, não da nenhum erro no log… Quando mando persistir um objeto não da nenhum erro, diz que é persistido, o sequence dele é incrementado …

Só que quando olho na base de dados, o maldito objeto não está lá, só o sequence foi incrementado na base de dados… da pra acreditar?

Voce criou alguma classe de factory para instanciar o EntityManager? se sim, tem como me passar para eu dar uma olhada?

No caso das classes, eu to adicionando via código mesmo, usando addAnnotatedClass, da uma olhada como ficou meu Factory:

package br.com.jorge.util.persistencia;

import java.util.HashMap;
import java.util.Map;

import javax.persistence.EntityManager;
import javax.persistence.EntityManagerFactory;

import org.apache.log4j.Logger;
import org.hibernate.ejb.Ejb3Configuration;

import br.com.jorge.negocio.acesso.impl.UsuarioBean;
import br.com.jorge.negocio.acesso.impl.BaseDadosBean;

/**
 * @author jorge
 * 
 * @date 12/12/2008
 * 
 */
public class JPAUtil
{
	private static Map<String, EntityManagerFactory> factories = 
		new HashMap<String, EntityManagerFactory>();
	private static Logger log = Logger.getLogger(JPAUtil.class);
	
	public static EntityManager obterEntityManager(String nomeDataSource)
	{
		EntityManager em = null;
		try
		{
			log.debug("Obtendo EntityManager para o DataSource "
					+ nomeDataSource);
			
			if ( !factories.containsKey(nomeDataSource) )
			{
				log.debug("EntitiManagerFactory ainda nao existe.");
				criarEMF(nomeDataSource);
			}
			
			log.debug("criando EntityManager");
			em = factories.get(nomeDataSource).createEntityManager();
			
			log.debug("Retornando EntityManager");
		}
		catch ( Exception e )
		{
			log.error(e);
		}
		return em;
	}
	
	private static void criarEMF(String nomeDataSource)
	{
		try
		{
			log.debug("Criando novo EntityManagerFactory para o DataSource "
					+ nomeDataSource);
			
			Ejb3Configuration ejbConf = new Ejb3Configuration();
			
			log.debug("Definindo as propriedades da conexao");
			
			ejbConf.setProperty("hibernate.connection.datasource", 
				"java:/" + nomeDataSource);
			ejbConf.setProperty("hibernate.dialect",
				"org.hibernate.dialect.PostgreSQLDialect");

			ejbConf.setProperty("hibernate.transaction.factory_class",
				"org.hibernate.transaction.JTATransactionFactory");
			ejbConf.setProperty("hibernate.transaction.manager_lookup_class",
				"org.hibernate.transaction.JBossTransactionManagerLookup");

			ejbConf.setProperty("hibernate.show_sql", "false");
			ejbConf.setProperty("hibernate.format_sql", "true");
			//ejbConf.setProperty("hibernate.hbm2ddl.auto", "update");
			
			log.debug("Adicionando classes do pacote acesso");
			ejbConf.addAnnotatedClass(UsuarioBean.class);
			ejbConf.addAnnotatedClass(BaseDadosBean.class);
			
			log.debug("Construindo EntityManagerFactory");
			EntityManagerFactory emf = ejbConf.createEntityManagerFactory();
			
			log.debug("Adicionando EntityManagerFactory ao Map de factories");
			factories.put(nomeDataSource, emf);
		}
		catch ( Exception e )
		{
			log.error(e);
		}
	}
	
}
GraveDigger

Oi,

Pode ter algo relacionado a sua transação, vc adicionou aquela linha referente as transações que citei acima?

Eu estou usando o Seam como framework, parte da configuração que faço usa ele, então acredito que não vá te ajudar mto, já que pelo visto vc não esta utilizando ele.

No final das contas eu chamo esse método que eu coloquei inicialmente.

Poste aqui o código que vc está usando para fazer o seu insert, ACREDITO que possa ter algo q ver com transações, ou então problemas no mapeamento, por isso, poste sua entidade tb.

Edit:

Agora que vi seu Ejb3Configuration.

Retire isso:

ejbConf.setProperty("hibernate.transaction.manager_lookup_class",  
                 "org.hibernate.transaction.JBossTransactionManagerLookup");

[]'s

J

Eu tava testando sem essa propriedade “hibernate.transaction.manager_lookup_class”, mas dava o mesmo problema, adicionei ela para ver se resolvia…

Por enquanto to fazendo um insert meio braçal, para fim de testes… estou informando o dataSource manualmente… o codigo é o seguinte:

public Usuario criarNovaConta(Usuario conta)
	{
		try
		{
			log.debug("Criando nova conta: " + conta.getLogin());
	
			log.debug("Obtendo EntityManager");
			EntityManager em = JPAUtil.obterEntityManager("baseDados001DS");
			
			log.debug("Persistindo a nova conta");
			conta = em.merge(conta);
			
		}
		catch (Exception e) {
			log.error(e);
		}
		return conta;
	}

Voce tentou usar o addAnnotatedClass() ? conseguiu resolver seu problema?

GraveDigger

Bom, se vc estver testando isso dentro do Jboss, adicione a annotation @Transactional a seu método, isso deve colocar ele dentro do escopo da transação e dar o commit no entityManager no final dela.

Quanto ao mapeamento, ainda não encontrei outra forma que não seja adicionar explicitamente o nome das classes, o que fica inviável pra mim, pois essa lógica está dentro de um jar que é apenas uma parte de um projeto maior, que por sua vez, possui outras entidades a serem mapeadas.

se eu conseguisse colocar isso num arquivo de configuração, somado a alguns recursos do Seam, eu conseguiria resolver meu problema, mas até agora não obtive sucesso :frowning: .

Suspeito que o addResource dependa do orm.xml, que é um arquivo mto chato de ser criado =/.

De qq forma, vou continuar as buscas aqui.

[]s

J

Nesse link http://www.hibernate.org/386.html?cmd=comphist&histnode=2844 tem um exemplo onde é feita uma busca dentro de um determinado jar…

//    Find all our persistent classes from persistence.jar
    JarVisitor.Filter[] filters = new JarVisitor.Filter[2];
    filters[0] = new JarVisitor.PackageFilter( false, null ) {
        public boolean accept(String javaElementName) {
            return true;
        }
    };
    filters[1] = new JarVisitor.ClassFilter(
            false, new Class[]{
            Entity.class,
            MappedSuperclass.class,
            Embeddable.class}
    ) {
        public boolean accept(String javaElementName) {
            return true;
        }
    };

//    Add all persistent classes to EJB3 config
    JarVisitor j = JarVisitor.getVisitor(Thread.currentThread().getContextClassLoader().getResource("persistence.jar"), filters);
    Set<JarVisitor.Entry>[] entries = (Set<JarVisitor.Entry>[])j.getMatchingEntries();
    List<String> classes = new ArrayList<String>();
    for (int i = 0; i < entries.length; i++)
    {
        for (JarVisitor.Entry c : entries[i])
        {
            Logger.info("Found entity " + c.getName());
            ejbconf.addAnnotatedClass(Class.forName(c.getName()));
        }
    }

Não entendi direito o código, mas assim que resolver meu problema de transação vou tentar implementar isso, pois meu projeto também ficará bem grande e se tornará inviável adicionar as classes uma a uma.

Estou pensando em ter um List com o nome dos Jars, então fazer o filtro nestes jars.

Espero que isso te ajude.

J

Po consegui fazer um insert e um select… mas tive que abrir e fechar a transação manualmente. Voce não conseguiu deixar a transação gerenciada pelo JBoss?

Agora vou partir pra outras frentes de batalha… será que tem como deixar a estrutura da base de dados ser atualizada dinamicamente?

qualquer novidade me avisa.

[]'s

GraveDigger

Vc não conseguiu usar a annotation que eu falei ?

Ela é justamente pra isso, pra evitar que vc tenha que lidar com a manipulação da transação manualmente, o que, em uma aplicaçao grande, como são nossos casos, seria um inferno.

Meus DAOs são subclasses de uma classe base do Seam que já provê essas funcionalidades, mas acredito que esses métodos que eu herdo são todos marcados como @Transactional.

Agora estou bem atarefados, mas no final da tarde vou fazer mais alguns testes e posto as novidades.

[]'s

J

Pelo que pesquisei na internet essa annotation não é do EJB3… acho que pertence ao seam ou do spring. Se eu estiver enganado me corrija, mas não achei o jar dela.

Em relação a adionar as classes Entity ao mapeamento, estou fazendo um teste com esse codigo do JarVisitor, a principio funcionou com um jar, agora vou tentar fazer um list de jars e percorrê-los. Mais tarde posto o código final.

[]s

J

Novidades,

Minha aplicação funcionou legal, consegui resolver o problema das transações, mapear as classes processando jars, fiz testes de persiste e find e funcionou legal…

Só que quando fui tentar abrir a conexão com 2 bases de dados tive problemas:

...
14:09:45,953 INFO  [DatasourceConnectionProvider] Using datasource: java:/db002DS
14:09:45,984 WARN  [loggerI18N] [com.arjuna.ats.internal.jta.transaction.arjunacore.lastResource.disallow] [com.arjuna.ats.internal.jta.transaction.arjunacore.lastResource.disallo
w] Adding multiple last resources is disallowed. Current resource is org.jboss.resource.connectionmanager.TxConnectionManager$LocalXAResource@1bc6b34
14:09:46,000 WARN  [SettingsFactory] Could not obtain connection metadata
org.jboss.util.NestedSQLException: Could not enlist in transaction on entering meta-aware object!; - nested throwable: (javax.transaction.SystemException: java.lang.Throwable: Una
bled to enlist resource, see the previous warnings. tx=TransactionImple < ac, BasicAction: -3f57ffbb:5b6:49492433:65 status: ActionStatus.ABORT_ONLY >); - nested throwable: (org.j
boss.resource.JBossResourceException: Could not enlist in transaction on entering meta-aware object!; - nested throwable: (javax.transaction.SystemException: java.lang.Throwable:
Unabled to enlist resource, see the previous warnings. tx=TransactionImple < ac, BasicAction: -3f57ffbb:5b6:49492433:65 status: ActionStatus.ABORT_ONLY >))
        at org.jboss.resource.adapter.jdbc.WrapperDataSource.getConnection(WrapperDataSource.java:95)
...

a partir desse warning, se eu tento realizar qualquer acesso ao segundo database esse WARN aparece, mas em forma de erro…

voce conseguiu fazer algum teste de abertura de várias conexões simultaneas?

[]s

GraveDigger

Cara, consegui sim, sem problemas.

Só tome cuidado com um detalhe, na própria documentação diz que um único Ejb3Configuration não garante a criação de mais de uma factory, só tenha certeza que vc está usando um novo Ejb3Configuration por factory que vc tenta criar.

Outra coisa, como vc fez em relação aos mapeamentos ?

[]'s

J

eu criei um metodo que adiciona as classes de entidade de um Jar para o ejb3configuration:

/**
	 * Percorre as classes de Entidade do Jar informado e adiciona as classes ao
	 * contexto de persistencia.
	 * 
	 * @param ejbConf
	 *            Objeto de configuração do contexto de persistencia.
	 * 
	 * @param nomeJar
	 *            Nome do Jar a ser procuradas as classes de Entidade.
	 * 
	 * @return Objeto de configuração do contexto de persistencia com as
	 *         entidades do jar adicionadas.
	 * 
	 */
	@SuppressWarnings("unchecked")
	private static Ejb3Configuration mapearClasses(Ejb3Configuration ejbConf,
			String nomeJar)
	{
		try
		{
			Filter[] filters = new Filter[2];
			filters[0] = new PackageFilter(false, null)
			{
				public boolean accept(String javaElementName)
				{
					return true;
				}
			};
			filters[1] = new ClassFilter(false, new Class[] {
					Entity.class, MappedSuperclass.class, Embeddable.class })
			{
				public boolean accept(String javaElementName)
				{
					return true;
				}
			};
			log.info("processando jar " + nomeJar);
			JarVisitor j = JarVisitorFactory.getVisitor(Thread.currentThread()
				.getContextClassLoader().getResource(nomeJar), filters);
			Set<Entry>[] entries = (Set<Entry>[]) 
				j.getMatchingEntries();
			for ( int i = 0; i < entries.length; i++ )
			{
				for ( Entry c : entries[i] )
				{
					log.info("Entidade encontrada: " + c.getName());
					ejbConf.addAnnotatedClass(Class.forName(c.getName()));
				}
			}
		}
		catch ( IOException e )
		{
			log.error(e);
		}
		catch ( ClassNotFoundException e )
		{
			log.error(e);
		}
		return ejbConf;
	}

Então eu percorro uma lista com os nomes de Jars e chamo essse método antes de criar o EMF.

Se voce tentar usar a solução acima, ela ta preparada para o jboss 5, quando eu usava o 4 os códigos eram um pouco diferentes, por causa da versão dos jars do hibernate. Algumas classes mudaram de lugar.

[]s
Jorge

J

Fui conferir aqui, e o Ejb3Configuration é instanciado e configurado novamente mesmo, para cada factory.

eu fiz o seguinte teste aqui para a criaçao destas duas conexões:

public class JPAUtil
{
	private static Map<String, EntityManagerFactory> factories = new HashMap<String, EntityManagerFactory>();
	private static final List<String> dsNames = Arrays.asList(
		"db001DS", "db002DS");

        ...
        
	public static void inicializarEMFs() 
	{
		for ( String dsName : dsNames )
		{
			criarEMF(dsName);
		}
	}

	@SuppressWarnings("deprecation")
	private static void criarEMF(String nomeDataSource)
	{
		try
		{
			log.info("Criando novo EntityManagerFactory para o DataSource "
					+ nomeDataSource);
			
			Ejb3Configuration ejbConf = new Ejb3Configuration();
			
			log.info("Definindo as propriedades da conexao");
			
			ejbConf.setProperty("hibernate.connection.datasource", "java:/"
					+ nomeDataSource);
			ejbConf.setProperty("hibernate.dialect",
				"org.hibernate.dialect.PostgreSQLDialect");
			
			ejbConf.setProperty("hibernate.transaction.factory_class",
				"org.hibernate.transaction.JTATransactionFactory");

			ejbConf.setProperty("hibernate.transaction.manager_lookup_class",
				"org.hibernate.transaction.JBossTransactionManagerLookup");
			
			ejbConf.setProperty("hibernate.show_sql", "false");
			ejbConf.setProperty("hibernate.format_sql", "true");
			
			ejbConf = mapearJars(ejbConf);
			
			log.info("Construindo EntityManagerFactory");
			EntityManagerFactory emf = ejbConf.createEntityManagerFactory();
			
			log.info("Adicionando EntityManagerFactory ao Map de factories");
			factories.put(nomeDataSource, emf);
		}
		catch ( Exception e )
		{
			log.error(e);
		}
	}
        ...

mas quando chamo o método inicializarEMFs() dá o erro que descrevi no outro post.

como voce está criando seu EMF, pode postar um exemplo?

valeu pela atenção

GraveDigger
public EntityManagerFactory createEnityManagerFactory(NetSarCustomer customer) {

		Ejb3Configuration conf = new Ejb3Configuration();
		
		conf.setProperty("hibernate.dialect", "br.com.netsar.infra.persistence.hibernate.mysql.CorrectMySQLDialect");
		conf.setProperty("hibernate.transaction.factory_class", "org.hibernate.transaction.JTATransactionFactory");
		conf.setProperty("hibernate.connection.driver_class", "com.mysql.jdbc.Driver");
		conf.setProperty("hibernate.show_sql", "true");
		
		conf.setProperty("hibernate.connection.datasource","java:/DS/" + customer.getMysqlDatabase
		conf.setProperty("hibernate.connection.url", "jdbc:mysql://localhost:3306/"	+ customer.getMysqlDatabase());
		
		conf.addResource("META-INF/hibernate1.cfg.xml");
		
		return conf.buildEntityManagerFactory();
	}

Percebi que vc adicionou:

ejbConf.setProperty("hibernate.transaction.manager_lookup_class",  
                 "org.hibernate.transaction.JBossTransactionManagerLookup");

e

ejbConf.createEntityManagerFactory();

ao invés de

ejbConf.buildEntityManagerFactory();

Algum motivo especial para essas opções que vc fez ?

Suspeito que seu problema seja justamente esse JbossTransactionManagerLookup, faça um teste sem ele.

Atualmente não estou mais nesse problema, estou fazendo outra atividade, por isso deixei de postar como estava antes :frowning:

Mas pretendo finalizar isso ainda essa semana e com sorte no fds volto pra esse problema, pra gente já matar isso de vez (se vc já não tiver matado até lá,hehe).

[]'s

Pedro Sena

J

Eu tinha usado o createEntityManagerFactory() porque usando o buildEntityManagerFactory() tava tendo uma exception de segurança, algo relacionado a hibernate.jacc.

Mudei agora para buildManagerFactory() e não deu mais esse erro de segurança. Deve ser alguma configuração que estava errada.

Comentei as linhas

ejbConf.setProperty("hibernate.transaction.manager_lookup_class",    
            "org.hibernate.transaction.JBossTransactionManagerLookup");

e agora além do erro ao instanciar o segundo EMF, tambem da o seguinte erro ao tentar fazer uma persistencia:

16:39:34,968 WARN  [JTATransaction] You should set hibernate.transaction.manager_lookup_class if cache is enabled
16:39:35,140 WARN  [JTATransaction] You should set hibernate.transaction.manager_lookup_class if cache is enabled
16:39:35,140 ERROR [LoginServiceBean] java.lang.IllegalStateException: JTA TransactionManager not available
16:39:35,140 ERROR [LoginServiceBean] java.lang.IllegalStateException: JTA TransactionManager not available

Percebi que voce está usando um arquivo:

conf.addResource("META-INF/hibernate1.cfg.xml");

Tem alguma configuração especial nele?

GraveDigger

Não tem nada especial nele.

Foi uma tentativa(frustrada) de colocar o mapeamento das classes em um arquivo externo.

[]'s

GraveDigger

Cara,

Fiz o teste aqui para a criação de mais de um EMF usando esse esquema e não obtive o mesmo warning que você.

Meu teste:

NetSarCustomer c1 = new NetSarCustomer();
		c1.setMysqlDatabase("teste1");
		
		List<EntityManagerFactory> emfs = new ArrayList<EntityManagerFactory>();
		
		MultiSchemaEntityManagerFactory fac = new MultiSchemaEntityManagerFactory();
		
		emfs.add(fac.createEnityManagerFactory(c1));
		
		NetSarCustomer c2 = new NetSarCustomer();
		c2.setMysqlDatabase("teste2");
		
		emfs.add(fac.createEnityManagerFactory(c2));
		
		for ( EntityManagerFactory f : emfs ) {
			EntityManager em = f.createEntityManager();
			assert em.find(Concept.class, 1) != null;
		}

Minha classe que cria as EMFs recebe um cliente como parâmetro, para poder instanciar a EMF correta para ele…

Mas tem um ponto, embora eu ache improvável, se vc estiver disposto podemos testar.

Eu rodo meus testes num Jboss Embedded e minha versão de prod é 4.2.x.

Se vc quiser me mandar seu arquivo para que eu faça deploy aqui e veja se localmente isso ocorre, seria interessante.

As vezes pode ser algo que passa pelo Jboss Embedded mas não pelo AS mesmo(improvável mas possível).

GraveDigger

Outra coisa:

Poste aqui seus arquivos de configurações do hibernate, pode ser algo lá também.

[]'s

J

Configurei meus arquivos de DS como o abaixo:

<?xml version="1.0" encoding="UTF-8"?>

<!-- ===================================================================== -->
<!--                                                                       -->
<!--  JBoss Server Configuration                                           -->
<!--                                                                       -->
<!-- ===================================================================== -->

<!-- $Id: flash-ds.xml,v 1.2 2006/03/30 18:42:14 nobody Exp $ -->
<!-- ==================================================================== -->
<!--  Datasource config for Postgres                                      -->
<!-- ==================================================================== -->


<datasources>
  <local-tx-datasource>
    <jndi-name>ums001DS</jndi-name>
    <connection-url>jdbc:postgresql://localhost:5432/ums001</connection-url>
    <driver-class>org.postgresql.Driver</driver-class>
    <user-name>ums</user-name>
    <password>ums</password>
    <new-connection-sql>select 1</new-connection-sql>
    <check-valid-connection-sql>select 1</check-valid-connection-sql>
    <min-pool-size>1</min-pool-size>
    <max-pool-size>5</max-pool-size>
    <idle-timeout-minutes>10</idle-timeout-minutes>
    
        <!-- sql to call when connection is created
        <new-connection-sql>some arbitrary sql</new-connection-sql>
        -->

        <!-- sql to call on an existing pooled connection when it is obtained from pool 
        <check-valid-connection-sql>some arbitrary sql</check-valid-connection-sql>
        -->

  </local-tx-datasource>

</datasources>

Eu teria um DS desses para cada Database que a aplicação acessar, a diferença neles seria o connection-url e o JNDI name.

Os arquivos de DS são os únicos usados para configurar o Ejb3Configuration, você usa mais algum?

Vou fazer mais uns testes aqui, amanha de manha eu passo meus fontes para voce tentar rodar aí. Hoje não dá tempo de eu fazer muita coisa aqui, saio daqui 15 minutos.

[]s

J

Voce pode me passar seu e-mail? aí eu envio para voce o EAR e os fontes, se necessário, para você tentar rodar no seu JBoss.

Preciso matar esse problema essa semana, sexta to saindo de férias.

[]s

GraveDigger

Enviado por MP

[]'s

J

Ok, valeu…

só uma dica pra voce que tentou usar o hibernate.cfg.xml: Tava vendo um livro sobre hibernate aqui e achei o seguinte exemplo:

Ejb3Configuration cfg = new Ejb3Configuration();
EntityManagerFactory emf =
cfg.configure("/custom/hibernate.cfg.xml")
.setProperty("hibernate.show_sql", "false")
.setInterceptor( new MyInterceptor() )
.addAnnotatedClass( hello.Message.class )
.addResource( "/Foo.hbm.xml")
.buildEntityManagerFactory();

acho que o cfg.configure("/custom/hibernate.cfg.xml") pode te ajudar.

[]s

GraveDigger

Cara,

Vi a solução que vc encontrou no site do hibernate, eu queria evitar o Class.forName por questões de performance, fazendo uma busca mais a fundo percebi que esse método não é tao pesado quanto eu imaginava(ao menos não para entidades que não tem nada mto especial como blocos static ou coisass do tipo) e que a própria solução proposta tb o utiliza.

Tendo isso em mente, criei um arquivo meu mesmo, nem xml é, contendo apenas as classes que eu preciso mapear e jogo isso para o Ejb3Configuration.

Para mim não é um grande problema declarar em um arquivo todas as entidades que eu quero, mesmo pq, pelo ambiente que tenho, preciso saber delas mesmo sem o jar pronto, então optei por essa solução declarativa.

Com uma ajudinha do Seam(esse framework é mto bom…) consegui configurar tudo bonito aqui e agora tenho minha solução final.

Mas ainda falta a sua, então vou ajudando na medida do possível aqui, acredito que até o fds mataremos isso.

[]'s

Criado 11 de dezembro de 2008
Ultima resposta 18 de dez. de 2008
Respostas 27
Participantes 2