Tutoriel pour apprendre à stocker en Blob ou OrdImage des images avec Hibernate via les UserType

Image non disponible

Cet article présente la manipulation d'images par Hibernate grâce aux UserType. Les images seront stockées dans des BLOB ou des OrdImage et les objets Java manipulés seront des java.awt.Image.

Une discussion a été ouverte pour les commentaires sur la publication de cet article : [1 commentaire Donner une note à l'article (5)]

Article lu   fois.

L'auteur

HomeViadeoLinkedIn

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. INTRODUCTION

I-A. Objectif

L'objectif de cet article est de présenter des solutions pour la manipulation d'images par Hibernate. Les objets Java java.awt.Image seront mappés soit avec des BLOB, soit avec des OrdImage.

I-B. Les « UserType »

Les « UserType » sont présentés dans la page sur les Types de la documentation d'Hibernate.

Ils permettent d'associer une classe Java et un ou des type(s) SQL (plusieurs types si la classe se convertit en plusieurs colonnes en base), et d'implémenter les méthodes permettant de réaliser les conversions. Les méthodes importantes d'« UserType » sont :

  • Class returnedClass() : indique la classe de l'objet retourné par la méthode nullSafeGet ;
  • int[] sqlTypes() : indique les types SQL qui peuvent être convertis ;
  • Object nullSafeGet(ResultSet rs, String[] names, SessionImplementor session, Object owner) : utilise les valeurs des colonnes names de rs pour instancier un objet de la classe indiquée par la méthode returnedClass() ;
  • void nullSafeSet(PreparedStatement st, Object value, int index, SessionImplementor session) : convertit value afin d'indiquer les informations dans st à partir de la position index.

I-C. Déroulement de l'article

Le schéma Oracle utilisé s'appelle « HIBERNATE_IMAGE ».

Hibernate permet de mapper en « automatique » des objets Java qui dans certains cas n'ont pas de correspondance directe dans la base de données (par exemple : les Boolean). Nous verrons donc, dans un premier tempsCREATION DDL A PARTIR D'UN OBJET IMAGE si Hibernate est capable de mapper sans « aide » un java.awt.Image en utilisant la génération de la table, afin de voir quel sera le type de la colonne s'il réussit.

Ensuite, nous verrons comment mapper un java.awt.Image avec un BLOB et ensuite avec un OrdImage grâce aux UserType.

I-D. Ressources

I-E. Versions des logiciels et bibliothèques

Pour la réalisation de cet article, voici les versions des logiciels et bibliothèques utilisés :

I-F. Bibliothèques nécessaires

Les bibliothèques nécessaires sont :

  • celles contenues dans le dossier « required » de la distribution d'Hibernate ;
  • celles de Junit et Hamcrest ;
  • celle d'Apache Log4J ;
  • les bibliothèques « slf4j-api-1.7.6.jar » et « slf4j-log4j12-1.7.6.jar » de la distribution de Simple Logging Facade for Java ;
  • le driver JDBC d'Oracle : « ojdbc6.jar » ;
  • pour la gestion des OrdImage (voir l'article Gestion avancée d'image sous Oracle avec Java (ORDImage) de Benoît Maréchal) : la bibliothèque « ordim.jar » contenue dans le sous-dossier « ord/jlib/ » de l'installation d'Oracle.

I-G. Images utilisées dans l'article

Les images utilisées dans cet article sont :

Image non disponible
logo_hibernate.png
Image non disponible
logo_dvp.png

II. CRÉATION DDL À PARTIR D'UN OBJET IMAGE

Dans un projet avec les bibliothèques indiquées au paragraphe Biliothèques nécessairesBibliothèques nécessaires, nous allons créer les fichiers de configuration « hibernate.cfg.xml » et « log4j.xml » ainsi que les classes « ImageAuto », « AbstractDAO », « ImageAutoDAO » et « ImageAutoTest ».

Le fichier de configuration « hibernate.cfg.xml » indique que Hibernate est paramétré pour créer la table à partir de la classe « ImageAuto ». La classe « ImageAuto » contient un champ de type « java.awt.Image ». La classe « ImageAutoDAO » hérite de « AbstractDAO » et doit permettre de réaliser les opérations sur l'entité « ImageAuto ». La classe « ImageAutoTest » permet de réaliser des tests sur la DAO.

Ainsi en exécutant les tests « ImageAutoTest », nous verrons si Hibernate réussit à mapper ce champ de type « java.awt.Image ».

II-A. Fichier hibernate.cfg.xml

Il s'agit d'un fichier de configuration simple. On note cependant que pour voir comment Hibernate s'en sort, on recrée le schéma de la base de données à chaque lancement, et l'on déclare une seule entité dans le mapping.

hibernate.cfg.xml
Sélectionnez
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- Database connection settings -->
        <property name="connection.driver_class">oracle.jdbc.OracleDriver</property>
        <property name="connection.url">jdbc:oracle:thin:@localhost:1521:TEST</property>
        <property name="connection.username">HIBERNATE_IMAGE</property>
        <property name="connection.password">HIBERNATE_IMAGE</property>
        
        <!-- Pool de connection (interne) -->
        <property name="connection.pool_size">1</property>
        
        <!-- SQL Dialect -->
        <property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
        
        <!-- Supprimer et recréer le schéma de base de données au démarrage -->
        <property name="hbm2ddl.auto">create</property>
        
        <mapping class="com.developpez.rpouiller.hibernateimages.entites.ImageAuto"/>
    </session-factory>
</hibernate-configuration>

II-B. Fichier log4j.xml

Voici le fichier de configuration « log4j.xml ».

log4j.xml
Sélectionnez
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE log4j:configuration SYSTEM "log4j.dtd" >
<log4j:configuration>
    <appender name="stdout" class="org.apache.log4j.ConsoleAppender">
        <layout class="org.apache.log4j.PatternLayout">
            <param name="ConversionPattern" value="%d{HH:mm:ss} %-5p %c{1}(%M):%L %m %n" />
        </layout>
    </appender>

    <!--sets the default priority log level -->
    <root>
        <priority value="info"></priority>
        <appender-ref ref="stdout" />
    </root>
</log4j:configuration>

II-C. Classe ImageAuto

Voici une entité avec une java.awt.Image où nous attendons « naïvement » d'Hibernate qu'il réussisse « automatiquement » à la convertir en base.

ImageAuto.java
Sélectionnez
package com.developpez.rpouiller.hibernateimages.entites;

import java.awt.Image;

import javax.persistence.Entity;
import javax.persistence.Id;

@Entity
public class ImageAuto {

    @Id
    private Integer id;
    private String libelle;
    private Image image;

    public Integer getId() {
        return id;
    }

    public void setId(final Integer pId) {
        id = pId;
    }

    public String getLibelle() {
        return libelle;
    }

    public void setLibelle(final String pLibelle) {
        libelle = pLibelle;
    }

    public Image getImage() {
        return image;
    }

    public void setImage(final Image pImage) {
        image = pImage;
    }
}

II-D. Classe AbstractDAO

Lors du chargement de la classe « AbstractDAO », la configuration sera chargée depuis le fichier « hibernate.cfg.xml ».

AbstractDAO.java
Sélectionnez
package com.developpez.rpouiller.hibernateimages.dao;

import org.apache.log4j.Logger;
import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.boot.registry.StandardServiceRegistryBuilder;
import org.hibernate.cfg.Configuration;
import org.hibernate.service.ServiceRegistry;

public class AbstractDAO {

    private static final Logger LOGGER = Logger.getLogger(AbstractDAO.class);
    private static final SessionFactory SESSION_FACTORY;

    static {
        try {
            final Configuration lConfiguration = new Configuration();
            lConfiguration.configure();
            final StandardServiceRegistryBuilder lStandardServiceRegistryBuilder = new StandardServiceRegistryBuilder();
            lStandardServiceRegistryBuilder.applySettings(lConfiguration.getProperties());
            final ServiceRegistry lServiceRegistry = lStandardServiceRegistryBuilder.build();
            SESSION_FACTORY = lConfiguration.buildSessionFactory(lServiceRegistry);
        } catch (final Throwable lThrowable) {
            LOGGER.error("Erreur lors de l'initialisation DAO", lThrowable);
            throw lThrowable;
        }
    }

    protected Session ouvrirTransaction() {
        final Session lSession = SESSION_FACTORY.openSession();
        lSession.beginTransaction();
        return lSession;
    }

    protected void fermerTransaction(final Session pSession) {
        pSession.getTransaction().commit();
        pSession.close();
    }
}

II-E. Classe ImageAutoDAO

ImageAutoDAO.java
Sélectionnez
package com.developpez.rpouiller.hibernateimages.dao;

import org.hibernate.Query;
import org.hibernate.Session;

import com.developpez.rpouiller.hibernateimages.entites.ImageAuto;

public class ImageAutoDAO extends AbstractDAO {

    /**
     * Vide la table de ImageAuto.
     */
    public void purgerImageAuto() {
        final Session lSession = ouvrirTransaction();
        final Query lQuery = lSession.createQuery(
                "delete from " + ImageAuto.class.getCanonicalName());
        lQuery.executeUpdate();
        fermerTransaction(lSession);
    }
}

II-F. Classe ImageAutoTest

Les tests sont vides. Nous allons simplement voir comment se comporte Hibernate. En effet lors du lancement du test, la JVM enchaîne le chargement de la classe « ImageAutoDAO » puis de « AbstractDAO » et de la configuration d'Hibernate.

ImageAutoTest.java
Sélectionnez
package com.developpez.rpouiller.hibernateimages.test;

import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;

import com.developpez.rpouiller.hibernateimages.dao.ImageAutoDAO;

public class ImageAutoTest {

    protected static final Logger LOGGER = Logger.getLogger(ImageAutoTest.class);
    private ImageAutoDAO dao = new ImageAutoDAO();

    @Before
    public void initialiser() {
        LOGGER.info("**************************************************************************");
        LOGGER.info("Purger les occurrences");
        LOGGER.info("**************************************************************************");
        dao.purgerImageAuto();
    }

    @Test
    public void test() {

    }
}

II-G. Trace d'exécution

L'exécution de la classe « ImageAutoTest » donne le résultat ci-dessous. On peut remarquer qu'Hibernate n'arrive pas à réaliser un mapping « automatique ». Nous allons donc l'aider dans les chapitres suivants avec des UserType.

Trace d'exécution en mapping « automatique »
Sélectionnez
21:00:25 INFO  Version(<clinit>):66 HCANN000001: Hibernate Commons Annotations {4.0.4.Final} 
21:00:25 INFO  Version(logVersion):54 HHH000412: Hibernate Core {4.3.5.Final} 
21:00:25 INFO  Environment(<clinit>):239 HHH000206: hibernate.properties not found 
21:00:25 INFO  Environment(buildBytecodeProvider):346 HHH000021: Bytecode provider name : javassist 
21:00:25 INFO  Configuration(configure):2073 HHH000043: Configuring from resource: /hibernate.cfg.xml 
21:00:25 INFO  Configuration(getConfigurationInputStream):2092 HHH000040: Configuration resource: /hibernate.cfg.xml 
21:00:26 INFO  Configuration(doConfigure):2214 HHH000041: Configured SessionFactory: null 
21:00:26 WARN  DriverManagerConnectionProviderImpl(configure):93 HHH000402: Using Hibernate built-in connection pool (not for production use!) 
21:00:26 INFO  DriverManagerConnectionProviderImpl(buildCreator):166 HHH000401: using driver [oracle.jdbc.OracleDriver] at URL [jdbc:oracle:thin:@localhost:1521:TEST] 
21:00:26 INFO  DriverManagerConnectionProviderImpl(buildCreator):175 HHH000046: Connection properties: {user=HIBERNATE_IMAGE, password=****} 
21:00:26 INFO  DriverManagerConnectionProviderImpl(buildCreator):180 HHH000006: Autocommit mode: false 
21:00:26 INFO  DriverManagerConnectionProviderImpl(configure):102 HHH000115: Hibernate connection pool size: 1 (min=1) 
21:00:27 INFO  Dialect(<init>):145 HHH000400: Using dialect: org.hibernate.dialect.Oracle10gDialect 
21:00:27 ERROR AbstractDAO(<clinit>):24 Erreur lors de l'initialisation DAO 
org.hibernate.MappingException: Could not determine type for: java.awt.Image, at table: ImageAuto, for columns: [org.hibernate.mapping.Column(image)]
    at org.hibernate.mapping.SimpleValue.getType(SimpleValue.java:336)
    at org.hibernate.mapping.SimpleValue.isValid(SimpleValue.java:310)
    at org.hibernate.mapping.Property.isValid(Property.java:241)
    at org.hibernate.mapping.PersistentClass.validate(PersistentClass.java:496)
    at org.hibernate.mapping.RootClass.validate(RootClass.java:270)
    at org.hibernate.cfg.Configuration.validate(Configuration.java:1358)
    at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1849)
    at com.developpez.rpouiller.hibernateimages.dao.AbstractDAO.<clinit>(AbstractDAO.java:22)
    at com.developpez.rpouiller.hibernateimages.test.ImageAutoTest.<init>(ImageAutoTest.java:12)
    .....

