- Leitura e entendimento do diagrama de classes
- Leitura e entendimento do diagrama de objetos
- Associações Um para muitos / muitos para um , Um para um , Muitos para muitos comum e Muitos para muitos com classe de associação
- Bidirecionais / direcionadas
- Conceito independente / dependente
- Classe de associação
- Herança
- Enumerações
- Atributos Embedded (ItemPedidoPK)
- Coleções ElementCollection (telefones de um cliente)
- Fazer uma implementação padrão do seguinte modelo conceitual:
- Criar a seguinte instância do modelo conceitual:
- Na camada de controladores REST vai ficar nosso Resource.
- Por padrão na hora de trabalhar na criação das classes de entidade colocar no pacote domain.
- Service oferece operações e consulta para os controladores REST.
- A camada de serviço não tem contato com nenhuma tecnologia específicas(Não tem contato com o Banco, REST e com tela).
- A camada de serviço vai utilizar a camada de acesso a dados para realizar regras de negócio que por ventura ainda não foi implantado na camada domínio.
- A camada de acesso a dados(Repository) tem o papel de conversar com o banco de dados, é nela que vamos realizar as operações de salvar, alterar, excluir e consultar(Tudo que envolva SQL).
- Como não é uma boa prática colocar blocos de try catch no controladores REST, vamos fazer um Handler que é um objeto especial que vai interceptar e vai lançar a resposta Http correta.
- Em teste realizados, o uso de @JsonManagedReference/@JsonBackRefence apresentou alguns problemas com o envio de dados Json em requisições . Assim, ao invés de usar @JsonManagedReference/@JsonBackRefence, vamos simplesmente utilizar o @JsonIgnore no lado da associação que não deve ser serializada.
começando pela categoria.
@RestController
@RequestMapping(value = "/categorias")
public class CategoriaResource {
@RequestMapping(method = RequestMothod.GET)
public String listar(){
return "REST está funcionando";
}
}
Criando nossa primeira entidade e colocando no pacote domain. Obs:Nela também acompanha Construtores, Getters, Setters e hashCode, mas não foi colocado na parte da documentação para não atrapalhar a visualização.
public class Categoria implements Serializable{
private static final long serialVersionUID = 1L;
private Integer id;
private String nome;
}
Fazendo atualização no método de listar.
@RequestMapping(method = RequestMothod.GET)
public List<Categoria> listar(){
Categoria cat1 = new Categoria(1, "Informática");
Categoria cat2 = new Categoria(2,"Escritório");
List<Categoria> lista = new ArrayList<>();
lista.add(cat1);
lista.add(cat2);
return lista;
}
- Testando no navegador e no Postman se está funcioando a requisição.
colocando as Anotações na classe Categoria para gerar no Banco
@Entity
public class Categoria implements Serializable{
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue (strategy = GenerationType.IDENTITY)
private Integer id;
private String nome;
}
Criando um Repository para acessar as categorias
@Repository
public interface CategoriaRepository extends JpaRepository<Categoria, Integer> {
}
A camada Service vai servir de operações e consultas para Categoria e implantado um serviço que busca uma Categoria.
@Service
public class CategoriaService {
@Autowired/*A dependência e automaticamente instanciada pelo Spring */
private CategoriaRepository repo;
public Categoria find(Integer id) {
Optional<Categoria> obj = repo.findById(id);
return obj.orElse(null);
}
}
Atualizando para os controladores serem capazes de buscar uma Categoria.
@RestController
@RequestMapping(value = "/categorias")
public class CategoriaResource {
@Autowired
private CategoriaService service;
@RequestMapping(value ="/{id}", method = RequestMethod.GET)
public ResponseEntity<?> find(@PathVariable Integer id) {
Categoria obj = service.find(id);
return ResponseEntity.ok().body(obj);
}
}
Incluindo manualmente no banco teste H2
INSERT INTO CATEGORIA(NOME) VALUES ('Informática');
INSERT INTO CATEGORIA(NOME) VALUES ('Escritório');
SELECT * FROM CATEGORIA
- Testando a requisição no Postman.
/categorias/1
/categorias/2
para não ter o trabalho de instanciar manualmente os objetos Categoria no banco de dados, vamos fazer isso automaticamente.
@Autowired
private CategoriaRepository categoriaRepository;
public static void main(String[] args) {
SpringApplication.run(EstudomcApplication.class, args);
}
@Override
public void run(String... args) throws Exception {
Categoria cat1 = new Categoria(null, "Informática");
Categoria cat2 = new Categoria(null, "Escritório");
categoriaRepository.saveAll(Arrays.asList(cat1, cat2));
}
Fazendo as associações entra elas e instanciar os objetos. Obs:Nela também acompanha Construtores, Getters, Setters e hashCode, mas não foi colocado na parte da documentação para não atrapalhar a visualização.
@Entity
public class Produto implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy =GenerationType.IDENTITY)
private Integer id;
private String nome;
private Double preco;
@ManyToMany
@JoinTable(name = "PRODUTO_CATEGORIA",
joinColumns = @JoinColumn(name= "produto_id"),
inverseJoinColumns = @JoinColumn(name = "categoria_id"))
private List<Categoria> categorias = new ArrayList<>();
}
@Repository
public interface ProdutoRepository extends JpaRepository<Produto, Integer> {
}
@Autowired
private ProdutoRepository produtoRepository;
Produto p1 = new Produto(null, "Computador", 2000.00);
Produto p2 = new Produto(null, "Impressora", 800.00);
Produto p3 = new Produto(null, "Mouse", 80.00);
cat1.getProdutos().addAll(Arrays.asList(p1, p2, p3));
cat2.getProdutos().addAll(Arrays.asList(p2));
p1.getCategorias().addAll(Arrays.asList(cat1));
p2.getCategorias().addAll(Arrays.asList(cat1, cat2));
p3.getCategorias().addAll(Arrays.asList(cat1));
categoriaRepository.saveAll(Arrays.asList(cat1, cat2));
produtoRepository.saveAll(Arrays.asList(p1, p2, p3));
Testando no H2 o relacionamento
@JsonManagedReference
@ManyToMany(mappedBy = "categorias")
private List<Produto> produtos = new ArrayList<>();
@JsonBackReference
@ManyToMany
@JoinTable(name = "PRODUTO_CATEGORIA",
joinColumns = @JoinColumn(name= "produto_id"),
inverseJoinColumns = @JoinColumn(name = "categoria_id"))
private List<Categoria> categorias = new ArrayList<>();
Incluindo um tratamento de Exceções, agora o método de serviço ele lança uma exceção caso o Id não exista.
public Categoria find(Integer id) {
Optional<Categoria> obj = repo.findById(id);
return obj.orElseThrow(() -> new ObjectNotFoundException(
"Objeto não encontrado! Id: " + id + ", Tipo: " + Categoria.class.getName()));
}
Como não é uma boa prática colocar blocos de try catch no controladores REST, vamos fazer um Handler que é um objeto especial que vai interceptar e vai lançar a resposta Http correta.
@ControllerAdvice
public class ResourceExceptionHandler {
@ExceptionHandler(ObjectNotFoundException.class)
public ResponseEntity<StandardError> objectNotFound(ObjectNotFoundException e, HttpServletRequest request){
StandardError err = new StandardError(HttpStatus.NOT_FOUND.value(), e.getMessage(), System.currentTimeMillis());
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(err);
}
}
public class StandardError implements Serializable {
private static final long serialVersionUID = 1L;
private Integer status;
private String msg;
private Long timeStamp;
}
Com o relacionamento de muitos para um. Obs:Nela também acompanha Construtores, Getters, Setters e hashCode, mas não foi colocado na parte da documentação para não atrapalhar a visualização.
@Entity
public class Cidade implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String nome;
@ManyToOne
@JoinColumn(name ="estado_id")
private Estado estado;
}
@Repository
public interface CidadeRepository extends JpaRepository<Cidade, Integer> {
}
@Autowired
private CidadeRepository cidadeRepository;
Cidade c1 = new Cidade(null, "Uberlândia", est1);
Cidade c2 = new Cidade(null, "São Paulo", est2);
Cidade c3 = new Cidade(null, "Campinas", est2);
cidadeRepository.saveAll(Arrays.asList(c1, c2, c3));
Com o relacionamento de um para muitos. Obs:Nela também acompanha Construtores, Getters, Setters e hashCode, mas não foi colocado na parte da documentação para não atrapalhar a visualização.
@Entity
public class Estado implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String nome;
@OneToMany(mappedBy = "estado")
private List<Cidade> cidade = new ArrayList<>();
}
@Repository
public interface EstadoRepository extends JpaRepository<Estado, Integer> {
}
@Autowired
private EstadoRepository estadoRepository;
Estado est1 = new Estado(null, "Minas Gerais");
Estado est2 = new Estado(null, "São Paulo");
// Adicionando cada cidade em seu estado
est1.getCidade().addAll(Arrays.asList(c1));
est2.getCidade().addAll(Arrays.asList(c2, c3));
estadoRepository.saveAll(Arrays.asList(est1, est2));
Testando no banco teste H2
public enum TipoCliente {
PESSOAFISICA(1, "Pessoa Física"),
PESSOAJURIDICA(2, "Pessoa Jurídica");
private int cod;
private String descricao;
private TipoCliente(int cod, String descricao) {
this.cod = cod;
this.descricao = descricao;
}
public int getCod() {
return cod;
}
public String getDescricao() {
return descricao;
}
public static TipoCliente toEnum(Integer cod) {
if (cod == null) {
return null;
}
for (TipoCliente x : TipoCliente.values()) {
if (cod.equals(x.getCod())) {
return x;
}
}
throw new IllegalArgumentException("Id inválido: " + cod);
}
}
Obs:Nela também acompanha Construtores, Getters, Setters e hashCode, mas não foi colocado na parte da documentação para não atrapalhar a visualização.
@Entity
public class Cliente implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String nome;
private String email;
private String cpfOuCnpj;
private Integer tipo;
@OneToMany(mappedBy = "cliente")
private List<Endereco> enderecos = new ArrayList<>();
@ElementCollection
@CollectionTable(name ="TELEFONE")
private Set<String> telefones = new HashSet<>();
}
@Repository
public interface ClienteRepository extends JpaRepository<Cliente, Integer> {
}
@Autowired
private ClienteRepository clienteRepository;
Cliente cli1 = new Cliente(null, "Maria Silva", "maria@gmail.com", "36378912377", TipoCliente.PESSOAFISICA);
// Salvando os dois telefones
cli1.getTelefones().addAll(Arrays.asList("27363323", "93838393"));
//Salvando os endereços
cli1.getEnderecos().addAll(Arrays.asList(e1, e2));
clienteRepository.saveAll(Arrays.asList(cli1));
Obs:Nela também acompanha Construtores, Getters, Setters e hashCode, mas não foi colocado na parte da documentação para não atrapalhar a visualização.
@Entity
public class Endereco implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private String logradouro;
private String numero;
private String complemento;
private String bairro;
private String cep;
@ManyToOne
@JoinColumn (name ="cliente_id")
private Cliente cliente;
@ManyToOne
@JoinColumn (name ="cidade_id")
private Cidade cidade;
}
@Repository
public interface EnderocoRepository extends JpaRepository<Endereco, Integer> {
}
@Autowired
private EnderecoRepository enderecoRepository;
Endereco e1 = new Endereco(null, "Rua Flores", "360", "Apto 303", "Jardim", "38220834", cli1, c1);
Endereco e2 = new Endereco(null, "Avenida Matos", "105", "Sala 800", "Centro", "38777012", cli1, c2);
enderecoRepository.saveAll(Arrays.asList(e1, e2));
@RestController
@RequestMapping(value = "/clientes")
public class ClienteResource {
@Autowired
private ClienteService service;
@RequestMapping(value ="/{id}", method = RequestMethod.GET)
public ResponseEntity<?> find(@PathVariable Integer id) {
Cliente obj = service.find(id);
return ResponseEntity.ok().body(obj);
}
}
Cliente
@JsonManagedReference
@OneToMany(mappedBy = "cliente")
private List<Endereco> enderecos = new ArrayList<>();
Endereço
@JsonBackReference
@ManyToOne
@JoinColumn (name ="cliente_id")
private Cliente cliente;
Cidade
@JsonManagedReference
@ManyToOne
@JoinColumn(name ="estado_id")
private Estado estado;
Estado
@JsonBackReference
@OneToMany(mappedBy = "estado")
private List<Cidade> cidade = new ArrayList<>();
@Service
public class ClienteService {
@Autowired
private ClienteRepository repo;
public Cliente find(Integer id) {
Optional<Cliente> obj = repo.findById(id);
return obj.orElseThrow(() -> new ObjectNotFoundException(
"Objeto não encontrado! Id: " + id + ", Tipo: " + Cliente.class.getName()));
}
}
Testando o Endpoint no Postman (clientes/{id})
Obs:Nela também acompanha Construtores, Getters, Setters e hashCode, mas não foi colocado na parte da documentação para não atrapalhar a visualização.
@Entity
public class Pedido implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
private Date instante;
@OneToOne(cascade=CascadeType.ALL, mappedBy ="pedido")
private Pagamento pagamento;
@ManyToOne
@JoinColumn(name="cliente_id")
private Cliente cliente;
@ManyToOne
@JoinColumn(name="endereco_de_entrega_id")
private Endereco enderecoDeEntrega;
}
@Entity
@Inheritance(strategy = InheritanceType.JOINED)
public abstract class Pagamento implements Serializable {
private static final long serialVersionUID = 1L;
@Id
private Integer id;
private Integer estado;
@OneToOne
@JoinColumn(name ="pedido_id")
@MapsId
private Pedido pedido;
}
public enum EstadoPagamento {
PENDENTE(1, "Pendente"),
QUITADO(2, "Quitado"),
CANCELADO(3, "Cancelado");
private int cod;
private String descricao;
private EstadoPagamento(int cod, String descricao) {
this.cod = cod;
this.descricao = descricao;
}
public int getCod() {
return cod;
}
public String getDescricao() {
return descricao;
}
public static EstadoPagamento toEnum(Integer cod) {
if (cod == null) {
return null;
}
for (EstadoPagamento x : EstadoPagamento.values()) {
if (cod.equals(x.getCod())) {
return x;
}
}
throw new IllegalArgumentException("Id inválido: " + cod);
}
}
@Entity
public class PagamentoComBoleto extends Pagamento{
private static final long serialVersionUID = 1L;
private Date dataVencimento;
private Date dataPagamento;
}
@Entity
public class PagamentoComCartao extends Pagamento {
private static final long serialVersionUID = 1L;
private Integer numeroDeParcelas;
}
@OneToOne
@JoinColumn(name ="pedido_id")
@MapsId
private Pedido pedido;
@OneToOne(cascade=CascadeType.ALL, mappedBy ="pedido")
private Pagamento pagamento;
@Repository
public interface PagamentoRepository extends JpaRepository<Pagamento, Integer> {
}
@Repository
public interface PedidoRepository extends JpaRepository<Pedido, Integer> {
}
@Autowired
private PagamentoRepository pagamentoRepository;
@Autowired
private PedidoRepository pedidoRepository;
SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm");
Pedido ped1 = new Pedido(null, sdf.parse("30/09/2017 10:32"), cli1, e1);
Pedido ped2 = new Pedido(null, sdf.parse("10/10/2017 19:35"), cli1, e2);
Pagamento pagto1 = new PagamentoComCartao(null, EstadoPagamento.QUITADO, ped1, 6);
ped1.setPagamento(pagto1);
Pagamento pagto2 = new PagamentoComBoleto(null, EstadoPagamento.PENDENTE, ped2, sdf.parse("20/10/2017 00:00"),null);
ped2.setPagamento(pagto2);
cli1.getPedidos().addAll(Arrays.asList(ped1, ped2));
pedidoRepository.saveAll(Arrays.asList(ped1, ped2));
pagamentoRepository.saveAll(Arrays.asList(pagto1, pagto2));
Testando no bando de dados H2
Obs:Nela também acompanha Construtores, Getters, Setters e hashCode, mas não foi colocado na parte da documentação para não atrapalhar a visualização.
@Entity
public class ItemPedido implements Serializable {
private static final long serialVersionUID = 1L;
@EmbeddedId
private ItemPedidoPK id = new ItemPedidoPK();
private Double desconto;
private Integer quantida;
private Double preco;
}
Obs:Nela também acompanha Construtores, Getters, Setters e hashCode, mas não foi colocado na parte da documentação para não atrapalhar a visualização.
@Embeddable
public class ItemPedidoPK implements Serializable {
private static final long serialVersionUID = 1L;
@ManyToOne
@JoinColumn(name="pedido_id")
private Pedido pedido;
@ManyToOne
@JoinColumn(name ="produto_id")
private Produto produto;
}
@Repository
public interface PedidoRepository extends JpaRepository<ItemPedido, Integer> {
}
@Autowired
private ItemPedidoRepository itemPedidoRepository;
ItemPedido ip1 = new ItemPedido(ped1, p1, 0.00, 1, 2000.00);
ItemPedido ip2 = new ItemPedido(ped1, p3, 0.00, 2, 80.00);
ItemPedido ip3 = new ItemPedido(ped2, p2, 100.00, 1, 800.00);
ped1.getItens().addAll(Arrays.asList(ip1, ip2));
ped2.getItens().addAll(Arrays.asList(ip3));
p1.getItens().addAll(Arrays.asList(ip1));
p2.getItens().addAll(Arrays.asList(ip3));
p3.getItens().addAll(Arrays.asList(ip2));
itemPedidoRepository.saveAll(Arrays.asList(ip1, ip2, ip3));
Testando no bando de dados H2
@RestController
@RequestMapping(value = "/pedidos")
public class PedidoResource {
@Autowired
private PedidoService service;
@RequestMapping(value ="/{id}", method = RequestMethod.GET)
public ResponseEntity<?> find(@PathVariable Integer id) {
Pedido obj = service.find(id);
return ResponseEntity.ok().body(obj);
}
}
@Service
public class PedidoService {
@Autowired
private PedidoRepository repo;
public Pedido find(Integer id) {
Optional<Pedido> obj = repo.findById(id);
return obj.orElseThrow(() -> new ObjectNotFoundException(
"Objeto não encontrado! Id: " + id + ", Tipo: " + Pedido.class.getName()));
}
}
Testando o Endpoint no Postman (pedidos/{id})
Em teste realizados, o uso de @JsonManagedReference/@JsonBackRefence apresentou alguns problemas com o envio de dados Json em requisições . Assim, ao invés de usar @JsonManagedReference/@JsonBackRefence, vamos simplesmente utilizar o @JsonIgnore no lado da associação que não deve ser serializada. Para isto faça:
- Para cada classe de domínio:
- Apague as anotações @JsonManagedReference existentes
- Troque as anotações @JsonBackRefence por @JsonIgnore
Seção 1: Implementação do modelo conceitual
- Prof. Dr. Nelio Alves
Download do projeto para testar em sua máquina: https://github.com/AugustoMello09/Modelo-conceitual/archive/refs/heads/master.zip