Foro sobre Java EE > [JPA 2.0 + Hibernate 4.0.1] Error merge @ManyToMany
Me respondo a mí mismo por si os ocurre lo mismo...
El problema no venía en el mapeo de JPA, sino en la vista... construía el ManyToMany a partir de un <h:selectManyCheckbox />, y al parecer hace conversiones a String directamente...
No se si tengo que implementar un conversor, investigaré más sobre el tema.
¡Un saludo!.
El problema no venía en el mapeo de JPA, sino en la vista... construía el ManyToMany a partir de un <h:selectManyCheckbox />, y al parecer hace conversiones a String directamente...
No se si tengo que implementar un conversor, investigaré más sobre el tema.
Si quieres manejar POJOs en el "value" del h:selectMany (o en general cualquier elemento que trabaje con listas de SelectItem) tienes que implementar un @FacesConverter. La buena noticia es que es trivial hacerlo.
Buenas antoniovl.
Pues la verdad es que estaba intentando hacerlo (incluso con los selectOne). Pero no consigo dar con la tecla, no se que hago mal. La idea es tener en un lado una lista de elementos, con su set. ¡Pero no lo consigo!.
Lo explico con código mejor:
Lo que tengo actualmente es:
Backing bean
/**
* Permiso seleccionado
* para el filtro de
* puestos de trabajo.
*/
private String permisoFiltro;
/**
* Carga los combos para los
* crud.
*/
@PostConstruct
public void cargarCombos() {
final List<Permiso> todosPermisos = this.permisosBO.getTodos();
this.permisos = new ArrayList<SelectItem>();
if ((todosPermisos != null) && !todosPermisos.isEmpty()) {
for (final Permiso permiso : todosPermisos) {
final SelectItem elem = new SelectItem();
elem.setDescription(permiso.getDescripcion());
elem.setLabel(permiso.getNombre());
elem.setValue(permiso.getId().toString());
this.permisos.add(elem);
}
}
}
/**
* Devuelve el permiso
* seleccionado para el filtro
* de puestos de trabajo.
* @return Permiso seleccionado
* para el filtro de
* puestos de trabajo
*/
public String getPermisoFiltro() {
return permisoFiltro;
}
/**
* Inserta el permiso
* seleccionado para el
* filtro de puestos de trabajo.
* @param permisoFiltro Permiso
* seleccionado para el
* filtro de puestos de
* trabajo
*/
public void setPermisoFiltro(
final String permisoFiltro) {
this.permisoFiltro = permisoFiltro;
}
/**
* Devuelve la lista de posibles
* permisios.
* @return Lista de posibles
* permisos
*/
public List<SelectItem> getPermisos() {
return permisos;
}
/**
* Inserta la lista
* de posibles permisos.
* @param permisos Lista de
* posibles permisos
*/
public void setPermisos(
final List<SelectItem> permisos) {
this.permisos = permisos;
}
Código xhtml
<div class="elemento-formulario">
<h:outputLabel for="permisoFiltro" value="#{puestosProp.lblPermiso}"/>
<h:selectOneMenu id="permisoFiltro" value="#{puestosListaBean.permisoFiltro}"
hideNoSelectionOption="false" title="#{puestosProp.lblPermiso}"
valueChangeListener="#{puestosListaBean.selecPermiso}">
<f:selectItems value="#{puestosListaBean.permisos}"/>
</h:selectOneMenu>
</div>
Lo que quiero conseguir, es tener en el backing bean lo siguiente:
/**
* Permiso seleccionado
* para el filtro de
* puestos de trabajo.
*/
private Permiso permisoFiltro;
/**
* Lista de permisos
* posibles.
*/
private List<Permiso> permisos;
/**
* Carga los combos para los
* crud.
*/
@PostConstruct
public void cargarCombos() {
this.permisos = this.permisosBO.getTodos();
}
/**
* Devuelve el permiso
* seleccionado para el filtro
* de puestos de trabajo.
* @return Permiso seleccionado
* para el filtro de
* puestos de trabajo
*/
public Permiso getPermisoFiltro() {
return permisoFiltro;
}
/**
* Inserta el permiso
* seleccionado para el
* filtro de puestos de trabajo.
* @param permisoFiltro Permiso
* seleccionado para el
* filtro de puestos de
* trabajo
*/
public void setPermisoFiltro(
final Permiso permisoFiltro) {
this.permisoFiltro = permisoFiltro;
}
/**
* Devuelve la lista de posibles
* permisios.
* @return Lista de posibles
* permisos
*/
public List<Permiso> getPermisos() {
return permisos;
}
/**
* Inserta la lista
* de posibles permisos.
* @param permisos Lista de
* posibles permisos
*/
public void setPermisos(
final List<Permiso> permisos) {
this.permisos = permisos;
}
En el caso de ser un <h:selectMany />, el valor sería una lista de elementos seleccionados... Pero no consigo hacer bien el converter al parecer, ni configurar bien la etiqueta en el xhtml.
!Muchas gracias por tu ayuda!
Un saludo.
Vamos a tratar de hacerlo lo mas corto posible:
En el backing bean, construye la lista de SelectItem como:
for (Permiso p : getPermisos()) {
SelectItem item = new SelectItem(p, p.getDescripcion());
selectItemsPermisos.add(item);
}
y creas una clase anotada con @FacesConverter:
@FacesConverter("permisoConverter")
public class PermisoConverter implements Converter {
@Override
public Object getAsObject(FacesContext context, UIComponent component, String value) {
/* Aqui tomas el String del parámetro "value" y lo conviertes a un objeto
"Permiso". Probablemente sea buena idea que el String en value sea
la PK del objeto. */
return buscarPermisoPorId(value);
}
@Override
public String getAsString(FacesContext context, UIComponent component, Object value) {
/* Aqui regresas un String que te identifique únicamente al Permiso. Este String
es el que utilizarás en getAsObject(). */
Permiso p = (Permiso)value;
return String.valueOf(p.getIdPermiso());
}
}
En la página, haces:
<h:selectOneMenu value="#{myBean.permisoFiltro}">
<f:selectItems value="#{myBean.selectItemsPermisos}"/>
<f:converter converterId="permisoConverter"/>
</h:selectOneMenu>
y listo. ¿Como ves?
Pues voy a probarlo, y te digo.
Usaba el atributo "converter", en lugar de la etiqueta... Pero empezaba a hacerme cosas raras.
¡Muchas gracias por tu ayuda!.
Un saludo.
Buenas tardes.
Tras resolver algunos problemas con la capa JSF (habréis visto algunos posts por ahí...) me he encontrado con el siguiente handicap.
Estoy modelando un @ManyToMany con JPA 2.0, usando la implementación de Hibernate Entity Manager que aporta JBoss 7.1.1.
La idea es bastante sencilla, pero no consigo saber que ocurre.
Tengo una tabla de usuarios, una tabla de puestos de trabajo, y una tabla de permisos.
Un usuario tiene asignado un puesto de trabajo, y un puesto de trabajo tiene varios perfiles. El problema es al editar un puesto de trabajo, me lanza la siguiente excepción:
java.lang.IllegalArgumentException: Can not set java.lang.Long field com.miproyecto.dto.Permiso.id to java.lang.String
El @ManyToMany lo tengo definido, tanto en la clase Puesto como en la clase Permiso de la siguiente forma:
Clase Puesto
public Class Puesto {
..............
/**
* Identificador del puesto.
*/
@Id
@Column(name = "ID_PUESTO")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
..............
/**
* Permisos asociados al puesto de trabajo.
*/
@OneToMany(fetch = FetchType.EAGER)
@JoinTable(name="PUESTOS_PERMISOS",
joinColumns=
@JoinColumn(name="ID_PUESTO", insertable = false, updatable = false),
inverseJoinColumns=
@JoinColumn(name="ID_PERMISO", insertable = false, updatable = false))
private List<Permiso> permisos;
Clase Permiso
public Class Permiso {
/**
* Identificador del
* permiso.
*/
@Id
@Column(name = "ID_PERMISO")
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
.......
/**
* Puestos de trabajo asociados.
*/
@ManyToMany(fetch = FetchType.LAZY, mappedBy = "permisos")
private List<Puesto> puestos;
La tabla que relaciona ambas entidades, se genera con el siguiente código SQL (con MariaDB como motor de base de datos.
CREATE TABLE IF NOT EXISTS `puestos_permisos` (
`ID_PUESTO` bigint(20) NOT NULL DEFAULT '0',
`ID_PERMISO` bigint(20) NOT NULL DEFAULT '0',
PRIMARY KEY (`ID_PUESTO`,`ID_PERMISO`),
KEY `FK_PERMISO_PUESTO` (`ID_PERMISO`),
CONSTRAINT `FK_PERMISO_PUESTO` FOREIGN KEY (`ID_PERMISO`) REFERENCES `permisos` (`ID_PERMISO`),
CONSTRAINT `FK_PUESTO_PERMISO` FOREIGN KEY (`ID_PUESTO`) REFERENCES `puestos` (`ID_PUESTO`)
He estado buscando por documentación, foros y demás, y he encontrado que a mucha gente le ocurre el mismo error.
Lo que me han recomendado es pasar del @ManyToMany, y mapear la relación en una entidad nueva, PermisoPuestoDTO o algo así. Pero claro, tendría que mantener esa entidad, crearme sus DAOs... Me comentaban que con Hibernate no funcionaba bien este tipo de mapeos, pero no estoy seguro.
¿Os funcionan bien los ManyToMany?. ¿Sabéis que puede estar pasando?.
El proyecto tiene bastantes relaciones de este tipo, por lo que sería estupendo mapear todas las relaciones de esta forma.
Un saludo, y muchas gracias por vuestra ayuda.