terça-feira, 25 de agosto de 2020

JPA - Carregando multiplas listas em uma mesma entidade evitando o erro MultipleBagFetchException

    É comum termos um entidade que precise carregar mais de uma lista, e quando essas listas precisam ser carregadas juntas no mesmo select, podemos nos deparar com o seguinte erro:
 
Caused by: org.hibernate.loader.MultipleBagFetchException: 
cannot simultaneously fetch multiple bags

    Bag significa algo como "bolsa" ou "sacola" (tradução livre rsrsrs), e isso quer dizer estamos tentando carregar mais de uma "bolsa" de dados e o JPA não sabe como organizar esses dados para nos entregar.

    Isso ocorre porque geralmente o banco nos devolve uma lista ordenada de dados (List), mas como o JPA está tentando montar uma lista que contém listas dentro delas, ele pode acabar desordenando essas listas. Então ele quer que você diga pra ele como tratar esses dados.

    Existem basicamente 3 formas de contornarmos esse problema:

  1. Nunca carregar as listas na mesma consulta.
  2. Usar Set ao invés de List ao criar as listas na entidade.
  3. Usando a anotação @OrderColumn.
   Vamos abordar cada uma delas a seguir:

    1 - Nunca carregar as listas na mesma consulta.

    Ao invés de você usar o FetchType.EAGER ou no select fazer um join fetch caso o carregamento seja FetchType.LAZY nas "n" listas da sua entidade, você carrega apenas uma delas no primeiro momento, e a(s) outra(s) você carrega separadamente, por exemplo chamando o método get da(s) outra(s) lista(s) com o objeto ainda gerenciado pelo JPA, de forma que ele vá ao banco e preencha essas listas.
     
    2 - Usar Set ao invés de List ao criar as listas na entidade.

    Outra solução é usar o Set ao invés do List. Como o Set não é ordenado como o List o JPA não vai se perder tentando ordenar essa lista. Lembrando que o Set não trabalha com dados duplicados.

    3 - Usando a anotação @OrderColumn.

    Você também pode criar um campo para o hibernate trabalhar a ordenação e não se perder. A desvantagem dessa solução é unicamente a de criar um campo extra para o JPA poder ordenar os dados. 

    Se o mapeamento for bidirecional, será preciso criar a propriedade e mapea-la na classe que detêm o mapeamento além de usá-la no @orderColumn.
    
@Entity
public class Paciente {
 
    //se tiver o mappedBy (bidirecional) precisa criar a propriedade na outra classe 
    @OneToMany(mappedBy="paciente")
    @OrderColumn(name="ordenacao_hibernate")
    private List<PacienteSintoma> lstPacienteSintoma;
 
    //se tiver o mappedBy (bidirecional) precisa criar a propriedade na outra classe 
    @OneToMany(mappedBy="paciente")
    @OrderColumn(name="ordenacao_hibernate")
    private List<PacienteDiagnostico> lstPacienteDiagnostico; 
 
    ...
}

@Entity
public class PacienteSintoma {
    ...
    //bidirecional, necessário mapear a propriedade
    @Column(name="ordenacao_hibernate")
    private int ordenacaoHibernate;

    @ManyToOne
    @JoinColumn(name="paciente_id", nullable=false)
    private Paciente  paciente;
 
    ...
}
 
@Entity
public class PacienteDiagnostico {
    ...
    //bidirecional, necessário mapear a propriedade
    @Column(name="ordenacao_hibernate")
    private int ordenacaoHibernate;

    @ManyToOne
    @JoinColumn(name="paciente_id", nullable=false)
    private Paciente  paciente;
 
    ...
} 
 
    Se o mapeamento for unidirecional, será preciso criar apenas o campo no banco e passá-lo no @orderColumn.

@Entity
public class Paciente {
 
    //unidirecional 
    @OneToMany
    @JoinColumn(name="paciente_id", nullable=false) 
    @OrderColumn(name="ordenacao_hibernate")
    private List<PacienteSintoma> lstPacienteSintoma;
 
    //se tiver o mappedBy (bidirecional) precisa criar a propriedade na outra classe 
    @OneToMany(mappedBy="paciente")
    @JoinColumn(name="paciente_id", nullable=false)  
    @OrderColumn(name="ordenacao_hibernate")
    private List<PacienteDiagnostico> lstPacienteDiagnostico; 
 
