Spring MVC 3.1 – Implementando uma aplicação CRUD com o Spring Data MongoDB (Part 3)

Nos artigos anteriores, vimos como configurar um servidor MongoDB no Windows e no Ubuntu. Nesse artigo, iremos discutir a estrutura do projeto, escrever as classes Java e organiza-las em camadas.

Índice de conteúdo

  • Parte 1: Introdução e Especificação funcional
  • Parte 2: Configuração do MongoDB
  • Parte 3: Classes Java
  • Parte 4: Configuração dos arquivos XML
  • Parte 5: Arquivos HTML Files (com AJAX)
  • Parte 6: Executando a aplicação

Estrutura do projeto

Nossa aplicação será um projeto Maven e dessa forma seguirá a estrutura do Maven. A medida que criarmos as classes, iremos organiza-las em camadas lógicas: domínio, repositório, serviço e controlador.

Abaixo segue uma previsão da estrutura de nosso projeto:

 

As camadas

Camada do domínio

Essa camada possuirá duas classes, User e Role. Elas representam a nossas coleções no banco de dados, user e role, respectivamente. Usaremos o Spring Data MongoDB para simplificar o acesso ao MongoDB. E para otimizar essas classes ao framework, adicionaremos a anotação @Document. Se você for familiarizado com o JPS, essa anotação é similar a anotação @Entity.

Observe que a classe User possui uma referência a propriedade Role. Para que possamos conseguir isso, precisamos criar um anotação no campo com @DBRef.

package org.krams.domain;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class User {
@Id
private String id;
private String firstName;
private String lastName;
private String username;
private String password;
@DBRef
private Role role;
        …getters/setters
}
view raw User.java This Gist brought to you by GitHub.

 

package org.krams.domain;
import org.springframework.data.annotation.Id;
import org.springframework.data.mongodb.core.mapping.Document;
@Document
public class Role {
@Id
private String id;
private Integer role;
…getters/setters
}
view raw Role.java This Gist brought to you by GitHub.

 

Visão geral do mapeamento das anotações

  • @Id – aplicado no nível do campo para marcar o campo usado para propósitos de identificação
  • @Document – aplicado no nível da classe para indicar que essa classe é uma candidata a ser mapeada por um banco de dados. Você pode especificar o nome da coleção onde o banco de dados será armazenado.
  • @DBRef – aplicado no nível do campo para indicar que ele deve ser armazenado usando com.mongodb.DBRef.

Fonte: http://static.springsource.org/spring-data/data-document/docs/current/reference/html/

 

Camada do controlador

Essa camada possui dois controladores, MediatorController UserController

  • MediatorController é responsável por redirecionar as requisições para as páginas apropriadas. Isso não é realmente necessário no nosso exemplo por propósitos de organização.
  • UserController é responsável por manipular requisições relativas ao usuário como a adição e remoção de registros.

 

package org.krams.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
@RequestMapping(“/”)
public class MediatorController {
@RequestMapping
public String getHomePage() {
return “redirect:/users”;
}
}

 

package org.krams.controller;
import org.krams.domain.Role;
import org.krams.domain.User;
import org.krams.dto.UserListDto;
import org.krams.service.UserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
@RequestMapping(“/users”)
public class UserController {
@Autowired
private UserService service;
@RequestMapping
public String getUsersPage() {
return “users”;
}
@RequestMapping(value=”/records”)
public @ResponseBody UserListDto getUsers() {
UserListDto userListDto = new UserListDto();
userListDto.setUsers(service.readAll());
return userListDto;
}
@RequestMapping(value=”/get”)
public @ResponseBody User get(@RequestBody User user) {
return service.read(user);
}
@RequestMapping(value=”/create”, method=RequestMethod.POST)
public @ResponseBody User create(
@RequestParam String username,
@RequestParam String password,
@RequestParam String firstName,
@RequestParam String lastName,
@RequestParam Integer role) {
Role newRole = new Role();
newRole.setRole(role);
User newUser = new User();
newUser.setUsername(username);
newUser.setPassword(password);
newUser.setFirstName(firstName);
newUser.setLastName(lastName);
newUser.setRole(newRole);
return service.create(newUser);
}
@RequestMapping(value=”/update”, method=RequestMethod.POST)
public @ResponseBody User update(
@RequestParam String username,
@RequestParam String firstName,
@RequestParam String lastName,
@RequestParam Integer role) {
Role existingRole = new Role();
existingRole.setRole(role);
User existingUser = new User();
existingUser.setUsername(username);
existingUser.setFirstName(firstName);
existingUser.setLastName(lastName);
existingUser.setRole(existingRole);
return service.update(existingUser);
}
@RequestMapping(value=”/delete”, method=RequestMethod.POST)
public @ResponseBody Boolean delete(
@RequestParam String username) {
User existingUser = new User();
existingUser.setUsername(username);
return service.delete(existingUser);
}
}

 

Camada de serviço

Essa camada possui dois serviços, UserService e InitMongoService

  • UserService é nosso serviço de CRUD para a coleção user. Todos os acessos a dados são delegados aos repositórios
  • InitMongoService é usado para inicializar nosso banco de dados com os dados de exemplo. Observe que estamos usando o próprio MongoTemplate ao invés do repositório baseado no  Spring Data MongoDB. Não existe nenhuma diferença entre as duas opções. De fato, é mais simples usar o Spring Data. Mas escolhemos usar o MongoTemplate para mostrar uma maneira alternativa de interagir com o banco de dados
