Pesquisar este blog

segunda-feira, 31 de agosto de 2015

Banco de dados Java com JPA, Hibernate e Spring Data.

O JPA é uma api do java para persistência, Java Persistence API. Persistência é um termo utilizado para se referir ao armazenamento dos dados em um sistema de banco de dados. Esta API surgiu para facilitar a utilização de bancos de dados, com o Java.
Hibernate é um ORM (Object Relational Mapping), ou ferramenta de mapeamento de um banco relacional para objetos, nele vamos mapear cada tabela do nosso banco de dados, para uma classe, e as suas relações também serão mapeadas.
Spring Data é um framework que facilita o trabalho de toda a camada de acesso e troca de informações com um banco de dados. Nele é possível gerarmos queries, CRUDs, sem ter que escrever muito código.
Lembrando que todo o código referente ao desenvolvimento deste sistema web, estarão nos repositórios do github1 e github2

Dependências

Para iniciar altere o seu arquivo pom.xml adicionando as seguintes dependências, para quem usa o maven:
        <dependency>
            <groupid>org.springframework.data</groupid>
            <artifactid>spring-data-jpa</artifactid>
            <version>1.8.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupid>org.hibernate</groupid>
            <artifactid>hibernate-entitymanager</artifactid>
            <version>5.0.0.Final</version>
        </dependency>
        <dependency>
            <groupid>com.h2database</groupid>
            <artifactid>h2</artifactid>
            <version>1.4.185</version>
        </dependency>
        <dependency>
            <groupid>com.zaxxer</groupid>
            <artifactid>HikariCP</artifactid>
            <version>2.2.5</version>
        </dependency>
E estas mesmas dependências no SBT, no arquivo build.sbt:
 libraryDependencies ++= Seq("org.springframework.data" % "spring-data-jpa" % "1.8.2.RELEASE" , "org.hibernate" % "hibernate-entitymanager" % "5.0.0.Final" , "com.h2database" % "h2" % "1.4.185", "com.zaxxer" % "HikariCP" % "2.2.5")
Como deu para notar a versão utilizando o SBT foi muito mais simples.
Com as configurações acima adicionamos três dependências ao nosso projeto. A primeira delas, o Spring-Data é um framework que nos ajuda a lidar com a manipulação dos dados no Java. O hivernate-entitymanager é quem realiza a iteração com o banco de dados, é ele que vai explicitamente, se comunicar com o banco de dados e executar as operações. Temos um banco de dados que é o h2, este banco não precisa de instalação, pois ele roda na memória e é muito bom para executarmos testes. A última dependência é o HikariCP que é um administrador de conexões com o banco de dados, também conhecido como connection pool, o hikari abre uma série de conexões com o banco de dados e administra estas conexões de forma e deixar o acesso ao banco mais rápido.

Configuração

Um dos primeiros passos para desenvolver um aplicativo que se conecta ao banco de dados é configurar o acesso ao banco, para realizar estas configurações, iremos utilizar as anotações do java.
O primeiro passo da configuração é criar uma classe de configuração que definirá as propriedades da camada de persistência, para isso criam os uma classe com a anotação @Configuration
@Configuration
public class JPAConfig{
//Definições
}
Agora iremos criar um arquivo de propriedades, que deverá ser armazenado na pasta resources, conforme descrito aqui

#Configuração de acesso ao banco
db.driver=org.h2.Driver
db.url=jdbc:h2:mem:dsd
db.username=sa
db.password=

#Configuração do Hibernate
hibernate.dialect=org.hibernate.dialect.H2Dialect
hibernate.hbm2ddl.auto=create-drop
hibernate.ejb.naming_strategy=org.hibernate.cfg.ImprovedNamingStrategy
hibernate.show_sql=false
hibernate.format_sql=true

Neste arquivo estamos configurando tanto um acesso ao banco H2 na memória, na primeira parte, quanto a configuração referente ao Hibernate.

Configurações dos Beans 

Definição do bean de conexão ao banco de dados:
  @Bean(destroyMethod = "close")
DataSource dataSource(Environment env) {
 HikariConfig dataSourceConfig = new HikariConfig();
  dataSourceConfig.setDriverClassName(env.getRequiredProperty("db.driver"));
  dataSourceConfig.setJdbcUrl(env.getRequiredProperty("db.url"));
  dataSourceConfig.setUsername(env.getRequiredProperty("db.username"));
  dataSourceConfig.setPassword(env.getRequiredProperty("db.password"));

  return new HikariDataSource(dataSourceConfig);
}
Aqui estamos assegurando que toda vez que o nosso software for desligado, ele chamará o método destroy da conexão, com isso evitamos que conexões ficam abertas com a base de dados.
Definição de uma conexão JDBC, setando as propriedades do driver, url, username e senha.
E a criação de um objeto HikariDataSoure, que será o objeto retornado, que contem a conexão com o banco de dados.

Bean EntityManagerFactory