III. MANIPULATION D'UN BLOB

Nous allons modifier le fichier de configuration « hibernate.cfg.xml » afin qu'il prenne en compte la classe « ImageBlob ». Nous allons créer cette dernière ainsi que la classe de UserType « ImageBlobUserType » qui lui est associée.

Ensuite nous créerons la classe « ImageBlobDAO » et la classe de test « ImageBlobTest » afin d'insérer un enregistrement et le lire (ce qui donne la trace visible au III-GTrace d'exécution de l'insertion/lecture). Finalement nous ajouterons un autre test afin de modifier un enregistrement précédemment créé pour le lire ensuite (voir la trace au III-JTrace d'exécution de la modification/lecture).

III-A. Script de création de la table

Requête de création de la table IMAGE_BLOB
Sélectionnez
CREATE TABLE IMAGE_BLOB
(
  ID       NUMBER(6) PRIMARY KEY,
  LIBELLE  VARCHAR2(20),
  IMAGE    BLOB
);

III-B. Fichier hibernate.cfg.xml

hibernate.cfg.xml
Sélectionnez
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- Database connection settings -->
        <property name="connection.driver_class">oracle.jdbc.OracleDriver</property>
        <property name="connection.url">jdbc:oracle:thin:@localhost:1521:TEST</property>
        <property name="connection.username">HIBERNATE_IMAGE</property>
        <property name="connection.password">HIBERNATE_IMAGE</property>
        
        <!-- Pool de connection (interne) -->
        <property name="connection.pool_size">1</property>
        
        <!-- SQL Dialect -->
        <property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
        
        <!-- Montrer toutes les requêtes générées-->
        <property name="show_sql">true</property>
        <property name="format_sql">true</property>
        
        <mapping  class="com.developpez.rpouiller.hibernateimages.entites.ImageBlob"/>
    </session-factory>
</hibernate-configuration>

