JPA - dificuldades com relacionamento ManyToMany

Opa galera blz?

Travei em uma parte de um projeto, se alguem poder me ajudar ficarei grato.

Minhas duvidas são as seguintes :

1 )
Tenho uma classe TAREFA e outra FUNCIONÁRIO.

E o relacionamento entre elas é de MUITOS PARA MUITOS.

Na hora de recuperar os dados da classe TAREFA uma exceção é lançada -> LazyInitializationException.

Isso estava acontecendo tbm com a classe FUNCIONÁRIO, só que eu adicionei um fetch= FetchType.EAGER na anotação @ManyToMany , e isso resolveu o problema.

Pensei que a solução da classe TAREFA era fazer a msm coisa, mais não era pois outro tipo de exceção é lançada -> MultipleBagFetchException

Qual seria a solução para este problema ?

Em um relacionamento muitos para muitos o correto é a criação de 3 tabelas, mais o meu projeto ta criando 4 tabelas no banco de dados
que são :

tarefa
funcionário
tarefa_funcionário
funcionário_tarefa

Eu anotei minha classe de forma errada ?

Segue abaixo as msgs de exceções na pilha e o código para os 2 casos :

EXCEÇÃO .LazyInitializationException

Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: civil.modelo.entidade.Tarefa.funcionarios, no session or session was closed at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationException(AbstractPersistentCollection.java:383) at org.hibernate.collection.AbstractPersistentCollection.throwLazyInitializationExceptionIfNotConnected(AbstractPersistentCollection.java:375) at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:368) at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:111) at org.hibernate.collection.PersistentBag.iterator(PersistentBag.java:272) at civil.modelo.entidade.TesteControle.recuperaTarefa(TesteControle.java:72) at civil.modelo.entidade.TesteControle.executa(TesteControle.java:147) at civil.modelo.entidade.Main.main(Main.java:49)

EXCEÇÃO MultipleBagFetchException


Exception in thread "main" javax.persistence.PersistenceException: [PersistenceUnit: PersistenciaCivilPU] Unable to build EntityManagerFactory
	at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:915)
	at org.hibernate.ejb.HibernatePersistence.createEntityManagerFactory(HibernatePersistence.java:57)
	at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:48)
	at javax.persistence.Persistence.createEntityManagerFactory(Persistence.java:32)
	at civil.modelo.entidade.TesteControle.<init>(TesteControle.java:19)
	at civil.modelo.entidade.Main.main(Main.java:49)
Caused by: org.hibernate.loader.MultipleBagFetchException: cannot simultaneously fetch multiple bags
	at org.hibernate.loader.BasicLoader.postInstantiate(BasicLoader.java:94)
	at org.hibernate.loader.entity.EntityLoader.<init>(EntityLoader.java:119)
	at org.hibernate.loader.entity.EntityLoader.<init>(EntityLoader.java:71)
	at org.hibernate.loader.entity.EntityLoader.<init>(EntityLoader.java:54)
	at org.hibernate.loader.entity.BatchingEntityLoader.createBatchingEntityLoader(BatchingEntityLoader.java:133)
	at org.hibernate.persister.entity.AbstractEntityPersister.createEntityLoader(AbstractEntityPersister.java:1914)
	at org.hibernate.persister.entity.AbstractEntityPersister.createEntityLoader(AbstractEntityPersister.java:1937)
	at org.hibernate.persister.entity.AbstractEntityPersister.createLoaders(AbstractEntityPersister.java:3205)
	at org.hibernate.persister.entity.AbstractEntityPersister.postInstantiate(AbstractEntityPersister.java:3191)
	at org.hibernate.persister.entity.SingleTableEntityPersister.postInstantiate(SingleTableEntityPersister.java:728)
	at org.hibernate.impl.SessionFactoryImpl.<init>(SessionFactoryImpl.java:348)
	at org.hibernate.cfg.Configuration.buildSessionFactory(Configuration.java:1872)
	at org.hibernate.ejb.Ejb3Configuration.buildEntityManagerFactory(Ejb3Configuration.java:906)

CLASSE TAREFA

[code]@Entity
@Table(name=“tarefa”)
public class Tarefa implements Serializable {

@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="id_Tarefa", unique=true, nullable=false)
private Long id;    

@Column(length=100)
private String nomeTarefa;

@Temporal(javax.persistence.TemporalType.DATE)
private Date horarioInico;

@ManyToMany
private List<Funcionario> funcionarios;

//getter e setters
}
[/code]

CLASSE FUNCIONÁRIO

[code]@Entity
@Table(name=“funcionario”)
public class Funcionario implements Serializable {

@Id
@GeneratedValue(strategy= GenerationType.IDENTITY)
@Column(name="id_funcionario", unique=true, nullable=false)
private Long id;

@Column(length=255, unique=true, nullable=false)
private String nome;

@ManyToMany(fetch= FetchType.EAGER)
@JoinTable(
            name="funcionario_tarefa",
            joinColumns={
                @JoinColumn(name="id_funcionario",
                            referencedColumnName="id_funcionario"
                            )
                        },
            inverseJoinColumns={
                @JoinColumn(name="id_tarefa",
                            referencedColumnName="id_tarefa"
                            )
                }
        )
private List<Tarefa> tarefas;

//gatters e setters
}
[/code]

