2009-11-29 12 views
7

¿Hay alguna manera de usar objetos Flyweight con el mapeo de persistencia en hibernación? Mi modelo de datos contiene muchos objetos que serán iguales. En lugar de tener una instancia separada para cada uno de esos mismos objetos, me gustaría utilizar el patrón de diseño Flyweight y hacer referencia siempre al mismo objeto físico. ¿Cómo lograr esto en Hibernate?Hibernar y Volar

Btw. ¿optimizan todas las JVM el uso de cadenas de forma que cuando se usa la misma cadena varias veces, siempre sea la misma instancia física?

Respuesta

3

Depende.

Para readonly valores puede implementar fácilmente un patrón flyweight creando un UserType personalizado que devolverá objetos de un grupo en lugar de nuevas instancias cada vez.

Para las entidades Hibernate es, por definición, sensato y quiere ser coherente en todas las transacciones y no compartirá entidades entre Sesiones para evitar condiciones de carrera de sus datos, y no creo que eso sea lo que desea.

Pero en caso de que sea (y esto es totalmente no recomendado sin saber realmente lo que está haciendo) puede implementar Interceptor.getEntity() que está destinado para el segundo nivel de almacenamiento en caché. En ese método, puede devolver una entidad (incluso algunas compartidas por otras sesiones) y efectivamente tendrá un patrón flyweight para sus entidades.

PERO no recomiendo esto para la coherencia de sus datos - mucho mejor tener valores de peso mosca inmutables reales referenciados por las entidades que también intentar y sobreponderar las entidades reales.

+0

El tipo de usuario es una buena idea para mapear el estado común de las instancias flyweight. (Hibernate y JDBC todavía iniciarán una gran cantidad de instancias. No hay forma de evitarlo. Al menos serán recolección de basura con bastante rapidez). ¿Podría un CompositeUserType representar el estado común completo en un objeto? –

+0

Sí, un tipo de usuario compuesto probablemente podría hacer eso, pero ¿por qué tener la entidad como entidad en primer lugar? Solo tiene que ser un objeto de valor (componente) desde el principio. –

0

Si sus objetos implementan igualdad por identidad, Hibernate solo tendrá la única instancia asociada con esa clave principal. No creo que sea exactamente la misma idea que Flyweight, pero el hecho es que no tendrás muchas instancias del mismo objeto Hibernate.

2

Sí, puede implementar el patrón Flyweight con Hibernate.

El patrón flyweight es una forma de minimizar el uso de la memoria por instancia. La estrategia es compartir tanto estado como sea posible entre las instancias flyweight. En su caso, el estado compartible es todo excepto el identificador de objeto de hibernación y algún estado adicional para mantener la identidad del objeto.

Cada instancia de flyweight necesita su propia object identity. El estado adicional es la forma de implementar identidad para distinguir entre objetos que comparten estado común.

public boolean equals(Object obj){ 
    Fly other; [..]//check type 
    //null checks ommitted 
    return other.myState.equals(myState) && other.commonState.equals(commonState); 
} 

Si la identidad del objeto es compartida entre instancias de hibernación interpretaría todas las instancias físicas (Referencias) como la misma instancia. Hibernate usa el método equals para verificar la identidad del objeto y su implementación equitativa debería devolver (! a.equals(a) == true) que es ilegal. Equal tiene que ser reflexivo. Si rompe este contrato, todas las bibliotecas que dependen del contrato se romperán (colecciones, hibernación, etc.).

No se puede implementar el método igual utilizando el identificador de objeto de hibernación para distinguir entre objetos. Esto haría que la identidad del objeto dependiera del estado de persistencia (persistente o transitorio).

Una forma de modelar el estado común en hibernación es una asociación uno a muchos entre objetos de estado compartido y objetos flyweight. (Tal vez alguien tiene una idea de cómo mapear los datos sin unir dos tablas?)

Cadena: Solo internalized strings serán compartidos. Esta no es la mejor solución la mayor parte del tiempo. Es apropiado para símbolos (nombre de clase, nombre del método, etc.). Las cadenas internalizadas nunca se recolectarán por basura y debe tener una instancia de String que se recolectará de todos modos new String("..").intern(). No guardará las asignaciones. Solo existe la pequeña ventaja de que la cadena base no sobrevivirá a una generación de gc o podría asignarse en la pila (con el análisis de escape habilitado y aplicable en la zona activa).

1

hacer todas las JVM optimizan el uso de cadenas de forma que cuando se utiliza la misma cadena varias veces, siempre será la misma instancia física?

Lo dudo mucho.En el mismo archivo de clase, cuando se define como:

String s1 = "Yes"; 
String s2 = "Yes"; 

es probable que tenga s1 == s1.

Pero si tiene como:

String x = loadTextFromFile(); // file contains es 
StringBuilder b = new StringBuilder(); 
s2 = b.append("Y").append(x).toString(); // s2 = "Yes" 

No creo que el tiempo de ejecución se va a comprobar y comparar todas las cuerdas cargadas a el valor de retorno de ellos constructor.

Por lo tanto, siempre compare objetos con iguales(). Eso es un buen consejo de todos modos ya que todos los buenos iguales comienzan con:

if (this == o) { 
    return true; 
} 
+0

No se trata de comparación, sino más bien de consumo de memoria. Como menciona Thomas Jung, puede usar el método String.intern() para obtener el String subyacente. ¡Pruébalo! – paweloque

+0

Lo que quería decir es que si un valor de cadena no se conoce en tiempo de compilación, o no se puede precalcular, el objeto será diferente (no ==) incluso si tiene, por coincidencia, el mismo valor. – extraneon