Desenvolvimento

3 dez, 2012

Apresentando o Spring Roo – Parte 07: Desenvolva aplicativos em Spring MongoDB usando o Spring Roo

Publicidade

A parte 06 desta série sobre o Spring Roo examinou vários recursos apresentados no Spring Roo 1.2. Um dos recursos que eu abordei é o suporte para o desenvolviment o de aplicativos MongoDB. O objetivo do Spring Roo MongoDB é trazer a proposta clássica do Spring, que é o aprimoramento da produtividade e um modelo de programação consistente para os aplicativos do MongoDB. O Spring MongoDB é um subprojeto do Spring Data. O Spring Data é um projeto principal de software livre que contém muitos subprojetos específicos para determinados armazenamentos de dados. Primeiro eu apresentarei o MongoDB e, em seguida, desenvolverei um aplicativo do Spring MongoDB usando o Spring Roo.

Apresentando o MongoDB

Em uma única linha, o MongoDB é um armazenamento de dados NoSQL de software livre, orientado a documentos, sem esquema, rápido e escalável horizontalmente. Essa descrição contém cinco palavras-chave cuja compreensão é muito importante. Vamos examiná-las uma por uma:

  • Orientado a documentos: no MongoDB não há o conceito de linha, como nos bancos de dados relacionais, mas existe o conceito de documento. O MongoDB armazena dados na forma de documentos de JSON binário (BSON), que é mapeado para os objetos de domínio. O MongoDB não tem o conceito de junção, mas há objetos aninhados. Considere o exemplo de um aplicativo de blog que tem dois objetos de domínio: blog e comentário. Em um banco dados relacional, um blog terá uma relação de muitos para um com a tabela de comentários e você precisa de uma junção para buscar os comentários de um blog. No MongoDB, as junções são evitadas por meio de documentos aninhados. O exemplo na Listagem 1 é um documento de blog que tem um array de comentários. Sem junções, é possível buscar todos os comentários de um blog. O MongoDB fornece recursos ricos de consulta para que seja possível filtrar facilmente os campos e elementos necessários.
{
"author" : "shekhar",
"text" : "Hello MongoDB",
"title" : "Hello MongoDB"
"comments" : [
{
"name" : "anonymous",
"comment" : "good blog"
},
{
"name" : "guest",
"comment" : "awesome blog"
}
],
}
  • Sem esquema: o MongoDB armazena documentos em coleções na forma de linhas que são armazenadas em tabelas no RDBMS. No entanto, o MongoDB não obriga você a definir um esquema rígido para a sua coleção. Cada documento no MongoDB pode ser totalmente diferente do que já está armazenado em uma coleção. Cada documento que está em uma coleção do MongoDB é heterogêneo e pode ter uma estrutura completamente diferente em comparação com a de outros documentos. Isso significa que é possível armazenar os documentos do blog e do autor na mesma coleção, como na Listagem 2.
{"author" : "shekhar", "text" : "Hello MongoDB","title" : "Hello MongoDB"}
{"fullname":"Shekhar Gulati","email":"shekhargulati84@gmail.com","password":"xxxxxx"}
  • O Fast: MongoDB é um armazenamento de dados de alto desempenho, tanto em leituras como em gravações. Com a configuração padrão, o desempenho de gravação do MongoDB é melhor do que o do RDBMS, porque tem o estilo “disparar e esquecer”, que grava na memória RAM e, em seguida, no disco. Para controlar o comportamento de gravação no MongoDB, especifique um valor de WriteConcern juntamente com a sua operação de gravação. Examine os valores diferentes de WriteConcern.
    1. Normal: é a opção padrão, na qual cada operação de gravação tem o estilo “disparar e esquecer” — ou seja, ela simplesmente grava no driver e retorna. Não aguarda a gravação para estar disponível para o servidor. Portanto, se outro encadeamento tenta ler o documento logo depois que ele foi gravado, pode não encontrá-lo. Há uma grande probabilidade de perda de dados com essa opção. Não considere essa opção quando a durabilidade de dados for importante e você só puder usar uma instância do servidor do MongoDB.
    2. None: é quase igual à opção Normal, com uma diferença. Em Normal, você obtém uma exceção se a rede cai ou se ocorrem outros problemas de rede. Com None, não há exceção relacionada a problemas de rede. Isso a torna pouco confiável.
    3. Safe: como o próprio nome indica, essa opção é mais segura que as opções Normal e None. A operação de gravação espera o servidor do MongoDB reconhecer a gravação, mas os dados não são gravados no disco nesse momento. Com a opção Safe, você não terá o problema no qual outro encadeamento tenta ler o objeto que você acabou de gravar e não encontrá-lo. Fornece a garantia de que os objetos serão encontrados depois de gravados. Isso é bom, mas mesmo assim é possível perder dados porque, caso eles não sejam gravados no disco e o servidor travar por algum motivo, os dados serão perdidos.
    4. Journal Safe: antes de falar sobre essa opção, primeiro vamos explicar o que é o journal no MongoDB. Journaling é um recurso do MongoDB onde é mantido um arquivo de log de gravação antecipada para todas as operações. Em cenários em que o MongoDB não é encerrado corretamente — como acontece quando se usa o comando kill -9 é possível recuperar os dados a partir dos arquivos de journal. Por padrão, os dados são gravados nos arquivos de journal a cada 100 milissegundos (ms). É possível mudar isso para um valor entre 2 ms e 300 ms. Na versão 2.0 o journal é habilitado por padrão em servidores do MongoDB de 64 bits. Com a opção de gravação Journal Safe, a gravação aguarda a atualização do arquivo de journal.
    5. Fysnc: com o Fsync de gravação, as operações esperam o servidor despejar os dados para o disco. Essa é a opção mais segura em um nó Single, já que você só perde os dados quando o disco rígido trava.

    Eu escrevi uma postagem detalhada no blog, intitulada “How MongoDB Different Write Concern Values Affect Performance On A Single Node?” (consulte Recursos). Leia essa postagem para obter mais detalhes.

  • Escalável horizontalmente: o MongoDB é um armazenamento de dados escalável horizontalmente, ou seja, ele manipula mais leituras/gravações e pode incluir mais servidores do MongoDB no cluster. Para obter a escalabilidade horizontal, o MongoDB usa sharding, ou seja, inclui mais instâncias do MongoDB, para manipular o aumento da carga e dos dados sem afetar o desempenho do aplicativo. A melhor parte é que os aplicativos não têm que fazer o sharding; o MongoDB o manipula automaticamente. O sharding automático está fora do escopo deste artigo. Consulte Recursos para obter um link para a documentação do MongoDB sobre sharding.
  • NoSQL: provavelmente você já ouviu o termo NoSQL nos últimos dois anos ou mais. NoSQL é o acrônimo de Not Only SQL. NoSQL designa uma ampla variedade de armazenamentos de dados que não segue o modelo RDBMS e não usa a SQL como principal linguagem de consulta. O MongoDB suporta a linguagem de consulta, mas não é como a SQL e se baseia em documentos JSON.