    ...
}

@Entity
public class PacienteSintoma {
    ...
    //bidirecional, necessário mapear a propriedade
    @Column(name="ordenacao_hibernate")
    private int ordenacaoHibernate;

    @ManyToOne
    @JoinColumn(name="paciente_id", nullable=false, insertable=false, updatable=false)
    private Paciente  paciente;
 
    ...
}
 
@Entity
public class PacienteDiagnostico {
    ...
    //bidirecional, necessário mapear a propriedade
    @Column(name="ordenacao_hibernate")
    private int ordenacaoHibernate;

    @ManyToOne
    @JoinColumn(name="paciente_id", nullable=false, insertable=false, updatable=false)
    private Paciente  paciente;
 
    ...
} 
Fonte: https://docs.jboss.org/hibernate/stable/annotations/reference/en/html/entity.html#entity-hibspec-collection-extratype-indexbidir


 

sexta-feira, 17 de julho de 2020

Resolvendo problema CORS no Spring

Olá, hoje vou falar sobre o CORS – Cross-Origin Resource Sharing que significa compartilhamento de recursos de origem cruzada.

Antes era muito comum o uso de iFrames ou similares para fazer com que um site acessasse diretamente conteúdo de outros o que também era válido para aplicações web mais complexas, o problema é que isso começou a ser explorado por pessoas mal intencionadas para se passarem por um site ou serviço que não são e roubar informações ou outras atividades ilícitas. Então os navegadores começaram a “bloquear” esse tipo de coisa, validando antes se a origem tem acesso ao backend vindo de um IP diferente ou não configurado em uma digamos “White list”.

         Trarar o CORS é obrigação do backend, e vamos ver como fazer isso no Spring. No caso, podemos fazer de duas formas: liberando geral ou especificando as URLs que terão o acesso garantido.

    Para liberar qualquer endereço, podemos desabilitar o CORS na classe de configuração de segurança:

import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;

@Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter {

    @Override
    public void configure(WebSecurity web) throws Exception {
        web.ignoring().antMatchers("/v2/api-docs",
                "/configuration/ui",
                "/swagger-resources/**",
                "/configuration/security",
                "/swagger-ui.html",
                "/webjars/**");
    }

    @Override
    public void configure(HttpSecurity http) throws Exception {

        http.authorizeRequests().antMatchers("/xxx/**").authenticated();
        http.authorizeRequests().anyRequest().permitAll();
        http.csrf().disable();

    }

}


        Ou podemos criar um bean na classe principal da aplicação e passar o padrão de URL como "/**":
       
@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
        }
    };
}


    Para liberar endereços específicos, o que é mais recomendado, basta passar o endereço no lugar do "/**":

@Bean
public WebMvcConfigurer corsConfigurer() {
    return new WebMvcConfigurer() {
        @Override
        public void addCorsMappings(CorsRegistry registry) {
            registry.addMapping("/greeting-javaconfig").allowedOrigins("http://localhost:9000");
        }
    };
}


     Outra forma de fazer é ao invés de criar o bean na classe principal, é criar uma classe específica com a configuração, deixando-a mais explícita:
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

@Configuration
@EnableWebMvc
public class CorsConfiguration implements WebMvcConfigurer {
    @Override
    public void addCorsMappings(CorsRegistry registry) {
        registry.addMapping("/**");
    }

}

 



 FOnte: https://spring.io/guides/gs/rest-service-cors/ 

quinta-feira, 4 de junho de 2020

Configurando o Spring Security 5 com autenticação em memória

    Do Spring Security 4 para o 5 configurar um usuário e senha em memória não é mais tão simples, porém nesse link abaixo tem a configuração necessária para fazê-lo basicamente como antes:

https://spring.io/guides/gs/securing-web/

    Basicamente o que muda é a classe WebSecurityConfig que passa a ficar assim:

import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.provisioning.InMemoryUserDetailsManager;

@EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {

@Override
protected void configure(HttpSecurity http) throws Exception {
http.authorizeRequests().antMatchers("/h2-console/**").permitAll().anyRequest().authenticated().and()
.httpBasic().and().csrf().disable();
}

@Bean
@Override
public UserDetailsService userDetailsService() {
UserDetails user = User.withDefaultPasswordEncoder().username("seu_usuario").password("sua_senha")
.roles("USER").build();

return new InMemoryUserDetailsManager(user);
}

}