EntityManagerFactory é a classe que cuida de Fabricar (Factory é um design pattern que administra a criação de objetos, conforme descrito aqui) os administradores de Entidades (Entity Managers), que são as classes responsáveis por prover métodos que realizam acesso aos dados do banco de dados:
  @Bean
    LocalContainerEntityManagerFactoryBean entityManagerFactory(DataSource dataSource,
                                                                Environment env) {
        LocalContainerEntityManagerFactoryBean entityManagerFactoryBean = new LocalContainerEntityManagerFactoryBean();
        entityManagerFactoryBean.setDataSource(dataSource);
        entityManagerFactoryBean.setJpaVendorAdapter(new HibernateJpaVendorAdapter());
        entityManagerFactoryBean.setPackagesToScan("br.unip.dsd","br.unip.dsd.modelos");

        Properties jpaProperties = new Properties();

        //Configures the used database dialect. This allows Hibernate to create SQL
        //that is optimized for the used database.
        jpaProperties.put("hibernate.dialect", env.getRequiredProperty("hibernate.dialect"));

        jpaProperties.put("hibernate.hbm2ddl.auto",
                env.getRequiredProperty("hibernate.hbm2ddl.auto")
        );

        jpaProperties.put("hibernate.ejb.naming_strategy",
                env.getRequiredProperty("hibernate.ejb.naming_strategy")
        );

        jpaProperties.put("hibernate.show_sql",
                env.getRequiredProperty("hibernate.show_sql")
        );

        jpaProperties.put("hibernate.format_sql",
                env.getRequiredProperty("hibernate.format_sql")
        );

        entityManagerFactoryBean.setJpaProperties(jpaProperties);

        return entityManagerFactoryBean;
    }
Com isto configuramos o acesso ao banco, pelo hibernate.

Transaction Manager

Transaction manager é a classe responsável por prover configurações de níveis de transação aos nossos métodos. Por exemplo, podemos ter várias entidades que devem ser cadastradas dentro de uma mesma transação, como no cadastro de um usuário, todos os dados referentes ao endereço, email, usuário e senha, devem ser todos registrados dentro de uma mesma transação,e é o Transaction Manager que nos ajuda a configurar este comportamento. O Spring Data possui um transaction manager desenvolvido para o JPA, o JPATransactionManager, e é ele que iremos utilizar:
    @Bean
    JpaTransactionManager transactionManager(EntityManagerFactory entityManagerFactory) {
        JpaTransactionManager transactionManager = new JpaTransactionManager();
        transactionManager.setEntityManagerFactory(entityManagerFactory);
        return transactionManager;
    }

Habilitando a configuração de Transações por Anotações

Existem algumas anotações que nos facilitam a vida na hora de habilitar as configurações descritas acima. Para habilitar a configuração das transações, iremos adicionar a anotação @EnableTransactionManager na nossa classe de configuração, que ficará assim:

@Configuration
@EnableTransactionManager
public class JPAConfig{
   //Definições
}

Configuração do Spring Data JPA

A configuração dos Repositórios do Spring Data JPA será realizada através da anotação @EnableJpaRepositories, que será adicionada a nossa classe de configuração:
@Configuration
@EnableJpaRepositories("br.unip.dsd.repositorios")
@EnableTransactionManagement
@ComponentScan(basePackages = {"br.unip.dsd.modelos", "br.unip.dsd.controller"})
@EnableWebMvc
@PropertySource("classpath:application.properties")
public class JPAConfig{
   //Definições
}
Nesta configuração define o pacote br.unip.dsd.repositorios, como o pacote onde ficarão as classes de repositório do Spring Data. Com isso todas as classes deste pacote serão analisadas, e todas as classes que extendem os repositórios serão adicionadas ao projeto, com as funcionalidades de um repositório.
Repositório é a denominação da camada que faz acesso aos dados. Em um repositório existem os métodos de criação, alteração, inserção, remoção e queries das nossas entidades, e cada entidade tem o seu respectivo repositório.

Entidades

Entidades são as classes que possuem os dados das tabelas dos banco de dados, também conhecidas como POJO.
Para criar uma entidade basta criar uma classe com os parâmetros da tabela, onde cada parâmetro é uma coluna da nossa tabela. A definição de uma classe como entidade, acontece através da anotação @Entity, conforme mostrado abaixo:
@Entity public class Cidade {
@Id
private Long id;
private String nome;

public Cidade() {
}

public Cidade(Long id, String nome) {
this.id = id;
this.nome = nome;
}

public Long getId() {
return id;
}

public void setId(Long id) {
this.id = id;
}

public String getNome() {
return nome;
}

public void setNome(String nome) {
this.nome = nome;
}

@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Cidade)) return false;

Cidade cidade = (Cidade) o;

if (id != null ? !id.equals(cidade.id) : cidade.id != null) return false;
if (nome != null ? !nome.equals(cidade.nome) : cidade.nome != null) return false;

