Je veux faire un traitement multi-threadable avec JTA.JTA Transaction et thread
Environnement:
- AS 6.3 JBOSS
- Java 7
- Oracle 11,2 g
- CDI
Traitement: Je veux produire un zip avec toutes les données de base de données. Ce fichier zip peut être grand, donc je veux démarrer un thread pour produire le flux en même temps que jboss l'envoie au client.
Mon entrée REST:
@Stateless
@Path("/exportProcess")
public class ExportProcessusResource {
@Inject
private IExport export;
@GET
@Path("/{processCode: [^/]+}")
@Produces(MediaType.APPLICATION_OCTET_STREAM)
public Response export(@PathParam("processCode") final String pProcessCode) {
return Response.ok(export.export(pProcessCode))
.header("Content-Disposition", "attachment; filename=" + pCodeProcessus + ".zip")
.build();
}
}
Mon modèle:
@Entity
@Table(name = "T_PROCESS")
@NamedQueries({
@NamedQuery(name = "Process.GetByCode", query = "SELECT p FROM Process p WHERE p.code=:code")
})
public class Process {
@Column(name = "CODE", length = 50, nullable = false)
private String code;
@OneToMany(mappedBy = "process", targetEntity = Step.class)
private Collection<Step> steps;
//Getters/Setters
}
@Entity
@Table(name = "T_STEP")
public class STEP {
@Id
@Column(name = "ID_STEP")
@GeneratedValue(strategy = GenerationType.AUTO, generator = "SEQ_ID_STEP")
@SequenceGenerator(name = "SEQ_ID_STEP", sequenceName = "SEQ_ID_STEP")
private int id;
@ManyToOne(targetEntity = Process.class)
@JoinColumn(name = "CODE_PROCESS", referencedColumnName = "CODE", nullable = false)
private Process process;
//Getters/Setters
}
Mon OAC:
public interface IProcessDao {
Processus getByCode(final String pCode);
}
public class ProcessDao implements IProcessDao {
@Override
public Processus getByCode(final String pCode) {
Processus lResult = null;
try {
final TypedQuery<Processus> lRequest = pEm.createNamedQuery("Process.GetByCode", Process.class);
lRequest.setParameter("code", pCode);
lResult = lRequest.getSingleResult();
} catch (final NoResultException e) {
// Return null
lResult = null;
}
return lResult;
}
}
Mon contrôleur:
public interface IExport {
/**
* Generate export
*
* @param pProcessCode Process code
* @return Datas
*/
InputStream export(final String pProcessCode);
}
public class Export implements IExport {
@PersistenceContext(unitName="authorizations")
private EntityManager entityManagerAuthorizations;
@Inject
private ExportThreadHelper exportThreadHelper;
@Override
public InputStream export(final String pProcessCode) {
//Check if user has the profile. Use database "AUTHORIZATIONS"
checkProfil(entityManagerAuthorizations, Profiles.ADMIN);
final PipedInputStream lInputStream = new PipedInputStream();
OutputStream lOutputStream = null;
try {
lOutputStream = new FileOutputStream("d:/test.zip");// new
// PipedOutputStream(lInputStream);
} catch (final IOException e) {
throw new RuntimeException("Cannot start zip generation", e);
}
final ZipOutputStream lZipOutputStream = new ZipOutputStream(lOutputStream);
final Runnable lRunnable = new Runnable() {
@Override
public void run() {
try {
exportThreadHelper.export(pProcessCode, lZipOutputStream);
} catch (final Exception e) {
logger.error(e);
} finally {
IOUtils.closeQuietly(lZipOutputStream);
}
}
};
//To execute in same thread :
//lRunnable.run();
//To execute in another thread
final Thread lThread = new Thread(lRunnable);
lThread.start();
try {
lThread.join();
} catch (final InterruptedException e1) {
throw new RuntimeException(e1);
}
try {
return new FileInputStream("d:/test.zip");
} catch (final FileNotFoundException e) {
logger.error(e);
}
return lInputStream;
}
}
public class ExportThreadHelper {
private class ProcessToExport {
//...
}
@PersistenceContext
@Named("Application")
private EntityManager entityManagerThreadable;
@Inject
private IProcessDao processDao;
public void export(final String pProcesssCode, final ZipOutputStream pZipOutputStream)
throws MyWayBusinessException {
try {
final ProcessToExport lProcessToExport = new ProcessToExport();
transaction(entityManagerThreadable, new Callable<Void>() {
@Override
public Void execute() {
final Process lProcess = processDao.getByCode(pProcesssCode);
for (final Step lStep : lProcess.getSteps()) {
//Many things
}
return null;
}
});
//MANY OTHER TREATMENTS
} catch (final Exception e) {
logger.error(e);
throw new RuntimeException("Cannot generate export", e);
}
}
@Override
@TransactionAttribute(TransactionAttributeType.REQUIRED)
protected <T> T transaction(final EntityManager pEntityManager, final Callable<T> pCallable) {
//I've tried with and without the annotation and with and without the "UserTransaction"
try {
final UserTransaction tx = com.arjuna.ats.jta.UserTransaction.userTransaction();
try {
tx.begin();
final T lResultat = pCallable.execute();
tx.commit();
return lResultat;
} catch (final Throwable e) {
tx.rollback();
throw e;
}
} catch (final Throwable e) {
throw new RuntimeException(e);
}
}
}
Mes persistence.xml:
<?xml version="1.0" encoding="UTF-8"?>
<persistence version="2.0"
xmlns="http://java.sun.com/xml/ns/persistence" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/persistence http://java.sun.com/xml/ns/persistence/persistence_2_0.xsd ">
<persistence-unit name="APPLICATION" transaction-type="JTA">
<jta-data-source>java:/jdbc/app</jta-data-source>
<class>Processus</class>
<class>Step</class>
<properties>
<!-- Scan for annotated classes and Hibernate mapping XML files -->
<property name="hibernate.archive.autodetection" value="class, hbm" />
</properties>
</persistence-unit>
<persistence-unit name="AUTHORIZATION" transaction-type="JTA">
<jta-data-source>java:/jdbc/AUTHORIZATION</jta-data-source>
<!-- many things... -->
</persistence-unit>
</persistence>
(j'ai nettoyé le code pour ne garder que des choses importantes). Et, si j'utilise la version monothread (lRunnable.run()) j'ai le fichier zip, mais si je lance la version multithread (thread.start()) (que j'ai bloqué ici pour assurer mes tests que la connexion n'est pas à proximité thread parent, mais après je vais retirer Thread.join()) J'ai cette exception:
erreur [... ExportThreadHelper] (discussion-115) a échoué à paresseusement initialize une collection de rôle: .steps, n'a pas pu initialiser proxy - no Session: org.hibernate.LazyInitializationException: impossible d'initialiser paresseusement une collection de rôle: .steps, n'a pas pu initialiser proxy - no Session at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException (AbstractPersistentCollection.java:569) [hibernate-core-4.2.14.SP1-redhat-1.jar: 4.2.14.SP1-redhat-1] à org .hibernate.collection.internal.AbstractPersistentCollection.withTemporarySessionIfNeeded (AbstractPersistentCollection.java:188) [hibernate-core-4.2.14.SP1-redhat-1.jar: 4.2.14.SP1-redhat-1] à org.hibernate .collection.internal.AbstractPersistentCollection.initialize (AbstractPersistentCollection.java:548) [hibernate-core-4.2.14.SP1-redhat-1.jar: 4.2.14.SP1-redhat-1] à org.hibernate.collection .internal.AbstractPersistentCollection.read (AbstractPersistentCollection.java:126) [hibernate-core-4.2.14.SP1-redhat-1.jar: 4.2.14.SP1-redhat-1] à org.hibernate.collection.internal.PersistentBag.iterator (PersistentBag.java:266) [hibernate-core-4.2.14.SP1-redhat-1.jar: 4.2.14.SP1-redhat-1] à ExportThreadHelper $ 1.execute (ExportThreadHelper.java:101) [metier-2.3.0-SNAPSHOT.jar:] à ExportThreadHelper $ 1.execute (ExportThreadHelper.java:1) [metier-2.3.0-SNAPSHOT.pot:] à ExportThreadHelper.transaction (ExportThreadHelper.java:148) [metier-2.3.0-SNAPSHOT.jar:] à ExportThreadHelper.export (ExportThreadHelper.java:97) [metier-2.3.0-SNAPSHOT. jar:] at ExportMetier $ 1.run (ExportMetier.java:62) [metier-2.3.0-SNAPSHOT.jar:] at java.lang.Thread.run (Thread.java:722) [rt.jar: 1.7 .0_04]
Avez-vous vu un problème dans mon code?
fichier ayant-vous nommé 'ExportThreadHelper.java' dans le projet, vous pouvez publier ce fichier. Lignes spécialement près de 90-150? – Amogh
En regardant d'abord à l'exception, il semble que vous essayez d'accéder 'Collection' depuis l'objet 'Process' hors de la session d'hibernation. Soit faire la session de mise en veille prolongée fermer ou de définir 'fetch =' FetchType.EAGER' à la collection privée étapes, ' –
Amogh
Merci de votre attention - ExportThreadHelper est dans la partie « contrôleur » - Je veux garder la chargement paresseux car il y a trop de données et je ne l'utilise pas dans tous les cas. – Chklang