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▲
- sur developpez.com :
- Gestion avancée d'image sous Oracle avec Java (ORDImage) de Benoît Maréchal.
- externes :
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▲
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.
<?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 ».
<?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.
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 ».
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▲
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.
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.
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▲
CREATE TABLE IMAGE_BLOB
(
ID NUMBER(6) PRIMARY KEY,
LIBELLE VARCHAR2(20),
IMAGE BLOB
);III-B. Fichier 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 ».
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.
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 ».
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.
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.
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_hibernateIII-H. Modification de la classe ImageBlobDAO▲
Nous ajoutons la méthode « modifierImageBlob(…) » à la classe « ImageBlobDAO ».
/**
* 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.
@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(…) ».
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_dvpIV. 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▲
CREATE TABLE IMAGE_ORDIMAGE
(
ID NUMBER(6) PRIMARY KEY,
LIBELLE VARCHAR2(20),
IMAGE ORDSYS.ORDIMAGE
);IV-B. Fichier 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-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() »).
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.
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 ».
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.
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▲
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_hibernateIV-H. Modification de la classe ImageOrdImageDAO▲
/**
* 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▲
@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(…) ».
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_dvpV. 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.