return true;
}

@Override
public int hashCode() {
int result = id != null ? id.hashCode() : 0;
result = 31 * result + (nome != null ? nome.hashCode() : 0);
return result;
}
}

Repare que:
  1. Temos um construtor com todos os argumentos e um vazio
  2. Todas os parâmetros, possuem um método de get e set (padrão Bean)
  3. Definição do método equals e hashCode, muito importantes para o Hibernate, já que é através deles que o Hibernate sabe se os dados daquela classe foram alterados e precisam ser persistidos no banco.
  4. A anotação @Entidade define esta classe como uma tabela
  5. A anotação @Id define uma coluna como chave primária daquela tabela.
Com isso mapeamos uma tabela para uma classe Java, agora para conseguir armazenar dados dela em um banco de dados, será necessário criar um Repositório.
Um fator muito importante dos bancos SQL, são os relacionamentos existentes entre as tabelas, para isso existem algumas anotações para facilitar a definição de relacionamento.

Relacionamentos

Dos possíveis relacionamentos existenstes, entre as tabelas, nos banco de dados temos:
  • um pra muitos (@OneToMany)
  • muitos pra um (@ManyToOne)
  • muitos pra muitos (@ManyToMany)
No exemplo do sistema desenvolvido neste blog, temos o usuário e seus dados, digamos que foi definido que um usuário pode ter vários endereços, portanto o relacionamento entre usuários e endereços será OneToMany, um usuário terá muitos endereços. Esta configuração pode ser feita assim:

public class Usuario{
@OneToMany(fetch=FetchType.LAZY, cascade = CascadeType.ALL, mappedBy="usuario")
private List<Endereco> endereco = Collections.<Endereco>.emptyList();
}


public class Endereco {
@ManyToOne(fetch=FetchType.LAZY)
@JoinColumn(name="usuario_id")
private Usuario usuario;
}
Aqui temos tanto o mapeamento um pra muitos, como o mapeamento muitos pra um. Como um endereço é de um usuário, ele terá um parâmetro do tipo Usuario definido na sua entidade. Já a entidade Usuario, possui muitos endereços, portanto ela terá um parâmetro com uma lista de Enderecos nela.
As anotações OneToMany e ManyToOne mapeiam o relacionamento entre estas entidades.
O atributo mappedBy determina o nome do parâmetro, da outra ponta do relacionamento, no caso o atributo usuario da classe Endereco, que será responsável por mapear este relacionamento. Este mapeamento é feito pela anotação JoinColumn na entidade Endereco.
O atributo fech=Lazy define quando o atributo será requisitado ao banco de dados, neste caso Lazy, ou preguiçoso, este parâmetro por padrão vem como nulo e só será requisitado ao banco de dados quando ele for acessado. E isto tem algumas implicações:
  • Toda vez que buscarmos um Endereço, ou um Usuario no banco de dados, não haverá nenhum select nas tabelas opostas de relacionamento, o que garante uma melhor performance. Repare que se for necessário ter os dados dos endereços, dentro da entidade usuário, ao fazer um select na tabela usuário, também deverá ser feito um select na tabela endereço, através do seu relacionamento.
  • Para acessar os dados do relacionamento, deveremos ter aberta a conexão com o banco de dados. Um erro muito comum é tentar acessar estes dados na interface, onde a conexão com o banco já fo fechada e acontecer um erro. Com isso lembre-se de acessar todos os relacionamentos necessários para a sua lógica de negócio dentro da camada de banco de dados.
Tipificar a lista usando o generics, List<Endereco> é uma boa prática de programação, com isso garantimos que só serão aceitos objetos do tipo Endereço, na lista de endereços.
Finalmente, o atributo cascade, define o que será feito com os relacionamentos em caso de remoção.
Por exemplo, caso tenhamos um usuário e tentamos remover ele, mas este usuário possui alguns endereços. O banco de dados não permite que você deixe dados órfãos nele, então este tipo de remoção geraria uma mensagem de erro pelo banco.
O JPA pode cuidar disto pra você, removendo todos os filhos daquele dado, antes de removê-lo, e isto é feito definindo o tipo de cascade, neste caso o ALL define que todos os filhos serão apagados na mesma transação que o pai, automaticamente. Estas configurações são um pouco delicadas, já que ao apagar o usuário, você também estará apagando os endereços, e algumas vezes este não é o comportamento desejado, tome muito cuidado ao definir esta propriedade.
Uma propriedade de um relacionamento um pra um (OneToOne) é definido da seguinte maneira:

public class Endereco {
@OneToOne(cascade = CascadeType.ALL, optional = false, fetch = FetchType.EAGER, orphanRemoval = true)
@PrimaryKeyJoinColumn
private Rua rua;
}
PrimaryKeyJoinColumn, indica que o join entre as duas entidades será realizado pela chave primária da entidade Rua, assim teremos uma coluna rua_id na entidade Endereço, que será utilizada para definir o relacionamento entre as duas entidades. Repare que aqui a propriedade de Fetch foi alterada para EAGER, ou seja, todo select realizado em endereço, trará os dados da Rua. Optional false quer dizer que esta propriedade não pode ser nula, e orphanRemoval diz que quando remover o dado de um Endereço, o dado da Rua também será removido.

