Spring EL: métodos Java con propiedades como argumentos.

Usando Spring EL es posible ejecutar métodos Java a partir de una clase o de un bean, y pasarle argumentos recogidos de los archivos de propiedades.


<bean id="myBean" class="com.xoubin.service.MyClass" />

<bean id="contextSource" class="org.springframework.ldap.core.support.LdapContextSource">
    <property name="url" value="${ldap.url}" />
    <property name="base" value="${ldap.base}" />
    <property name="userDn" value="${ldap.userdn}" />
    <property name="password" value="#{ myBean.getPass('arg0', '${ldap.encrypted.id}') }" />
</bean>

Mostrar y actualizar checkboxes en relaciones N:M con Spring e Hibernate

Supongamos una JOIN TABLE en un caso de relación N:M, como pueden ser profesores y materias: un profesor imparte una o varias materias, y una materia puede ser impartida por más de un profesor:

PROFESOR
id
nombre
apellidos

MATERIA
id
nombre
codigo

PROFESOR_MATERIA
profesor_id
materia_id

De aquí obtenemos dos clases, Profesor y Materia:

Profesor.java

@Entity
@Table(name = "profesor")
@PrimaryKeyJoinColumn(name = "persona_id", referencedColumnName = "id")
public class Profesor extends Person implements Serializable {

	@ManyToMany(fetch=FetchType.EAGER, cascade = {CascadeType.ALL})
	@JoinTable(name="profesor_materia",
	            joinColumns={@JoinColumn(name="profesor_id")},
	            inverseJoinColumns={@JoinColumn(name="materia_id")})
	private Set materias = new HashSet();

	public void setMaterias(Set materias) {
		this.materias = materias;
	}

	public Set getMaterias() {
		return materias;
	}
…

Materia.java

@Entity
@Table(name="materia")
public class Materia implements Serializable{

	@ManyToMany(fetch=FetchType.EAGER)
	@JoinTable(
			name="profesor_materia",
			joinColumns={@JoinColumn(name="materia_id", referencedColumnName="id")},
			inverseJoinColumns={@JoinColumn(name="profesor_id", referencedColumnName="id")}
		)
    	private Set profesores = new HashSet();

	// getters y setters
…

Suponiendo que queremos crear una vista donde podamos añadir profesores a una materia, la clave está en la anotación @JoinTable (incompatible con @MappedBy), donde especificamos mediante el atributo name que la tabla intermedia que queremos usar es PROFESOR_MATERIA; con joinColumns indicamos que la relación con MATERIA es mediante la columna materia_id, y que esta apunta al atributo id de la clase Materia.

inverseJoinColumns especifica la relación en sentido contrario, es decir, de PROFESOR a MATERIA. Indicamos, igual que antes, que la columna de la tabla intermedia es profesor_id, y que esta apunta al atributo id de la clase Profesor.

Completando ambas clases, y como parte fundamental del proceso de inserción y de actualización de registros, necesitamos sobrescribir los métodos equals y hashCode:

@Override
	public boolean equals(Object obj) {
	    if (this == obj)
	        return true;
	    if (obj == null)
	        return false;
	    if (!(obj instanceof Materia))
	        return false;
	    final Materia other = (Materia) obj;
	    if (getId() == null) {
	        if (other.getId() != null)
	            return false;
	    } else if (!getId().equals(other.getId()))
	        return false;
	    if (getNombre() == null) {
	        if (other.getNombre() != null)
	            return false;
	    } else if (!getNombre().equals(other.getNombre()))
	        return false;
	    return true;
	}

	@Override
	public int hashCode() {
	    final int prime = 31;
	    int result = 1;
	    result = prime * result + ((getId() == null) ? 0 : getId().hashCode());
	    result = prime * result + ((getNombre() == null) ? 0 : getNombre().hashCode());
	    return result;
	}

Ahora pasamos al controlador. Lo primero que necesitaremos será un método que devuelva la lista de profesores disponibles para impartir nuestras materias, y asignarlo a un atributo de modelo:

@ModelAttribute("profesoresList")
	public ListpopulateWebProfesorList() {
		return this.profesorManager.getProfesores();
	}

Esto nos permitirá acceder en la vista a esta lista mediante el tag form:checkboxes, como veremos después. Pero antes, debemos implementar un método que le diga a Spring que debe hacer con cada uno de los elementos de tipo Profesor que le llegue cuando escojamos uno (o varios) profesores de nuestra lista. Para eso, usamos un bind:

@InitBinder
	public void initBinder(WebDataBinder binder) {
		binder.registerCustomEditor(Set.class, "profesores", new CustomCollectionEditor(Set.class){
	            protected Object convertElement(Object prof){
        		        if (prof instanceof String) {
                			Profesor profesor = profesorManager.getProfesor(prof.toString());
		                return profesor;
                		}
	                return null;
        		    }
	        });
	}

Para cada elemento de tipo Set de la propiedad “profesores” (siendo esta propiedad una colección de la clase Materias), realiza una conversión obteniendo la instancia apropiada de Profesor en función del id pasado como parámetro al ProfesorManager. Es decir, dado un id seleccionado en el checkbox, Spring obtiene la instancia correspondiente de Profesor.

Una vez hecho esto, ya podemos usar el tag en la vista:

<form:checkboxes path="profesores" items="${profesoresList}" itemLabel="nombre" itemValue="id" cssClass="checkboxes" />

Esto crea un conjunto de checks a partir de una lista (profesoresList) asociados a la propiedad correspondiente de la clase Materia (profesores), estableciendo como texto a mostrar el nombre (itemLabel), y el id como valor a enviar al servidor cada vez que se seleccione un elemento (itemValue).