segunda-feira, 11 de maio de 2020

Pegando o valor de uma sequence no Postgres

    Quando vamos fazer um insert no banco, o ideal é usarmos a própria sequence para inserir a chave primária, evitando que ela fique desatualizada e gerando erros.

    Para chamarmos a sequence para ela gerar o valor a ser inserido na chave primária podemos usar:

select nextval('nome_da_sequence');

    Quando precisarmos atualizar o valor de alguma sequence, podemos usar o seguinte comando:


select setval('nome_da_sequence', valor_aqui);

sexta-feira, 8 de maio de 2020

Evitando o erro: sun.awt.X11GraphicsEnvironment


Para evitar problemas com aplicações Java rodando em servidores Linux sem modo gráfico disponível, é sempre bom usar o parâmetro:

-Djava.awt.headless=true

Isso vai evitar que em algum momento você se depare com um erro no servidor e gaste um bom tempo até se lembrar desse problema. E qual o problema que esse parâmetro evita?

Bom, você pode diretamente usar alguma classe do pacote sun.aws ou alguma biblioteca que use classes desse pacote (como o IReport, etc), e o problema é que esse pacote trabalha com algumas coisas que precisam ser renderizadas e tenta usar bibliotecas que se comunicam com a interface gráfica do servidor pra isso, e ao serem usadas suas classes ele verifica se o servidor tem modo gráfico disponível ou não, caso não, ele lança uma exceção.

Provavelmente a exceção vai ter algo a ver com sun.awt.X11GraphicsEnvironment

O parâmetro indicado acima a grosso modo diz ao Java pra ignorar o uso das bibliotecas do modo gráfico do servidor.

Então esse é um bom parâmetro pra usar sempre.  



Fonte:
http://www.jguru.com/faq/view.jsp?EID=222296

quarta-feira, 8 de abril de 2020

Como fazer sua Aplicação Web transformar texto em áudios (Text-To-Speech)

       Nesse post eu vou falar sobre algo muito útil, a transformação de texto em voz em aplicações web. Sabe quando você quer fazer aqueles paineis de chamada de clientes, ou mesmo ler um texto para o usuário por questões de acessibilidade? Quando pensamos nisso achamos que pode ser algo muito complexo né? Na verdade é algo bem simples!
         
Os navegadores já trazem por padrão um reprodutor de áudio pronto para ser usado via JavaScript, basta passar as frases e o navegador irá reproduzir na linguagem na qual está configurado. Também é possível configurar outras línguas.
         
          Vamos ver esse simples código:

<html>
        <body>
                 <h1 id="speech">Olá, bem vindo ao blog "Olá Mundo - Java"</h1>
        </body>
        <script>
        /**
         *
         * @author Daniel Oliveira
         */
       
        function reproduzirNome(){
                 var falar = document.getElementById('speech');
                 var synth = window.speechSynthesis;
                 var utterNome = new SpeechSynthesisUtterance();
                 var utterSala = new SpeechSynthesisUtterance();
       
                 utterNome.volume = 1; // 0 to 1
                 utterNome.rate = 1; // 0.1 to 10
                 utterNome.pitch = 1.5; // 0 to 2
                 utterNome.lang = "pt-PT";
                
                 for(var i = 0;i < 2; i++){
                         sleep(1000);
                         utterNome.text = falar.textContent; //campos input pega-se o "value"
                         sleep(1000);
                         synth.speak(utterNome);
                                         
                 }
                
        }
       
        function sleep(milliseconds) {
                 const date = Date.now();
                 var currentDate = null;
                 do {
                         currentDate = Date.now();
                 } while (currentDate - date < milliseconds);
        }
       
        window.onload = reproduzirNome();
        </script>
</html>

          Basta pegar esse código e colocar em um projeto seu para ver funcionando. Uma observação importante é que qualquer áudio ou vídeo para ser reproduzido automaticamente precisa de permissão do usuário. Essa permissão é dada no navegador no cadeado(se for https) ou no “i” (se for http) ao lado do endereço:







          Existem outras configurações de controles, tipo de voz, etc, basta dar uma pesquisada nos links abaixo:


          Viu como é fácil?! Isso era o que eu tinha para esse post, até a próxima!

