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

Régis POUILLER

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 : [ Commentez ]

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

- Auteur : Régis POUILLER

- Parution : 12 mai 2014

- Licence : 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.

Sommaire

- I. INTRODUCTION

- I-A. Objectif

- I-B. Les « UserType »

- I-C. Déroulement de l'article

- I-D. Ressources

- I-E. Versions des logiciels et bibliothèques

- I-F. Bibliothèques nécessaires

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

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

- II-A. Fichier hibernate.cfg.xml

- II-B. Fichier log4j.xml

- II-C. Classe ImageAuto

- II-D. Classe AbstractDAO

- II-E. Classe ImageAutoDAO

- II-F. Classe ImageAutoTest

- II-G. Trace d'exécution

- III. MANIPULATION D'UN BLOB

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

- III-B. Fichier hibernate.cfg.xml

- III-C. Classe ImageBlobUserType

- III-D. Classe ImageBlob

- III-E. Classe ImageBlobDAO

- III-F. Classe ImageBlobTest

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

- III-H. Modification de la classe ImageBlobDAO

- III-I. Modification de la classe ImageBlobTest

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

- IV. MANIPULATION D'UN ORDIMAGE

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

- IV-B. Fichier hibernate.cfg.xml

- IV-C. Classe ImageOrdImageUserType

- IV-D. Classe ImageOrdImage

- IV-E. Classe ImageOrdImageDAO

- IV-F. Classe ImageOrdImageTest

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

- IV-H. Modification de la classe ImageOrdImageDAO

- IV-I. Modification de la classe ImageOrgImageTest

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

- V. CONCLUSION

- VI. REMERCIEMENTS

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 temps 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

- sur developpez.com :

- Gestion avancée d'image sous Oracle avec Java (ORDImage) de Benoît Maréchal.

- externes :

- Documentation Hibernate sur les types ;

- Init OrdImage object without inserting anything to DB.

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 :

- Oracle : 11gR2 ;

- Driver JDBC pour Oracle : 11gR2 pour JDK6 ;

- Hibernate : 4.3.5-Final ;

- Simple Logging Facade for Java :1.7.6 ;

- Apache log4j :1.2.17 ;

- JUnit :4.11 ;

- Hamcrest :1.3 ;

- JDK : 1.7 ;

- Eclipse : 4.3 Kepler.

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 :

logo_hibernate.png

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é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

<?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

<?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

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

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

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

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 »

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-G). Finalement nous ajouterons un autre test afin de modifier un enregistrement précédemment créé pour le lire ensuite (voir la trace au III-J).

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

Requête de création de la table IMAGE_BLOB

CREATE TABLE IMAGE_BLOB
(
ID       NUMBER(6) PRIMARY KEY,
LIBELLE  VARCHAR2(20),
IMAGE    BLOB
);

III-B - Fichier hibernate.cfg.xml

hibernate.cfg.xml

<?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

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

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

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

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

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

/**
* 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

@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

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 III, 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-G). Finalement nous ajouterons un autre test afin de modifier un enregistrement précédemment créé pour le lire ensuite (voir la trace au IV-J).

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

Requête de création de la table IMAGE_ORDIMAGE

CREATE TABLE IMAGE_ORDIMAGE
(
ID       NUMBER(6) PRIMARY KEY,
LIBELLE  VARCHAR2(20),
IMAGE    ORDSYS.ORDIMAGE
);

IV-B - Fichier hibernate.cfg.xml

hibernate.cfg.xml

<?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-C. 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

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 ImageBlob.

ImageOrdImage.java

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

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

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

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

/**
* 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

@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

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 :

- www.developpez.com qui me permet de publier cet article ;

- Nono40 et djibril pour leurs outils ;

- Mickael Baron et OButterlin pour leur relecture technique ;

- Jacques THÉRY pour sa relecture orthographique rigoureuse.