Qual melhor maneira/pratica para deixar a conexão com o banco dinâmica?

Qual melhor maneira/pratica para deixar a conexão com o banco dinâmica?

Pergunto isso pois estou construindo uma arquitetura com Jenkins, Ansible e Docker. Nesse processo, caso a imagem esteja sendo gerada para o ambiente de desenvolvimento, a URL do banco será uma, assim como para testes e produção.

Pensei em variáveis de ambiente, mas não sei como usar no persistence.xml. Outra coisa que pensei foi em usar JNDI e o tomcat gera para mim o acesso, aí cada ambiente teria uma server.xml.

Como vocês fazem? O que consideram a melhor prática para isso?

Você pode definir persistence units diferentes, com data sources diferentes (ou parâmetros de conexão diferentes), e utilizar a PU de produção no código de produção e a de testes no código de testes.

Ok, só para ver se entendi direito. no próprio persistence.xml crio as PU.

<persistence-unit name="default">        
<class>model.Entity1</class>
<class>model.Entity2</class>
<class>model.Entity3</class>
...
</persistence-unit>

<persistence-unit name="dev">        
<properties>
  <property name="javax.persistence.jdbc.user" value="user"/>
  <property name="javax.persistence.jdbc.password" value="pass"/>
  <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://host_dev:5432/db_dev"/>
  <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
</properties>
</persistence-unit>

<persistence-unit name="prod">	
<properties>
  <property name="javax.persistence.jdbc.user" value="user"/>
  <property name="javax.persistence.jdbc.password" value="pass"/>
  <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://host_prod:5432/db_prod"/>
  <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
</properties>
</persistence-unit>

Isso?

E como informo no momento de subir a aplicação qual será o PU que irei utilizar?

Estou usando VRaptor 4

A parte das classes tem que ser definidas pra cada PU, mesmo que de forma repetida, ou então, você pode deixar o container de aplicação escanear o classpath e definir as entidades automaticamente, ao invés de defini-las manualmente com <class>...</class>.

Sobre como adquirir a PU de forma dinâmica, existem algumas maneiras diferentes. Infelizmente o JPA (ainda) não suporta de maneira nativa a utilização de persistence units de forma dinâmica.

  1. Por exemplo, no meu caso, eu sempre tento automatizar os testes funcionais, utilizando o Arquillian. Ele que sobe o servidor, dá deploy no .war que eu definir, executa os testes, dá undeploy no .war e desliga o servidor. Como sou eu que defino o que vai dentro do .war, eu coloco um persistence.xml especial que tem só a PU de testes. Quando eu gero o .war real, o persistence.xml que é utilizado é o de produção;

  2. Eu nunca mexi com VRaptor, vou te responder como daria para fazer no Java EE “puro” e você adapta pro teu caso. Pelo que eu entendi, você utiliza o mesmo código para produção e para testes manuais, e quer trocar a PU de acordo com a situação, correto? Pois bem, podemos fazer o seguinte: utilizar um producer do CDI para injetar o PU correto. Você pode usar a anotação @Inject para injetar o teu EntityManager, ao invés de @PersistenceContext, e abrir brecha para a utilização de um producer. Você vai criar uma classe com um método assim:

     @Produces
     public EntityManager createEntityManager() {
         // aqui você vai criar o entity manager com a PU que quiser, utilizando
         // Persistence.createEntityManagerFactory(nome, null)
     }
    

    A (grande) desvantagem dessa abordagem é que o persistence context desse entity manager não vai ser tratado pelo container, você tem que tratar manualmente;

  3. Você pode também simplesmente ficar indo no persistence.xml comentar a PU que não irá ser utilizada naquele momento (comenta a de produção quando for testar e a de teste quando for pra produção). Com isso você não precisa mexer em anotação nenhuma;

  4. Outra solução é você escrever um script pra Bash que procura em todo o projeto a anotação @PersistenceUnit, e troca o argumento do parâmetro unitName. A ideia é que se você chamar o script como em ./trocaPU teste, por exemplo, ele vai percorrer todas as classes e definir as anotações para @PersistenceUnit(unitName='teste').

Consegui pensar nessas formas agora. Dei uma pesquisada e não encontrei muito alem disso. Espero que, caso haja alguma forma mais inteligente, alguém compartilhe conosco!

Achei em outro post é bem simples. O V Raptor já tem um plugin que cuida disso, é só setar no properties correto. Ele escolhe o properties pela váriavel de ambiente.
http://respostas.guj.com.br/46305-vraptor-4--vraptor-jpa--configurando-persistence-unit-com-environmnet

Você me adiantou a pergunta, preciso ficar escrevendo as classes repetidas? hehe

Como faço para ele encontrar sozinho como você mencionou?

