2009-05-26 12 views
18

estoy acaba de empezar con Hibernate, y todos los ejemplos que estoy viendo hasta ahora se parecen mucho a la clase particular en la documentación de Hibernate:¿Hay alguna manera de declarar los campos finales para los objetos gestionados por Hibernate?

package org.hibernate.tutorial.domain; 
import java.util.Date; 

public class Event { 

    private Long id; 
    private String title; 
    private Date date; 

    public Event() {} 

    /* Accessor methods... */ 
} 

Específicamente: ninguno de los campos se declaran como final, y debe haber un constructor sin argumentos para que el marco de Hibernate pueda instanciar la clase y establecer sus campos.

Pero aquí está la cosa - me realmente no me gusta hacer mis clases mutables de cualquier manera siempre que puedo evitarlo (Prácticas de Java: inmutables objetos hacen un argumento bastante fuerte para hacer esto). Así que ¿hay alguna forma de hacer que Hibernate funcione incluso si tuviera que declarar cada uno de los campos 'final'?

Entiendo que Hibernate utiliza Reflection para instanciar sus clases y, por lo tanto, necesita poder invocar un constructor de algún tipo sin correr el riesgo de elegir el constructor equivocado o pasar el valor incorrecto a uno de sus parámetros, por lo probablemente sea más seguro invocar el constructor no-arg y establecer cada campo de uno en uno. Sin embargo, ¿no debería ser posible proporcionar la información necesaria a Hibernate para que pueda instanciar con seguridad objetos inmutables?

public class Event { 

    private final Long id; 
    private final String title; 
    private final Date date; 

    public Event(@SetsProperty("id") Long id, 
     @SetsProperty("title") String title, 
     @SetsProperty("date") Date date) { 

     this.id = id; 
     this.title = title; 
     this.date = new Date(date.getTime()); 
    } 

    /* Accessor methods... */ 
} 

El @SetsProperty anotación es, por supuesto ficticio, pero no parece que debería estar fuera de su alcance.

Respuesta

5

Esto suena como que no es un caso de uso para Hibernate, ya que muchas de las operaciones que realiza la preocupación del estado mutable:

  • objetos que se fusionan
  • estado sucio comprobar
  • vaciar los cambios

Dicho esto, si le preocupa la inmutabilidad, puede optar por proporcionar envoltorios alrededor de sus objetos, con copia-constructores:

public class FinalEvent { 
    private final Integer id; 

    public FinalEvent(Event event) { 
     id = event.id; 
    } 
} 

Sin embargo, significa trabajo extra.


Ahora que lo pienso de ella, Hibernate sesiones suelen ser de hilo atado y esto anula al menos uno de los beneficios de los campos finales - publicación segura.

¿Qué otros beneficios de los campos finales está buscando?

+1

+1: Bonita idea sobre el uso de una clase contenedora: parece que sería una buena solución cuando se necesitan clases/campos finales. Incluso se podría evitar la ambigüedad sobre qué versión (mutable vs. wrapper) se debería usar externamente haciendo que la versión mutable sea visible solo para las clases de acceso a datos. En cuanto a la utilización final, mi razón principal es que a menudo es más fácil garantizar (en lugar de suponer) que cada campo se asigna exactamente una vez en la construcción. Tenemos una gran cantidad de datos que deben ser constantes durante toda la vida de la JVM que los utiliza, por lo que es útil aclarar esto. –

+1

Los campos finales evitan que un usuario asigne accidentalmente algo que no debería tener e introduce un error sutil. Si sabes que algo nunca cambiará, querrás que sea definitivo. En el futuro, si descubres que debería cambiar, siempre puedes eliminar esa restricción. – corsiKa

13

Objeto inmutable significa un objeto sin métodos que modifican su estado (es decir, sus campos). Los campos no necesitan ser finales. Por lo tanto, puede eliminar todos los mutadores y configurar Hibernate para usar campos de acceso en lugar de accesos o simplemente puede marcar el constructor no-arg y los mutadores como obsoletos. Es un poco de solución, pero es mejor que nada.

+4

+1: Gracias - esto también es útil (eliminar instaladores y usar el acceso basado en el campo) para los casos donde no es posible/práctico hacer una clase contenedora. No se puede decir que a mí demasiado encantados con el tipo de reflexión Kung-Fu que está pasando detrás de las escenas, aunque - campos privados que no están realmente privado y todas. Me hace sentir que debería agregar algo de la etiqueta @ReadTheHibernateBookBeforeUsingThisClass para los usuarios (como yo) que no están tan familiarizados con los detalles de cómo funciona Hibernate. –

2

Puede lograr el resultado deseado utilizando un patrón de generador. Leí un posting hace un tiempo en los foros de Hibernate discutiendo la idea (aunque nunca lo implementé yo mismo ...)

4

Esta me ha estado molestando durante mucho tiempo también.Una idea que he estado probando recientemente es esta: defina interfaces de solo lectura para sus clases de modelo y haga que sus DAO y cualquier fábrica devuelvan esas en su lugar al objeto. Eso significa que, aunque la implementación es mutable, una vez que se deja el objeto DAO/fábrica ya no se puede ajustar.

así:

public interface Grape { 
    public Color getColor(); 
} 

public class HibernateGrapeDao { 
    public Grape findGrape(int grapeId) { 
    HibernateGrape grape = hibernate.find(... 
    return grape; 
    } 
} 

class HibernateGrape implements Grape { 
.... 
} 

que incluso quieren mantener las clases que implementan paquete-privada para el paquete de DAO, para que nadie pueda jugar con ellos directamente. Un poco más de trabajo, pero probablemente ayude a mantener las cosas más limpias a largo plazo. Y, obviamente, tenga cuidado con todo el negocio de igualdad/identidad.

10

realidad en el JDK 1.5 + Hibernate puede manejar (a través de la reflexión) cambiando campos finales. Cree un constructor predeterminado protegido() que establezca los campos como predeterminados/nulos, etc ... Hibernate puede anular esos valores cuando crea una instancia del objeto.

Todo esto es posible gracias a los cambios en el modelo de Java 1.5 de memoria - los cambios de interés (permitiendo final a no ser tan final), donde hizo para permitir la serialización/deserialización.

public class Event { 

private final Long id; 
private final String title; 
private final Date date; 

// Purely for serialization/deserialization 
protected Event() { 
    id = null; 
    title = null; 
    date = null; 
} 

public Event(Long id, String title, Data date) { 
    this.id = id; 
    this.title = title; 
    this.date = date; 
} 

/* Accessor methods... */ 

}

0

Anote en su clase con @Access (AccessType.FIELD), entonces usted puede hacer sus campos final. De esta manera:

@Access(AccessType.FIELD) 
public final class Event { 

    private final Long id; 
    private final String title; 
    private final Date date; 

    private Event() { 
     id = null; 
     title = null; 
     date = null; 
    } 

    public Event(Long id, String title, Date date) { 
     this.id = id; 
     this.title = title; 
     this.date = date; 
    } 
} 
Cuestiones relacionadas