O MongoDB suporta operações semelhantes às do RDBMS, como índices, consultas, planos de explicação e funções de agregação (como grupo), para permanecer próximo ao RDBMS e reduzir a curva de aprendizado. Outro recurso legal suportado pelo MongoDB é a capacidade de realizar tarefas de Map Reduce e indexação e consulta geoespacial (consulte a documentação em Recursos).

Terminologia do MongoDB

A terminologia do MongoDB é muito simples e fácil de entender e lembrar. A Tabela 1 mostra uma comparação com os bancos de dados relacionais.

RDBMS MongoDB
Banco de Dados Banco de Dados
Tabela Coleção
Linha Documento

No MongoDB, tudo reside em um banco de dados e é possível criar um banco de dados por meio de um comando. Um banco de dados no MongoDB pode ter várias coleções, da mesma forma que um banco de dados no RDBMS pode ter várias tarefas. Uma coleção no MongoDB pode ter vários documentos, da mesma forma que uma tabela no RDBMS pode ter várias linhas. A diferença é que, no RDBMS, a linha tem uma estrutura fixa que vale para todos os membros, ao passo que, no MongoDB, o documento tem uma estrutura pouco rígida — dois documentos da mesma coleção podem ter estruturas totalmente diferentes.

Brinque com o MongoDB

Antes de trabalhar com o MongoDB, siga o processo com quatro etapas para fazer o download e executar um servidor do MongoDB.

  1. Faça o download do MongoDB: é possível fazer o download do tarball ou zip da versão mais recente (atualmente, 2.0.4) do MongoDB para o seu sistema operacional a partir do website do MongoDB (consulte Recursos).
  2. Depois de fazer o download do MongoDB, extraia o zip para um local conveniente e torne os arquivos do shell executáveis.
  3. A partir da linha de comando, entre na pasta bin da instalação do MongoDB que foi extraída e execute mongod. Por padrão, o MongoDB armazena todos os dados em /data/db — portanto, talvez seja necessário criar o diretório e fazer com que o seu usuário seja o proprietário. Também é possível optar por não usar o diretório padrão /data/db directory especificando o local com o atributo dbpath, como na Listagem 3.
shekhar@shekhar:~/tools/mongodb/mongodb2/bin$ ./mongod
--dbpath=/home/shekhar/data/db
Sat Mar 31 19:16:48
Sat Mar 31 19:16:48 warning: 32-bit servers don't have journalling enabled by
default. Please use --journal if you want durability.
Sat Mar 31 19:16:48
Sat Mar 31 19:16:48 [initandlisten] MongoDB starting : pid=6033 port=27017
dbpath=/home/shekhar/data/db 32-bit host=shekhar
Sat Mar 31 19:16:48 [initandlisten]
Sat Mar 31 19:16:48 [initandlisten] ** NOTE: when using MongoDB 32 bit, you
are limited to about 2 gigabytes of data
Sat Mar 31 19:16:48 [initandlisten] ** see
http://blog.mongodb.org/post/137788967/32-bit-limitations
Sat Mar 31 19:16:48 [initandlisten] ** with --journal, the limit is lower
Sat Mar 31 19:16:48 [initandlisten]
Sat Mar 31 19:16:48 [initandlisten] db version v2.0.1, pdfile version 4.5
Sat Mar 31 19:16:48 [initandlisten] git version:
3a5cf0e2134a830d38d2d1aae7e88cac31bdd684
Sat Mar 31 19:16:48 [initandlisten] build info: Linux domU-12-31-39-01-70-B4
2.6.21.7-2.fc8xen #1 SMP Fri Feb 15 12:39:36 EST 2008 i686 BOOST_LIB_VERSION=1_41
Sat Mar 31 19:16:48 [initandlisten] options: { dbpath: "/home/shekhar/data/db" }
Sat Mar 31 19:16:48 [websvr] admin web console waiting for connections on port 28017
Sat Mar 31 19:16:48 [initandlisten] waiting for connections on port 27017

Depois de iniciar o servidor do MongoDB, é possível visualizar algumas informações básicas no console da web em http://localhost:28017/. Para realizar operações úteis, é necessário um cliente. O MongoDB fornece um cliente de linha de comando chamado mongo , que é possível encontrar na pasta bin.

Para iniciar o cliente, execute ./mongo e você verá a saída da Listagem 4.

shekhar@shekhar:~/tools/mongodb/mongodb2/bin$ ./mongo
MongoDB shell version: 2.0.1
connecting to: test

A saída na Listagem 4 mostra que o cliente mongo se conectou ao banco de dados de teste. Depois de se conectar ao servidor, é possível realizar várias operações, como inserir um documento em uma coleção e localizar o documento.

Criar o banco de dados

Para começar, vamos criar um banco de dados. O MongoDB não tem um comando para criar um banco de dados. Basta selecionar um banco de dados com o comando use para criar o banco de dados, como na Listagem 5.