III-C. Classe ImageBlobUserType

Les méthodes importantes sont « returnedClass() » et « sqlTypes() » qui associent la classe Java et le type SQL; ainsi que « nullSafeGet(…) » et « nullSafeSet(…) » qui font la conversion en lecture et en écriture.

Il s'agit de la classe la plus importante de ce chapitre.

On remarque que « returnedClass() » indique que l'on travaille sur des « java.awt.Image » et « sqlTypes() » sur des « Types.LONGVARBINARY » (soit le type correspondant au Blob).

La méthode « nullSafeGet(…) » lit la colonne grâce à « ImageIO.read(…) » et retourne l'image récupérée.

La méthode « nullSafeSet(…) » écrit l'image grâce à « ImageIO.write((RenderedImage)…) » dans un « ByteArrayOutputStream » avant de passer le tableau de « byte » au « PreparedStatement ».

ImageBlobUserType.java
Sélectionnez
package com.developpez.rpouiller.hibernateimages.usertypes;

import java.awt.Image;
import java.awt.image.RenderedImage;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.Serializable;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import javax.imageio.ImageIO;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;
import org.jboss.logging.Logger;

public class ImageBlobUserType implements UserType {

    private static final Logger LOGGER = Logger.getLogger(ImageBlobUserType.class);

    @Override
    public Object assemble(final Serializable pCached, final Object pOwner) throws HibernateException {
        return pCached;
    }

    @Override
    public Object deepCopy(final Object pValue) throws HibernateException {
        return pValue;
    }

    @Override
    public Serializable disassemble(final Object pValue) throws HibernateException {
        return (Serializable) pValue;
    }

    @Override
    public boolean equals(final Object pObject0, final Object pObject1) throws HibernateException {
        if (pObject0 == pObject1) {
            return true;
        }
        if (null == pObject0 || null == pObject1) {
            return false;
        }
        return pObject0.equals(pObject1);
    }

    @Override
    public int hashCode(final Object pObject) throws HibernateException {
        return pObject.hashCode();
    }

    @Override
    public boolean isMutable() {
        return false;
    }

    @Override
    public Object nullSafeGet(final ResultSet pResultSet, final String[] pNames,
            final SessionImplementor pSession, final Object pOwner) throws HibernateException,
            SQLException {

        LOGGER.info(new StringBuilder("Lecture dans la colonne ").append(pNames[0]));
        final InputStream lInputStream = pResultSet.getBinaryStream(pNames[0]);
        Image lImage = null;
        try {
            lImage = ImageIO.read(lInputStream);
        }
        catch (final IOException lIOException) {
            LOGGER.error(lIOException.getLocalizedMessage(), lIOException);
        }
        return lImage;
    }

    @Override
    public void nullSafeSet(final PreparedStatement pStatement, final Object pValue,
            final int pIndex, final SessionImplementor pSession) throws HibernateException, SQLException {

        LOGGER.info(new StringBuilder("Enregistrement à l'index ").append(pIndex));
        if (pValue == null) {
            pStatement.setNull(pIndex, sqlTypes()[0]);
        }
        else {
            final java.awt.Image lImage = (java.awt.Image) pValue;
            final ByteArrayOutputStream lBos = new ByteArrayOutputStream();
            try {
                ImageIO.write((RenderedImage) lImage, "png", lBos);
            }
            catch (final IOException lIOException) {
                LOGGER.error(lIOException.getLocalizedMessage(), lIOException);
            }
            final ByteArrayInputStream lBis = new ByteArrayInputStream(lBos.toByteArray());
            pStatement.setBinaryStream(pIndex, lBis, lBos.toByteArray().length);
        }
    }

    @Override
    public Object replace(final Object pOriginal, final Object pTarget, final Object pOwner) throws HibernateException {
        return pOriginal;
    }

    @Override
    public Class<?> returnedClass() {
        return java.awt.Image.class;
    }

    @Override
    public int[] sqlTypes() {
        return new int[] {Types.LONGVARBINARY};
    }
}

III-D. Classe ImageBlob

Nous voyons que la classe entité comporte des annotations (« @TypeDefs », « @TypeDef » et « @Type ») afin d'indiquer à Hibernate quel UserType il doit utiliser pour la conversion.

ImageBlob.java
Sélectionnez
package com.developpez.rpouiller.hibernateimages.entites;

import java.awt.Image;

import javax.persistence.Entity;
import javax.persistence.Id;

import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;

import com.developpez.rpouiller.hibernateimages.usertypes.ImageBlobUserType;

@Entity(name="IMAGE_BLOB")
@TypeDefs(value={@TypeDef(name="imageBlob", typeClass=ImageBlobUserType.class)})
public class ImageBlob {

    @Id
    private Integer id;
    private String libelle;
    @Type(type="imageBlob")
    private Image image;

    // Méthodes getter et setter non représentées

}

III-E. Classe ImageBlobDAO

Nous créons une classe « ImageBlobDAO » afin d'implémenter les opérations sur les entités « ImageBlob ».

ImageBlobDAO.java
Sélectionnez
package com.developpez.rpouiller.hibernateimages.dao;

import java.awt.Image;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import org.apache.log4j.Logger;
import org.hibernate.Query;
import org.hibernate.Session;

import com.developpez.rpouiller.hibernateimages.entites.ImageBlob;

public class ImageBlobDAO extends AbstractDAO {

    protected static final Logger LOGGER = Logger.getLogger(ImageBlobDAO.class);

    /**
     * Vide la table IMAGE_BLOB.
     */
    public void purgerImageBlob() {
        final Session lSession = ouvrirTransaction();
        final Query lQuery = lSession.createQuery(
                "delete from " + ImageBlob.class.getCanonicalName());
        lQuery.executeUpdate();
        fermerTransaction(lSession);
    }

    /**
     * Cette méthode lit une image depuis un champ Blob.
     */
    public void lireImageBlob(final Integer pId, final File pFile)
            throws IOException {

        final Session lSession = ouvrirTransaction();
        LOGGER.info("Lecture de l'image");
        final ImageBlob lImageBlobFromBase = (ImageBlob) lSession.get(ImageBlob.class, pId);
        LOGGER.info(new StringBuilder("- Id : ").append(lImageBlobFromBase.getId()));
        LOGGER.info(new StringBuilder("- Libelle : ").append(lImageBlobFromBase.getLibelle()));
        ImageIO.write((RenderedImage) lImageBlobFromBase.getImage(), "png", pFile);
        fermerTransaction(lSession);
    }

    /**
     * Cette méthode insère une image dans un champ Blob.
     */
    public void insererImageBlob(final Integer pId, final String pLibelle,
            final File pFile) throws IOException {

        final Session lSession = ouvrirTransaction();
        LOGGER.info("Insertion de l'image");
        final Image lImageHibernate = ImageIO.read(pFile);
        final ImageBlob lImageBlob = new ImageBlob();
        lImageBlob.setId(pId);
        lImageBlob.setLibelle(pLibelle);
        lImageBlob.setImage(lImageHibernate);
        lSession.save(lImageBlob);
        fermerTransaction(lSession);
    }
}

III-F. Classe ImageBlobTest

La classe « ImageBlobTest » permet de tester les opérations d'insertion et de lecture de la DAO.

ImageBlobTest.java
Sélectionnez
package com.developpez.rpouiller.hibernateimages.test;

import java.io.File;
import java.io.IOException;

import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;

import com.developpez.rpouiller.hibernateimages.dao.ImageBlobDAO;

public class ImageBlobTest {

    private static final Logger LOGGER = Logger.getLogger(ImageBlobTest.class);
    private ImageBlobDAO dao = new ImageBlobDAO();

