Relacionamento 1:N BD SQL java

8 respostas
R

Boa noite… ou madrugado… ou dia

Estou desenvolvendo um sistema para captação de clientes. Os clientes já podem ser inseridos, atualizados, listados. Agora eu preciso que cada cliente pudesse ter vários registros de contatos.

Obsevem os códigos.

public class Cliente {
	private long id;
        private Calendar dataRegistro;
        private String empresa;
        private String endereco;
        private String bairro;
        private String complemento;
        private String cidade;
        private String estado;
        private String telefone1;
        private String site;
        private String nome;
        private String cargo;
        private String telefone2;
        private String email;
        
        //getters and setters.
public class Contato {

	private long id;
	private String numero;
	private Calendar dataContato;
	private Calendar proximoContato;
	private String resumo;
      
        //getters and setters.

Preciso que essas duas classes se relacionem. Um cliente pode ter varios registros de contato.

Assim como eu tenho adicionar-cliente.jps criei o arquivo adicionar-registro.jsp, neste terá que aparecer alguns dos dados da tabela Cliente.

Como devo tratar isso no ContatoDAO, implementando o SQL Insert, Update?

As colunas empresa, nome, email, site da tabela ‘Cliente’ quero mostrar na tela adicionar-registro.jsp.

8 Respostas

N

Opa, Renato!
Tudo bem?

Bom, vamos lá:

Renato Yury:
Preciso que essas duas classes se relacionem. Um cliente pode ter varios registros de contato.
Você tem a relação feita no seu banco de dados, certo? Na sua tabela de contatos, há uma chave estrangeira para o cliente, correto? Se sim, você deve adicionar uma coleção de contatos no cliente. Afinal, o cliente possui vários contatos, não?
public class Cliente {

    private Long ID;
    private String nome;
    private List<Contato> contatos;
}
e...
public class Contato {

    private Long ID;
    private String nome;
    private String endereco;
    private Long cliente; // <--- opcional esse atributo; normalmente eu coloco pra guardar o registro da chave estrangeira, se precisar.
}
Renato Yury:
Assim como eu tenho adicionar-cliente.jps criei o arquivo adicionar-registro.jsp, neste terá que aparecer alguns dos dados da tabela Cliente.

Como devo tratar isso no ContatoDAO, implementando o SQL Insert, Update?

As colunas empresa, nome, email, site da tabela 'Cliente' quero mostrar na tela adicionar-registro.jsp.

Você tem um DAO para cada uma das entidades, certo? Portanto, você trata normalmente as operações. Eu gosto de fazer aquela referência da chave estrangeira por esse motivo.
public class DAOContato {

    private Connection currentConnection;
    public DAOContato(Connection currentConnection) {
        this.currentConnection = currentConnection;
    }

    public void create(Contato entityContato) throws SQLException {

        String selectSentence = "INSERT INTO CONTATOS (ID, CLIENTE, NOME, ENDERECO) VALUES (?, ?, ?, ?)";
        PreparedStatement selectStatement = currentConnection.prepareStatement(selectSentence);
        selectStatement.setLong(1, entityContato.getID());
        selectStatement.setLong(2, entityContato.getCliente());
        selectStatement.setString(3, entityContato.getNome());
        selectStatement.setString(4, entityContato.getEndereco());
        selectStatement.execute();

    }
}
Na sua classe de controle, você pode fazer assim:
public class ControllerContato extends HttpServlet {

