VRaptor + CRUD + Ajax + Navegação por estados

Boa Noite pessoal, continuando com meus estudos com vraptor fiz um crud com ajax simulando uma navegação por estados, faço o crud todo sem dar refresh na página mas como não tenho muita prática com jquery+ vraptor pergunto aos companheiros se a abordagem que utilizei no exemplo é uma boa abordagem ou não, e se utilizar ajax dessa forma que utilizei é correto e se posso ter problemas de performance ou segurança com tal abordagem, fiz o exemplo utilizando esse exemplo aqui em php: http://www.codeofaninja.com/2013/05/crud-with-php-jquery.html

Vamos ao exemplo: utilizei hibernate com apenas 1 entidade,o fornecedor


@Entity
public class Fornecedor implements Serializable{
	private static final long serialVersionUID = 1L;
	@Id
	@GeneratedValue(strategy=GenerationType.IDENTITY)
	private Long id;
	
	private String nome;
	private String descricao;
	private String cpfCnpj;
	
	
	
	@Column(name="cpf_cnpj")
	public String getCpfCnpj() {
		return cpfCnpj;
	}
	public void setCpfCnpj(String cpfCnpj) {
		this.cpfCnpj = cpfCnpj;
	}
	
	public Long getId() {
		return id;
	}
	public void setId(Long id) {
		this.id = id;
	}
	public String getNome() {
		return nome;
	}
	public void setNome(String nome) {
		this.nome = nome;
	}
	public String getDescricao() {
		return descricao;
	}
	public void setDescricao(String descricao) {
		this.descricao = descricao;
	}
	

}

Meu controller

@Resource
public class FornecedorController {
	
	Result result;
	
	public FornecedorController(Result result){
		
		this.result = result;
	}
	

	@Path("/lista")
	public void lista(){	
	
	}
	
	
	
	public void adiciona(Fornecedor fornecedor){
		
		Session s = (Session) HibernateUtil.openSession();
		Transaction t  = s.beginTransaction();
		s.persist(fornecedor);
		t.commit();
		s.close();
		result.use(Results.http()).setStatusCode(200);
	}
	
	
	public void altera(Fornecedor fornecedor){
		
		Session s = (Session) HibernateUtil.openSession();
		Transaction t  = s.beginTransaction();
		s.update(fornecedor);
		t.commit();
		s.close();
		result.use(Results.http()).setStatusCode(200);
	}
	
	
	
	
	public void lista2(){
	
	Session s = (Session) HibernateUtil.openSession();
	Criteria c= s.createCriteria(Fornecedor.class);
	List<Fornecedor> fornecedores = c.list();
	s.close();
	result.include("fornecedorList",fornecedores);
	}
	
	
	
	public void edita(Long id){
		Session s = (Session) HibernateUtil.openSession();
		Fornecedor f = (Fornecedor)s.get(Fornecedor.class,id);
		 s.close();
		 result.include("fornecedor",f);
	}
	
	
	
	public void delete(Long id){
		Session s = (Session) HibernateUtil.openSession();
		Transaction t  = s.beginTransaction();
		Fornecedor f = (Fornecedor) s.get(Fornecedor.class, id);
		s.delete(f);
		t.commit();
		s.close();
		result.use(Results.http()).setStatusCode(200);
	}
	

	public void novo(){
		Fornecedor f = new Fornecedor();
		result.include("fornecedor",f);
	}
	

}

minha jsp principal: lista.jsp que contem as funções ajax

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>

<script type="text/javascript" src="js/jquery-1.8.3.min.js"></script>  
 <link rel="stylesheet" type="text/css" href="css/style2.css">
</head>
<body>
<div>
<div id="visualizarFornecedores"  class='customBtn'>Visualizar Fornecedores</div>
<div id="adicionarFornecedor"  class='customBtn'>+ Adicionar Fornecedor</div>
 <div id='loaderImage'><img src='img/ajax_loader.gif' /></div>
</div>

<div id='pageContent'>
</div>
<script type="text/javascript">

$(document).ready(function(){

$("#loaderImage").show();
mostrarFornecedores();

$('#visualizarFornecedores').click(function(){
    $('#loaderImage').show();
    mostrarFornecedores();
});


$('#adicionarFornecedor').click(function(){
	showCreateNewFornecedor();
});



$(document).on('submit',"#createFornecedor",function(){
 $.post("/vraptor-blank-project/fornecedor/adiciona",$("#createFornecedor").serialize()).done(function(data) {
	mostrarFornecedores();
}); 
return false;
});


$(document).on('click', '.deleteBtn', function(){ 
    if(confirm('está certo disso?')){
        var id = $(this).closest('td').find('.fornecedorId').text();
        $.post("/vraptor-blank-project/fornecedor/delete", { id: id })
            .done(function(data) {
                $('#loaderImage').show();
              mostrarFornecedores();
                
            });
    }
});



$(document).on('click', '.editBtn', function(){ 
    var id = $(this).closest('td').find('.fornecedorId').text();
    $('#loaderImage').show();
    setTimeout("$('#pageContent').load('/vraptor-blank-project/fornecedor/edita?id=" + id + "', function(){ $('#loaderImage').hide(); });",1000);
    
}); 


$(document).on('submit', '#updateFornecedorForm', function() {
   $('#loaderImage').show();
   $.post("/vraptor-blank-project/fornecedor/altera", $("#updateFornecedorForm").serialize())
       .done(function(data) {
    	   mostrarFornecedores();
       });
           
   return false;
});
});




