EJB 3.0 + JPA 1.0 Jboss 7.1.1 x lazyinitializationexception

Boa tarde a todos.
Fiz uma pesquisa no fórum, mas não achei um cenário que abordasse isso.
Tenho uma aplicação que Utiliza EJBs rodando no Jboss 7.1.1, mas a minha aplicação client é em plataforma RCP da eclipse.
Não é Web sendo assim não possui um Web Xml onde eu possa implementar o pattern OpenEntityManagerInViewFilter.
E Estou tendo problemas de Lazy, quando monto uma tabela que é para ser hierárquica.
Ou seja a Classe possui relacionamento com ela mesma.

Dê uma olhada aqui para entender o problema. Assim fica mais facil de achar uma solução.

Quatro soluções para LazyInitializationException

Eu indicaria join fetch na query.

eu usei Join Fetch, mas após debugar o problema, o acontece por causa do seguinte cenário,

Eu tenho a Tabela Processo e essa tabela faz um relacionamento com ela mesma, então um processo pode ter N filhos ou nenhum.
e no caso eu tenho dois processos pais ,o primeiro possui 3 subprocessos e o segundo não possui nenhum.
na minha aplicação client, na hora que eu monto minha tabela, preciso verificar qual processo pai possui filhos ai dou um process.getProcesses no primeiro registro passa normal o segundo da erro de lazy, porque na verdade ele não possui filhos, eu verifico de meu getProcesses é diferente de nullo e se não está vazio, na hora de fazer a regra booleana com o null já abre a exception.

[quote=rocha.erick]eu usei Join Fetch, mas após debugar o problema, o acontece por causa do seguinte cenário,

Eu tenho a Tabela Processo e essa tabela faz um relacionamento com ela mesma, então um processo pode ter N filhos ou nenhum.
e no caso eu tenho dois processos pais ,o primeiro possui 3 subprocessos e o segundo não possui nenhum.
na minha aplicação client, na hora que eu monto minha tabela, preciso verificar qual processo pai possui filhos ai dou um process.getProcesses no primeiro registro passa normal o segundo da erro de lazy, porque na verdade ele não possui filhos, eu verifico de meu getProcesses é diferente de nullo e se não está vazio, na hora de fazer a regra booleana com o null já abre a exception.[/quote]Não entendi o que você quis dizer, mas posso te afirmar isso com certeza: existe algum valor que está sendo acessado pelo GET e não está sendo trazido com o fetch da query.

Bom dia! Herbert.
Vou tentar demonstrar o cenário o mais detalhado possível para ver se vc consegue entender oque se sucede.

A minha classe Process que é meu Model.


@Entity
@Table(name = "PROCESS")
public class Process implements Serializable {

	/**
	 * 
	 */
	private static final long serialVersionUID = -348971265800330963L;

	@Id
	@SequenceGenerator(name = "SEQ_PROCESS", sequenceName = "SEQ_PROCESS", allocationSize = 1, initialValue = 1)
	@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "SEQ_PROCESS")
	private Long id;

	@Temporal(TemporalType.DATE)
	@Column(name = "START_DATE")
	private Calendar startDate;

	@Temporal(TemporalType.TIME)
	@Column(name = "START_TIME")
	private Date startTime;

	@OneToOne
	@JoinColumn(name = "PRODUCT")
	private AbstractProduct product;

	@OneToOne
	@JoinColumn(name = "OWNER")
	private Employee owner;

	@Temporal(TemporalType.DATE)
	@Column(name = "EXPECTED_END_DATE")
	private Calendar expectedEndDate;

	@Temporal(TemporalType.DATE)
	@Column(name = "END_DATE")
	private Calendar endDate;

	@Temporal(TemporalType.TIME)
	@Column(name = "END_TIME")
	private Date endTime;

	@Enumerated(EnumType.STRING)
	@Column(name = "STATUS")
	private ProcessStatus status;

	@ManyToOne
	@JoinColumn(name = "parent")
	private Process parent;
	
	@Column(name="SALE_ORDER")
	private Long saleOrder;

	@OneToMany(mappedBy = "parent",cascade=CascadeType.ALL)
	private List<Process> processes;
}

Note que ela possui um relacionamento com ela mesma.