    public void execute(...) {

        Connection currentConnection = ConnectionHelper.getConnection();
        DAOCliente daoCliente = new DAOCliente(currentConnection);
        DAOContato daoContato = new DAOContato(currentConnection);

        Cliente entityCliente = new Cliente(); 
        entityCliente.setNome(request.getParameter("NOME"));
        //preenche o objeto...

        // Suponhamos que você tenha um dataTable na sua página onde guarda os registros
        // dos contatos dos clientes. Então, você pega os valores desse dataTable e adiciona
        // à coleção de contatos dos clientes. É só um exemplo, mas você faz do seu jeito:
        for (Contato contatoEntity : request.getParameter("CONTATOS")) {
            entityCliente.getContatos().add(contatoEntity);
        }

        // Se você tá adicionando o cliente junto, você chama o [b]create[/b] do DAOCliente.
        daoCliente.create(entityCliente);
        for (Contato contatoEntity : daoCliente.getContatos()) {

            contatoEntity.setCliente(entityCliente.getID()); //<-- referencia o ID do cliente no objeto.
            daoContato.create(contato); // <-- adiciona o contato à tabela.
        }

        currentConnection.commit();
    }
}

Sacou a ideia? Abraços!

R

Massa… entendi.

Bem clara a explicação. Muito obrigado!

Mas vamos discutir esse ControllerContato, a servlet.

public class AdicionaRegistroLogic implements Logica {

	@Override
	public void executa(HttpServletRequest request, HttpServletResponse response)
			throws Exception {
		PrintWriter out = response.getWriter();

		        String status			= request.getParameter("status");
			String dataEmTexto		= request.getParameter("proximoContato");
				Calendar proximoContato = null;
				// fazendo a conversão da data
				try {
					Date date = new SimpleDateFormat("dd/MM/yyyy").parse(dataEmTexto);
					proximoContato = Calendar.getInstance();
					proximoContato.setTime(date);
				}catch (Exception e) {
					out.println("Erro de conversão da data");
					return;
				}
			String resumo 			= request.getParameter("resumo");
			
	   Contato contato = new Contato();		
		 contato.setStatus(status);
		 contato.setProximoContato(proximoContato);
		 contato.setResumo(resumo);
		
   //Dessa forma que segue eu uso para adicionar o cliente, por exemplo. Coloquei parecido só pra eu entender
  //Percebi que a grande sacada é o for que observei no seu exemplo.
  //Confesso que estou com dificuldade para implementar no meu código.
		 
			Connection connection = (Connection)request.getAttribute("conexao");
			ContatoDAO dao = new ContatoDAO();
			dao.adicionaContato(contato);
			connection.close();
		
		RequestDispatcher rd = request.getRequestDispatcher("/contato-adicionado.jsp");
                rd.forward(request,response);
        
        System.out.println("Contato adicionado com sucesso!");
	}

Agradeço a disposição!

N

Como você imprime os valores dos contatos na sua página?
Como você recebe os valores dos contatos na sua Servlet?
Seus Beans estão mapeados do jeito que mostrei [uma coleção de contatos no cliente]?

Mostre como você faz e daí montamos o código!
Abraços!

R

Deixa eu te dizer...

Na tentativa de clariar as ideias para uma solução copiei a forma como eu fiz com a parte de cadastros de clientes.

Tenho um menu lateral com o link Catalogo Captação. Abre uma tela no container com uma lista dos contatos.

Na tela tenho as colunas:
* Empresa Cliente (tabela clientes do bd);
* Nome Contato (tabela clientes do bd);
* Status (tabela contatos bd);
* Proximo Contato (tabela contatos do bd)
* E um link para os registros de contatos.

Usei um ArryList que está do meu dao para popular essa tabela.
<c:forEach var="contato" items="${dao.listaContatos}">
	<table>
        <tr>
		<td> </td> //Nesta linha preciso receber o parametro da empresa do cliente.
		<td> </td> //Nesta o nome do contato da empresa. 								
		<td>${contato.status}</td>
		<td><fmt:formatDate value="${contato.proximoContato.time}" pattern="dd/MM/yyyy"/></td>
		<td><a href="registro-contato.jsp"\>Registros de Contato</a></td> 
	</tr>
        </table>

Clicando no link deverá abrir a pagina do ultimo registro cadastrado, uma opção de criar um novo registro.

Se eu fosse usar o mesmo modelo para popular o form para receber os dados do contato seria da seguinte forma

[code]





//Resumo da empresa


...
//Registro do contato


...

Vixe... acho que deu pra entender!

Minha servlet está no post anterior.

Deveria então usar Collection no meu dao!? Como faria?

O beans está no mesmo modelo do exemplo que citou.

Agradeço a força Nicolas. Abraço!

N

Se você fizesse da seguinte maneira, para popular sua tabela...

<c:forEach var="cliente" items="${dao.listarClientes}">

