Oi David!
Primeiramente obrigado pela sua resposta! Realmente, acredito que eu tenha sido muito sucinto na apresentação do código.
Sobre a montagem do código menu realmente não é difícil, tanto é que na função PHP acima isso é feito em poucas linhas, e sem utilizar nenhuma estrutura de armazenamento intermediária, eu só estou me perdendo um pouco na maneira Hibernate de pensar (retorno de listas das entidades).
Sobre a lista, estou recuperando como Lista porque são os métodos possíveis (get) que a Entidade me permite. E que terei que utilizar para depois programar os CRUDs.
Segue códigos das entidades Usuario, Perfil e Menu respectivamente:
Usuario.java
package br.com.internetsistemas.nucleo.model.entity;
import java.io.Serializable;
import javax.persistence.*;
/**
* Entidade de usuários do sistema.
*/
@Entity
@org.hibernate.annotations.Proxy(lazy=false)
@Table(name="Usuario")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class Usuario implements Serializable {
private static final long serialVersionUID = 1L;
@Column(name="ID", nullable=false)
@Id
@GeneratedValue(generator="V7F00010112B65A686F00112C")
@org.hibernate.annotations.GenericGenerator(name="V7F00010112B65A686F00112C", strategy="identity")
private int ID;
@Column(name="login", nullable=false, unique=true, length=30)
private String login;
@Column(name="senha", nullable=false, length=65)
private String senha;
@Column(name="email", nullable=true, length=100)
private String email;
@Column(name="ultimoLogin", nullable=true)
private java.sql.Timestamp ultimoLogin;
@Column(name="admin", nullable=false, length=1)
private boolean admin;
@Column(name="ativo", nullable=false, length=1)
private boolean ativo;
@ManyToOne(targetEntity=br.com.internetsistemas.nucleo.model.entity.Perfil.class)
@org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.LOCK})
@JoinColumns({ @JoinColumn(name="PerfilID", referencedColumnName="ID") })
@Basic(fetch=FetchType.LAZY)
private br.com.internetsistemas.nucleo.model.entity.Perfil perfil;
public void setID(int value) {
this.ID = value;
}
public int getID() {
return ID;
}
public int getORMID() {
return getID();
}
public void setLogin(String value) {
this.login = value;
}
public String getLogin() {
return login;
}
public void setSenha(String value) {
this.senha = value;
}
public String getSenha() {
return senha;
}
public void setEmail(String value) {
this.email = value;
}
public String getEmail() {
return email;
}
public void setUltimoLogin(java.sql.Timestamp value) {
this.ultimoLogin = value;
}
public java.sql.Timestamp getUltimoLogin() {
return ultimoLogin;
}
public void setAdmin(boolean value) {
this.admin = value;
}
public boolean getAdmin() {
return admin;
}
public void setAtivo(boolean value) {
this.ativo = value;
}
public boolean getAtivo() {
return ativo;
}
public void setPerfil(br.com.internetsistemas.nucleo.model.entity.Perfil value) {
this.perfil = value;
}
public br.com.internetsistemas.nucleo.model.entity.Perfil getPerfil() {
return perfil;
}
public String toString() {
return toString(false);
}
public String toString(boolean idOnly) {
if (idOnly) {
return String.valueOf(getID());
}
else {
StringBuffer sb = new StringBuffer();
sb.append("Usuario[ ");
sb.append("ID=").append(getID()).append(" ");
sb.append("Login=").append(getLogin()).append(" ");
sb.append("Senha=").append(getSenha()).append(" ");
sb.append("Email=").append(getEmail()).append(" ");
sb.append("UltimoLogin=").append(getUltimoLogin()).append(" ");
sb.append("Admin=").append(getAdmin()).append(" ");
sb.append("Ativo=").append(getAtivo()).append(" ");
if (getPerfil() != null)
sb.append("Perfil.Persist_ID=").append(getPerfil().toString(true)).append(" ");
else
sb.append("Perfil=null ");
sb.append("]");
return sb.toString();
}
}
}
Perfil.java
package br.com.internetsistemas.nucleo.model.entity;
import java.io.Serializable;
import javax.persistence.*;
/**
* Entidade de perfil de usuários.
*/
@Entity
@org.hibernate.annotations.Proxy(lazy=false)
@Table(name="Perfil")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class Perfil implements Serializable {
private static final long serialVersionUID = 1L;
@Column(name="ID", nullable=false)
@Id
@GeneratedValue(generator="V7F00010112B65A686F70112D")
@org.hibernate.annotations.GenericGenerator(name="V7F00010112B65A686F70112D", strategy="identity")
private int ID;
@Column(name="nome", nullable=false, length=45)
private String nome;
@ManyToMany(targetEntity=br.com.internetsistemas.nucleo.model.entity.Permissao.class)
@org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK})
@JoinTable(name="Perfil_Permissao", joinColumns={ @JoinColumn(name="PerfilID") }, inverseJoinColumns={ @JoinColumn(name="PermissaoID") })
@org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.EXTRA)
private java.util.Set<br.com.internetsistemas.nucleo.model.entity.Permissao> permissao = new java.util.HashSet<br.com.internetsistemas.nucleo.model.entity.Permissao>();
@ManyToMany(targetEntity=br.com.internetsistemas.nucleo.model.entity.Menu.class)
@org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK})
@JoinTable(name="Perfil_Menu", joinColumns={ @JoinColumn(name="PerfilID") }, inverseJoinColumns={ @JoinColumn(name="MenuID") })
@org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.EXTRA)
private java.util.Set<br.com.internetsistemas.nucleo.model.entity.Menu> menu = new java.util.HashSet<br.com.internetsistemas.nucleo.model.entity.Menu>();
@OneToMany(mappedBy="perfil", targetEntity=br.com.internetsistemas.nucleo.model.entity.Usuario.class)
@org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK})
@org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.EXTRA)
private java.util.Set<br.com.internetsistemas.nucleo.model.entity.Usuario> usuario = new java.util.HashSet<br.com.internetsistemas.nucleo.model.entity.Usuario>();
public void setID(int value) {
this.ID = value;
}
public int getID() {
return ID;
}
public int getORMID() {
return getID();
}
public void setNome(String value) {
this.nome = value;
}
public String getNome() {
return nome;
}
public void setPermissao(java.util.Set<br.com.internetsistemas.nucleo.model.entity.Permissao> value) {
this.permissao = value;
}
public java.util.Set<br.com.internetsistemas.nucleo.model.entity.Permissao> getPermissao() {
return permissao;
}
public void setMenu(java.util.Set<br.com.internetsistemas.nucleo.model.entity.Menu> value) {
this.menu = value;
}
public java.util.Set<br.com.internetsistemas.nucleo.model.entity.Menu> getMenu() {
return menu;
}
public void setUsuario(java.util.Set<br.com.internetsistemas.nucleo.model.entity.Usuario> value) {
this.usuario = value;
}
public java.util.Set<br.com.internetsistemas.nucleo.model.entity.Usuario> getUsuario() {
return usuario;
}
public String toString() {
return toString(false);
}
public String toString(boolean idOnly) {
if (idOnly) {
return String.valueOf(getID());
}
else {
StringBuffer sb = new StringBuffer();
sb.append("Perfil[ ");
sb.append("ID=").append(getID()).append(" ");
sb.append("Nome=").append(getNome()).append(" ");
sb.append("Permissao.size=").append(getPermissao().size()).append(" ");
sb.append("Menu.size=").append(getMenu().size()).append(" ");
sb.append("Usuario.size=").append(getUsuario().size()).append(" ");
sb.append("]");
return sb.toString();
}
}
}
Menu.java
package br.com.internetsistemas.nucleo.model.entity;
import java.io.Serializable;
import javax.persistence.*;
/**
* Entidade com os menus, submenus e páginas linkadas com o controller.
*/
@Entity
@org.hibernate.annotations.Proxy(lazy=false)
@Table(name="Menu")
@Inheritance(strategy=InheritanceType.SINGLE_TABLE)
public class Menu implements Serializable {
private static final long serialVersionUID = 1L;
@Column(name="ID", nullable=false)
@Id
@GeneratedValue(generator="V7F00010112B65A686FC0112F")
@org.hibernate.annotations.GenericGenerator(name="V7F00010112B65A686FC0112F", strategy="identity")
private int ID;
@Column(name="nome", nullable=false, length=255)
private String nome;
@Column(name="tipo", nullable=false, length=30)
@Enumerated(EnumType.STRING)
private MenuType tipo;
@Column(name="icone", nullable=true, length=100)
private String icone;
@Column(name="url", nullable=true, length=100)
private String url;
@Column(name="ordem", nullable=false, length=10)
private int ordem;
@Column(name="ativo", nullable=false, length=1)
private boolean ativo;
@Column(name="idRef", nullable=true, length=10)
private Integer idRef;
@OneToOne(targetEntity=br.com.internetsistemas.nucleo.model.entity.Permissao.class)
@org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK})
@JoinColumns({ @JoinColumn(name="PermissaoID") })
@Basic(fetch=FetchType.LAZY)
private br.com.internetsistemas.nucleo.model.entity.Permissao permissao;
@ManyToMany(mappedBy="menu", targetEntity=br.com.internetsistemas.nucleo.model.entity.Perfil.class)
@org.hibernate.annotations.Cascade({org.hibernate.annotations.CascadeType.SAVE_UPDATE, org.hibernate.annotations.CascadeType.LOCK})
@org.hibernate.annotations.LazyCollection(org.hibernate.annotations.LazyCollectionOption.EXTRA)
private java.util.Set<br.com.internetsistemas.nucleo.model.entity.Perfil> perfil = new java.util.HashSet<br.com.internetsistemas.nucleo.model.entity.Perfil>();
public void setID(int value) {
this.ID = value;
}
public int getID() {
return ID;
}
public int getORMID() {
return getID();
}
public void setNome(String value) {
this.nome = value;
}
public String getNome() {
return nome;
}
public void setTipo(MenuType value) {
this.tipo = value;
}
public MenuType getTipo() {
return tipo;
}
public void setIcone(String value) {
this.icone = value;
}
public String getIcone() {
return icone;
}
public void setUrl(String value) {
this.url = value;
}
public String getUrl() {
return url;
}
public void setAtivo(boolean value) {
this.ativo = value;
}
public boolean getAtivo() {
return ativo;
}
public void setIdRef(int value) {
setIdRef(new Integer(value));
}
public void setIdRef(Integer value) {
this.idRef = value;
}
public Integer getIdRef() {
return idRef;
}
public void setOrdem(int value) {
this.ordem = value;
}
public int getOrdem() {
return ordem;
}
public void setPerfil(java.util.Set<br.com.internetsistemas.nucleo.model.entity.Perfil> value) {
this.perfil = value;
}
public java.util.Set<br.com.internetsistemas.nucleo.model.entity.Perfil> getPerfil() {
return perfil;
}
public void setPermissao(br.com.internetsistemas.nucleo.model.entity.Permissao value) {
this.permissao = value;
}
public br.com.internetsistemas.nucleo.model.entity.Permissao getPermissao() {
return permissao;
}
public String toString() {
return toString(false);
}
public String toString(boolean idOnly) {
if (idOnly) {
return String.valueOf(getID());
}
else {
StringBuffer sb = new StringBuffer();
sb.append("Menu[ ");
sb.append("ID=").append(getID()).append(" ");
sb.append("Nome=").append(getNome()).append(" ");
sb.append("Tipo=").append(getTipo()).append(" ");
sb.append("Icone=").append(getIcone()).append(" ");
sb.append("Url=").append(getUrl()).append(" ");
sb.append("Ordem=").append(getOrdem()).append(" ");
sb.append("Ativo=").append(getAtivo()).append(" ");
sb.append("IdRef=").append(getIdRef()).append(" ");
if (getPermissao() != null)
sb.append("Permissao.Persist_ID=").append(getPermissao().toString(true)).append(" ");
else
sb.append("Permissao=null ");
sb.append("Perfil.size=").append(getPerfil().size()).append(" ");
sb.append("]");
return sb.toString();
}
}
}
As entidades utilizo na camada de serviço (UsuarioService), função calcularmenu:
UsuarioServiceImpl.java
package br.com.internetsistemas.nucleo.service.impl;
import java.util.ArrayList;
import org.jgrapht.UndirectedGraph;
import org.jgrapht.graph.DefaultEdge;
import org.jgrapht.graph.SimpleGraph;
import br.com.caelum.vraptor.ioc.Component;
import br.com.internetsistemas.nucleo.model.dao.GenericDao;
import br.com.internetsistemas.nucleo.model.entity.Menu;
import br.com.internetsistemas.nucleo.model.entity.Perfil;
import br.com.internetsistemas.nucleo.model.entity.Usuario;
import br.com.internetsistemas.nucleo.service.UsuarioService;
import br.com.internetsistemas.nucleo.util.db.datetime.DBDateTime;
import br.com.internetsistemas.nucleo.util.db.query.QueryParam;
import br.com.internetsistemas.nucleo.util.sec.Crypt;
@Component
public class UsuarioServiceImpl implements UsuarioService {
private final GenericDao<Usuario> usuarioDao;
private final GenericDao<Menu> menuDao;
private final GenericDao<Perfil> perfilDao;
public UsuarioServiceImpl(GenericDao<Usuario> usuarioDao,
GenericDao<Menu> menuDao, GenericDao<Perfil> perfilDao) {
this.usuarioDao = usuarioDao;
this.menuDao = menuDao;
this.perfilDao = perfilDao;
}
@Override
public Usuario verificarLogin(String login, String senha) {
QueryParam param = new QueryParam();
param.setParam("login", login);
param.setParam("senha", Crypt.sha256(senha));
param.setParam("ativo", true);
return usuarioDao
.findUniqueResult(
"FROM Usuario u WHERE u.login = :login AND u.senha = :senha AND u.ativo = :ativo",
param);
}
@Override
public String calcularMenu(Usuario usuario) {
// Apenas para "debug" no console
Perfil perfil = usuario.getPerfil();
if(perfil != null)
{
for (Menu menu : new ArrayList<Menu>(perfil.getMenu())) {
System.out.println(menu);
}
}
//return null;
// retorna menu fixo por enquanto!
return "<ul class=\"menuh\"><li class=\"menuv\"><a href=\"home\">Home</a></li><li class=\"menuv\"><a href=\"#\">Administração</a><ul class=\"menu\"><li class=\"topo\"></li><li class=\"menu\"><a href=\"cad_categorias.php\">Categorias</a></li><li class=\"menu\"><a href=\"index.php\">Produtos</a></li></ul></li></ul>";
}
private String calcularMenu(Usuario usuario, int idRef) {
return null;
}
@Override
public void registrarUltimoLogin(Usuario usuario) {
usuario.setUltimoLogin(DBDateTime.now());
usuarioDao.merge(usuario);
}
}
Que por sua vez é chamada no controller do vraptor:
HomeController.java
package br.com.internetsistemas.nucleo.controller;
import javax.servlet.http.HttpServletRequest;
import br.com.caelum.vraptor.Get;
import br.com.caelum.vraptor.Path;
import br.com.caelum.vraptor.Post;
import br.com.caelum.vraptor.Resource;
import br.com.caelum.vraptor.Result;
import br.com.internetsistemas.nucleo.annotation.IgnorarAutenticacao;
import br.com.internetsistemas.nucleo.config.AppConfig;
import br.com.internetsistemas.nucleo.model.entity.Usuario;
import br.com.internetsistemas.nucleo.service.UsuarioService;
import br.com.internetsistemas.nucleo.session.UsuarioSession;
@Resource
public class HomeController {
private final HttpServletRequest request;
private final Result result;
private final UsuarioService usuarioService;
private final UsuarioSession usuarioSession;
public HomeController(HttpServletRequest request, Result result,
UsuarioService usuarioService, UsuarioSession usuarioSession) {
this.request = request;
this.result = result;
this.usuarioService = usuarioService;
this.usuarioSession = usuarioSession;
}
@Path("home")
public void home() {
}
@Get
@IgnorarAutenticacao
public void login() {
String hostname = request.getLocalName();
// FIXME Adicionar camada de criptografia nas LICS.
// FIXME Adicionar mais hostnames para camada de LIC.
boolean licValida = hostname.equals(AppConfig.LIC);
// Caso a licença não seja válida redireciona para a página de erro de
// configuração.
if (!licValida) {
result.redirectTo(this.getClass()).erroConf();
//TODO Enviar e-mail ou logar ocorrência de LIC inválida.
}
// Caso as tentativas de login estejam excedidas redireciona para página
// de erro de tentativas.
if (usuarioSession.isTentativasExcedidas()) {
result.redirectTo(this.getClass()).erroTentativas();
}
}
@Post
@IgnorarAutenticacao
public void login(final String login, final String senha) {
Usuario usuario = usuarioService.verificarLogin(login, senha);
if (usuario == null) {
usuarioSession.registrarTentativa();
if (usuarioSession.isTentativasExcedidas()) {
result.redirectTo(this.getClass()).erroTentativas();
} else {
result.redirectTo(this.getClass()).erroLogin();
}
} else {
usuarioService.registrarUltimoLogin(usuario);
// Logando o usuário e calculando os menus
usuarioSession.login(usuario, usuarioService.calcularMenu(usuario));
result.redirectTo(this.getClass()).home();
}
}
public void logout() {
usuarioSession.logout();
result.redirectTo(this.getClass()).login();
}
@IgnorarAutenticacao
public void erroConf() {
}
@IgnorarAutenticacao
public void erroTentativas() {
}
@IgnorarAutenticacao
public void erroLogin() {
}
}
Tinha pensado em uma solução de armazenamento tipo árvore (como você citou) para: 1º) armazenar a partir do list (utilizando o Menu.getID() e Menu.getIDRef()) e 2º) recuperar mais facilmente através de um interator ou algo do gênero.
Uma outra opção é utilizar grafos para essa finalidade adicionando e ligando os vértices como exemplifica a biblioteca JGraphT, porem acredito que isso é dar um “tiro de canhão”:
UndirectedGraph<String, DefaultEdge> g =
new SimpleGraph<String, DefaultEdge>(DefaultEdge.class);
String v1 = "v1";
String v2 = "v2";
String v3 = "v3";
String v4 = "v4";
// add the vertices
g.addVertex(v1);
g.addVertex(v2);
g.addVertex(v3);
g.addVertex(v4);
// add edges to create a circuit
g.addEdge(v1, v2);
g.addEdge(v2, v3);
g.addEdge(v3, v4);
g.addEdge(v4, v1);
O que você acha? Não sei se fui mais claro.
PS: Em anexo segue a estrutura relacional.
Abraços!