Meu jpql:

SELECT p FROM Process p LEFT JOIN FETCH p.processes WHERE p.parent is null ;

a query é para trazer todos os processos que possuem subprocessos e os que não tem subprocessos que sejam processos raizes;
Na minha base eu tenho apenas 5 registros

ID START_DATE START_TIME PRODUCT OWNER EXPECTED_END_DATE END_DATE END_TIME STATUS PARENT TEAM SALE_ORDER


9 08/03/13 00:00:00 01/01/70 16:04:47,000000000 135 41 GENERATED 101
10 08/03/13 00:00:00 01/01/70 16:04:47,000000000 132 41 GENERATED 9 101
11 08/03/13 00:00:00 01/01/70 16:04:47,000000000 134 41 GENERATED 9 101
12 08/03/13 00:00:00 01/01/70 16:04:47,000000000 133 41 GENERATED 9 101
13 08/03/13 00:00:00 01/01/70 16:13:04,000000000 134 41 GENERATED 103

Registros que o parent é null são dois, só que o primeiro possui três subprocessos, a meu ver a minha query, é para retornar 2 registro e o registro com id = 9 é para via o fecth ele me trazer os filhos desse registro, o registro com o id = 13 é para vir somente ele pois não possui filhos.

Bom como disse minha aplicação são EJBs, isso acontece no meu dao, passa pelo meu facade é um EJB Statelles, implementando as interfaces Local e remote, com métodos que somente passam adiante o retorno do meu DAO;

Na minha aplicação Client, que é plataforma RCP, tenho um plugin que acessa esses serviços via a Api de acesso do Jboss 7.1.1.
Na app client, o retorno desse serviço solicitado, é uma lista da minha classe Process, no caso com apenas 2 registros os com ID = 9 e 13, e no Debug que eu fiz, explorando o registro com ID = 9 eu possuo todos os filhos no seu relacionamento.
Com esse retorno de consulta e quero preencher um TreeViewer que pertence a api do JFace, que implementa um modelo MVC para exibir um Tree na tela.

Para eu possuir esse treeviewer funcionando na tela, eu preciso implementar a interface do Jface ITreeContentProvider, que possui alguns métodos, eis minha implementação


public class ProcessContentProvider implements ITreeContentProvider{
	
	private Object[] EMPTY_ARRAY = new Object[1];
	
	@Override
	public void dispose() {
	}

	@Override
	public void inputChanged(Viewer viewer, Object oldInput, Object newInput) {		
	}

	@SuppressWarnings("unchecked")
	@Override
	public Object[] getElements(Object inputElement) {
		return ((List<Process>)inputElement).toArray();
	}

	@Override
	public Object[] getChildren(Object parentElement) {
		Process process = (Process)parentElement;
		if (process.getProcesses() != null && !process.getProcesses().isEmpty()){
			List<Process> processes = new ArrayList<Process>();
			for (Process child : process.getProcesses()){
				processes.add(child);
			}
			return processes.toArray();
		}
		return EMPTY_ARRAY;
	}

	@Override
	public Object getParent(Object element) {
		Process process = (Process)element;
		if (process.getParent() != null){
			return process.getParent();
		}
		return null;
	}

	@Override
	public boolean hasChildren(Object element) {
		Process process = (Process)element;
		return process.getProcesses() != null && !process.getProcesses().isEmpty();
	}

}

Note o método hashChildren, que o treeViewer utiliza para ver se o registro corrente possui filhos, de acordo com a stack trace