    <table>
        <tr>
            // Primeiro, você printa os dados do seu cliente [no caso, ID e nome].
            <td><input type="hidden" id="clienteID" value="${cliente.ID}" /></td>
            <td><input type="text" id="clienteNome" value="${cliente.nome}" /></td>

            // Logo após, você pega quais são os contatos desse cliente e printa também.
            <c:forEach var="contato" items="${cliente.contatos}">
	            <table>
                    <tr>
                            // O ID do cliente logo acima que você queria.
		            <td><input type="hidden" id="clienteID" value="${cliente.ID}" /></td>							
		            <td>${contato.status}</td>
		            <td><fmt:formatDate value="${contato.proximoContato.time}" pattern="dd/MM/yyyy"/></td>
		            <td><a href="registro-contato.jsp"\>Registros de Contato</a></td> 

	                </tr>
                </table>
            </c:forEach>

        </tr>
    </table>

</c:forEach>

[Fiz no olho, mas dá pra ter uma ideia do que eu quis dizer].
Bom, com esse link, você redireciona para outra página de cadastro, é isso?
Você pode passar para sua outra página os valores do cliente que você necessita que, no caso, é o ID. Você vai colocar esse ID do cliente em um HiddenField, pois só você precisa saber que ele está lá, o cliente não. Daí, você cadastra normalmente, faz a chamada do seu Servlet. Nisso, você tem o ID do cliente em um HiddenField, e pode utilizar-se dele para cadastrar!

public class AdicionarContato extends HttpServlet {
 
    private RequestDispatcher requestDispatcher = null;

    public void execute(...) {

        String errorMessage = null;
        try {
           
            Connection currentConnection = (Connection) request.getAttribute("conexao"); 
            SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy");
            DSOContato contatoDSO = new DSOContato(currentConnection);


            Contato contatoEntity = new Contato();

            contatoEntity.setProximoContato( dateFormat.parse(String.valueOf(request.getParameter("proximoContato"))) );
            contatoEntity.setCliente( Integer.valueOf(String.valueOf(request.getParameter("clienteID"))) );
            contatoEntity.setResumo( String.valueOf(request.getParameter("resumo")) );
            contatoEntity.setStatus( String.valueOf(request.getParameter("status")) );

          
            contatoDSO.createContato(contatoEntity);
            requestDispatcher = request.getRequestDispatcher("/listarcontatos.jsp");
            requestDispatcher.forward(request, response);
        }
        catch (SQLException errSQL) {

            errorMessage = "Database error: " + errSQL.getMessage();
            requestDispatcher = request.getRequestDispatcher("/error.jsp");  
            requestDispatcher.forward(request,response);
        }
        catch (Exception err) {

            errorMessage = "General error: " + errSQL.getMessage();
            requestDispatcher = request.getRequestDispatcher("/error.jsp");  
            requestDispatcher.forward(request,response);
        }
    }
}
Temos a classe de serviço:
public class DSOContato {

    Connection currentConnection;
    public DSOContato(Connection currentConnection) {
        this.currentConnection = currentConnection;
    }

    public void createContato(Contato entityToCreate) throws SQLException {

        DAOContato contatoDAO = new DAOContato(currentConnection);
        contatoDAO.create(entityToCreate);
    }

    public List<Contato> loadContatosByCliente(Long clienteID) throws SQLException {

        DAOContato contatoDAO = new DAOContato(currentConnection);
        Map<String, Object> criteria = new HashMap<String, Object>();

        criteria.put("cliente", clienteID);
        return contatoDAO.loadByCriteria(criteria);
    }
}
E a de persistência:
public class DAOContato {

    Connection currentConnection;
    public DAOContato(Connection currentConnection) {
        this.currentConnection = currentConnection;
    }