Repositórios 

A definição de um repositório, é um dos pontos mais interessantes deste aparato de frameworks utilizados neste post.
O Spring Data facilita bastante o desenvolvimento de transações com o banco de dados, principalmente por que quase zera a necessidade de código para realizar operações com o banco de dados, veja no exemplo abaixo:
package br.unip.dsd.repositorios;

import br.unip.dsd.modelos.Usuario;
import org.springframework.data.jpa.repository.JpaRepository;

public interface RepositorioUsuario extends JpaRepository {
   public Usuario findById(Long id);
   public String findNameById(Long id);
   public Usuario findByNameAndId(String name, Long id);

}

Pronto, já temos uma classe que consegue inserir, apagar, atualizar, buscar, contar e fazer todas as operações básicas de uma entidade. Mas como isso?
Lembrem-se que configuramos o pacote br.unip.dsd.repositorios como sendo o pacote de repositórios do nosso projeto? Então com isso estas anotações criam os métodos básicos de CRUD para a nossa entidade Usuario, bastando somente extendermos a interface JpaRepository.
Um outro ponto interessante aqui, é que ao criarmos métodos findByParametro, ele automaticamente gera as queries de busca, pelo nome do parâmetro e você não precisa escrever nada de SQL, nada! Assim foi feito o método findById, você passa um id long, e o Spring gera a query pra você.
E se eu precisar de mais um atributo na minha query? Tudo bem, veja o método findByNameAndId, ele fará uma busca pelo nome e pelo id. Mas e se eu precisar buscar apenas um atributo? Veja o método findNameById, neste caso ele retorna apenas o atributo nome, referente ao id.
Isto facilita muito a nossa vida, pois não "sujamos" o nosso código com queries SQL, não precisamos escrever quase nada de código e tudo o que precisamos, praticamente já vem pronto de bonus.
Claro que se você precisar implementar algum código de query, isso é possível extendendo esta classe e implementando o seu método, mas na maioria das vezes isso não será necessário.

Apresentação

domingo, 30 de agosto de 2015

Metadados em Java com anotações

Anotações foi uma forma de configurar metadados em classes java, introduzido na versão Java 5.0
Uma anotação do java é definido por
@Nome_da_anotação

O que são metadados?

Metadados são dados, sobre os dados e um belo exemplo disse é a definição de uma tabela no banco de dados. Os seus metadados são a definição dos nomes das colunas, e das suas propriedades, como, quem é a chave primária, quais são os tipos das colunas.
Com os metadados podemos configurar dados sobre os dados no java, por exemplo, usando o JPA devemos diferenciar quais sao as classes, usadas para mapear os dados do banco de dados, ou seja, quais classes representam uma tabela no banco de dados, para isso devemos adicionar
@Entity
Nas classes que mapeiam as tabelas do banco, com isso o JPA irá identificar aquelas classes como tabelas.
Antes da versão 5 do java, a maioria dos metadados era configurada em arquivos .xml e no início havia um grande embate entre programadores que preferiam configurar os metadados em arquvos .xml e programadores que preferiam configurar os dados com anotações. A maioria dos frameworks permite tanto a configuração através de arquivos .xml, quanto a configuração por anotações.

segunda-feira, 24 de agosto de 2015

Tomcat como container do seu aplicativo web

Para disponibilizar um projeto na internet, vamos precisar de um container web, mas o que é um container.
Container é um servidor onde o código da sua aplicação vai rodar. Ele basicamente recebe um arquivo war, e roda ele.
Aqui vamos descrever como se instala o TomCat.

Instalação

Na página do Tomcat existem os links para o download do servidor, após baixar o arquivo, descompacte-o e pronto, o servidor está pronto para rodar.
Mas antes de rodar vamos configurar um usuário para acessar o aplicativo de administração do servidor, que por padrão está desabilitado.
Dentro do diretório conf, existe um arquivo tomcat-users.xml, edite este arquivo, descomente as seguintes linhas:
  <role rolename="tomcat"/>
  <role rolename="role1"/>
  <user username="tomcat" password="tomcat" roles="tomcat,manager-gui"/>
  <user username="both" password="tomcat" roles="tomcat,role1"/>
  <user username="role1" password="tomcat" roles="role1"/>
Para que a aplicação de administração do TomCat seja acessada, devemos adicionar uma role manager-gui a algum usuário, e neste caso adicionamos ao usuário tomcat.
A partir de agora, na próxima inicialização do servidor, o usuário tomcat conseguirá acessar e usar o aplicativo de administração de apps do TomCat.

Inicialização e Teste