org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.rochatec.metallurgical.model.Process.processes, no session or session was closed
	at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:393)
	at org.hibernate.collection.internal.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:385)
	at org.hibernate.collection.internal.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:125)
	at org.hibernate.collection.internal.PersistentBag.isEmpty(PersistentBag.java:249)
	at com.rochatec.metallurgical.ui.provider.ProcessContentProvider.hasChildren(ProcessContentProvider.java:54)
	at org.eclipse.jface.viewers.AbstractTreeViewer.isExpandable(AbstractTreeViewer.java:2127)
	at org.eclipse.jface.viewers.TreeViewer.isExpandable(TreeViewer.java:588)
	at org.eclipse.jface.viewers.AbstractTreeViewer.isExpandable(AbstractTreeViewer.java:2153)
	at org.eclipse.jface.viewers.AbstractTreeViewer.updatePlus(AbstractTreeViewer.java:2835)
	at org.eclipse.jface.viewers.TreeViewer.updatePlus(TreeViewer.java:852)
	at org.eclipse.jface.viewers.AbstractTreeViewer.createTreeItem(AbstractTreeViewer.java:834)
	at org.eclipse.jface.viewers.AbstractTreeViewer$1.run(AbstractTreeViewer.java:808)
	at org.eclipse.swt.custom.BusyIndicator.showWhile(BusyIndicator.java:70)
	at org.eclipse.jface.viewers.AbstractTreeViewer.createChildren(AbstractTreeViewer.java:782)
	at org.eclipse.jface.viewers.TreeViewer.createChildren(TreeViewer.java:644)
	at org.eclipse.jface.viewers.AbstractTreeViewer.createChildren(AbstractTreeViewer.java:753)
	at org.eclipse.jface.viewers.AbstractTreeViewer.handleTreeExpand(AbstractTreeViewer.java:1485)
	at org.eclipse.jface.viewers.TreeViewer.handleTreeExpand(TreeViewer.java:952)
	at org.eclipse.jface.viewers.AbstractTreeViewer$4.treeExpanded(AbstractTreeViewer.java:1496)
	at org.eclipse.swt.widgets.TypedListener.handleEvent(TypedListener.java:132)
	at org.eclipse.swt.widgets.EventTable.sendEvent(EventTable.java:84)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1053)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1077)
	at org.eclipse.swt.widgets.Widget.sendEvent(Widget.java:1062)
	at org.eclipse.swt.widgets.Tree.wmNotifyChild(Tree.java:7538)
	at org.eclipse.swt.widgets.Control.wmNotify(Control.java:5534)
	at org.eclipse.swt.widgets.Composite.wmNotify(Composite.java:1896)
	at org.eclipse.swt.widgets.Control.WM_NOTIFY(Control.java:5086)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:4584)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:4972)
	at org.eclipse.swt.internal.win32.OS.CallWindowProcW(Native Method)
	at org.eclipse.swt.internal.win32.OS.CallWindowProc(OS.java:2425)
	at org.eclipse.swt.widgets.Tree.callWindowProc(Tree.java:1533)
	at org.eclipse.swt.widgets.Tree.WM_LBUTTONDOWN(Tree.java:6411)
	at org.eclipse.swt.widgets.Control.windowProc(Control.java:4564)
	at org.eclipse.swt.widgets.Tree.windowProc(Tree.java:5937)
	at org.eclipse.swt.widgets.Display.windowProc(Display.java:4972)
	at org.eclipse.swt.internal.win32.OS.DispatchMessageW(Native Method)
	at org.eclipse.swt.internal.win32.OS.DispatchMessage(OS.java:2531)
	at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3752)
	at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:2701)
	at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2665)
	at org.eclipse.ui.internal.Workbench.access$4(Workbench.java:2499)
	at org.eclipse.ui.internal.Workbench$7.run(Workbench.java:679)
	at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:332)
	at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:668)
	at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
	at com.rochatec.metallurgical.app.Application.start(Application.java:35)
	at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:196)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
	at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:344)
	at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
	at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
	at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
	at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
	at java.lang.reflect.Method.invoke(Method.java:597)
	at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:622)
	at org.eclipse.equinox.launcher.Main.basicRun(Main.java:577)
	at org.eclipse.equinox.launcher.Main.run(Main.java:1410)
	at org.eclipse.equinox.launcher.Main.main(Main.java:1386)

é exatamente nesse momento que acontece o erro, mas existe uma peculiariade, quando eu executo a consulta e seto o resultado dessa consulta no treeviewer e ele renderiza a tela, ele funciona certinho, são dois registros, normal, mas como o primeiro registro possui filhos e eu quero expandir esse registro para ver seus filhos, é nesse momento que ele lança o LazyInitializationException, mas o mais estranho é que de 3 filhos que ele deveria exibir, exibe somente um.
Não sei se consegui ser claro, tentei ser o mais detalhista possivel, mas acredito eu, os meus dados já estão no meu cliente, não é para dar lazy.