> use springroopart7
switched to db springroopart7

O comando na Listagem 5 cria um banco de dados chamado springroopart7 e alterna o contexto para um banco de dados recém-criado. Esse comando só criará o banco de dados quando ele não existir. Do contrário, ele apenas alterna o cliente para o banco de dados já existente.

Crie coleções

Depois de criar um banco de dados, a próxima etapa lógica é criar uma coleção dentro de um banco de dados. É possível criar uma coleção usando a operação db.createCollection() como na Listagem 6.

> db.createCollection("blogs")
{ "ok" : 1 }

Na Listagem 6, você executou a operação para criar uma coleção chamada “blogs” e o MongoDB respondeu que a criou com êxito.

A outra forma de criar uma coleção é salvar o documento em uma coleção. Em seguida, o MongoDB cria uma coleção caso ela não exista. Do contrário, o MongoDB incluirá um documento na coleção já existente. Isso será abordado a seguir.

Inserindo documentos

Agora que você tem uma coleção de blogs, insira um blog na coleção. É necessário criar um documento JSON e inseri-lo como na Listagem 7.

> db.blogs.insert({"author":"shekhar","title" : "Getting started with MongoDB",
"text":"MongoDB is an open-source document oriented, schema-free, fast and horizontally
scalable NoSQL datastore",
"tags":["mongodb","nosql"]})

O comando na Listagem 7 cria um novo documento de blog e o insere na coleção de blogs. Como se pode ver, o documento é muito rico, já que contém tags de campo matricial. É possível consultar facilmente todos os blogs correspondentes a uma tag específica. É possível ter campos do tipo texto, numérico, data, arrays, etc.

Localizando documentos

Já que você persistiu apenas um documento, é possível vê-lo usando a operação findOne , que retorna qualquer documento da coleção ou o primeiro documento que corresponde à consulta especificada. Para localizar qualquer documento dentro da coleção de blogs, execute o comando na Listagem 8.

> db.blogs.findOne()
{
"_id" : ObjectId("4f784a1c61d2e3bcf01bcff6"),
"author" : "shekhar",
"title" : "Getting started with MongoDB",
"text" : "MongoDB is an open-source document oriented, schema-free, fast and
horizontally scalable NoSQL datastore",
"tags" : [
"mongodb",
"nosql"
]
}

Se você só quer localizar um documento cujo autor é “shekhar,” execute db.blogs.findOne({"author":"shekhar"})

Para consultar tags, execute o comando na Listagem 9. Como se pode ver, ela parece igual às consultas anteriores.

> db.blogs.findOne({"tags":"mongodb"})
{
"_id" : ObjectId("4f784a1c61d2e3bcf01bcff6"),
"author" : "shekhar",
"title" : "Getting started with MongoDB",
"text" : "MongoDB is an open-source document oriented, schema-free, fast and
horizontally scalable NoSQL datastore",
"tags" : [
"mongodb",
"nosql"
]
}

As consultas a seguir só retornarão um documento. Se você quer localizar todos os documentos cujo autor é shekhar e têm tags de mongodb, é possível usar o método find : > db.blogs.find({"author":"shekhar","tags":"mongodb"})

Você deve saber que o método find retorna um cursor, não um documento. Por padrão, no cliente do MongoDB, você vê os dez primeiros documentos. Para ver todos os documentos itere sobre o cursor (consulte a Listagem 10).

> var cursor = db.blogs.find({"author":"shekhar","tags":"mongodb"})
> while(cursor.hasNext())printjson(cursor.next())
{
"_id" : ObjectId("4f784a1c61d2e3bcf01bcff6"),
"author" : "shekhar",
"title" : "Getting started with MongoDB",
"text" : "MongoDB is an open-source document oriented, schema-free, fast and
horizontally scalable NoSQL datastore",
"tags" : [
"mongodb",
"nosql"
]
}

Outros comandos

É possível executar vários outros comandos no MongoDB. Para ver todas as operações disponíveis em uma coleção, digite o comando help como na Listagem 11. Por uma questão de concisão, não mostrei todos os comandos. Você verá todos os comandos ao executar no seu próprio cliente do mongo.

> db.blogs.help()
DBCollection help
db.blogs.find().help() - show DBCursor help
db.blogs.count()
db.blogs.dataSize()
db.blogs.distinct( key ) - for example db.blogs.distinct( 'x' )
db.blogs.drop() drop the collection
db.blogs.dropIndex(name)
db.blogs.dropIndexes()
db.blogs.ensureIndex(keypattern[,options]) - options is an object with these
possible fields: name, unique, dropDups
db.blogs.reIndex()
db.blogs.find([query],[fields]) - query is an optional query filter. fields is
optional set of fields to return.

Agora que você tem uma compreensão básica do MongoDB, é possível avançar e criar um aplicativo do Spring usando o MongoDB como armazenamento de dados. É possível consultar a documentação do MongoDB ou outros artigos sobre o tema publicados no developerWorks (consulte Recursos).

 Design do esquema

O aplicativo que você irá criar se chama ShortNotes. Ele tem os dois objetos de domínio: Notebook e Note. Um bloco de notas pode ter muitas notas. Há várias formas de projetar o esquema para esse aplicativo. Vamos examinar várias opções de design de esquema.

Notebook com um array integrado de Notes

Um documento Notebook com um array aninhado e integrado de documentos Note parece ser o design de esquema mais óbvio para um armazenamento de dados de documentos. Esse design evita junções e fornece um modelo de domínio rico. A Listagem 12 mostra uma coleção de blocos de notas contendo dois documentos de bloco de notas com um array integrado de documentos de notas.