Para inicializar o servidor, basta rodar o arquivo startup, que fica dentro do diretório bin/. Após o servidor inicializar, acesse a página do TomCat e você já deve estar acessando o servidor.
 Se você está vendo a página acima, é sinal que o seu servidor está rodando corretamente e que está tudo pronto para fazermos o primeiro deploy.

Deploy

Do lado direito temos 3 botões, sendo o do meio Manager App, ao clicar neste botão iremos para o aplicativo de Administração de apps, mas antes, precisamos logar.
O TomCat já vem com alguns aplicativos instalados por padrão, não se assuste, você pode pará-los ou mesmo apagá-los através dos botões disponíveis na coluna Commands.
Após entrar no aplicativo, procure por:
WAR file to deploy
Nesta seção será onde faremos o deploy do nosso aplicativo, o que também será um procedimento bem simples.
Procure por um arquivo .war dentro do diretório target do seu projeto, se você rodou o comando mvn package, como descrito aqui você deve ter um arquivo:
nome_do_projeto_versao.war
no diretório target. Selecione este arquivo e clique em deploy, se tudo correr bem, seu software já está rodando, e para acessá-lo você deve clicar no link da coluna Path, da tabela de aplicativos.
Com isso você já deve estar acessando o seu aplicativo e nos próximos posts, vamos estudar como podemos adicionar funcionalidades a ele.

Debugando

Para debugar um aplicativo que está sendo executado em um servidor, temos que configurar o debug remoto do Java. Isso acontece pois não estamos executando o nosso software diretamente, e sim dentro do Tomcat.
A configuração é bem simples, para startar o Tomcat no modo debug, basta startá-lo assim:
$CATALINA_HOME/bin/catalina.sh jpda start
Com isso o tomcat inicia com o debugger aberto na port 8000, para alterar alguma configuração do debug sete as seguintes variáveis de ambiente:
JPDA_ADDRESS: especifica a porta utilizada pelo debuggerJPDA_SUSPEND: especifica se a JVM deve ser suspendida após a inicialização 
O que no Windows é feito como:
set JPDA_ADDRESS="8001"
E nos unix* é feito como:
export JPDA_ADDRESS="8001"
Para se conectar ao TomCat como debugger basta inicializar a sua IDE e configurar um Debugger remoto, apontando para a porta configurada como debug e inicializar o modo debug.

Controlando o seu build no Java com o Maven

Ao desenvolver um software, vamos precisar utilizar bibliotecas, apis e softwares de terceiros. Quanto maior a complexidade do seu projeto, maior será o número de bibliotecas utilizadas como dependência, com isso, o controle das versões e dos arquivos destas dependências torna-se uma tarefa dispendiosa.
Com a evolução dos softwares, o processo de build tornou-se complicado e a sua administração começou a ocupar um bom tempo de esforço do projeto, afim de facilitar esta administração foram criadas algumas ferramentas.
As ferramentas de controle de build, nos ajudam a controlar as dependências do projeto, controlar o conflito de dependências, executar a compilação, e os testes do projeto.
Existem diversas ferramentas que podem te auxiliar na administração do build, a mais usada, e descrita aqui será o Apache Maven. A minha preferida, o SBT, foi descrita neste post.

Estrutura de um projeto maven

O principal arquivo de um projeto maven é o pom.xml. O nome pom vem de PrOject Management, ou administrador de projeto, nele serão definidas as configurações do build, este arquivo deverá estar localizado na pasta raíz do projeto.
A estrutura de pastas de um projeto maven é a seguinte:
  • projeto
    • pom.xml
      • src
        • main
          •  resourcces
          • java
            • pacote
              • App.java
        • test
          • java
            • pacote
              • TestApp.java
Todo código do projeto deverá ficar dentro da pasta src, que vem de source (ou fonte em inglês), sendo que este código pode ser subdividido por linguagens, por isso há umapasta java dentro do diretório src, que é onde ficam os códigos fonte do aplicativo, escritos em java. Já os testes de java devem estar dentro da pasta src/test.
Dentro do diretório resources ficam os arquivos de configuração e metadados do nosso projeto.

Definição de um projeto usando o maven

Um exemplo de pom para o nosso projeto web seria
<project br="" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0">
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelversion>4.0.0</modelversion>

<groupid>seu.pacote.basico</groupid>
<artifactid>projeto</artifactid>
<version>1.0-SNAPSHOT</version>
<packaging>jar</packaging>

<name>Projeto web desenvolvido em DSD</name>
<url>http://seusite.com.br</url>

<dependencies>
<dependency>
<groupid>junit</groupid>
<artifactid>junit</artifactid>
<version>4.8.2</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>