    public void create(Contato entityToCreate) throws SQLException {
        // Sua operação para adicionar o contato à base de dados.
    }

    public List<Contato> loadByCriteria(Map<String, Object> criteria) throws SQLException {

        String selectSentence = "SELECT * FROM DBO.CONTATOS WHERE 1 = 1";
        if (criteria != null) {

            if (criteria.containsKey("cliente")) {
                selectSentence = String.format("%s AND CLIENTE = %s", selectSentence, criteria.get("cliente")); 
            }
        }

        // popular uma coleção de contatos e retorná-la.
    }
}
Agora, para buscar os clientes e seus contatos [do jeito que falei: uma coleção de contatos no cliente], faça algo do tipo:
public class DAOCliente {

    Connection currentConnection;
    public DAOCliente(Connection currentConnection) {
        this.currentConnection = currentConnection;
    }

    public void create(Contato entityToCreate) throws SQLException {
        // Sua operação para adicionar o contato à base de dados.
    }

    public List<Cliente> loadByCriteria(Map<String, Object> criteria) throws SQLException {

        String selectSentence = "SELECT * FROM DBO.CLIENTES WHERE 1 = 1";
        if (criteria != null) {
        }

        PreparedStatement selectStatement = currentConnection.prepareStatement(selectSentence);
        ResultSet selectResult = selectStatement.executeQuery();
        while (selectResult.next()) {
            clientesCollection.add(loadEntity(selectResult, criteria));
        }
    }