    @Before
    public void initialiser() {
        LOGGER.info("**************************************************************************");
        LOGGER.info("Purger les occurrences");
        LOGGER.info("**************************************************************************");
        dao.purgerImageBlob();
    }

    @Test
    public void testInsererLire() throws IOException {
        LOGGER.info("**************************************************************************");
        LOGGER.info("Insérer une image");
        LOGGER.info("**************************************************************************");
        dao.insererImageBlob(1, "logo_hibernate", new File("images\\logo_hibernate.png"));
        LOGGER.info("**************************************************************************");
        LOGGER.info("Lire l'image");
        LOGGER.info("**************************************************************************");
        dao.lireImageBlob(1, new File("logo_hibernate_from_base_blob.png"));
    }
}

III-G. Trace d'exécution de l'insertion/lecture

Nous voyons dans la trace, tout d'abord l'insertion (requête SQL insert) avec l'appel de la méthode « nullSafeSet(…) », suivie de la lecture (requête SQL select) avec l'appel de la méthode « nullSafeGet(…) ». La trace finit avec l'affichage des informations provenant de la base de données. Le fichier « logo_hibernate_from_base_blob.png » est visible dans le dossier du projet.

Trace d'exécution de l'insertion/lecture avec OrdImage
Sélectionnez
21:01:10 INFO  Version(<clinit>):66 HCANN000001: Hibernate Commons Annotations {4.0.4.Final} 
21:01:10 INFO  Version(logVersion):54 HHH000412: Hibernate Core {4.3.5.Final} 
21:01:10 INFO  Environment(<clinit>):239 HHH000206: hibernate.properties not found 
21:01:10 INFO  Environment(buildBytecodeProvider):346 HHH000021: Bytecode provider name : javassist 
21:01:10 INFO  Configuration(configure):2073 HHH000043: Configuring from resource: /hibernate.cfg.xml 
21:01:10 INFO  Configuration(getConfigurationInputStream):2092 HHH000040: Configuration resource: /hibernate.cfg.xml 
21:01:10 INFO  Configuration(doConfigure):2214 HHH000041: Configured SessionFactory: null 
21:01:10 WARN  DriverManagerConnectionProviderImpl(configure):93 HHH000402: Using Hibernate built-in connection pool (not for production use!) 
21:01:10 INFO  DriverManagerConnectionProviderImpl(buildCreator):166 HHH000401: using driver [oracle.jdbc.OracleDriver] at URL [jdbc:oracle:thin:@localhost:1521:TEST] 
21:01:10 INFO  DriverManagerConnectionProviderImpl(buildCreator):175 HHH000046: Connection properties: {user=HIBERNATE_IMAGE, password=****} 
21:01:10 INFO  DriverManagerConnectionProviderImpl(buildCreator):180 HHH000006: Autocommit mode: false 
21:01:10 INFO  DriverManagerConnectionProviderImpl(configure):102 HHH000115: Hibernate connection pool size: 1 (min=1) 
21:01:11 INFO  Dialect(<init>):145 HHH000400: Using dialect: org.hibernate.dialect.Oracle10gDialect 
21:01:11 INFO  TransactionFactoryInitiator(initiateService):62 HHH000399: Using default transaction strategy (direct JDBC transactions) 
21:01:11 INFO  ASTQueryTranslatorFactory(<init>):47 HHH000397: Using ASTQueryTranslatorFactory 
21:01:11 INFO  ImageBlobTest(initialiser):19 ************************************************************************** 
21:01:11 INFO  ImageBlobTest(initialiser):20 Purger les occurrences 
21:01:11 INFO  ImageBlobTest(initialiser):21 ************************************************************************** 
Hibernate: 
    delete 
    from
        IMAGE_BLOB
21:01:11 INFO  ImageBlobTest(testInsererLire):27 ************************************************************************** 
21:01:11 INFO  ImageBlobTest(testInsererLire):28 Insérer une image 
21:01:11 INFO  ImageBlobTest(testInsererLire):29 ************************************************************************** 
21:01:11 INFO  ImageBlobDAO(insererImageBlob):53 Insertion de l'image 
Hibernate: 
    insert 
    into
        IMAGE_BLOB
        (image, libelle, id) 
    values
        (?, ?, ?)
21:01:11 INFO  ImageBlobUserType(nullSafeSet):83 Enregistrement à l'index 1 
21:01:12 INFO  ImageBlobTest(testInsererLire):31 ************************************************************************** 
21:01:12 INFO  ImageBlobTest(testInsererLire):32 Lire l'image 
21:01:12 INFO  ImageBlobTest(testInsererLire):33 ************************************************************************** 
21:01:12 INFO  ImageBlobDAO(lireImageBlob):38 Lecture de l'image 
Hibernate: 
    select
        imageblob0_.id as id1_0_0_,
        imageblob0_.image as image2_0_0_,
        imageblob0_.libelle as libelle3_0_0_ 
    from
        IMAGE_BLOB imageblob0_ 
    where
        imageblob0_.id=?
21:01:12 INFO  ImageBlobUserType(nullSafeGet):67 Lecture dans la colonne image2_0_0_ 
21:01:12 INFO  ImageBlobDAO(lireImageBlob):40 - Id : 1 
21:01:12 INFO  ImageBlobDAO(lireImageBlob):41 - Libelle : logo_hibernate

III-H. Modification de la classe ImageBlobDAO

Nous ajoutons la méthode « modifierImageBlob(…) » à la classe « ImageBlobDAO ».

ImageBlobDAO.java
Sélectionnez
    /**
     * Cette méthode modifie une image dans un champ Blob.
     */
    public void modifierImageBlob(final Integer pId, final String pLibelle,
            final File pFile) throws IOException {

        final Session lSession = ouvrirTransaction();
        LOGGER.info("Modification de l'image");
        final Image lImageHibernate = ImageIO.read(pFile);
        final ImageBlob lImageBlob = new ImageBlob();
        lImageBlob.setId(pId);
        lImageBlob.setLibelle(pLibelle);
        lImageBlob.setImage(lImageHibernate);
        lSession.update(lImageBlob);
        fermerTransaction(lSession);
    }

III-I. Modification de la classe ImageBlobTest

Nous ajoutons également une méthode à la classe « ImageBlobTest » afin de tester la modification.

ImageBlobTest.java
Sélectionnez
    @Test
    public void testInsererModiferLire() throws IOException {
        LOGGER.info("**************************************************************************");
        LOGGER.info("Insérer une image");
        LOGGER.info("**************************************************************************");
        dao.insererImageBlob(1, "logo_hibernate", new File("images\\logo_hibernate.png"));
        LOGGER.info("**************************************************************************");
        LOGGER.info("Modifier une image");
        LOGGER.info("**************************************************************************");
        dao.modifierImageBlob(1, "logo_dvp", new File("images\\logo_dvp.png"));
        LOGGER.info("**************************************************************************");
        LOGGER.info("Lire l'image après modification");
        LOGGER.info("**************************************************************************");
        dao.lireImageBlob(1, new File("logo_dvp_from_base_blob.png"));
    }

III-J. Trace d'exécution de la modification/lecture

En lançant le test ci-dessus, nous voyons en plus la trace de modification (requête SQL update) avec l'appel de la méthode « nullSafeSet(…) ».