Nas primeiras 3 linhas deste xml estamos definindo o cabeçalho do pom, todo arquivo de xml possui um cabeçalho, isso ajuda os softwares de desenvolvimento e ao maven a identificar quais versões do pom que estão sendo utilizadas nele. As definições do projeto vem logo abaixo:
  • groupId geralmente contém o site da empresa que está desenvolvendo o projeto.
  • artifactId é o nome do projeto que está sendo desenvolvido.
  • name possui uma pequena descrição do projeto
  • url contem o site do projeto
  • dependencies é onde iremos definir as dependencias do projeto
Repare que para definir uma dependência, utilizamos o mesmo padrão de groupId e artifactId que usamos na definiçao do nosso projeto. Caso você queira distribuir o seu projeto em forma de api, basta colocá-lo em algum repositório e mapear este repositório em outro projeto, que o maven se encarregará de baixar esta api e colocá-la dentro do seu projeto.
Administrar as dependências do projeto, é uma das principais funcionalidades do maven, se não tivessemos um administrador de build, deveriamos procurar, baixar e copiar no classpath, todos os jars de libs utilizados em nossos projetos. Além de administrar os conflitos existentes entre eles, o que seria uma tarefa muito tediosa.
Como o maven baixa as dependências dos nossos projetos, não faz sentido fazer isso a todo comando de build, por isso o maven cria um diretório .m2 no seu HOME_FOLDER e faz uma cópia de todos as libs utilizadas em seus projetos dentro deste diretório, com isso ele agiliza o processo de build já que, estando lá, ele não vai precisar baixar novamente uma biblioteca.
Uma outra informação que foi definida para este projeto é o tipo de empacotamento. No exemplo acima temos o formato do arquivo como jar, que é o formato padrão do java. Já em um projeto web, este formato será o war.

Iniciando um projeto Maven