{
"_id" : ObjectId("4f7ff4c9c2fceff338eb9a82"),
"name" : "MongoDB Notebook",
"author" : "shekhar",
"created" : ISODate("2012-04-07T08:02:59.572Z"),
"notes" : [
{
"_id" :"f7ff4c9c2fceff338eb9a443"
"title" : "Getting Started with MongoDB",
"note" : "Getting Started with MongoDB",
"created" : ISODate("2012-04-07T08:02:59.572Z")
},
{
"_id" :"efvvgf7ff4c9ceff338eb9a443"
"title" : "Setting up Replication",
"note" : "Setting up Replica Set in MongoDB",
"created" : ISODate("2012-04-07T08:02:59.572Z")
}
]
}
{
"_id" : ObjectId("4f7ff4dcc2fceff338eb9a83"),
"name" : "MongoDB Notebook",
"author" : "tom",
"created" : ISODate("2012-04-07T08:03:31.491Z"),
"notes" : [
{
"_id" :"be7ff4c332fceff338eb9a443"
"title" : "Getting Started with MongoDB",
"note" : "Getting Started with MongoDB",
"created" : ISODate("2012-04-07T08:03:31.491Z")
},
{
"_id" :"aaff4c9c3fceff338eb9a443"
"title" : "Setting up Sharding",
"note" : "Setting up Sharded Cluster in MongoDB",
"created" : ISODate("2012-04-07T08:03:31.491Z")
}
]
}

O esquema na Listagem 12 parece bom e permite consultar blocos de notas e notas.

Para localizar todos os blocos de notas com o nome MongoDB Notebook e o autor shekhar, pode-se escrever a seguinte consulta:

db.notebooks.find({"name":"MongoDB Notebook","author":"shekhar"})

Também é possível consultar uma nota com facilidade usando este comando:

db.notebooks.findOne({"notes._id":"be7ff4c332fceff338eb9a443"})

As coisas ficam um pouco complexas quando é necessário atualizar um documento de nota específico no array ou excluir um elemento específico do array. Pode-se fazer isso usando modificadores $set, $pull , mas para isso é preciso ter um bom conhecimento sobre eles. O exemplo na Listagem 13 remove a nota com _id f7ff4c9c2fceff338eb9a443 e o bloco de notas com o autor shekhar.

> db.notebooks.update({"author":"shekhar"},{"$pull":{"notes":
{"_id":"f7ff4c9c2fceff338eb9a443"}}})
>
>
> db.notebooks.findOne({"author":"shekhar"})
{
"_id" : ObjectId("4f80137fc2fceff338eb9a8a"),
"author" : "shekhar",
"created" : ISODate("2012-04-07T10:14:01.827Z"),
"name" : "MongoDB Notebook",
"notes" : [
{
"_id" : "efvvgf7ff4c9ceff338eb9a443",
"title" : "Setting up Replication",
"note" : "Setting up Replica Set in MongoDB",
"created" : ISODate("2012-04-07T10:14:01.827Z")
}
]
}

Agora o esquema tem alguns problemas.

  • O primeiro problema do esquema é a impossibilidade de recuperar uma nota específica. Por exemplo, se você quer localizar uma nota com o _id de “f7ff4c9c2fceff338eb9a443”, talvez pense que é possível consultá-la facilmente para localizar a nota como na Listagem 14.
> db.notebooks.find({"notes._id":"f7ff4c9c2fceff338eb9a443"})
>{
"_id" : ObjectId("4f7ff9edc2fceff338eb9a84"),
"name" : "MongoDB Notebook",
"author" : "shekhar",
"created" : ISODate("2012-04-07T08:24:41.782Z"),
"notes" : [
{
"_id" : "f7ff4c9c2fceff338eb9a443",
"title" : "Getting Started with MongoDB",
"note" : "Getting Started with MongoDB",
"created" : ISODate("2012-04-07T08:24:41.782Z")
},
{
"_id" : "efvvgf7ff4c9ceff338eb9a443",
"title" : "Setting up Replication",
"note" : "Setting up Replica Set in MongoDB",
"created" : ISODate("2012-04-07T08:24:41.782Z")
}
]
}
  • Essa ação retorna o objeto de bloco de notas com todas as notas nele. Será necessário fazer toda a filtragem no lado do cliente para obter o objeto de nota necessário. Essa solicitação de recurso foi registrada no MongoDB jira (consulte Recursos) e você pode ver esse recurso no futuro.
  • O segundo problema que pode ocorrer acontece quando você tem milhares de notas em um único documento de bloco de notas. As gravações no MongoDB podem falhar porque a restrição de tamanho de um único documento do MongoDB é de 16 MB. Se o seu documento único tem mais de 16 MB, pense em normalizar o esquema para ter várias coleções.
  • O terceiro problema ocorre quando você tem milhares de notas e todas elas são grandes. Pode haver problemas de memória no lado do cliente. Em um cenário desse tipo, pense na paginação usando o operador $slice do MongoDB.

Abordagem relacional: Notebook e Note como coleções separadas

A segunda abordagem ao design do esquema para o caso de uso é ter coleções separadas para Notebook e Notes, nas quais cada documento de nota contém uma referência ao seu bloco de notas. Aparentemente, isso vai contra o esquema de design do MongoDB, já que se aproxima do paradigma do RDBMS. No entanto, essa é uma das abordagens sugeridas em alguns casos de uso. A questão é que você não é obrigado a usar nenhuma dessas abordagens. O design deve ser determinado pelo caso de uso do aplicativo. Já que o Notebook é um documento muito simples com apenas dois campos, é possível que cada documento de nota contenha um documento de bloco de notas em vez de apenas um identificador de bloco de notas. Isso evita a integração entre coleções e simplifica as consultas. A Listagem 15 mostra o documento de bloco de notas.

{
"name" : "MongoDB Notebook",
"author" : "shekhar",
"created" : ISODate("2012-04-07T09:36:01.062Z")
}

A nota no “MongoDB Notebook” seria parecida com a da Listagem 16.

{
"_id" : ObjectId("4f800b5cc2fceff338eb9a87"),
"title" : "Getting Started with MongoDB",
"note" : "Getting Started with MongoDB",
"created" : ISODate("2012-04-07T09:38:42.462Z"),
"notebook" : {
"_id" : ObjectId("4f800af3c2fceff338eb9a86"),
"name" : "MongoDB Notebook",
"author" : "shekhar",
"created" : ISODate("2012-04-07T09:36:01.062Z")
}
}

