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▲
Les images utilisées dans cet article sont :
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_hibernate
III-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_dvp
IV. MANIPULATION D'UN ORDIMAGE▲
Comme pour le IIIMANIPULATION D'UN BLOB, nous allons modifier le fichier de configuration « hibernate.cfg.xml » afin qu'il prenne en compte la classe « ImageOrdImage ». Nous allons également créer cette dernière ainsi que la classe de UserType « ImageOrdImageUserType » qui lui est associée.
Ensuite de la même manière, nous créerons la classe « ImageOrdImageDAO » et la classe de test « ImageOrdImageTest » afin d'insérer un enregistrement et le lire (ce qui donne la trace visible au IV-GTrace d'exécution de l'insertion/lecture). Finalement nous ajouterons un autre test afin de modifier un enregistrement précédemment créé pour le lire ensuite (voir la trace au IV-JTrace d'exécution de la modification/lecture).
IV-A. Script de création de la table▲
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_hibernate
IV-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_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.