METODO DE QUE PERSISTE FUNCIONARIO

public void create(Funcionario funcionario) { if (funcionario.getTarefas() == null) { funcionario.setTarefas(new ArrayList<Tarefa>()); } EntityManager em = null; try { em = getEntityManager(); em.getTransaction().begin(); List<Tarefa> attachedTarefas = new ArrayList<Tarefa>(); for (Tarefa tarefasTarefaToAttach : funcionario.getTarefas()) { tarefasTarefaToAttach = em.getReference(tarefasTarefaToAttach.getClass(), tarefasTarefaToAttach.getId()); attachedTarefas.add(tarefasTarefaToAttach); } funcionario.setTarefas(attachedTarefas); em.persist(funcionario); for (Tarefa tarefasTarefa : funcionario.getTarefas()) { tarefasTarefa.getFuncionarios().add(funcionario); tarefasTarefa = em.merge(tarefasTarefa); } em.getTransaction().commit(); } finally { if (em != null) { em.close(); } } }

METODO QUE PERSISTE TAREFA

public void create(Tarefa tarefa) { if (tarefa.getFuncionarios() == null) { tarefa.setFuncionarios(new ArrayList<Funcionario>()); } EntityManager em = null; try { em = getEntityManager(); em.getTransaction().begin(); List<Funcionario> attachedFuncionarios = new ArrayList<Funcionario>(); for (Funcionario funcionariosFuncionarioToAttach : tarefa.getFuncionarios()) { funcionariosFuncionarioToAttach = em.getReference(funcionariosFuncionarioToAttach.getClass(), funcionariosFuncionarioToAttach.getId()); attachedFuncionarios.add(funcionariosFuncionarioToAttach); } tarefa.setFuncionarios(attachedFuncionarios); em.persist(tarefa); for (Funcionario funcionariosFuncionario : tarefa.getFuncionarios()) { funcionariosFuncionario.getTarefas().add(tarefa); funcionariosFuncionario = em.merge(funcionariosFuncionario); } em.getTransaction().commit(); } finally { if (em != null) { em.close(); } } }

Foi mal pelo post gigante, mais faz tempo que eu travei nessa parte e não achei soluções, vlw galera.

Pesquise pelo hibernate.initialize. Provavelmente irá resolver teu problema.

Vlw pela dica drsmachado.

Na busca sobre a informação eu vi que teria que usar um Hibernate.initialize() antes de fexar a sessão.

Mais meu metodo de procura não me da essa opção, esse metodo foi gerado pelo netbeans, JPAController:

public Tarefa findTarefa(Long id) { EntityManager em = getEntityManager(); try { return em.find(Tarefa.class, id); } finally { em.close(); } }

alguma sugestão?

obrigado.

conseguiu resolver o problema? estou passando pelo mesmo…

Masami

Resolvi sim, qual dos 2 problemas que eu citei que vc tbm teve ?

Sua classe DAO do JPA foi gerada pelo netbeans ?

os dois, o LazyInitializationException ocorre quando clico na pagina 2 de um p:dataTable, dai se eu mudo o mapeamento para EAGER ocorre MultipleBagFetchException.

Sim, são Facade geredos pelo netbeans para EJB+JPA que eu modifiquei para Spring 3+JPA

Então é o seguinte, o seu metodo de busca deve estar assim ?

public Tarefa findTarefa(Long id) { EntityManager em = getEntityManager(); try { return em.find(Tarefa.class, id); } finally { em.close(); } }

Vc deve deixar seu atributo que representa o ManyToMany deve ser mapeado assim -> @ManyToMany(fetch= FetchType.LAZY)

Isso pq vc recuperara a lista de objetos depois da busca principal, então o metodo fica assim :

[code] public Tarefa buscar(Long id){
Tarefa t = em.find(Tarefa.class, id);

    Query q = em.createQuery("select Ta.funcionarios from Tarefa as Ta");
    
    t.setFuncionarios(q.getResultList());
    
    return t;
}}[/code]

Faz uma busca da entidade, depois busca a lista do outro Objeto e seta ele.

Deu pra entender ?

Tem mais algum problema acontecendo ?

Eu to com alguns problemas aki tbm, diferentes desse , talvez vc possa me ajudar tbm kk.

vlw

Acabei de descobrir uma maneira bem mais facil aqui lendo sobre JPA.

O método FIND () tem comportamento EAGER e o GET R EFERENCE () tem comportamento LAZY.

então é só mapear o atributo como LAZY e o metodo fica :

[code] public Funcionario buscar(Long id){
Funcionario f = em.getReference(Funcionario.class, id);

    return f;
}

[/code]


O meu metodo de atualizar não esta funcionando o seu está ?

e nele vc usa merge ?