Com esse design, é fácil consultar coleções de notas e blocos de notas. Para localizar todas as notas do Notebook com o nome “MongoDB Notebook,” escreva a seguinte consulta: db.notes.findOne({"notebook.name":"MongoDB Notebook"})

Também é possível criar um índice em notebook.name para deixar a leitura das consultas muito rápida: db.notes.ensureIndex({"notebook.name":1})

Esse design de esquema tem um problema — se você modifica um documento em uma coleção de Notebooks, deve modificar manualmente suas referências na coleção de Notes.

Nesse aplicativo, você usa a abordagem relacional com duas coleções separadas para Notes e Notebooks.

 Instalando o Spring Roo

Antes de começar a desenvolver o aplicativo, certifique-se de ter o Spring Roo 1.2.1 na sua máquina. Para instalar o Spring Roo independente:

  1. Faça o download do shell de linha de comando do Roo ou use o plug-in integrado SpringSource Tool Suite (STS) desse software (consulte Recursos). Eu sugiro que você faça o download dos dois e os use em conjunto, porque o STS oferece muitos recursos adicionais no Eclipse para aplicativos baseados em Spring.
  2. Descompacte o arquivo ZIP do Spring Roo em um local à sua escolha.
  3. Configure a variável de ambiente:
  • Em uma máquina Windows, inclua %ROO_HOME%\bin no caminho, onde ROO_HOME é o caminho dos arquivos do Roo descompactados.
  • Em uma máquina *nix, crie um link simbólico para $ROO_HOME/bin/roo.sh (como em sudo ln -s ~/spring-roo-1.x.x/bin/roo.sh/usr/bin/roo)

Criando o aplicativo ShortNotes usando o Spring Roo

1. Agora que o Spring Roo está instalado e funcionando, é possível começar a desenvolver o aplicativo. Siga estas etapas para criar o aplicativo ShortNotes.

2. Abra o shell de linha de comando do sistema operacional e crie um diretório chamado shortnotes usando o comando mkdir .

3. Entre no diretório shortnotes e digite roo (ou roo.sh) e pressione Enter. Essa ação carrega o shell do Roo.

4. Primeiro, crie o projeto dentro do shell do Roo usando o comando de projeto:project --topLevelPackage com.xebia.shortnotes --projectName shortnotesO comando de projeto cria o projeto modelo Spring Maven. Os principais artefatos produzidos por esse comando são pom.xml e o arquivo applicationContext.xml. O pom.xml irá conter todos os JARs necessários do Spring e diversos plug-ins e declarações de repositório do maven. O arquivo applicationContext.xml é um arquivo de contexto genérico do Spring cujos componentes específicos varrem o pacote com.xebia.shortnotes para que o Spring possa localizar todas as classes anotadas com as anotações @Component, @Service e @Repository . A varredura de componentes irá excluir todas as classes anotadas com a anotação @Controller , já que elas serão carregadas pelo contexto do aplicativo da web.

  • Inclui as dependências necessárias no pom.xml
  • Cria um novo arquivo de contexto do Spring, applicationContext-mongo.xml
  • Cria o arquivo database.properties

As dependências incluídas por esse comando são para o suporte ao MongoDB e à validação de beans. O arquivo database.properties contém propriedades como host, port, databaseName. O arquivo applicationContext-mongo.xml contém as informações mais importantes. Ele usa o namespace do mongo, evitando a gravação de declarações de beans do Spring e facilita muito a vida dos desenvolvedores. Consulte o fragmento do applicationContext-mongo.xml na Listagem 17.

Digite o comando hintpara obter uma sugestão da próxima ação lógica. Ele sugerirá que você faça a configuração do jpa ou do mongo. Já que você está desenvolvendo um aplicativo respaldado pelo MongoDB, use o comando de instalação do mongo:mongo setup --databaseName shortnotes --host localhost --port 27017O comando de instalação do mongo não tem atributos obrigatórios, mas para esta demonstração eu especifiquei os atributos databaseName, hoste port . Se você não especificar esse comando, o Spring Roo usará valores padrão, que são iguais aos que você especificou. É possível especificar mais três atributos: username, password e cloudfoundry. Os atributos username e password são usados para acessar o MongoDB caso o seu armazenamento de dados exija informações de autenticação. O atributo cloudfoundryé usado quando você quer implementar o aplicativo na plataforma cloudfoundry como um serviço. Eu irei mostrá-lo mais adiante neste artigo.O comando de instalação do mongo faz três coisas:

<mongo:db-factory dbname="${mongo.database}" host="${mongo.host}" -----> (1)
id="mongoDbFactory" port="${mongo.port}"/>

<mongo:repositories base-package="com.xebia.shortnotes"/> -------------> (2)

<context:annotation-config/> ------------------------------------------> (3)

<bean ----->(4) id="mongoTemplate">

<constructor-arg ref="mongoDbFactory"/>

</bean>