sexta-feira, 27 de março de 2020

Multiplas SSHs Key para Repositórios Git

As vezes precisamos de mais de uma chave SSH para podermos acessar repositórios Git com usuários diferentes, como por exemplo seu usuário pessoal e o usuário do trabalho. 

       Se os projetos estiverem no mesmo repositório a configuração é um pouco diferente; se forem em locais difetentes: GitHub, GitLab, BitBucket, etc,  é mais simples.

       Vamos começar. A primeira parte é igual, não importa se os projetos estão em um mesmo repositório ou não, que é a geração da chave.

Estamos assumindo que você já possui uma chave configurada para um usuário e quer criar a segunda chave SSH. Então, vamos começar executando o git-bash, um terminal Linux para o Windows que também faz parte da instalação do Git. Após aberto o git-bash, digite:

ssh-keygen -t rsa -b 4096 -C "seu-email-outro-usuario@aqui.com"

Você será indagado sobre onde salvar a chave, sendo que não podemos criar a chave SSH no caminho e nome default, pois já existe uma chave SSH lá. Mude o caminho ou o nome do arquivo, por exemplo, acrescentando .ssh/id_rsa_trabalho no final do nome da chave. Continue a geração, entrando com a senha e confirmando:

Enter passphrase (empty for no passphrase):
Enter same passphrase again:

Caso não queira informar uma senha, pode apenas confirmar sem digitar nada (é recomendado criar uma senha, claro!). Então o prompt lhe informa que a sua chave privada e a sua chave publica estão no caminho indicado previamente, que se for o default é: C:\Users\seu-usuario\.ssh.

Copie e adicione sua chave pública à sua conta no repositório, o comando abaixo copia sua chave para a memória e deixa pronto para colar no lá:

clip < ~/.ssh/id_rsa_trabalho.pub

Nesse momento temos a nossa segunda chave SSH, precisamos configurar quando usar cada uma. Para isso, dentro do diretório do C:\Users\seu-usuario\.ssh que é onde ficam as chaves por padrão, crie um arquivo chamado config. Nesse arquivo vamos configurar o acesso a cada repositório por uma chave.

      Segue o exemplo de um arquivo para acessar o GitHub e o Bitbucket com usuários diferentes, e o bitbucket sendo acessado através da nossa nova chave:

Host bitbucket.org
    HostName bitbucket.org
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/id_rsa_trabalho

Host github.com
    HostName github.com
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/id_rsa


Pronto! Se você queria acessar projetos em repositórios diferentes com chaves diferentes sua máquina já está configurada, basta clonar normalmente seus projetos.

     Agora se você quer acessar com duas chaves no mesmo repositório, precisamos do pulo do gato. Já viu que quando vai clonar um projeto seu a URL é mais ou menos assim:

git@github.com:GbDanielO/p-calendar-ptbr.git

      Veja que vem o nome do repositório, como no destaque, depois o nome do projeto. Vamos usar isso pra diferenciar qual chave usar, mas ao invés de separar por ":" vamos separar por "traço". Vamos imaginar que a nossa url fosse: git@github.com:trabalho/p-calendar-ptbr.git. Você precisa fazer dessa forma:


Host github.com-trabalho
    HostName github.com
    PreferredAuthentications publickey
    IdentityFile ~/.ssh/id_rsa_trabalho

Host github.com
    HostName github.com
    PreferredAuthentications publickey

    IdentityFile ~/.ssh/id_rsa

       Note que o HostName é igual para as duas configurações, o que muda é o Host, que identifica qual chave usar. ATENÇÃO! Agora com essa mudança, sua forma de fazer o clone do projeto no repositório trabalho mudou. O outro acesso continua como antes.

       Ficará dessa forma:

git clone git@github.com-trabalho:trabalho/p-calendar-ptbr.git

       Pronto, no demais é tudo igual.




Fonte:
https://superuser.com/questions/366649/ssh-config-same-host-but-different-keys-and-usernames
https://backefront.com.br/diferentes-sshkey-mesmo-computador/
https://victortorres.net.br/2018/12/03/mais-de-uma-chave-ssh-no-git