meu metodo é diferente, mas entendi como vc resolveu, só que eu não preciso fazer isso, o Spring carrega “automaticamente” os objetos com FetchType.LAZY pra mim.

so que estou tendo um problema ao usar a paginação do p:dataTable do PrimeFaces, quando clico nas 2ª,3ª,… pagina é lançado a exceção:

e como não consegui resolver este problema que acredito ser do Spring, mudei o mapeamento para FetchType.EAGER.
mas acho inviavel fazer de uma maneira parecida com a que vc fez, pois minha busca retorna um ‘List’ e não apenas um ‘ServicosExec’

uso merge, veja meu “dao”:

[code]
@Transactional
public abstract class AbstractFacade {

private Class<T> entityClass;
private static final Logger logger = Logger.getLogger(AbstractFacade.class.getName());

public AbstractFacade(Class<T> entityClass) {
    this.entityClass = entityClass;
}

protected abstract EntityManager getEntityManager();

public void create(T entity) {
    getEntityManager().persist(entity);
    logger.log(Level.INFO, "{0}: {1}", new Object[]{entityClass.getName(), entity.toString()});
}

public void edit(T entity) {
    getEntityManager().merge(entity);
    logger.log(Level.INFO, "{0}: {1}", new Object[]{entityClass.getName(), entity.toString()});
}

public void remove(T entity) {
    getEntityManager().remove(getEntityManager().merge(entity));
    logger.log(Level.INFO, "{0}: {1}", new Object[]{entityClass.getName(), entity.toString()});
}

public T find(Object id) {
    logger.log(Level.INFO, "{0}: {1}", new Object[]{entityClass.getName(), id.toString()});
    return getEntityManager().find(entityClass, id);
}

public List<T> findAll() {
    javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
    cq.select(cq.from(entityClass));
    logger.log(Level.INFO, entityClass.getName());
    return getEntityManager().createQuery(cq).getResultList();
}

public List<T> findRange(int[] range) {
    javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
    cq.select(cq.from(entityClass));
    javax.persistence.Query q = getEntityManager().createQuery(cq);
    q.setMaxResults(range[1] - range[0]);
    q.setFirstResult(range[0]);
    logger.log(Level.INFO, entityClass.getName());
    return q.getResultList();
}

public int count() {
    javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery();
    javax.persistence.criteria.Root<T> rt = cq.from(entityClass);
    cq.select(getEntityManager().getCriteriaBuilder().count(rt));
    javax.persistence.Query q = getEntityManager().createQuery(cq);
    logger.log(Level.INFO, entityClass.getName());
    return ((Long) q.getSingleResult()).intValue();
}

}[/code]

[code]
@Repository
@Transactional
public class ServicosExecFacade extends AbstractFacade {

protected EntityManager entityManager;
private static final Logger logger = Logger.getLogger(ServicosExecFacade.class.getName());

@PersistenceContext(unitName = "gsmPU")
public void setEntityManager(EntityManager entityManager) {
    this.entityManager = entityManager;
}

@Override
public EntityManager getEntityManager() {
    return entityManager;
}

public ServicosExecFacade() {
    super(ServicosExec.class);
}

@Transactional
public Date findMaxDataExecucao() {
    Query query = getEntityManager().createNamedQuery("ServicosExec.findMaxDataExecucao");
    logger.log(Level.INFO, query.getSingleResult().toString());
    return (Date) query.getSingleResult();
}

}[/code]

Seu problema é bem mais complexo :S

O método que vc ta usando que ta lançando exceção é esse aqui ?

public List<T> findAll() { javax.persistence.criteria.CriteriaQuery cq = getEntityManager().getCriteriaBuilder().createQuery(); cq.select(cq.from(entityClass)); logger.log(Level.INFO, entityClass.getName()); return getEntityManager().createQuery(cq).getResultList(); }

Um método como esse não resolveria seu problema não ?

[code]public List umMetodoQualquer() {
Query query = manager.createNamedQuery(“SELECT p FROM Pessoa p”);
List pessoas = query.getResultList();

return pessoas;
}
[/code]

O seu merge ta funcionando cara?

o merge esta funcionando perfeitamente.

o metodo não lança a exceção, o problema é saber trabalhar da maneira correta com FetchType.LAZY/FetchType.EAGER e/ou PrimeFaces+Spring

[quote=Ady_Junior]Um método como esse não resolveria seu problema não ?

[code]public List umMetodoQualquer() {
Query query = manager.createNamedQuery(“SELECT p FROM Pessoa p”);
List pessoas = query.getResultList();

return pessoas;
}[/code]
[/quote]

pior q não, usando criteria ou JPQL o problema continua

Que foda :S

Kdê os caras que sacam de JPA do GUJ ? kk

Eu tenho uma apostila féra de JPA aqui, to tirando minhas duvidas por ela, e tenho uma de JSF com JPA tbm, vc quer que eu te mande elas ?

se quiser passa seu email.

flw ;]

valneyrs@gmail.com
mande ai pra eu ver.

vlw