Que legal, ele faz algo parecido com o que eu falei pra você fazer ali na opção 2 :grin: Você sabe me dizer se o VRaptor que cuida do ciclo de vida do EntityManagerFactory, como gerenciar pool de conexões, abrir e fechar os entity managers automaticamente e coisas do gênero? Porque se faz, é bem interessante a pegada deles.

Sobre o scan de entidades: por padrão, em aplicações Java EE que são implantadas no servidor de aplicação, isso é feito de forma automática quando você injeta o EntityManager. Ele procura o persistence.xml com as configurações e lê o classpath inteiro atrás de classes anotadas com @Entity. Inclusive, existe uma tag no persistence.xml chamada <exclude-unlisted-classes>...</exclude-unlisted-classes>, que pode receber os valores true ou false e falam para o container se você quer desligar a leitura automática de entidades.

Já no Java SE, você precisa colocar as classes no persistence.xml, a não ser que o teu provider disponibilize a funcionalidade de scan automático (o que é o caso do Hibernate).

Não sei como é o esquema no VRaptor. Talvez tenha algum jeito de configurar com alguma variável de ambiente ou algum arquivo de configurações, se não é ligado por padrão.

Vou ser sincero, não sei muito bem ainda, comecei a usar a pouco tempo, mas acredito seja pelo escopo, pois ele implementa o CDI, então temos anotações como SessionScoped, Resquest, Application. Se vai abrir ou não sessão, você anota o metodo com @Transational

Eles conseguiram deixar tudo muito simples.

Se quiser um exemplo rápido vai no http://www.setupmyproject.com/setup e segue os passos. Por padrão ele gera um Maven Project, mas não gosto muito, então mudei o meu para Gradle

E para a JPA carregar sozinha as entities adicionei essas tags

<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<shared-cache-mode>ALL</shared-cache-mode>
<validation-mode>AUTO</validation-mode>

Não conhecia esse site, bem interessante. Obrigado por compartilhar!

Coloca isso aqui dentro da tag <persistence-unit> que talvez funcione:

<exclude-unlisted-classes>false</exclude-unlisted-classes>

E isso daqui dentro da tag <properties>:

<property name="hibernate.archive.autodetection" value="class, hbm" />

Realmente não posso te responder com 100% de certeza, porque não sei como funciona o VRaptor por baixo dos panos. Testa aí e fala se funcionou!

Eu utilizei que nem acima, alguns posts no stackoverflow que encontrei não recomendam fazer assim por conta do JSE. Então se do jeito que eu fiz funciona para todos preferi deixar assim

<exclude-unlisted-classes>false</exclude-unlisted-classes>

Terminei o teste agora e funcionou. Como a troca é só pela váriavel de ambiente fica fácil agora criar uma imagem no Docker.

Só preciso agora entender as melhores práticas de DevOps. Tenho estudado muito esse tópico, que é o que eu menos conheço. Já fiz funcionar tudo individualmente, falta só o Jenkins e aí é integrar tudo.

Valew a ajuda Ivbarbosa. Ajudou muito.

Só não sei onde marco como resolvido agora huahua,

Muito obrigado

Abss

1 curtida

Para quem está visitando esse tópico recomendo ler toda discussão.

mas resumindo o utilizei foi a criação de 2 persistence-unit. O default apontando para o desenvolvimento e o production para produção. Também configurei ambos para carregar as classes automaticamente

<persistence-unit name="default">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<shared-cache-mode>ALL</shared-cache-mode>
<validation-mode>AUTO</validation-mode>
<properties>
  <property name="javax.persistence.jdbc.user" value="user"/>
  <property name="javax.persistence.jdbc.password" value="pwd"/>
  <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://dev-host:5432/dev-db"/>
  <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
</properties>
</persistence-unit>

<persistence-unit name="production">
<provider>org.hibernate.jpa.HibernatePersistenceProvider</provider>
<shared-cache-mode>ALL</shared-cache-mode>
<validation-mode>AUTO</validation-mode>
<properties>
  <property name="javax.persistence.jdbc.user" value="user"/>
  <property name="javax.persistence.jdbc.password" value="pwd"/>
  <property name="hibernate.show_sql" value="true"/>
  <property name="hibernate.format_sql" value="true"/>
  <property name="hibernate.hbm2ddl.auto" value="update"/>
  <property name="javax.persistence.jdbc.url" value="jdbc:postgresql://prod-host:5432/prod-db"/>
  <property name="javax.persistence.jdbc.driver" value="org.postgresql.Driver"/>
</properties>
</persistence-unit>

Com o vraptor eu só preciso configurar o properties development.properties e o production.properties com o atributo br.com.caelum.vraptor.jpa.persistenceunit=<nome da pu>

O vraptor carrega esse valor da variável de ambiente ou no web.xml, assim que fica mais fácil a troca da PU conforme o servidor em que a aplicação está hospedada.

2 curtidas

Utilizem profile e filter do Maven ou Gradle

É o jeito correto de fazer isso e não utilizar diferentes persistence-unit