function mostrarFornecedores(){
	$("#pageContent").load("/vraptor-blank-project/fornecedor/lista2");
	 $('#loaderImage').hide();
}


function showCreateNewFornecedor(){
	$("#pageContent").load("/vraptor-blank-project/fornecedor/novo");
	 $('#loaderImage').hide();
	
}

</script>


</body>
</html>

a página de listagem: lista2.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
<script type="text/javascript" src="js/jquery-1.8.3.min.js"></script>  
</head>
<body>
<table id='tfhover' class='tftable' border='1'>
<tr>
<th>id</th>
<th>Nome</th>
<th>CNPJ</th>
<th style='text-align:center;'>Ações</th>
<c:forEach items="${fornecedorList}" var="f" >
<tr>
<td><c:out value="${f.id}"/></td>
<td><c:out value="${f.nome}"/></td>
<td><c:out value="${f.descricao}"/></td>
<td>
<div class='fornecedorId'>${f.id}</div>
<div class='editBtn customBtn'>[Editar]</div>
<div class='deleteBtn customBtn'>[Delete]</div>
</td>
</tr>
</c:forEach>
</tr>
</table>
</body>
</html>

o formulário de inclusão: novo.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<form id="createFornecedor" method="post" action="#">
<table>
<tr>
<td>Nome</td>
<td><input type="text" id="nome" name="fornecedor.nome" value="${fornecedor.nome}"></td>
</tr>
<tr>
<td>Cnpj</td>
<td><input type="text" id="nome" name="fornecedor.cpfCnpj" value="${fornecedor.cpfCnpj}"></td>
</tr>
<tr>
<td>Descrição</td>
<td><input type="text" id="nome" name="fornecedor.descricao" value="${fornecedor.descricao}"></td>
</tr>
<tr>
<td colspan="2">
<input type="submit" value="Salvar" class='customBtn'/>
<input type="reset" value="Resetar" class='customBtn'/>
</td>
</tr>
</table>
</form>
</body>
</html>

e o formulario de alteração: edita.jsp

<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
    pageEncoding="ISO-8859-1"%>
    <%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=ISO-8859-1">
<title>Insert title here</title>
</head>
<body>
<form id="updateFornecedorForm" method="post" action="#">
<table>
<tr>
<td>Nome</td>
<td><input type="text" id="nome" name="fornecedor.nome" value="${fornecedor.nome}"></td>
</tr>
<tr>
<td>Cnpj</td>
<td><input type="text" id="cnpj" name="fornecedor.cpfCnpj" value="${fornecedor.cpfCnpj}"></td>
</tr>
<tr>
<td>Descrição</td>
<td><input type="text" id="desc" name="fornecedor.descricao" value="${fornecedor.descricao}"></td>
</tr>
<tr>
<td colspan="2">
<input type="hidden" name="fornecedor.id" value="${fornecedor.id}" />
<input type="submit" value="Salvar" class='customBtn'/>
<input type="reset" value="Resetar" class='customBtn'/>
</td>
</tr>
</table>
</form>
</body>
</html>

pra terminar o css: style2.css

body{
    font: normal normal 110% Arial, Serif;
}
    