    private Cliente loadEntity(ResultSet selectResult, Map<String, Object> criteria) throws SQLException {

        DSOContato contatoDSO = new DSOContato(currentConnection);
        Cliente clienteEntity = new Cliente();
        // preenche o objeto...
    
        // e busca quais são os contatos do cliente!
        clienteEntity.setContatos(contatoDSO.loadContatosByCliente(clienteEntity.getID()));
    }
}

A partir dese objeto, você pode popular a tabela com o do modo que eu mostrei ali em cima!
Tá, ficou grande, mas espero que tenha sido útil de algum modo!

Abraços!

R

Vixe… Desculpa a a falta de conhecimento. Fico louco ou fico bom nisso aqui. Espero que a segunda opção ocorra!

E com toda certeza, está ajudando e muito. Mais uma vez, agradeço.

Meu irmão é o seguinte…

Eu entendi o esquema do seu exemplo. Mas eu gostaria de enquadrar o relacionamento no meu modelo. É possível?

Vamos por partes.

Do jeito que está nas duas tabelas adiciono e listo os registros do banco. ressalto que, não estão se relacionando. Como devo implementar para que seja feito o relacionamento?

Se não for possivel implementar nesse modelo, por favor, me explique.

ClienteDAO

public void adicionaCliente(Cliente cliente){
		
		String sql = "insert into clientes (dataRegistro, empresa, endereco, bairro, complemento, cidade, estado, " +
						"telefone1, site, nome, cargo, telefone2, email) " +
							"values (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)";
		
		try{
			//PreparedStatement para inserção
			PreparedStatement stmt = connection.prepareStatement(sql);
			
			//Setando os valores
			
			stmt.setDate  (1, new java.sql.Date(Calendar.getInstance().getTimeInMillis()));
			stmt.setString(2, cliente.getEmpresa());
			stmt.setString(3, cliente.getEndereco());
            stmt.setString(4, cliente.getBairro());
            stmt.setString(5, cliente.getComplemento());
            stmt.setString(6, cliente.getCidade());
            stmt.setString(7, cliente.getEstado());                     
			stmt.setString(8, cliente.getTelefone1());
            stmt.setString(9, cliente.getSite());		
			stmt.setString(10, cliente.getNome());
			stmt.setString(11, cliente.getCargo());
			stmt.setString(12, cliente.getTelefone2());
			stmt.setString(13, cliente.getEmail());
			
			//Executa a ação AdicionarCliente e Fechando o banco
			stmt.execute();
			stmt.close();
			System.out.println("Cliente gravado com sucesso!");
		}catch (SQLException e){
			System.out.println("Não foi possivel gravar cliente! " + e);
			throw new RuntimeException(e);			
		}		
	}
public List<Cliente> getListaClientes(){		
            try{
                PreparedStatement stmt = this.connection.prepareStatement("select * from clientes order by empresa");
                ResultSet rs = stmt.executeQuery();
			
                    List<Cliente> clientes = new ArrayList<Cliente>();
		
                    while(rs.next()){
                    //Criando os objetos Clientes
                    Cliente cliente = new Cliente();
                    
                    cliente.setId_cliente	(rs.getLong("id_cliente"));
                    	//Montando a data
                    	Calendar date = Calendar.getInstance();
                    	date.setTime 		(rs.getDate("dataRegistro"));
                    cliente.setDataRegistro	(date);
                    cliente.setEmpresa		(rs.getString("empresa"));
                    cliente.setEndereco		(rs.getString("endereco"));
                    cliente.setBairro		(rs.getString("bairro"));
                    cliente.setComplemento	(rs.getString("complemento"));
                    cliente.setCidade		(rs.getString("cidade"));
                    cliente.setEstado		(rs.getString("estado"));
                    cliente.setTelefone1    (rs.getString("telefone1"));
                    cliente.setSite			(rs.getString("site"));								
                    cliente.setNome			(rs.getString("nome"));
                    cliente.setCargo		(rs.getString("cargo"));
                    cliente.setTelefone2    (rs.getString("telefone2"));
                    cliente.setEmail		(rs.getString("email"));
				
                    //Adicionando os objetos na lista
                    clientes.add(cliente);                   
                    }
                  rs.close();
                  stmt.close();
                  return clientes;	
                  
            }catch(SQLException e){
            	System.out.println("Não foi possível imprimir lista dos clientes" + e);
                throw new RuntimeException(e);
            }
        }

ContatoDAO

public void adicionaContato(Contato contato){
			
			String sql = "insert into contatos (dataContato, status, proximoContato, resumo) " +
								"values (?, ?, ?, ?)";
			
			try{
				//PreparedStatement para inserção
				PreparedStatement stmt = connection.prepareStatement(sql);
				
				//Setando os valores			
				stmt.setDate  (1, new java.sql.Date(Calendar.getInstance().getTimeInMillis()));
				stmt.setString(2, contato.getStatus());
				stmt.setDate  (3, new Date(contato.getProximoContato().getTimeInMillis()));
				stmt.setString(4, contato.getResumo());
	            
				//Executa a ação AdicionarCliente e Fechando o banco
				stmt.execute();
				stmt.close();
				System.out.println("Contato gravado com sucesso!");
			}catch (SQLException e){
				System.out.println("Não foi possivel gravar cliente! " + e);
				throw new RuntimeException(e);			
			}		
		}
		
		//Cria a lista de Contatos Cadastrados
		public List<Contato> getListaContatos(){
			try{
                PreparedStatement stmt = this.connection.prepareStatement("select * from contatos order by proximoContato");
                ResultSet rs = stmt.executeQuery();
			
                    List<Contato> contatos = new ArrayList<Contato>();
		
                    while(rs.next()){
                    	Contato contato = new Contato();
                    		contato.setId_contato   (rs.getLong("id_contato"));
                    			//Montando a data
                        		Calendar data = Calendar.getInstance();
                        		data.setTime 		(rs.getDate("dataContato"));
                        	contato.setDataContato(data);
                        	contato.setStatus		(rs.getString("status"));
                        		//Montando a data
                    			Calendar date = Calendar.getInstance();
                    			date.setTime 		(rs.getDate("proximoContato"));
                    		contato.setProximoContato(date);
                    		contato.setResumo 		(rs.getString("resumo"));
                    		
                    		contatos.add(contato);
                    }
                    rs.close();
                    stmt.close();
                    return contatos;
					}catch(SQLException e){
						System.out.println("Não foi possível imprimir lista dos clientes" + e);
						throw new RuntimeException(e);
					}
			}

Agradeço amigo Nicolas.

N

O que eu fiz foi separar tudo isso que você montou em métodos específicos e em camadas pra facilitar a visualização.

Renato Yury:
ContatoDAO
public void adicionaContato(Contato contato){
			
                        // AQUI !!!!
			String sql = "insert into contatos (dataContato, status, proximoContato, resumo) " +
								"values (?, ?, ?, ?)";
			
			try{
				PreparedStatement stmt = connection.prepareStatement(sql);
				
				stmt.setDate  (1, new java.sql.Date(Calendar.getInstance().getTimeInMillis()));
				stmt.setString(2, contato.getStatus());
				stmt.setDate  (3, new Date(contato.getProximoContato().getTimeInMillis()));
				stmt.setString(4, contato.getResumo());
	            
				stmt.execute();
				stmt.close();
				System.out.println("Contato gravado com sucesso!");
			}catch (SQLException e){
				System.out.println("Não foi possivel gravar cliente! " + e);
				throw new RuntimeException(e);			
			}		
		}
		