Trace d'exécution de l'insertion/lecture avec OrdImage
Sélectionnez
21:02:30 INFO  Version(<clinit>):66 HCANN000001: Hibernate Commons Annotations {4.0.4.Final} 
21:02:30 INFO  Version(logVersion):54 HHH000412: Hibernate Core {4.3.5.Final} 
21:02:30 INFO  Environment(<clinit>):239 HHH000206: hibernate.properties not found 
21:02:30 INFO  Environment(buildBytecodeProvider):346 HHH000021: Bytecode provider name : javassist 
21:02:30 INFO  Configuration(configure):2073 HHH000043: Configuring from resource: /hibernate.cfg.xml 
21:02:30 INFO  Configuration(getConfigurationInputStream):2092 HHH000040: Configuration resource: /hibernate.cfg.xml 
21:02:30 INFO  Configuration(doConfigure):2214 HHH000041: Configured SessionFactory: null 
21:02:30 WARN  DriverManagerConnectionProviderImpl(configure):93 HHH000402: Using Hibernate built-in connection pool (not for production use!) 
21:02:30 INFO  DriverManagerConnectionProviderImpl(buildCreator):166 HHH000401: using driver [oracle.jdbc.OracleDriver] at URL [jdbc:oracle:thin:@localhost:1521:TEST] 
21:02:30 INFO  DriverManagerConnectionProviderImpl(buildCreator):175 HHH000046: Connection properties: {user=HIBERNATE_IMAGE, password=****} 
21:02:30 INFO  DriverManagerConnectionProviderImpl(buildCreator):180 HHH000006: Autocommit mode: false 
21:02:30 INFO  DriverManagerConnectionProviderImpl(configure):102 HHH000115: Hibernate connection pool size: 1 (min=1) 
21:02:31 INFO  Dialect(<init>):145 HHH000400: Using dialect: org.hibernate.dialect.Oracle10gDialect 
21:02:31 INFO  TransactionFactoryInitiator(initiateService):62 HHH000399: Using default transaction strategy (direct JDBC transactions) 
21:02:31 INFO  ASTQueryTranslatorFactory(<init>):47 HHH000397: Using ASTQueryTranslatorFactory 
21:02:31 INFO  ImageBlobTest(initialiser):19 ************************************************************************** 
21:02:31 INFO  ImageBlobTest(initialiser):20 Purger les occurrences 
21:02:31 INFO  ImageBlobTest(initialiser):21 ************************************************************************** 
Hibernate: 
    delete 
    from
        IMAGE_BLOB
21:02:31 INFO  ImageBlobTest(testInsererModiferLire):39 ************************************************************************** 
21:02:31 INFO  ImageBlobTest(testInsererModiferLire):40 Insérer une image 
21:02:31 INFO  ImageBlobTest(testInsererModiferLire):41 ************************************************************************** 
21:02:31 INFO  ImageBlobDAO(insererImageBlob):53 Insertion de l'image 
Hibernate: 
    insert 
    into
        IMAGE_BLOB
        (image, libelle, id) 
    values
        (?, ?, ?)
21:02:31 INFO  ImageBlobUserType(nullSafeSet):83 Enregistrement à l'index 1 
21:02:32 INFO  ImageBlobTest(testInsererModiferLire):43 ************************************************************************** 
21:02:32 INFO  ImageBlobTest(testInsererModiferLire):44 Modifier une image 
21:02:32 INFO  ImageBlobTest(testInsererModiferLire):45 ************************************************************************** 
21:02:32 INFO  ImageBlobDAO(modifierImageBlob):70 Modification de l'image 
Hibernate: 
    update
        IMAGE_BLOB 
    set
        image=?,
        libelle=? 
    where
        id=?
21:02:32 INFO  ImageBlobUserType(nullSafeSet):83 Enregistrement à l'index 1 
21:02:35 INFO  ImageBlobTest(testInsererModiferLire):47 ************************************************************************** 
21:02:35 INFO  ImageBlobTest(testInsererModiferLire):48 Lire l'image après modification 
21:02:35 INFO  ImageBlobTest(testInsererModiferLire):49 ************************************************************************** 
21:02:35 INFO  ImageBlobDAO(lireImageBlob):38 Lecture de l'image 
Hibernate: 
    select
        imageblob0_.id as id1_0_0_,
        imageblob0_.image as image2_0_0_,
        imageblob0_.libelle as libelle3_0_0_ 
    from
        IMAGE_BLOB imageblob0_ 
    where
        imageblob0_.id=?
21:02:35 INFO  ImageBlobUserType(nullSafeGet):67 Lecture dans la colonne image2_0_0_ 
21:02:36 INFO  ImageBlobDAO(lireImageBlob):40 - Id : 1 
21:02:36 INFO  ImageBlobDAO(lireImageBlob):41 - Libelle : logo_dvp

IV. MANIPULATION D'UN ORDIMAGE

Comme pour le IIIMANIPULATION D'UN BLOB, nous allons modifier le fichier de configuration « hibernate.cfg.xml » afin qu'il prenne en compte la classe « ImageOrdImage ». Nous allons également créer cette dernière ainsi que la classe de UserType « ImageOrdImageUserType » qui lui est associée.

Ensuite de la même manière, nous créerons la classe « ImageOrdImageDAO » et la classe de test « ImageOrdImageTest » afin d'insérer un enregistrement et le lire (ce qui donne la trace visible au IV-GTrace d'exécution de l'insertion/lecture). Finalement nous ajouterons un autre test afin de modifier un enregistrement précédemment créé pour le lire ensuite (voir la trace au IV-JTrace d'exécution de la modification/lecture).

IV-A. Script de création de la table

Requête de création de la table IMAGE_ORDIMAGE
Sélectionnez
CREATE TABLE IMAGE_ORDIMAGE
(
  ID       NUMBER(6) PRIMARY KEY,
  LIBELLE  VARCHAR2(20),
  IMAGE    ORDSYS.ORDIMAGE
);

IV-B. Fichier hibernate.cfg.xml

hibernate.cfg.xml
Sélectionnez
<?xml version="1.0" encoding="utf-8"?>
<!DOCTYPE hibernate-configuration PUBLIC "-//Hibernate/Hibernate Configuration DTD 3.0//EN"
    "http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
    <session-factory>
        <!-- Database connection settings -->
        <property name="connection.driver_class">oracle.jdbc.OracleDriver</property>
        <property name="connection.url">jdbc:oracle:thin:@localhost:1521:TEST</property>
        <property name="connection.username">HIBERNATE_IMAGE</property>
        <property name="connection.password">HIBERNATE_IMAGE</property>
        
        <!-- Pool de connection (interne) -->
        <property name="connection.pool_size">1</property>
        
        <!-- SQL Dialect -->
        <property name="dialect">org.hibernate.dialect.Oracle10gDialect</property>
        
        <!-- Montrer toutes les requêtes générées-->
        <property name="show_sql">true</property>
        <property name="format_sql">true</property>
        
        <mapping  class="com.developpez.rpouiller.hibernateimages.entites.ImageBlob"/>
        <mapping class="com.developpez.rpouiller.hibernateimages.entites.ImageOrdImage"/>
    </session-factory>
</hibernate-configuration>

IV-C. Classe ImageOrdImageUserType