O applicationContext-mongo.xml tem um pequeno erro. O erro é que dbname corresponde a uma propriedade chamada mongo.database , mas essa propriedade não existe no arquivo database.properties. Substitua mongo.database por mongo.name. Veja as quatro linhas declaradas no fragmento de XML na Listagem 17.

  • A primeira linha cria um factory que produzirá uma instância do Mongo para se conectar ao banco de dados do MongoDB chamado shortnotes, que executa no host local, na porta 27017. Essa declaração também pode tomar username e password para fins de autenticação. Outro atributo importante e útil que não é usado nesta declaração é write-concern, que eu já mencionei. Ele permite mudar o comportamento de gravação. O valor padrão é NONE, que tem o estilo “disparar e esquecer” e dá margem à perda de dados.
  • A segunda linha garante que todas as interfaces que estendem a interface MongoRepository ou qualquer um dos seus supertipos no pacote com.xebia.shortnotes tenham um bean de repositório do Spring. Um bean de implementação de repositório será criado dinamicamente e terá suporte à paginação e CRUD. Isso elimina uma boa parte do trabalho do desenvolvedor e remove uma grande quantidade de código padronizado relacionado aos métodos de CRUD e paginação. Também pode gerar métodos de CRUD e de localizador, com base em algumas convenções. Por exemplo, para procurar todos os blocos de notas de um autor em seu caso de uso, é possível declarar um método. Assim, a implementação deste é gerada no tempo de execução, como exemplificado abaixo. Consulte a documentação do Spring MongoDB em Recursos para obter detalhes adicionais.
 public List<Notebook> findByAuthor(String author)
  • A terceira linha é usada para converter todos os RuntimeException para uma exceção adequada da hierarquia de exceções do Spring.
  • A última linha cria um bean de MongoTemplate que pode ser usado para realizar operações de create, delete, find e update com vários outros métodos úteis. Também fornece mapeamento entre os seus objetos de domínio e os documentos do MongoDB. Com o MongoTemplate , é possível fazer tudo o que você faz com repositórios, mas os repositórios não fazem tudo o que o MongoDB faz. Essa é a classe mais importante de suporte ao MongoDB. Os métodos expostos na classe MongoTemplate corresponde de forma muito próxima aos métodos que você irá executar no cliente de linha de comando do MongoDB. A classe MongoTemplate usa as APIs de consulta e critérios para desenvolver consultas. No exemplo do seu caso de uso, para procurar todos os blocos de notas de um autor, insira:
List<Notebook> myNotebooks = 
     mongoTemplate.find(Query.query(Criteria.where("author").is
               ("shekhar")),Notebook.class)

5. Depois de instalar o MongoDB, crie as entidades Notebook e Note. Use o comando entity mongo da Listagem 18.

entity mongo --class ~.domain.Notebook --testAutomatically
entity mongo --class ~.domain.Note --testAutomatically

Os dois comandos da Listagem 18 criam Notebook.java e Note.java, juntamente com alguns ITDs, e testam o código da infraestrutura. O atributo testAutomatically é responsável pela criação dos testes de integração para as respectivas classes de domínio. Os artefatos importantes gerados pelo comando entity são Notebook_Roo_Mongo_Entity.aj e Note_Roo_Mongo_Entity.aj. Esses ITDs especificam que as entidades devem ter a anotação @Persistent e que o ID é do tipo BigInteger. O Spring MongoDB converte o ID BigInteger para ObjectId antes de persistir. O restante dos artefatos se destina a especificar getters e setters para os campos de entidade e o método toString() .

6. Agora que você criou entidades, inclua campos nelas usando o comando field . Primeiro inclua campos na entidade Notebook usando os comandos na Listagem 19.

focus --class ~.domain.Notebook
field string --fieldName name --notNull
field string --fieldName author --notNull
field date --fieldName created --type java.util.Date --notNull

Agora inclua campos na entidade Note com os comandos na Listagem 20.

focus --class ~.domain.Note
field string --fieldName title --notNull
field string --fieldName content --notNull --sizeMax 4000
field date --fieldName created --type java.util.Date --notNull

O interessante sobre os comandos na Listagem 20 é que o nome do campo criado nas duas entidades será um campo não atualizável. Isso significa que, quando você fornece um “andaime” de uma interface de usuário para essas entidades, não é possível editar o campo criado.

7. O modelo de domínio que você criou até agora não está completo, já que você não definiu a relação entre Notebook e Notes. Como já foi mencionado antes no design do esquema, cada documento de nota terá um documento de bloco de notas integrado. Isso significa que, se você tiver um Notebook com 100 Notes, terá um documento original de bloco de notas na coleção de Notebook e 100 outras cópias do bloco de notas em 100 notas. No entanto, esse design de esquema é mais simples e mais adequado para os seus requisitos. Para incluir um campo de bloco de notas em uma entidade Note, digite:

field reference --type ~.domain.Notebook --fieldName notebook --notNull

Esse comando garante que Note só será criado quando já houver um Notebook.

8. Agora que o seu modelo de domínio está completo, é possível criar repositórios para as entidades criadas anteriormente. Os repositórios são fornecidos pelo suporte ao Spring MongoDB. Para criar um repositório para a entidade Notebook, execute o comando na Listagem 21. A saída de exemplo também é mostrada.

repository mongo --interface ~.repository.NotebookRepository
--entity ~.domain.Notebook
Created SRC_MAIN_JAVA/com/xebia/shortnotes/repository
Created SRC_MAIN_JAVA/com/xebia/shortnotes/repository/NotebookRepository.java
Created SRC_MAIN_JAVA/com/xebia/shortnotes/repository/
NotebookRepository_Roo_Mongo_Repository.aj

A saída informa a criação de uma pasta de repositório, um arquivo Java™ e um ITD. O arquivo NotebookRepository.java contém um método findAll() para localizar todos os Notebooks e é anotado com @RooMongoRepository, que instrui o Roo a gerar o arquivo de ITD NotebookRepository_Roo_Mongo_Repository.aj. No tempo de compilação, o código do ITD será enviado por push para a interface. Todo o código fica no ITD. A Listagem 22 mostra o arquivo de ITD gerado pelo Spring Roo.

privileged aspect NotebookRepository_Roo_Mongo_Repository {
declare parents: NotebookRepository extends
PagingAndSortingRepository<Notebook, BigInteger>;
declare @type: NotebookRepository: @Repository;
}

O ITD define que a interface NoteBookRepository deve ser anotada com a anotação @Repository e que a interface deve estender a interface PagingAndSortingRepository . Quando a interface NotebookRepository for compilada, ela terá a anotação @Repository e estenderá a PagingAndSortingRepository. Como já foi mencionado na etapa 4, a implementação do repositório será criada no tempo de execução. Já que você implementa a interface PagingAndSortingRepository , terá métodos CRUD e métodos adicionais para recuperar entidades usando paginação e classificação.

Nas linhas semelhantes, é possível criar um repositório para a entidade Note:

repository mongo --interface ~.repository.NoteRepository --entity ~.domain.Note

9. Em seguida, inclua uma camada de serviço correspondente a uma entidade Notebook e Note. Isso atua como uma fachada entre a camada do controlador e a camada do repositório. É possível colocar aqui a lógica de negócios específica para o aplicativo. A Listagem 23 mostra o comando de serviço para a entidade Notebook e a sua saída.

service --interface ~.service.NotebookService --entity ~.domain.Notebook
Created SRC_MAIN_JAVA/com/xebia/shortnotes/service
Created SRC_MAIN_JAVA/com/xebia/shortnotes/service/NotebookService.java
Created SRC_MAIN_JAVA/com/xebia/shortnotes/service/NotebookServiceImpl.java
Created SRC_MAIN_JAVA/com/xebia/shortnotes/service/NotebookService_Roo_Service.aj
Created SRC_MAIN_JAVA/com/xebia/shortnotes/service/NotebookServiceImpl_Roo_Service.aj

Nas linhas semelhantes, é possível criar uma camada de serviço para a entidade Note: service --interface ~.service.NoteService --entity ~.domain.Note

1o. Finalmente, é possível criar controladores Spring MVC para o aplicativo usando comandos web mvc , como na Listagem 24.

web mvc setup
web mvc all --package ~.web

O comando web mvc setup configurará a infraestrutura necessária para um aplicativo do Spring MVC ao incluir dependências do Spring MVC em pom.xml, criar um arquivo de contexto de aplicativo da web chamado webmvc-config.xml, gerar bibliotecas de tags e configurar a internacionalização. O comando web mvc all cria classes de controlador e gera as visualizações correspondentes.

11. Encerre o shell do roo.

12. Inicie o servidor do MongoDB usando o executável mongod .

13. Para executar o aplicativo, digite mvn tomcat:run — esse comando iniciará o servidor tomcat. Abra um navegador da web e acesse http://localhost:8080/shortnotes/. É possível criar um bloco de notas e, em seguida, uma nota especificando o bloco de notas que deve ser usado. Por exemplo, eu criei um bloco de notas chamado MongoDB Notes com o autor shekhar. Em seguida, criei um título e conteúdo como Getting Started with MongoDB e designei a ele o bloco de notas que criamos. A Listagem 25 mostra o documento Notebook e o documento Notes.

{
"_id" : ObjectId("4f808ee084aeed43f24b692b"),
"_class" : "com.xebia.shortnotes.domain.Notebook",
"name" : "MongoDB Notes",
"author" : "shekhar",
"created" : ISODate("2012-04-07T19:00:48.386Z")
}

{
"_id" : ObjectId("4f808ef184aeed43f24b692c"),
"_class" : "com.xebia.shortnotes.domain.Note",
"title" : "Getting Started with MongoDB",
"content" : "Getting Started with MongoDB",
"created" : ISODate("2012-04-07T19:01:05.031Z"),
"notebook" : {
"_id" : ObjectId("4f808ee084aeed43f24b692b"),
"name" : "MongoDB Notes",
"author" : "shekhar",
"created" : ISODate("2012-04-07T19:00:48.386Z")
}
}

14. Se você atualizar o nome do bloco de notas para MongoDB, verá que as notas que têm esse bloco não serão atualizadas. Esse comportamento é esperado. Para corrigir isso, atualize todos os Notes após a atualização do Notebook. Crie um novo método updateNotesWithNotebook em NoteServiceImpl como na Listagem 26.

@Autowired
MongoTemplate mongoTemplate;

public void updateNotesWithNoteBook(Notebook notebook){
Update update = new Update().set("notebook.name",
notebook.getName()).set("notebook.author", notebook.getAuthor());

Query query = Query.query(Criteria.where("notebook._id").is(new
ObjectId(notebook.getId().toString(16))));
mongoTemplate.updateMulti(query, update, Note.class);

O código na Listagem 26 utiliza o método updateMulti do MongoDBTemplate para atualizar todos os documentos que correspondem ao Notebook atualizado. O método updateMulti() aceita três argumentos: query object, update object e collection entity type. O objeto de consulta especifica a seleção de todos os documentos Note que têm Notebooks com o ID. que foi dado. Em seguida, você cria um objeto update para configurar o autor e nome do bloco de notas.

Depois de criar o método, é necessário chamar isso depois de atualizar o Notebook em NotebookController (consulte a Listagem 27). Certifique-se de realizar o push do método update do ITD para a classe Java NotebookController .

@Autowired
private NoteService noteService;

@RequestMapping(method = RequestMethod.PUT, produces = "text/html")
public String update(@Valid Notebook notebook, BindingResult bindingResult,
Model uiModel, HttpServletRequest httpServletRequest) {

if (bindingResult.hasErrors()) {
populateEditForm(uiModel, notebook);
return "notebooks/update";
}
uiModel.asMap().clear();
notebookService.updateNotebook(notebook);
noteService.updateNotesWithNoteBook(notebook);
return "redirect:/notebooks/" +
encodeUrlPathSegment(notebook.getId().toString(), httpServletRequest);
}

Você terá o mesmo problema quando um Notebook for excluído, já que os Notes continuará tendo referências ao Notebook. Faz sentido remover todas as notas de um determinado bloco caso ele seja excluído. Inclua o método removeNotes() na classe NoteServiceImpl como na Listagem 28.

public void removeNotes(Notebook notebook){
mongoTemplate.remove(Query.query(Criteria.where("notebook._id").is(new
ObjectId(notebook.getId().toString(16)))), Note.class);
}

O método removeNotes() é chamado quando Notebook é excluído. Inclua a chamada a removeNotes no método delete() de NotebookController como na Listagem 29.

@RequestMapping(value = "/{id}", method = RequestMethod.DELETE,
produces = "text/html")
public String delete(@PathVariable("id") BigInteger id,
@RequestParam(value = "page", required = false) Integer page,
@RequestParam(value = "size", required = false) Integer size,
Model uiModel) {
Notebook notebook = notebookService.findNotebook(id);

noteService.removeNotes(notebook);
notebookService.deleteNotebook(notebook);
uiModel.asMap().clear();
uiModel.addAttribute("page", (page == null) ? "1" :
page.toString());
uiModel.addAttribute("size", (size == null) ? "10" :
size.toString());
return "redirect:/notebooks";
}

É possível executar o aplicativo novamente usando mvn tomcat:run, criar um bloco de notas e, em seguida, criar notas e atualizar o bloco. Você verá que as notas também têm um documento de bloco de notas atualizado. Também é possível tentar excluir o bloco de notas. Todas as notas do bloco também são excluídas.

Implementando no Cloud Foundry

Agora que você criou o aplicativo shortnotes, faz sentido implementá-lo. Os aplicativos de Spring podem ser implementados na nuvem pública do Cloud Foundry sem modificações. Expliquei o Cloud Foundry detalhadamente na Parte 4 e na Parte 6. Consulte esses artigos se não estiver familiarizado com o software. Neste artigo, você usará uma gem de vmc ruby para implementar o aplicativo de conferência no Cloud Foundry. Siga estas etapas:

  • Ative o shell do roo novamente, já que é necessário executar o comando mongo setup outra vez. Dessa vez, você alternará para a instância do MongoDB no cloud foundry. Execute o comando mostrado a seguir no shell do Roo. Essa ação atualizará o applicationContext-mongo.xml.
mongo setup --cloudFoundry
  • Instale o cliente do vmc na sua máquina. (Para ver um tutorial passo a passo sobre como instalar a interface da linha de comandos, consulte Recursos.)
  • Efetue login na nuvem pública do Cloud Foundry usando as credenciais que você registrou no software. Digite o comando vmc login para que o seu email e senha sejam solicitados, como na Listagem 30.
shekhar@shekhar:~/dev/writing/shortnotes/target$ vmc login 
Attempting login to [http://api.cloudfoundry.com] 
Email: shekhargulati84@gmail.com 
Password: ************* 
Successfully logged into [http://api.cloudfoundry.com]
  • Depois de instalar o cliente do vmc, execute o desenvolvimento maven do aplicativo shortnotes. Digite mvn clean install
  • Depois de desenvolver o projeto, realize o push do aplicativo para o Cloud Foundry. Para realizar o push do código a partir da linha de comando, navegue para a pasta de destino e digite o comando vmc push. O comando vmc push fará perguntas. Responda-as de acordo com a Listagem 31.
shekhar@shekhar:~/dev/writing/dw/shortnotes/target$ vmc push
Would you like to deploy from the current directory? [Yn]: Y
Application Name: shortnotes
Application Deployed URL [shortnotes.cloudfoundry.com]:
Detected a Java SpringSource Spring Application, is this correct? [Yn]: Y
Memory Reservation (64M, 128M, 256M, 512M, 1G) [512M]:
Creating Application: OK
Would you like to bind any services to 'shortnotes'? [yN]: y
Would you like to use an existing provisioned service? [yN]: N
The following system services are available
1: mongodb
2: mysql
3: postgresql
4: rabbitmq
5: redis
Please select one you wish to provision: 1
Specify the name of the service [mongodb-484f6]:
Creating Service: OK
Binding Service [mongodb-484f6]: OK
Uploading Application:
Checking for available resources: OK
Processing resources: OK
Packing application: OK
Uploading (85K): OK
Push Status: OK
Staging Application: OK
Starting Application: OK
  • Finalmente, você verá o aplicativo shortnotes executando na nuvem em http://shortnotes.cloudfoundry.com/

Para obter o código fonte do aplicativo shortnotes, clone o repositório do shortnotes no github (consulte Recursos).

Conclusão

Este artigo apresentou a você o MongoDB e mostrou como desenvolver aplicativos de Spring MongoDB usando o Spring Roo. Você viu os fundamentos do MongoDB, a escolha do melhor esquema para os seus requisitos e como o Spring Roo ajuda na autoinicialização rápida de aplicativos do Spring MongoDB.

Neste artigo, não abordei recursos como MongoDB MapReduce, modificadores de array, configuração da replicação no MongoDB, consulta geoespacial e outros Há muito mais a explorar. Você encontra bons recursos na documentação listada em Recursos.

***

A solução IBM Rational para Collaborative Lifecycle Management combina Rational Team Concert, Rational Quality Manager e Rational Requirements Composer em uma única imagem, disponível no IBM SmartCloud Enterprise. Esta imagem de solução integrada pode ajudar a equipe do departamento de software a melhorar sua produtividade com recursos integrados de application lifecycle management (ALM).

Recursos

Aprender

Obter produtos e tecnologias

Discutir

Conecte-se com outros usuários do developerWorks enquanto explora os blogs, fóruns, grupos e wikis voltados para desenvolvedores. Ajude a desenvolver o software livre do mundo real na comunidade do developerWorks.

***

Sobre o autor: Shekhar Gulati é consultor de Java que trabalha na Xebia Índia. Ele tem seis anos de experiência corporativa em Java. Tem ampla experiência nos projetos do portfólio Spring, como Spring, Spring-WS e Spring Roo. Seus interesses envolvem Spring, bancos de dados NoSQL, Hadoop, estruturas RAD como o Spring Roo, computação em nuvem (principalmente serviços PaaS como o Google App Engine, CloudFoundry, OpenShift), Hadoop. Ele escreve constantemente para JavaLobby, Developer.com, IBM developerWorks e seu próprio blog em http://whyjava.wordpress.com/. Você pode segui-lo no Twitter @ http://twitter.com/#!/shekhargulati.

***

Artigo original disponível em: http://www.ibm.com/developerworks/br/library/os-springroo7/index.html