.customBtn {
    cursor:pointer;
    margin:0 .3em 0 0;
    -moz-box-shadow:inset 0px 1px 0px 0px #caefab;
    -webkit-box-shadow:inset 0px 1px 0px 0px #caefab;
    box-shadow:inset 0px 1px 0px 0px #caefab;
    background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #77d42a), color-stop(1, #5cb811) );
    background:-moz-linear-gradient( center top, #77d42a 5%, #5cb811 100% );
    filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#77d42a', endColorstr='#5cb811');
    background-color:#77d42a;
    -moz-border-radius:6px;
    -webkit-border-radius:6px;
    border-radius:6px;
    border:1px solid #268a16;
    display:inline-block;
    color:#ffffff;
    font-family:arial;
    font-size:15px;
    font-weight:bold;
    padding:6px 24px;
    text-decoration:none;
    /*text-shadow:1px 1px 0px #f1f1f1;*/
}

.customBtn:hover {
    background:-webkit-gradient( linear, left top, left bottom, color-stop(0.05, #5cb811), color-stop(1, #77d42a) );
    background:-moz-linear-gradient( center top, #5cb811 5%, #77d42a 100% );
    filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#5cb811', endColorstr='#77d42a');
    background-color:#5cb811;
}

.customBtn:active {
    position:relative;
    top:1px;
}

input[type=text],
input[type=password]{
    padding:.5em;
}

table.tftable {font-size:12px;color:#333333;width:50%;border-width: 1px;border-color: #a9a9a9;border-collapse: collapse;}
table.tftable th {font-size:12px;background-color:#b8b8b8;border-width: 1px;padding: 8px;border-style: solid;border-color: #a9a9a9;text-align:left;}
table.tftable tr {background-color:#ffffff;}
table.tftable td {font-size:12px;border-width: 1px;padding: 8px;border-style: solid;border-color: #a9a9a9;}

.userId{
    display:none;
}

#viewUsers,
#addUser{
    float:left;
}

#loaderImage{
    float:left;
    line-height:32px;
}

Vocês costumam fazer navegação com ajax dessa forma ou existe uma forma mais apropriada?

Hmmm vamos lá… Existem MUITAS coisas que precisam ser feitas, SEM considerar o fato de você fazer um CRUD com ajax.

Seguem:
1 - Primeiramente, leia e estude a apostila sobre VRaptor, Hibernate e JavaScript da Caelum. Somente com essa apostila, muitos pontos serão completamente melhorados no seu código.
2 - Não use “HibernateUtil”. Na apostila explica como fazer.
3 - Não controle a manipulação de registros no banco de dados diretamente pelo controller. Na apostila explica como fazer.
4 - Não receba a entidade e persista, você pode gerar vários problemas de segurança e bugs. Use as classes chamadas DTO.
5 - Provavelmente você não tem testes automatizados, mais conhecido como TDD.
6 - Evite ter 2 jsps para o mesmo formulário.
7 - Coloque o javascript em um arquivo .js. Isso ajuda o browser a ser mais rápido.
8 - Não é necessário usar a tag <c:out>, basta fazer assim: <td>${f.id}</td>
9 - Use a tag <c:import> do JSTL para importar o cabeçalho e rodapé das suas páginas.
10 - Use o retorno em JSON para as requisições ajax.

Existem mais um monte de dicas, acredito que essas já ajudam bastante…

Alguns links interessantes:
Apostilas abertas da Caelum (muito boas) - http://www.caelum.com.br/apostilas/
Várias questões que envolvem desenvolvimento na web - http://programmers.stackexchange.com/questions/46716/what-technical-details-should-a-programmer-of-a-web-application-consider-before

Algumas coisas que você provavelmente vai precisar aprender:

  • Design Patterns, o que são e como aplicá-los
  • Git
  • Práticas de desenvolvimento ágil

Aqui são os cursos online da Caelum: http://www.alura.com.br/
Eles são pagos, mas é um valor muito baixo perto do que realmente podem te proporcionar. Minha opinião é que valem a pena (eu fiz muitos)

Boa sorte!!

obrigado por responder Rafael,valeu pelas dicas quanto as dicas 1,2,3,5,6,7,8,9 concordo com vc,não trabalho assim, apenas coloquei o exemplo pra funcionar,sem me preocupar com performance,arquitetura,segurança etc…,pode ver que nem tem tratamento de erros nem client nem server side, apenas quis fazer uma coisa rápida que funcionasse pra efeito de demonstração.

Quanto ao item 4 você tem razão também,fica mais fácil de se proteger contra alguns problemas chatos usando um DTO, mas realmente aboli isso de DTO,VO,BO dos meus sistemas usava na época do tapestry, talvéz trabalhando com frameworks action basead realmente essa camada de dto pode ser útil, um dos problemas que podemos ter usando as entidades diretamente é o envio de campos que não deveriam ser enviados,coloquei até como sugestão em um post do blog da caelum que o vraptor pudesse ter uma anotação @AllowedFields como no SpringMvc

Minha real dúvida está no quesito 10 ,porque realmente no exemplo em nenhum momento eu retornei um Json para a view no entanto o exemplo funciona igual ao do exemplo do php que passei, e queria saber se essa abordagem com ajax seria correta.nem tudo que funciona foi feito da maneira correta(vide os outros itens que vc corretamente enumerou) queria saber se posso trabalhar com ajax dessa forma que fizou tem que ser só retornando mesmo json

Entendi. Então vamos focar no item 10.

O VRaptor tem várias formas de alterar o resultado da requisição, pela classe Result.

result.use(Results.status()).ok(); // é a mesma coisa que result.use(Results.http()).setStatusCode(200);

Não tem problema nenhum você usar o retorno pelo status, mas, se der erro e vc usar o status 400 (ou o badRequest), você só vai poder retornar uma mensagem de erro, ou seja, nenhum objeto ou outras coisas… De qualquer forma, você pode usar o callback error:

$.ajax({
   // outros atributos
   error: function( jqXHR jqXHR, String textStatus, String errorThrown ) {
      fnDisplayError(errorThrown);
   }
});

Já o retorno em JSON, você tem o problema de, mesmo quando você estiver retornando um erro, ele sejá jogado no callback “success” o que não faz sentido nenhum. O lado positivo é que você pode retornar uma lista de erros e outras coisas para ajudar o usuário.
Enfim, cabe a você decidir se quer devolver apenas uma mensagem de erro e o próprio JS aplica o que for default ou devolver tudo certinho direto do browser.

Com json, fica assim:

Vale a pena lembrar o conceito de wrapper aqui… EVITE usar uma instancia de uma entidade.