Cette classe ressemble à la classe « ImageBlobUserType » du III-CClasse ImageBlobUserType. Les différences sont principalement dans la méthode « nullSafeGet(…) » (ajout de la recherche de l'index de la colonne) et « nullSafeSet(…) » (appel d'un bloc SQL anonyme afin d'initialiser un objet OrdImage).

Comme pour « ImageBlobUserType », il s'agit de la classe la plus importante de ce chapitre.

On remarque que « returnedClass() » indique toujours que l'on travaille sur des « java.awt.Image » et « sqlTypes() » sur des « Types.STRUCT » (soit le type correspondant au OrdImage).

La méthode « nullSafeGet(…) » lit la colonne « ORAData » grâce à « ImageIO.read(…) » et retourne l'image récupérée.

La méthode « nullSafeSet(…) » est un peu plus compliquée. Il faut premièrement, récupérer un « OrdImage » initialisé par Oracle, grâce au code contenu dans la méthode « getOrdImage » (créée grâce aux informations de Init OrdImage object without inserting anything to DB). Après, il faut charger l'« OrdImage » en passant par un « ByteArrayOutputStream » et la méthode « loadDataFromByteArray() ». Finalement, on passe l'« OrdImage » au « PreparedStatement » après contrôle de l'« OrdImage » (méthodes « setProperties() » et « checkProperties() »).

ImageOrdImageUserType.java
Sélectionnez
package com.developpez.rpouiller.hibernateimages.usertypes;

import java.awt.Image;
import java.awt.image.RenderedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.Serializable;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Types;

import javax.imageio.ImageIO;

import oracle.jdbc.OracleCallableStatement;
import oracle.jdbc.OraclePreparedStatement;
import oracle.jdbc.internal.OracleResultSet;
import oracle.ord.im.OrdImage;
import oracle.ord.im.OrdImageBase;
import oracle.sql.ORAData;

import org.hibernate.HibernateException;
import org.hibernate.engine.spi.SessionImplementor;
import org.hibernate.usertype.UserType;
import org.jboss.logging.Logger;

public class ImageOrdImageUserType implements UserType {

    private static final Logger LOGGER = Logger.getLogger(ImageOrdImageUserType.class);

    @Override
    public Object assemble(final Serializable pCached, final Object pOwner) throws HibernateException {
        return pCached;
    }

    @Override
    public Object deepCopy(final Object pValue) throws HibernateException {
        return pValue;
    }

    @Override
    public Serializable disassemble(final Object pValue) throws HibernateException {
        return (Serializable) pValue;
    }

    @Override
    public boolean equals(final Object pObject0, final Object pObject1) throws HibernateException {
        if (pObject0 == pObject1) {
            return true;
        }
        if (null == pObject0 || null == pObject1) {
            return false;
        }
        return pObject0.equals(pObject1);
    }

    @Override
    public int hashCode(final Object pObject) throws HibernateException {
        return pObject.hashCode();
    }

    @Override
    public boolean isMutable() {
        return false;
    }

    private OrdImage getOrdImage(final Connection conn) throws SQLException {
        final OracleCallableStatement lStatement = (OracleCallableStatement) conn.prepareCall(
                "DECLARE"
                + "  IMG ORDSYS.ORDIMAGE;"
                + "BEGIN"
                + "  IMG := ORDImage.init();" // init an image
                + "  DBMS_LOB.CreateTemporary(IMG.source.localdata, TRUE);" // an empty BLOB for the image
                + "  ? := IMG;" // return both the image and its signature as values of output parameters
                + "END;");
        try {
            lStatement.registerOutParameter(1, OrdImageBase._SQL_TYPECODE, OrdImageBase._SQL_NAME);
            lStatement.executeUpdate();
            final OrdImage lOrdImage = (OrdImage) lStatement.getORAData(1, OrdImage.getORADataFactory());
            return lOrdImage;
        }
        finally {
            lStatement.close();
        }
    }

    @Override
    public Object nullSafeGet(final ResultSet pResultSet, final String[] pNames,
            final SessionImplementor pSession, final Object pOwner) throws HibernateException,
            SQLException {

        LOGGER.info(new StringBuilder("Lecture dans la colonne ").append(pNames[0]));
        final OracleResultSet lOracleResultSet = (OracleResultSet) pResultSet;
        final ORAData lORAData = lOracleResultSet.getORAData(pNames[0], OrdImage.getORADataFactory());
        final OrdImage lOrdImage = (OrdImage) lORAData;
        Image lImage = null;
        try {
            lImage = ImageIO.read(lOrdImage.getDataInStream());
        }
        catch (final IOException lIOException) {
            LOGGER.error(lIOException.getLocalizedMessage(), lIOException);
        }
        return lImage;
    }

    @Override
    public void nullSafeSet(final PreparedStatement pStatement, final Object pValue,
            final int pIndex, final SessionImplementor pSession) throws HibernateException, SQLException {

        LOGGER.info(new StringBuilder("Enregistrement à l'index ").append(pIndex));
        if (pValue == null) {
            pStatement.setNull(pIndex, sqlTypes()[0]);
        }
        else {
            final java.awt.Image lImage = (java.awt.Image) pValue;
            final OrdImage lOrdImage = getOrdImage(pStatement.getConnection());
            final ByteArrayOutputStream lBos = new ByteArrayOutputStream();
            try {
                ImageIO.write((RenderedImage) lImage, "png", lBos);
                lOrdImage.loadDataFromByteArray(lBos.toByteArray());
            }
            catch (final IOException lIOException) {
                LOGGER.error(lIOException.getLocalizedMessage(), lIOException);
            }
            lOrdImage.setProperties();
            lOrdImage.checkProperties();
            final OraclePreparedStatement lOraclePreparedStatement = (OraclePreparedStatement) pStatement;
            lOraclePreparedStatement.setORAData(pIndex, lOrdImage);
        }
    }

    @Override
    public Object replace(final Object pOriginal, final Object pTarget, final Object pOwner) throws HibernateException {
        return pOriginal;
    }

    @Override
    public Class<?> returnedClass() {
        return java.awt.Image.class;
    }

    @Override
    public int[] sqlTypes() {
        return new int[] {Types.STRUCT};
    }
}

IV-D. Classe ImageOrdImage

Voir l'explication pour la classe ImageBlobClasse ImageBlob.

ImageOrdImage.java
Sélectionnez
package com.developpez.rpouiller.hibernateimages.entites;

import java.awt.Image;

import javax.persistence.Entity;
import javax.persistence.Id;

import org.hibernate.annotations.Type;
import org.hibernate.annotations.TypeDef;
import org.hibernate.annotations.TypeDefs;

import com.developpez.rpouiller.hibernateimages.usertypes.ImageOrdImageUserType;

@Entity(name="IMAGE_ORDIMAGE")
@TypeDefs(value={@TypeDef(name="imageOrdImage", typeClass=ImageOrdImageUserType.class)})
public class ImageOrdImage {

    @Id
    private Integer id;
    private String libelle;
    @Type(type="imageOrdImage")
    private Image image;

    // Méthodes getter et setter non représentées

}

IV-E. Classe ImageOrdImageDAO

Nous créons une classe « ImageOrdImageDAO » afin d'implémenter les opérations sur les entités « ImageOrdImage ».

ImageOrdImageDAO.java
Sélectionnez
package com.developpez.rpouiller.hibernateimages.dao;

import java.awt.Image;
import java.awt.image.RenderedImage;
import java.io.File;
import java.io.IOException;

import javax.imageio.ImageIO;

import org.apache.log4j.Logger;
import org.hibernate.Query;
import org.hibernate.Session;

import com.developpez.rpouiller.hibernateimages.entites.ImageOrdImage;

public class ImageOrdImageDAO extends AbstractDAO {

    protected static final Logger LOGGER = Logger.getLogger(ImageOrdImageDAO.class);

    /**
     * Vide la table IMAGE_ORDIMAGE.
     */
    public void purgerImageOrdImage() {
        final Session lSession = ouvrirTransaction();
        final Query lQuery = lSession.createQuery(
                "delete from " + ImageOrdImage.class.getCanonicalName());
        lQuery.executeUpdate();
        fermerTransaction(lSession);
    }

    /**
     * Cette méthode lit une image depuis un champ OrdImage.
     */
    public void lireImageOrdImage(final Integer pId, final File pFile) throws IOException {
        final Session lSession = ouvrirTransaction();
        LOGGER.info("Lecture de l'image");
        final ImageOrdImage lImageOrdImageFromBase = (ImageOrdImage) lSession.get(ImageOrdImage.class, pId);
        LOGGER.info(new StringBuilder("- Id : ").append(lImageOrdImageFromBase.getId()));
        LOGGER.info(new StringBuilder("- Libelle : ").append(lImageOrdImageFromBase.getLibelle()));
        ImageIO.write((RenderedImage) lImageOrdImageFromBase.getImage(), "png", pFile);
        fermerTransaction(lSession);
    }

    /**
     * Cette méthode insère une image dans un champ OrdImage.
     */
    public void insererImageOrdImage(final Integer pId,
            final String pLibelle, final File pFile) throws IOException {

        final Session lSession = ouvrirTransaction();
        LOGGER.info("Insertion de l'image");
        final Image lImageHibernate = ImageIO.read(pFile);
        final ImageOrdImage lImageOrdImage = new ImageOrdImage();
        lImageOrdImage.setId(pId);
        lImageOrdImage.setLibelle(pLibelle);
        lImageOrdImage.setImage(lImageHibernate);
        lSession.save(lImageOrdImage);
        fermerTransaction(lSession);
    }
}

IV-F. Classe ImageOrdImageTest

La classe « ImageOrdImageTest » permet de tester les opérations d'insertion et de lecture de la DAO.

ImageOrdImageTest.java
Sélectionnez
package com.developpez.rpouiller.hibernateimages.test;

import java.io.File;
import java.io.IOException;

import org.apache.log4j.Logger;
import org.junit.Before;
import org.junit.Test;

import com.developpez.rpouiller.hibernateimages.dao.ImageOrdImageDAO;

public class ImageOrdImageTest {

    private static final Logger LOGGER = Logger.getLogger(ImageOrdImageTest.class);
    private ImageOrdImageDAO dao = new ImageOrdImageDAO();

    @Before
    public void initialiser() {
        LOGGER.info("**************************************************************************");
        LOGGER.info("Purger les occurrences");
        LOGGER.info("**************************************************************************");
        dao.purgerImageOrdImage();
    }

    @Test
    public void testInsererLire() throws IOException {
        LOGGER.info("**************************************************************************");
        LOGGER.info("Insérer une image");
        LOGGER.info("**************************************************************************");
        dao.insererImageOrdImage(1, "logo_hibernate", new File("images\\logo_hibernate.png"));
        LOGGER.info("**************************************************************************");
        LOGGER.info("Lire l'image");
        LOGGER.info("**************************************************************************");
        dao.lireImageOrdImage(1, new File("logo_hibernate_from_base_ordimage.png"));
    }
}

IV-G. Trace d'exécution de l'insertion/lecture

Trace d'exécution de l'insertion/lecture avec OrdImage
Sélectionnez
21:13:19 INFO  Version(<clinit>):66 HCANN000001: Hibernate Commons Annotations {4.0.4.Final} 
21:13:19 INFO  Version(logVersion):54 HHH000412: Hibernate Core {4.3.5.Final} 
21:13:19 INFO  Environment(<clinit>):239 HHH000206: hibernate.properties not found 
21:13:19 INFO  Environment(buildBytecodeProvider):346 HHH000021: Bytecode provider name : javassist 
21:13:19 INFO  Configuration(configure):2073 HHH000043: Configuring from resource: /hibernate.cfg.xml 
21:13:19 INFO  Configuration(getConfigurationInputStream):2092 HHH000040: Configuration resource: /hibernate.cfg.xml 
21:13:19 INFO  Configuration(doConfigure):2214 HHH000041: Configured SessionFactory: null 
21:13:19 WARN  DriverManagerConnectionProviderImpl(configure):93 HHH000402: Using Hibernate built-in connection pool (not for production use!) 
21:13:19 INFO  DriverManagerConnectionProviderImpl(buildCreator):166 HHH000401: using driver [oracle.jdbc.OracleDriver] at URL [jdbc:oracle:thin:@localhost:1521:TEST] 
21:13:19 INFO  DriverManagerConnectionProviderImpl(buildCreator):175 HHH000046: Connection properties: {user=HIBERNATE_IMAGE, password=****} 
21:13:19 INFO  DriverManagerConnectionProviderImpl(buildCreator):180 HHH000006: Autocommit mode: false 
21:13:19 INFO  DriverManagerConnectionProviderImpl(configure):102 HHH000115: Hibernate connection pool size: 1 (min=1) 
21:13:19 INFO  Dialect(<init>):145 HHH000400: Using dialect: org.hibernate.dialect.Oracle10gDialect 
21:13:20 INFO  TransactionFactoryInitiator(initiateService):62 HHH000399: Using default transaction strategy (direct JDBC transactions) 
21:13:20 INFO  ASTQueryTranslatorFactory(<init>):47 HHH000397: Using ASTQueryTranslatorFactory 
21:13:20 INFO  ImageOrdImageTest(initialiser):19 ************************************************************************** 
21:13:20 INFO  ImageOrdImageTest(initialiser):20 Purger les occurrences 
21:13:20 INFO  ImageOrdImageTest(initialiser):21 ************************************************************************** 
Hibernate: 
    delete 
    from
        IMAGE_ORDIMAGE
21:13:20 INFO  ImageOrdImageTest(testInsererLire):27 ************************************************************************** 
21:13:20 INFO  ImageOrdImageTest(testInsererLire):28 Insérer une image 
21:13:20 INFO  ImageOrdImageTest(testInsererLire):29 ************************************************************************** 
21:13:20 INFO  ImageOrdImageDAO(insererImageOrdImage):51 Insertion de l'image 
Hibernate: 
    insert 
    into
        IMAGE_ORDIMAGE
        (image, libelle, id) 
    values
        (?, ?, ?)
21:13:20 INFO  ImageOrdImageUserType(nullSafeSet):111 Enregistrement à l'index 1 
21:13:22 INFO  ImageOrdImageTest(testInsererLire):31 ************************************************************************** 
21:13:22 INFO  ImageOrdImageTest(testInsererLire):32 Lire l'image 
21:13:22 INFO  ImageOrdImageTest(testInsererLire):33 ************************************************************************** 
21:13:22 INFO  ImageOrdImageDAO(lireImageOrdImage):36 Lecture de l'image 
Hibernate: 
    select
        imageordim0_.id as id1_1_0_,
        imageordim0_.image as image2_1_0_,
        imageordim0_.libelle as libelle3_1_0_ 
    from
        IMAGE_ORDIMAGE imageordim0_ 
    where
        imageordim0_.id=?
21:13:22 INFO  ImageOrdImageUserType(nullSafeGet):93 Lecture dans la colonne image2_1_0_ 
21:13:22 INFO  ImageOrdImageDAO(lireImageOrdImage):38 - Id : 1 
21:13:22 INFO  ImageOrdImageDAO(lireImageOrdImage):39 - Libelle : logo_hibernate

IV-H. Modification de la classe ImageOrdImageDAO

ImageOrdImageDAO.java
Sélectionnez
    /**
     * Cette méthode modifie une image dans un champ OrdImage.
     */
    public void modifierImageOrdImage(final Integer pId,
            final String pLibelle, final File pFile) throws IOException {

        final Session lSession = ouvrirTransaction();
        LOGGER.info("Modification de l'image");
        final Image lImageHibernate = ImageIO.read(pFile);
        final ImageOrdImage lImageOrdImage = new ImageOrdImage();
        lImageOrdImage.setId(pId);
        lImageOrdImage.setLibelle(pLibelle);
        lImageOrdImage.setImage(lImageHibernate);
        lSession.update(lImageOrdImage);
        fermerTransaction(lSession);
    }

IV-I. Modification de la classe ImageOrgImageTest

Méthode main et nouvelle méthode de ImageOrgImageTest.java
Sélectionnez
    @Test
    public void testInsererModiferLire() throws IOException {
        LOGGER.info("**************************************************************************");
        LOGGER.info("Insérer une image");
        LOGGER.info("**************************************************************************");
        dao.insererImageOrdImage(1, "logo_hibernate", new File("images\\logo_hibernate.png"));
        LOGGER.info("**************************************************************************");
        LOGGER.info("Modifier une image");
        LOGGER.info("**************************************************************************");
        dao.modifierImageOrdImage(1, "logo_dvp", new File("images\\logo_dvp.png"));
        LOGGER.info("**************************************************************************");
        LOGGER.info("Lire l'image après modification");
        LOGGER.info("**************************************************************************");
        dao.lireImageOrdImage(1, new File("logo_dvp_from_base_ordimage.png"));
    }

IV-J. Trace d'exécution de la modification/lecture

Nous voyons une trace ressemblant à celle d'insertion/lecture à la différence qu'il s'agit d'une modification (requête SQL update) avec l'appel de la méthode « nullSafeSet(…) ».

Trace d'exécution de l'insertion/lecture avec OrdImage
Sélectionnez
21:16:31 INFO  Version(<clinit>):66 HCANN000001: Hibernate Commons Annotations {4.0.4.Final} 
21:16:31 INFO  Version(logVersion):54 HHH000412: Hibernate Core {4.3.5.Final} 
21:16:31 INFO  Environment(<clinit>):239 HHH000206: hibernate.properties not found 
21:16:31 INFO  Environment(buildBytecodeProvider):346 HHH000021: Bytecode provider name : javassist 
21:16:31 INFO  Configuration(configure):2073 HHH000043: Configuring from resource: /hibernate.cfg.xml 
21:16:31 INFO  Configuration(getConfigurationInputStream):2092 HHH000040: Configuration resource: /hibernate.cfg.xml 
21:16:31 INFO  Configuration(doConfigure):2214 HHH000041: Configured SessionFactory: null 
21:16:31 WARN  DriverManagerConnectionProviderImpl(configure):93 HHH000402: Using Hibernate built-in connection pool (not for production use!) 
21:16:31 INFO  DriverManagerConnectionProviderImpl(buildCreator):166 HHH000401: using driver [oracle.jdbc.OracleDriver] at URL [jdbc:oracle:thin:@localhost:1521:TEST] 
21:16:31 INFO  DriverManagerConnectionProviderImpl(buildCreator):175 HHH000046: Connection properties: {user=HIBERNATE_IMAGE, password=****} 
21:16:31 INFO  DriverManagerConnectionProviderImpl(buildCreator):180 HHH000006: Autocommit mode: false 
21:16:31 INFO  DriverManagerConnectionProviderImpl(configure):102 HHH000115: Hibernate connection pool size: 1 (min=1) 
21:16:32 INFO  Dialect(<init>):145 HHH000400: Using dialect: org.hibernate.dialect.Oracle10gDialect 
21:16:32 INFO  TransactionFactoryInitiator(initiateService):62 HHH000399: Using default transaction strategy (direct JDBC transactions) 
21:16:32 INFO  ASTQueryTranslatorFactory(<init>):47 HHH000397: Using ASTQueryTranslatorFactory 
21:16:33 INFO  ImageOrdImageTest(initialiser):19 ************************************************************************** 
21:16:33 INFO  ImageOrdImageTest(initialiser):20 Purger les occurrences 
21:16:33 INFO  ImageOrdImageTest(initialiser):21 ************************************************************************** 
Hibernate: 
    delete 
    from
        IMAGE_ORDIMAGE
21:16:33 INFO  ImageOrdImageTest(testInsererModiferLire):39 ************************************************************************** 
21:16:33 INFO  ImageOrdImageTest(testInsererModiferLire):40 Insérer une image 
21:16:33 INFO  ImageOrdImageTest(testInsererModiferLire):41 ************************************************************************** 
21:16:33 INFO  ImageOrdImageDAO(insererImageOrdImage):51 Insertion de l'image 
Hibernate: 
    insert 
    into
        IMAGE_ORDIMAGE
        (image, libelle, id) 
    values
        (?, ?, ?)
21:16:33 INFO  ImageOrdImageUserType(nullSafeSet):111 Enregistrement à l'index 1 
21:16:34 INFO  ImageOrdImageTest(testInsererModiferLire):43 ************************************************************************** 
21:16:34 INFO  ImageOrdImageTest(testInsererModiferLire):44 Modifier une image 
21:16:34 INFO  ImageOrdImageTest(testInsererModiferLire):45 ************************************************************************** 
21:16:34 INFO  ImageOrdImageDAO(modifierImageOrdImage):68 Modification de l'image 
Hibernate: 
    update
        IMAGE_ORDIMAGE 
    set
        image=?,
        libelle=? 
    where
        id=?
21:16:34 INFO  ImageOrdImageUserType(nullSafeSet):111 Enregistrement à l'index 1 
21:16:38 INFO  ImageOrdImageTest(testInsererModiferLire):47 ************************************************************************** 
21:16:38 INFO  ImageOrdImageTest(testInsererModiferLire):48 Lire l'image après modification 
21:16:38 INFO  ImageOrdImageTest(testInsererModiferLire):49 ************************************************************************** 
21:16:38 INFO  ImageOrdImageDAO(lireImageOrdImage):36 Lecture de l'image 
Hibernate: 
    select
        imageordim0_.id as id1_1_0_,
        imageordim0_.image as image2_1_0_,
        imageordim0_.libelle as libelle3_1_0_ 
    from
        IMAGE_ORDIMAGE imageordim0_ 
    where
        imageordim0_.id=?
21:16:38 INFO  ImageOrdImageUserType(nullSafeGet):93 Lecture dans la colonne image2_1_0_ 
21:16:38 INFO  ImageOrdImageDAO(lireImageOrdImage):38 - Id : 1 
21:16:38 INFO  ImageOrdImageDAO(lireImageOrdImage):39 - Libelle : logo_dvp

V. CONCLUSION

Si vous êtes de bons observateurs, vous avez remarqué que les fichiers d'image ayant servi pour l'insertion et la modification n'ont pas la même taille que leurs homologues de la lecture. C'est simplement que les images sont passées au travers des méthodes « ImageIO.read(…) » puis « ImageIO.write(…) » ce qui donne un résultat différent dans les fichiers (comme si l'on enchaînait ces méthodes sans passer par la base de données ou Hibernate) car l'algorithme d'écriture des images est avec pertes.

Ensuite, comme nous venons de le voir, les UserType permettent de gérer de manière simple, des images avec Hibernate. Mais, ceci n'est qu'un exemple de leurs possibilités : ils peuvent permettre de mapper différentes classes Java avec un type SQL. Autre exemple : « java.awt.Color » avec un « NUMBER ».

Les sources de cet article sont présentes sur GitHub : https://github.com/regis1512/hibernate-images.

VI. REMERCIEMENTS

Je remercie très sincèrement :

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2013 Régis POUILLER. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts. Droits de diffusion permanents accordés à Developpez LLC.