		public List<Contato> getListaContatos(){
			try{
                PreparedStatement stmt = this.connection.prepareStatement("select * from contatos order by proximoContato");
                ResultSet rs = stmt.executeQuery();
			
                    List<Contato> contatos = new ArrayList<Contato>();
		
                    while(rs.next()){
                    	Contato contato = new Contato();
                                // E AQUI !!!!
                    		contato.setId_contato   (rs.getLong("id_contato"));
                        		Calendar data = Calendar.getInstance();
                        		data.setTime 		(rs.getDate("dataContato"));
                        	contato.setDataContato(data);
                        	contato.setStatus		(rs.getString("status"));
                    			Calendar date = Calendar.getInstance();
                    			date.setTime 		(rs.getDate("proximoContato"));
                    		contato.setProximoContato(date);
                    		contato.setResumo 		(rs.getString("resumo"));
                    		
                    		contatos.add(contato);
                    }
                    rs.close();
                    stmt.close();
                    return contatos;
					}catch(SQLException e){
						System.out.println("Não foi possível imprimir lista dos clientes" + e);
						throw new RuntimeException(e);
					}
			}

Coloquei dois comentários: olhe para eles. Quando você adiciona o contato, você não o referencia a nenhum cliente. Quando você busca, aí sim você adiciona o id_contato no seu objeto. Concorda que tá totalmente incoerente isso? Como você vai fazer para referenciar um contato de um cliente sendo que, na sua inserção, você não adiciona a referência do cliente?

Sua sentenca de inserção deve ficar:
INSERT INTO CONTATOS (ID, CLIENTE, DATACONTATO, STATUS, PROXIMOCONTATO, RESUMO) VALUES (?, ?, ?, ?, ?, ?)
E, ao enviar o objeto para sua camada de persistência, através das suas classes de serviço, você deve preencher o atributo id_contato, se não não adianta. Na hora de carregar o objeto cliente, você carrega os contatos juntos. É para isso que serve a coleção de contatos no objeto cliente. Você cria uma pesquisa de contatos por cliente:
public List<Contato> pesquisarContatosPorCliente(Long clienteID) {

    String sentenca = "SELECT * FROM CONTATOS WHERE ID_CLIENTE = ?";
    PreparedStatement statement = connection.prepareStatement(sentenca);
    statement.setLong(1, clienteID);
    //...
}
Na hora de montar o objeto do cliente, seja lá onde você faz isso:
Cliente clienteEntity = new Cliente();

clienteEntity.setID(resultset.getLong("ID"));
clienteEntity.setNome(resultset.getString("NOME"));
//...
clienteEntity.setContatos( pesquisarContatosPorCliente(resultset.getLong("ID")) );
É essa a ideia.
R

Certo.

Vou seguir essas orientações e fazer alguns testes aqui antes de ir para a implementação da logica.

Valeu brow!!!

Criado 29 de agosto de 2011
Ultima resposta 31 de ago. de 2011
Respostas 8
Participantes 2
Empresa: Contato: dataContato: Status: