2009-07-17 8 views
15

Il y a beaucoup de choses que l'on peut trouver sur ce googling un peu mais je n'ai pas encore trouvé une solution réalisable à ce problème.Paresseusement charger un clob en hibernation

Fondamentalement, ce que j'ai est un gros CLOB sur une classe particulière que je veux avoir chargé à la demande. La façon naïve de le faire serait:

class MyType { 

    // ... 

    @Basic(fetch=FetchType.LAZY) 
    @Lob 
    public String getBlob() { 
    return blob; 
    } 
} 

qui ne fonctionne pas bien, apparemment en raison du fait que je utilise les pilotes d'oracle, à savoir des objets Lob ne sont pas traités comme des poignées simples mais sont toujours chargées. Ou alors j'ai été amené à croire de mes incursions. Il y a une solution qui utilise une instrumentation spéciale pour le chargement des propriétés paresseuses, mais comme les docs Hibernate semblent suggérer qu'ils ne sont pas intéressés à faire ce travail correctement, je préfère ne pas suivre cette voie. Surtout avec devoir exécuter une passe de compilation supplémentaire et tout. Donc la solution suivante que j'avais envisagée était de séparer cet objet à un autre type et de définir une association. Malheureusement, alors que les docs donnent des informations contradictoires, il est évident pour moi que le chargement différé ne fonctionne pas avec les associations OneToOne avec une clé primaire partagée. Je définirais un côté de l'association comme ManyToOne, mais je ne suis pas sûr de savoir comment le faire quand il y a une clé primaire partagée.

Donc, quelqu'un peut-il suggérer la meilleure façon de s'y prendre?

+0

Pourriez-vous préciser pourquoi cela ne fonctionne pas avec Oracle? – skaffman

+0

Je me suis étoffé un peu, je ne sais pas exactement quel est le problème exact (un peu vague sur les détails). Si le problème est avec le mapping, pourriez-vous en donner un qui devrait charger le clob correctement? – wds

+0

Si vous l'avez essayé avec Oracle et qu'il a échoué, comment cela a-t-il échoué? – skaffman

Répondre

5

Selon this seul PostgreSQL implémente Blob comme vraiment paresseux. Donc, la meilleure solution est de déplacer le blob à une autre table. Avez-vous besoin d'utiliser une clé primaire partagée? Pourquoi ne pas faire quelque chose comme ceci:

public class MyBlobWrapper { 
    @Id 
    public Long getId() { 
     return id; 
    } 
    @Lob 
    public String getBlob() { 
     return blob; 
    } 
    @OneToOne(fetch=FetchType.LAZY,optional=false) 
    public MyClass getParent() { 
     return parent; 
    } 
} 
+0

Le blob est juste un champ dans la même table donc oui, il faut utiliser une clé primaire partagée. Votre approche fonctionne tant que optional = "false" est défini du côté propriétaire. Je suppose que ça va se casser horriblement quand l'objet est nul? – wds

+0

D'oh. Bien sûr, il ne sera jamais nul puisque la clé primaire existe. Ce sera juste le champ de clob qui est nul. Un peu d'un thinko là, merci. :-) – wds

+0

Ceci est le mappage nécessaire du côté parent btw (MyType). vous pourriez vouloir l'inclure dans votre réponse: @OneToOne (fetch = FetchType.LAZY, optional = false) – wds

4

Au lieu de faire équilibrisme avec annotations mise en veille prolongée, on peut juste essayer convertir le champ de String en Clob (ou Blob):

@Lob 
@Basic(fetch=FetchType.LAZY) 
@Column(name = "FIELD_COLUMN") 
public Clob getFieldClob() { 
    return fieldClob; 
} 

public void setFieldClob(Clob fieldClob) { 
    this.fieldClob = fieldClob; 
} 

@Transient 
public String getField() 
{ 
    if (this.getFieldClob()==null){ 
    return null; 
    } 
    try { 
    return MyOwnUtils.readStream(this.getFieldClob().getCharacterStream()); 
    } catch (Exception e) { 
    e.printStackTrace(); 
    } 

    return null; 
} 

public void setField(String field) 
{ 
    this.fieldClob = Hibernate.createClob(field); 
} 

Travaillait pour le moi (le champ a commencé à charger paresseusement, sur Oracle).

+0

cela cause le champ à charger paresseusement? D'après ce que je peux dire cela ajoute juste un getter/setter de commodité pour accéder au lob comme une chaîne? –

+0

@BenGeorge Notez que l'annotation Lob est placée sur un getter Clob dans ma réponse, par opposition à un getter String dans la question. Et je n'utilise pas le champ Clob directement, mais seulement via un getter transitoire. Attention: la version d'Hibernate sur laquelle j'ai essayé ça, était, si je me souviens bien, 3.2. Pour vérifier que le chargement était en effet paresseux, j'ai utilisé Wireshark pour inspecter le trafic de/vers la base de données. –

3

Puisque vous semblez utiliser Hibernate je me demande si votre problème est lié à la fonction Veille prolongée suivant:

Using Lazy Properties Fetching

Hibernate3 supporte le chargement paresseux des propriétés individuelles. Cette technique d'optimisation est également connue sous le nom de groupes de récupération. S'il vous plaît noter que c'est principalement une caractéristique de marketing; l'optimisation des lectures de lignes est beaucoup plus importante que l'optimisation des lectures de colonnes. Cependant, charger seulement certaines propriétés d'une classe pourrait être utile dans des cas extrêmes. Par exemple, lorsque les tables héritées ont des centaines de colonnes et que le modèle ne peut pas être amélioré.

La propriété paresseuse chargement nécessite l'instrumentation bytecode de construction. Si vos classes persistantes ne sont pas améliorées, Hibernate ignorera les paramètres de propriété paresseux et retournera à la récupération immédiate.

Voir Bytecode Instrumentation for Hibernate Using Maven.

0

Ancien message, mais un seul qui m'a aidé, grâce à la réponse @TadeuszKopec.

On dirait qu'il est difficile de faire un chargement paresseux de blob avec JPA. J'ai essayé l'association @OneToOne, mais cela complique plus que l'aide. Je viens déplacé les octets à une autre classe, sans association avec MyClass (parent Même table, même id.):

@Entity 
@Table(name="MyTable") 
public class MyBlobWrapper{ 

    @Id 
    @Column(name = "id") // id of MyTable, same as MyClass 
    private Long id; 

    @Lob 
    private byte[] bytes; 
} 

@Entity 
@Table(name="MyTable") 
public class MyClass{ 

    @Id 
    @Column(name = "id") 
    private Long id; 
    // other fields ..... 
} 

n'oubliez pas de parent rincer, avant d'enregistrer le blob:

em.persist(parent); 
em.flush(); 
em.merge(new MyBlobWrapper(parent_id,new byte[1000])); 

Maintenant, je peux charger le pdf seul:

String query1 = " select PDF from MyBlobWrapper PDF where PDF.id = :id"; 

Je suis juste débutant avec JPA, espérons que cela aide.

Questions connexes