package org.krams.service;
import java.util.List;
import java.util.UUID;
import org.krams.domain.User;
import org.krams.repository.RoleRepository;
import org.krams.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class UserService {
@Autowired
private UserRepository userRepository;
@Autowired
private RoleRepository roleRepository;
public User create(User user) {
user.setId(UUID.randomUUID().toString());
user.getRole().setId(UUID.randomUUID().toString());
// We must save both separately since there is no cascading feature
// in Spring Data MongoDB (for now)
roleRepository.save(user.getRole());
return userRepository.save(user);
}
public User read(User user) {
return user;
}
public List<User> readAll() {
return userRepository.findAll();
}
public User update(User user) {
User existingUser = userRepository.findByUsername(user.getUsername());
if (existingUser == null) {
return null;
}
existingUser.setFirstName(user.getFirstName());
existingUser.setLastName(user.getLastName());
existingUser.getRole().setRole(user.getRole().getRole());
// We must save both separately since there is no cascading feature
// in Spring Data MongoDB (for now)
roleRepository.save(existingUser.getRole());
return userRepository.save(existingUser);
}
public Boolean delete(User user) {
User existingUser = userRepository.findByUsername(user.getUsername());
if (existingUser == null) {
return false;
}
// We must delete both separately since there is no cascading feature
// in Spring Data MongoDB (for now)
roleRepository.delete(existingUser.getRole());
userRepository.delete(existingUser);
return true;
}
}

 

package org.krams.service;
import java.util.UUID;
import org.krams.domain.Role;
import org.krams.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.mongodb.core.MongoTemplate;
/**
* Service for initializing MongoDB with sample data using {@link MongoTemplate}
*/
public class InitMongoService {
@Autowired
private MongoTemplate mongoTemplate;
public void init() {
// Drop existing collections
mongoTemplate.dropCollection(“role”);
mongoTemplate.dropCollection(“user”);
// Create new records
Role adminRole = new Role();
adminRole.setId(UUID.randomUUID().toString());
adminRole.setRole(1);
Role userRole = new Role();
userRole.setId(UUID.randomUUID().toString());
userRole.setRole(2);
User john = new User();
john.setId(UUID.randomUUID().toString());
john.setFirstName(“John”);
john.setLastName(“Smith”);
john.setPassword(“21232f297a57a5a743894a0e4a801fc3”);
john.setRole(adminRole);
john.setUsername(“john”);
User jane = new User();
jane.setId(UUID.randomUUID().toString());
jane.setFirstName(“Jane”);
jane.setLastName(“Adams”);
jane.setPassword(“ee11cbb19052e40b07aac0ca060c23ee”);
jane.setRole(userRole);
jane.setUsername(“jane”);
// Insert to db
mongoTemplate.insert(john, “user”);
mongoTemplate.insert(jane, “user”);
mongoTemplate.insert(adminRole, “role”);
mongoTemplate.insert(userRole, “role”);
}
}

 

Nota
O framework de mapeamento não lida com salvamentos em cascara. Se você alterar um objeto Account que esteja referenciado por um objeto Person, precisa salvar o objeto Account separadamente. Chamar o método de salvamento do objeto Person não irá salvar automaticamente os objetos Account.

Fonte: Spring Data MongoDB Reference (7.3.3. Using DBRefs)

 

Camada de repositório

Essa camada possui dois repositórios, UserRepository and RoleRepository. Esses dois são nossos objetos de acesso aos dados (DAO). Com a ajuda do Spring Data MongoDB, o Spring fornecerá automaticamente a implementação atual. Observe os métodos personalizados que temos que adicionar para adicionar uma assinatura ao método.

O que é o Spring Data MongoDB?
O Spring Data for MongoDB é uma parte do projeto Spring Data project cujo objetivo é fornecer um modelo familiar e consistente baseado no Spring para novos armazéns de dados enquanto retem recursos de armazenamento específicos. O projeto Spring Data MongoDB fornece integração com o documento de banco de dados do MongoDB. Áreas chaves funcionais do Spring Data MOngoDB são o modelo centrado no POJO para interagir com um DBCollection do MongoDB e fácil escrita de uma camada de acesso a dados no estilo Repositório

Fonte: http://www.springsource.org/spring-data/mongodb

 

package org.krams.repository;
import org.krams.domain.User;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface UserRepository extends MongoRepository<User, String> {
User findByUsername(String username);
}

 

package org.krams.repository;
import org.krams.domain.Role;
import org.springframework.data.mongodb.repository.MongoRepository;
public interface RoleRepository extends MongoRepository<Role, String> {
}

Classes utilitárias

A classe TraceInterceptor é classe utilitária baseado no AOP para nos ajudar a depurar a aplicação. Essa classe é uma sub-classe de CustomizableTraceInterceptor (veja Spring Data JPA FAQ para obter mais detalhes)

package org.krams.aop;
import org.aopalliance.intercept.MethodInvocation;
import org.apache.commons.logging.Log;
import org.apache.log4j.Logger;
import org.springframework.aop.interceptor.CustomizableTraceInterceptor;
/**
* Extends {@link CustomizableTraceInterceptor} to provide custom logging levels
*/
public class TraceInterceptor extends CustomizableTraceInterceptor {
private static final long serialVersionUID = 287162721460370957L;
protected static Logger logger4J = Logger.getLogger(“aop”);
@Override
protected void writeToLog(Log logger, String message, Throwable ex) {
if (ex != null) {
logger4J.debug(message, ex);
} else {
logger4J.debug(message);
}
}
@Override
protected boolean isInterceptorEnabled(MethodInvocation invocation, Log logger) {
return true;
}
}

Fonte

  • http://krams915.blogspot.com.br/2012/01/spring-mvc-31-implement-crud-with_7897.html