Gérer simplement les Blob avec Spring 4 et Hibernate 4

Spring_frameworkhibernate_logo_a

 

 

 

Dans la première partie de cet article nous avons évoqué les CLOB/BLOB avec spring-jdbc. Nous poursuivons l'exploration du même thème mais cette fois avec Spring 4 et Hibernate 4. L'approche est un peu semblable mais pas trop et les difficultés sont pratiquement identiques.

Merci de (re)lire [juste] l'introduction de l'article précédent pour mieux suivre cette seconde partie.

  • Nous nous concentrons là aussi sur la base postgres.
  • La démo utilise java 8, hibernate 4, spring 4 et postgres 9.4.
  • Le use-case étant le même que la première partie.

Générons le projet démo maven java 8 standard avec spring-boot depuis intializr.

Nous commençons par écrire la seule classe modèle annotée avec @Entity de javax.persistence. Et nous laissons hibernate générer le schéma de la table dans postgres nommée blobmail ici.

La configuration de spring nécessite zéro xml car avec le fichier application.properties de spring-boot ainsi que la classe de config java de spring nommée ConfigApp tout est bien défini.

Pour les pressés, ce qui est intéressant à noter :

  • La définition de l'attribut BLOB dans la classe modèle,
  • Les appels dans les tests Junit des méthodes createBlob et l'emploi de Blob.

SOMMAIRE

  • Arborescence du projet,
  • Écrire la classe modèle,
  • Écrire l'interface dao et son implémentation,
  • Écrire la classe de config de spring,
  • Fichier properties de spring-boot,
  • Écrire test JUnit.

 

Arborescence du projet

arbo_projet_blob_hibernate_springboot

 

Classe modèle

Voici le code de la classe modèle qui est une entité hibernate simple mis à part le champ Blob:

...
import java.io.Serializable;
import java.sql.Blob;

import javax.persistence.*;

import org.hibernate.annotations.Type;

@Entity
@Table(name="mail")
public class Email implements Serializable {

	@GeneratedValue
        @Column(name="id")
	@Id private long id;
	
	private String objet;
	
	@Lob @Type(type="org.hibernate.type.BlobType")
	private Blob corps;
	
	private String idregion;

....getters setters omis...
classe modèle

Les deux lignes n° 19-20 définissent un champ LOB (Large OBject). D'où l'annotation @Lob.

L'annotation spécifique Hibernate précise le type, ici BlobType. Nous optons pour un BlobType.

D'autres choix sont possibles text ou clob (@Type(type = "org.hibernate.type.TextType" ou  @Type(type="org.hibernate.type.MaterializedClobType").

Il semble qu'il y ait d'autres possibilités (que nous n'avons pas creusées) comme définir le type avec columnDefinition. Exemple, @Column(columnDefinition="text"). Ainsi le code adhère moins à l'implémentation hibernate!

 

Classe DAO

L'interface nommée EmailDao déclare ceci:

import java.util.List;

public interface EmailDao {
	Long save(Email email);
	List<Email> list();
	Email get(Long id);
	void remove(Long id);
}
interface dao

Ce sont les méthodes CRUD standards.

Voici un exemple d'implémentation jpa hibernate [non optimisée]:

Code_blog_blob_hibernate_springBoot

Rien de nouveau ici, tout ce qui est traditionnel avec hibernate!

 

Classe de config Spring

Cette classe configure SessionFactory d'hibernate comme illustré par le code qui suit:

Code2Config_blog_blob_hibernate_springBoot

Avant de passer aux tests unitaires JUnit, voici le fichier des propriétés de spring-boot:

APPLICATION.PROPERTIES

C'est le même contenu que le fichier application.properties de la première partie.

Test JUnit

Le test unitaire JUnit permet de créer un blob, de le lire et [partiellement] de le supprimer.

C'est ici que nous verrons quelques détails intéressants concernant la manipulation des Blob.

Par exemple, le test testInsertEmailXLob permet d'insérer le contenu du fichier emailLOB.txt  [supposé présent dans le répertoire /tmp] dans le champ blob (nommé corps).

Puis testReadBlob, permet de récupérer le contenu du champ blob qui sera stocké dans un fichier.

 

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Blob;
import java.sql.SQLException;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.test.SpringApplicationConfiguration;
import org.springframework.test.annotation.Rollback;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;

@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(classes = HibernateSpringClobBlobApplication.class)
@Transactional
@Rollback(false)
public class BinaryHibernateSpringBlobTests {
	private Logger logger = LoggerFactory.getLogger(BinaryHibernateSpringBlobTests.class);
	@Autowired SessionFactory sessionFactory;
	@Autowired @Qualifier("emailDao") EmailDao emailDao;

	final static long idMail=62L;
	
	@Test public void testInsertEmailXLob(){
		Email email=new Email();
		
		Session session = sessionFactory.getCurrentSession();
		
		try (final InputStream istream = new FileInputStream(new File("/tmp/emailLOB.txt"))) 
		{
			session.beginTransaction();
			Blob blob = session.getLobHelper().createBlob(istream, istream.available()); 
			email.setCorps(blob);
			email.setIdregion("48");
			email.setObjet("test hibernate & lob pour "+email.getIdregion());
			Long id = emailDao.save(email);
			session.getTransaction().commit();
			blob.free();
			Assert.assertNotNull(id);
		} catch (Exception e) {
			//a traiter e.printStackTrace();
		}
	}

	@Test public void testReadBlob() throws SQLException{
		
		try{	
			Session session = sessionFactory.getCurrentSession();
			session.beginTransaction();
			
			Email email=emailDao.get(idMail);
			if(email==null){
				logger.warn("\tAucun mail avec id="+idMail);
				return;
			}
			Assert.assertNotNull(email);
			session.setReadOnly(email, true);
			Blob blob= email.getCorps();
			
			try(OutputStream fos = new FileOutputStream("/tmp/readClobOut.txt")){ 
	          fos.write(blob.getBytes(1, (int)blob.length()));     
			}
			
			blob.free();
			session.getTransaction().commit();
			Assert.assertNotNull(email);
			
		}catch(Exception e){
			//a traiter e.printStackTrace();
		}
	}
	
	@Test 
	public void testRemove(){
		emailDao.remove(idMail);
	}
}
Test JUnit

 

Attention, les valeurs indiquées sont à adapter à votre contexte. Vérifiez le chemin indiqué pour le fichier.

La chose intéressante est l'appel de session.getLobHelper().createBlob qui permet justement de créer et renvoyer un blob.

Aussi, n'oubliez pas de libérer les blobs dés que possible (ressources coûteuses).

Les annotations @Transactional et @Rollback(false) permettent de créer effectivement dans la base les données lob.

Il est recommandé dans les tests de switcher à true le paramètre de @Rollback.

 

La prochaine et dernière partie de cet article explore spring-data et les blob/clob.

 

Laisser un commentaire

Votre adresse de messagerie ne sera pas publiée. Les champs obligatoires sont indiqués avec *

Captcha *

Ce site utilise Akismet pour réduire les indésirables. En savoir plus sur comment les données de vos commentaires sont utilisées.