Depois de instalado, para chamar o maven, basta digitar mvn no console da sua máquina, ou no command prompt do windows.
O maven possui um comando usado para criar a estrutura de um projeto, neste comando definimos atributos como o groupId, o attributeId, e o tipo do projeto. Para inicializarmos o nosso projeto web, usaremos o seguinte comando:
mvn archetype:generate -DgroupId={seu-group-id} -DartifactId={seu-artifact-id} -DarchetypeArtifactId=maven-archetype-webapp -DinteractiveMode=false
Ao executar este comando, o maven irá criar toda a estrutura de um projeto web, assim como o seu pom.xml básico e a estrutura de diretórios de um projeto web.
A estrutura de um projeto web basicamente é a mesma de um projeto java, porém ela irá conter mais dois diretórios para armazenar as informações referentes a web.
  1. projeto
  2. |-- pom.xml
  3. `-- src
  4. |-- main
  5. |`-- resources
  6. |`-- webapp
  7. |`--    WEB-INF
  8. |`--        web.xml
  9. | `--   index.jsp
  10. | `-- java
  11. | `--   pacote
  12. | `--     App.java
  13. `-- test
  14. `--   java
  15. `--     pacote
  16. `--        AppTest.java
Dentro do diretório webapp teremos os nossos arquivos de UI, como exemplo o index.jsp. e no diretório WEB-INF contem informações que serão acessíveis pelo seu código java, mas que não serão publicadas, ou seja, não poderão ser acessadas diretamente pelo usuário.

Resultado da execução de um comando

Toda vez que executarmos um comando do maven, ele irá nos mostrar um log de resultado, como o mostrado abaixo:
 [INFO] ----------------------------------------------------------------------------
[INFO] Using following parameters for creating project from Old (1.x) Archetype: maven-archetype-webapp:1.0
[INFO] ----------------------------------------------------------------------------
[INFO] Parameter: basedir, Value: /home/dirceu/projects/dsdweb
[INFO] Parameter: package, Value: br.unip
[INFO] Parameter: groupId, Value: br.unip
[INFO] Parameter: artifactId, Value: dsd-web
[INFO] Parameter: packageName, Value: br.unip
[INFO] Parameter: version, Value: 1.0-SNAPSHOT
[INFO] project created from Old (1.x) Archetype in dir: /home/dirceu/projects/dsdweb/dsd-web
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 02:04 min
[INFO] Finished at: 2015-08-23T23:50:16-03:00
[INFO] Final Memory: 12M/93M
[INFO] ------------------------------------------------------------------------
BUILD SUCCESS significa que o nosso comando rodou sem problemas.
Qualquer erro que aconteça durante o processo de build, resultará em uma mensagem com um:
BUILD FAILURE
e o motivo da falha será explicado com mensagens de erros, conforme mostrado abaixo:
[INFO] ------------------------------------------------------------------------
[INFO] BUILD FAILURE
[INFO] ------------------------------------------------------------------------
[INFO] Total time: 40.229 s
[INFO] Finished at: 2015-08-24T00:06:34-03:00
[INFO] Final Memory: 15M/98M
[INFO] ------------------------------------------------------------------------
[ERROR] Failed to execute goal org.apache.maven.plugins:maven-archetype-plugin:2.4:generate (default-cli) on project standalone-pom: Directory dsdweb already exists  - please run from a clean directory -> [Help 1]
Repare que a explicação do erro está grifada, como o diretório já existe ele não conseguiu criar um projeto.

Argumentos de build do Maven

Para fazermos um build, não basta chamar o comando mvn, devemos passar alguns argumentos para definir o que queremos fazer com o nosso projeto.
Ao executar o maven, você deverá dizer a ele o que fazer, a seguir serão mostrados os comandos disponíveis no maven:
  • validate - valida se todas as informações do projeto estão disponíveis
  • compile - compila o projecto
  • test - executa os testes do projeto
  • package - pega o código compilado do projeto e cria o arquivo para distribuí-lo, no formato definido pela configuração.
  • integration-test - faz um deploy do pacote em um ambiente onde pode ser rodado os testes de integração
  • verify - roda qualquer checagem necessária para assegurar que o projeto atende a todos os requisitos de qualidade
  • install - instala o pacote em um repositório local
  • deploy - copia o pacote final para o repositório onde ele será distribuído
Entenda que estes comandos possuem dependências entre eles, por exemplo, não faz sentido inicializarmos a compilação, se o projeto não passou pela validação. Por isso quando você digita qualquer um destes comandos, o maven verifica as suas dependencias e, se você se esqueceu de alguma, ele irá executá-la automaticamente.
Para gerarmos um arquivo do nosso projeto, podemos fazer:
  • mvn package 
  • mvn validate test compile package

 Encontrando dependências

Quando estamos desenvolvendo um projeto, uma dúvida recorrente é onde e como encontrar as dependências. Geralmente no website dos projetos, eles já nos disponibilizam qual será o texto que devemos colar para adicinar alguma dependencia ao nosso projeto.
Os conteúdos das bibliotecas são distribuídos em repositórios, e a apache possui um repositório central, que já vem configurado por padrão. Todas as bibliotecas e apis que estiverem definidas lá, podem ser adicionadas ao seu projeto sem problemas. No site dele você pode procurar por apis, e ele te mostra quais são as versões disponíveis, além do texto a ser colocado no seu pom.xml para adicionar aquela biblioteca.
Quando o maven não encontrar alguma biblioteca que você adicionou, é por que ela não está armazenada neste repositório, neste caso você deverá procurar pela internet, onde é que aquela biblioteca está armazenada.

terça-feira, 18 de agosto de 2015

Banco de dados para o sistema Web de Cadastro de Usuários

Neste post vamos discutir como montar um banco de dados para um cadastro de usuários para um sistema web simples.

Estrutura do Banco de dados

Ao desenvolver um sistema devemos inicialmente pensar em como dividir os dados, que serão utilizados naquele sistema. Pensando-se em um sistema de armazenamento comumente utilizado, como um banco de dados SQL, devemos pensar também em normalização de dados.

Normalização de dados

Normalização de dados são um dos passos que devem ser executados ao se planejar um banco de dados. A normalização consiste na divisão dos dados, afim de evitar uma replicação de dados. Por exemplo, não faz sentido termos mais de uma tabela no banco de dados, que esteja armazenando emails. Portanto, pensar em uma tabela de emails faz sentido.
Para termos um banco de dados normalizado, quanto mais separados e isolados estiverem os dados, melhor.
Por isolação dos dados, entenda que o intúito aqui é não replicar um dado em mais de um lugar. Por exemplo em uma tabela de endereços, se tivermos um banco denormalizado, todos os dados ficariam em apenas uma tabela. Então caso tivéssemos 10.000 usuários morando na cidade de São Paulo, teríamos a entrada São Paulo, 10.000 vezes nesta tabela, portanto este dado estaria replicado.
Existem múltiplas formas de se escrever São Paulo, com e sem acento, maiúsculo e minúsculo, e depender de um usuário digitar estas informações, faz com que os dados no sistema fiquem muito diferentes. Com isso, depois de um tempo ficará muito difícil conseguir extrair os dados de quantos e quais usuários vivem na cidade de São Paulo, o que pode ser muito útil para se planejar uma promoção.
Um sistema de armazenamento de dados de endereço, totalmente normalizado teria tabelas para armazemanento dos tipos de Logradouros, das Ruas, Bairros, das Cidades, dos Estados e uma tabela que agregaria todos estes dados, com o número, formando assim o endereço.
A normalização dos dados de um sistema gera uma economia de armazenamento, já que os dados quase não seriam replicados. Porém, como para se obter um dados deveríamos ler várias tabelas, isso faz com que a busca dos dados fique um pouco mais lenta, já que cada operação de leitura em uma tabela é uma operação para o banco de dados, se tivermos tabelas muito grandes isto pode acarretar que udma operação de leitura leve muito tempo.

Isolamento dos dados

Alguns dados, considerados sensíveis, devem ficar isolados em uma tabela separada, já que caso alguém invada o seu sistema de banco de dados, fique mais difícil conseguir juntar os dados sensíveis aos seus verdadeiros donos.
Pode-se também usar níveis de acessos diferentes nestas tabelas, por exemplo, apenas alguns usuários terem acessos aos dados destas tabelas, ou mesmo usuários diferentes com permissão de acesso, Um acessa apenas a tabela usuários e outro acessa apenas a tabela de dados sensíveis.
Teremos uma discussão em aula a respeito de como modelar os dados deste sistema


Dados de um sistema de cadastro de usuários

Aqui serão enumerados os dados que iremos usar no cadastro dos usuários do nosso sistema:

Nome do usuário
Idade
Email
Endereço (rua, cidade, estado, bairro, cep, nr, complemento)
CPF, RG, ...
Senha
Disciplinas
Notas
Profissão

Parte da evolução desta disciplina serão as discussões de como dividir estes dados.

E isto também deve fazer parte de quem está construindo um sistema do zero. O banco de dados é uma das principais fontes de problemas, então é muito bom pensar nele com carinho.
Muitas vezes só descobrimos as falhas do nosso modelo, após o sistema estar em funcionamento e neste ponto, qualquer modificação dele é complicada, ainda mais em um sistema web, que deve ficar online 24h por dia, 7 dias por semana.

Além do banco de dados

Por ser um sistema web, devemos prever métodos para aliviar o banco e pensar em uma estrutura de serviços afim de suportar outros tipos de armazenamento de dados, que não um SQL.
Existe um grande movimento hoje, que prega a utilização de Bancos de dados NO-SQL, veja este NO é de Not Only (não apenas) e não uma negação dos bancos de SQL.
Um banco de dados SQL tem suas vantagens e suas limitações, por isso devemos pensar em como é possível contornar estas limitações.

Múltiplos acessos aos mesmos dados

Quando temos um sistema onde o mesmo dado é acessado diversas vezes, cabe o uso de um sistema de cache. O cache nada mais é do que um sistema de armazenamento de chave -> valor, definimos alguma coisa como sendo a chave, que deverá ser única, e a cada select, verifia-se no sistema de cache se os dados estão por lá, se estiverem, você não precisará ir ao banco de dados para coletar esteas informações, aliviando um pouco a sua carga.
Como os caches possuem armazenamento em memória, o acesso aos dados fica bem mais rápido, porém a quantidade de dados que podemos armazenar nele é bem limitada.
Com isso, devemos ter alguma maneira de expirar estes dados, e alguns dos sistemas de armazenamento possuem isso implementado. Você seta um timeout, e caso o dado não seja acessado durante aquele período estabelecido, ele simplesmente será descartado.
Uma outra estratégia de descarte, é descartar os dados mais antigos, quando a memória está cheia e não há mais espaço para armazenar, o sistema começa a descartar os dados mais antigos, antes de armazenar algo novo.

Bancos de Documentos

Existem alguns tipos de banco de dados que armazenam documentos. Estes bancos são bem úteis para armazenar dados de documentos, por documento podemos ter arquivos de texto, jsons, xmls e páginas htmls. Os buscadores da internet, por exemplo, indexam o conteúdo dos sites para que ao digitarmos algumas palavras, eles consigam achar as páginas que são mais próximas do que estamos procurando.
Existem uma série de algorítimos para calcular qual é a ordem de relevância de cada uma das páginas que estão armazenadas nestes bancos.
Este tipo de banco, geralmente não possui uma estrutura fixa, ou seja, você pode gravar mais de um tipo de dados diferente, em um mesmo índice, o que faz com que estes bancos sejam bem rápidos para a pesquisa, porém por não ter um modelo definido, torna-se difícil relacionar os dados e buscar ligações entre os dados.
Por serem feitos para armazenar documentos eles possuem uma grande capacidade de armazenamento.

Banco de dados Orientado a Objeto.

 Banco de dados orientados a objetos eliminam a necessidade de mapeamento entre os objetos a serem armazenados e os dados armazenados, uma vez que eles armazenam o objeto em si. Com isso a velocidade de inserção destes bancos de dados é bem elevada. Porém alguns tipos de consultas, principalmente as que possuem ligações entre os dados, como os relacionamentos, são difíceis de serem realizadas.
Algumas das implementações destes bancos entendem SQL, o que fez com que as consultas fossem realizadas mais facilmente.
Pouco código é necessário para armazenar e carregar os dados.

Banco de dados de Grafos

Banco de dados de armazenamento de grafos, são baseados na teoria dos grafos. Estes bancos armazenam os dados em forma de grafo e possuem três propriedades:
  • Os nós, que são as entidades, como pessoas, endereços, conta bancária
  • As propriedades, que são informações pertinentes a cada um dos nós, como amizades, conexões 
  • E finalmente, as linhas que conectam os nós, cada linha indica qual nó esta ligado a um outro nó.
Estes bancos são muito utilizados em redes sociais e são muito rapidos para fazer buscas de ligações a partir de um nó.
Apresentação