Hibernate: cannot simultaneously fetch multiple bags

22 respostas
C

Tenho um sistema onde todos os relacionamentos @OneToMany são LAZY. Como eu uso Criteria, na hora de montar a consulta, preciso fazer o Join manualmente nessas listas para elas ficarem EAGER na minha consulta e trazer os objetos.

Quem faz isso é meu método findById onde eu passo além do ID do objeto a ser encontrado, os relacionamentos @OneToMany que vou precisar na consulta.

public T findById(PK id, String[] searchAssociations) {
		System.out.println("@@@ public T findById(PK id, String[] searchAssociations)");
		
		Session mySession = ((HibernateEntityManager) getEntityManager()).getSession();
		
		Criteria criteria = mySession.createCriteria(persistentClass);
		criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
		criteria.add(Restrictions.eq("id", id));

		// Todos meus @OneToMany são LAZY LOADED.
		// Aqui estou realizando o Fetch dos relacionamentos 1 para muitos.
		// Por exemplo, searchAssociations[2] seria algo como "emails,telefones,enderecos" e etc.
		// Todos relacionamentos @OneToMany dentro de uma entidade Funcionario por exemplo.
		if (searchAssociations[2] != null)
			for (String field : searchAssociations[2].split("[,]")) {
				System.out.println(" ==> Fazendo fetch em : " + field);
				criteria.createCriteria(field, field, Criteria.LEFT_JOIN);
			}

		List<T> tList = criteria.list();
		
		if (tList.size() > 1)
			System.out.println(" ==> Ops... encontrei mais de um resultado!");

		T obj = (T) tList.get(0);
		
		System.out.println(" ==> findById retornou uma entidade: " + obj.getClass());
		System.out.println(" ==> Sessão ainda está aberta? " + mySession.isOpen());
		
		// Tentativa de chamar os métodos getTelefones() e getEmails() via reflection para ver se o Hibernate
		// polupava as listas @OneToMany de um Funcionario. Note que chequei se a sessão estava aberta e estava (TRUE). Não funcionou.
		// Tentei o getEmails().size() também.
//		try {
//			obj.getClass().getMethod((new StringBuilder()).append("get").append("Emails").toString()).invoke(obj, new Object[] {});
//			obj.getClass().getMethod((new StringBuilder()).append("get").append("Telefones").toString()).invoke(obj, new Object[] {});
//		} catch (IllegalArgumentException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		} catch (SecurityException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		} catch (IllegalAccessException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		} catch (InvocationTargetException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		} catch (NoSuchMethodException e) {
//			// TODO Auto-generated catch block
//			e.printStackTrace();
//		}
		
		return obj;
//		return (T) criteria.uniqueResult();
	}

LOG (o pau acontece no criteria.list()):

Estava lendo nos fóruns do Hibernate e me parece que 2 fetchs EAGER em relacionamento @OneToMany ao mesmo tempo é conceitualmente impossível por causa do produto cartesiano gerado.

O engraçado é que eu utilizava esta mesma técnica no meu antigo trabalho e funcionava. Tenho o projeto antigo aqui ainda que está em produção e eu utilizo a mesma técnica.

Será que são versões do Hibernate? Eu uso o JBoss 4.2.3 que já vem com o Hibernate. Alguém já teve essa mesma experiência?

desde jah obrigado

22 Respostas

fantomas

Acho que isto aconteceu comigo, a diferença é que estava utilizando JPA/Hibernate.

Me parece que a solução foi trocar as listas do tipo List para o tipo Set nas associações.

Faz um teste e diz ai.

flws

C

Tb estou utilizando JPA/Hibernate.
Qd eu troco meus Lists por Sets, meu JSPX (minha rich:dataTable) não consegue encontrar a propriedade dentro do objeto Set. Quando deixo Lists ele consegue.
No meu sistema eu chamo os funcionários de colaboradores.

<rich:dataTable value="#{cadastroColaboradorBean.entity.emails}" var="colaboradorEmail" rows="5" width="100%" >
   <rich:column>
      <h:outputText value="#{colaboradorEmail.id}" />
   </rich:column>
</rich:dataTable>

O erro que acontece agora:

Achei muito estranho isso por que no meu antigo trabalho só utilizavamos List.
Mas realmente a documentação oficial manda serem Sets.
Por que ele não conseguiu achar a propriedade?

package br.com.facul.cadastro.model;

import java.io.Serializable;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.OneToMany;
import javax.persistence.Table;

import br.com.facul.cadastro.config.SystemConstants;

@Entity
@Table(name = "COLABORADOR", schema = SystemConstants.DATABASE_SCHEMA)
public class Colaborador implements Serializable {

	private static final long serialVersionUID = 1L;

	private Long id;
	private EstadoCivil estadoCivil = new EstadoCivil();
	private Escolaridade escolaridade = new Escolaridade();
	private String codigo;
	private String nome;
	private Set<ColaboradorEmail> emails = new HashSet<ColaboradorEmail>();
	private Set<ColaboradorTelefone> telefones = new HashSet<ColaboradorTelefone>();
	private Set<ColaboradorEndereco> enderecos = new HashSet<ColaboradorEndereco>();

	public Colaborador() {
	}

	@Id
	@GeneratedValue
	@Column(name = "ID")
	public Long getId() {
		return id;
	}

	public void setId(Long aId) {
		id = aId;
	}

	@ManyToOne
	@JoinColumn(name = "ID_ESTADO_CIVIL")
	public EstadoCivil getEstadoCivil() {
		return estadoCivil;
	}

	public void setEstadoCivil(EstadoCivil aEstadoCivil) {
		estadoCivil = aEstadoCivil;
	}

	@ManyToOne
	@JoinColumn(name = "ID_ESCOLARIDADE")
	public Escolaridade getEscolaridade() {
		return escolaridade;
	}

	public void setEscolaridade(Escolaridade aEscolaridade) {
		escolaridade = aEscolaridade;
	}

	@Column(name = "CODIGO", length = 10)
	public String getCodigo() {
		return codigo;
	}

	public void setCodigo(String aCodigo) {
		codigo = aCodigo;
	}

	@Column(name = "NOME", length = 50)
	public String getNome() {
		return nome;
	}

	public void setNome(String aNome) {
		nome = aNome;
	}

	@OneToMany(mappedBy = "colaborador")
	public Set<ColaboradorEmail> getEmails() {
		return emails;
	}

	public void setEmails(Set<ColaboradorEmail> aEmails) {
		emails = aEmails;
	}

	@OneToMany(mappedBy = "colaborador")
	public Set<ColaboradorTelefone> getTelefones() {
		return telefones;
	}

	public void setTelefones(Set<ColaboradorTelefone> aTelefones) {
		telefones = aTelefones;
	}

	@OneToMany(mappedBy = "colaborador")
	public Set<ColaboradorEndereco> getEnderecos() {
		return enderecos;
	}

	public void setEnderecos(Set<ColaboradorEndereco> aEnderecos) {
		enderecos = aEnderecos;
	}

	@Override
	public int hashCode() {
		final int prime = 31;
		int result = 1;
		result = prime * result + ((id == null) ? 0 : id.hashCode());
		return result;
	}

	@Override
	public boolean equals(Object obj) {
		if (this == obj)
			return true;
		if (obj == null)
			return false;
		if (getClass() != obj.getClass())
			return false;
		Colaborador other = (Colaborador) obj;
		if (id == null) {
			if (other.id != null)
				return false;
		} else if (!id.equals(other.id))
			return false;
		return true;
	}

}
fantomas

E se vc fizesse assim:

@OneToMany(mappedBy = "colaborador")  
public List<ColaboradorEmail> getEmails() {  
  List<ColaboradorEmail> e = new ArrayList<ColaboradorEmail>(0);
  
  e.addAll(emails);

  return e;  
}

P.S. Eu não gosto muito disto, mas acho que lhe ajudar.

flws

C

vlw fantomas, mas deu o mesmo erro
o erro tah acontecento eh no criteria.list() dentro do meu método findByID()
preciso de uma forma de o Hibernate conseguir fazer essas duas associações
agora o q eu achei estranho foi o fato de quando eu mudei os Lists pra Sets; não consegui acessar as propriedades @OneToMany dos meu Entity Beans na minha rich:dataTable enquanto q com Lists eu conseguia
com os Sets parece q o cannot simultaneously fetch multiple bags é corrigido, mas, dah o pau no JSPX

estranho q igual como eu disse antes… no meu antigo trabalho não tinha nada disso… era tudo com Lists e o Criterias cheios de fetchs. fetchs até de objetos dentro de objetos

Lavieri

set com certeza eles não entenderão, isso se tiver paginação…

teria q ser algo como LinkedHashSet …

para que possa ter os métodos get(index) …

C

Tentei com o LinkedHashSet… olha o erro q deu no JSPX:

Tentei tb das duas formas no meu Entity Bean:

Agora, se eu mudo os Sets pra Lists, dah pau no criteria.list() e o cannot simultaneously fetch multiple bags.
Há dois dias to dormindo em cima disso e não resolvo… e estranho q nunca tinha me acontecido isso antes

vlw

Lavieri

tipo… não kero decifrar o que seu código ali que monta um criteia faz…

escreve ele aki… sem os Fors e sem os loots… e mostra como são tuas classes (axo q essa parte ja tem no post) … ai da pra dar 1 olhada

fantomas

Acho que vc viajou no meu exemplo (ou eu estou viajando rsrsrs).

@OneToMany(mappedBy = "colaborador")  
public List<ColaboradorEmail> getEmails() {  
  List<ColaboradorEmail> e = new ArrayList<ColaboradorEmail>(0);
  
  e.addAll(emails);

  return e;  
}

Note que o retorno do método é um LIST e NÃO um SET a página não teria como saber desta diferença de estruturas.

flws

C

Lavieri
Meu método findById pretende achar um Colaborador com todos os seu telefones e e-mails.
Meu entity bean Colaborador eu postei logo acima.
"“emails” e “telefones” são Lists @OneToMany dentro do meu Entity bean Colaborador.
Abaixo o q o meu findById faz na verdade (sem o comando for):

Criteria criteria = getSession().createCriteria(Colaborador.class);
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);
criteria.add(Restrictions.eq("id", id));
// Agora preciso da lista de e-mails e de telefones do colaborador.
// "emails" e "telefones" são propriedades List @OneToMany no meu entity bean Colaborador.
criteria.createCriteria("emails", Criteria.LEFT_JOIN);
criteria.createCriteria("telefones", Criteria.LEFT_JOIN);
return (T) criteria.uniqueResult();

fantomas
eu q tinha viajado :slight_smile:
to testando aqui

C

fantomas
com essa tecnica sua eu consegui um comportamento estranho
a exceção é lançada toda hora, porém, a página JSF não para de funcionar (ela não “morre” com a exceção) e os e-mails e os telefones estão vindo
pra falar a verdade funciou,… soh q a exceção cannot simultaneously fetch multiple bags eh lancada toda hora soh q apenas no log do console
algumas coisas dinamicas do ajax do richfaces tb pararam de funcionar…
acho q pq a excecao acontece qd ele tah renderizando a view… entao o jsf para de renderizar

alguma ideia pq a excecao continua?
e eu tava afim de fazer sem essa “gambi” hehe… achei meio deselegante

vlw

Lavieri

bom... ja vi e ja testei... e o problema é justamente o não LAZY do seu criteira... ele ta fazendo chamada de 2 mapeamento ao mesmo tempo...por isso da erro!...

bom, mapeie suas classes com LAZY e tire as listas de emails e telefones do seu criteria q tudo dará certo..

...........

Criteria criteria = getSession().createCriteria(Colaborador.class);   
criteria.setResultTransformer(Criteria.DISTINCT_ROOT_ENTITY);   //&lt;&lt;== não sei se precisa... mas enfim... como quiser...
criteria.add(Restrictions.idEq(id));   // &lt;=== ID equals é melhor 
// Agora preciso da lista de e-mails e de telefones do colaborador.   
// &quot;emails&quot; e &quot;telefones&quot; são propriedades List @OneToMany no meu entity bean Colaborador.   
//criteria.createCriteria(&quot;emails&quot;, Criteria.LEFT_JOIN);   &lt;=== REMOVE
//criteria.createCriteria(&quot;telefones&quot;, Criteria.LEFT_JOIN);   &lt;&lt;=== REMOVE
return (T) criteria.uniqueResult();

mudanças no seu mapeamento...

@OneToMany(mappedBy = &quot;colaborador&quot;, fetch=LAZY)   
    public Set&lt;ColaboradorEmail&gt; getEmails() {   
        return emails;   
    }   
  
    @OneToMany(mappedBy = "colaborador", fetch=LAZY)   
    public Set&lt;ColaboradorTelefone&gt; getTelefones() {   
        return telefones;   
    }

assim o mapeamento funciona...pq cada lista sera carregada a sua vez... vc não pode ter + de um EAGER na mesma entidade...

Lavieri

fiz um teste aqui, com classes semelhantes...

Estados 1 - N Cidades
Estados 1 - N Times de Futebol

@Entity
public class Estado implements Serializable {

    @Id
    @GeneratedValue(strategy=IDENTITY)
    @Column(name="id", unique=true, nullable=false)
    private Integer id;

    @OneToMany(cascade={MERGE,PERSIST,REFRESH}, fetch=LAZY, mappedBy="estado")
    private Set&lt;Cidade&gt; cidades = new LinkedHashSet&lt;Cidade&gt;(0);

    @OneToMany(cascade={MERGE,PERSIST,REFRESH}, fetch=LAZY, mappedBy="estado")
    private Set&lt;Time&gt; times = new LinkedHashSet&lt;Time&gt;(0);
//...
}
Mapeamento Cidade
public class Cidade implements Serializable {

    @Id
    @GeneratedValue(strategy = IDENTITY)
    @Column(name="id", unique=true, nullable=false)
    private Integer id;

    @ManyToOne(fetch=LAZY,cascade={PERSIST,MERGE,REFRESH})
    @JoinColumn(name="estado_id", nullable=false)
    private Estado estado;
//...
}
Mapeamento Times de Futebol
@Entity
public class Time implements Serializable {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Integer id;

    @ManyToOne(fetch=LAZY,cascade={PERSIST,MERGE,REFRESH})
    @JoinColumn(name="estado_id", nullable=false)
    private Estado estado;
//...
}

um exemplo de criteria q fiz...

public static void main(String[] args) {
        Estado paraiba = (Estado)SESSION.createCriteria(Estado.class)
                .add(Restrictions.idEq(15))
                .uniqueResult();
        
        System.out.println("Cidades");
        System.out.println(Arrays.toString(paraiba.getCidades().toArray()));

        System.out.println("\nTimes de futebol");
        System.out.println(Arrays.toString(paraiba.getTimes().toArray()));

    }
resultado...
Cidades
[Lagoa Seca, Lastro, Lagoa, Lagoa de Dentro, Juazeirinho, Junco do Seridó, ..., Mato Grosso, Maturéia, Mogeiro]

Times de futebol
[Nacional de Patos, Bota Fogo da Paraíba, Campinese, Treze]

os 3 pontinhos é pq tem 253 cidades ia ficar chato escrever aqui...

C

MUITO OBRIGADO Lavieri :slight_smile:

resolveu o problema… e o erro dos Sets continuou persistindo… nao acha a propriedade no dataTable… então fiz com list mesmo
a maneira q eu estava tentando fazer q estava errada… brigadao pelo exemplo

e aproveitando… qual a maneira correta de se pegar relacionamentos @MAnyToOne e @ManyToMany
agora sei q para @OneToMany é só dar um getList().size()
vou me certificar q TUDO esteja LAZY

Lavieri

CommanderShepard:
MUITO OBRIGADO Lavieri :slight_smile:

resolveu o problema… e o erro dos Sets continuou persistindo… nao acha a propriedade no dataTable… então fiz com list mesmo
a maneira q eu estava tentando fazer q estava errada… brigadao pelo exemplo

e aproveitando… qual a maneira correta de se pegar relacionamentos @MAnyToOne e @ManyToMany
agora sei q para @OneToMany é só dar um getList().size()
vou me certificar q TUDO esteja LAZY

não entendi sua pergunta… como assim maneira de pegar o relacionamento ?? vc fala pra popular um campo LAZY ?? se for isso basta fazer chamada no método, que ele popula

no meu exemplo ali de cima, c eu tivesse um time, e quizer popular o estado basta fazer… timeDeFutebol.getEstado(); que ele carrega o campo que era LAZY, qual é a sua intenção com isso ?? pode fechar a sessão ? pq ficar carregando os dados, antes de precisar deles vai ficar xeio de coisa na memoria aota …

se a duvida era de como fazer um Mapeamento de MantoToOne … tme um exemplo aqui em cima… se for de como fazer @ManyToMany olha esses 2 links
http://www.guj.com.br/posts/list/121586.java
http://www.guj.com.br/posts/list/121523.java

C

tipo
coloquei meus relacionamentos @ManyToOne pra Lazy agora:

// Entidade Colaborador

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "ID_ESTADO_CIVIL")
	public EstadoCivil getEstadoCivil() {
		return estadoCivil;
	}

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "ID_ESCOLARIDADE")
	public Escolaridade getEscolaridade() {
		return escolaridade;
	}

e tenho a seguinte situação:
na tela de cadastro de colaboradores, tenho um botão “Pesquisar tudo”, que traz todos os colaboradores listados em uma tabela
nessa tabela tem a coluna do EstadoCivil e da Escolaridade do Colaborador
mas como agora eles estão como Lazy, ao pesquisar todos os colaborares, os dados de EstadoCivil e Escolaridade não virão e a tabela vai dar pau
meu método findAll():

public List<Colaborador> findAll() {
		Criteria criteria = getSession().createCriteria(Colaborador.class);
		return criteria.list();
	}

como faço pra trazer o EstadoCivil e Escolaridade na colsulta?
vou deixar todo os @ManyToOne como Lazy por que mais pra frente vou ter mais relacionamentos @ManyToOne, mas que aparecerão em poucas consultas

Lavieri

Quem te falou que não virão ?? so não virar se vc fechar a conexão com o DB, o que não há sentido em ser feito…

fora isso o Hibernate é Inteligente, e não duplica objetos atoa… ele sempre usa a mesma instancia caso tenha chance…

LAZY quer dizer que só sera carregado se for chamado…

Bom por exemplo…

public static void main(String[] args) { List&lt;Cidade&gt; cidades = REPOSITORIES.findAll(Cidade.class); System.out.println(cidades.get(6).getEstado() == cidades.get(7).getEstado()); }
ai eu busco todas as cidades… e verifico se os estados das cidades 6 e 7 (que no caso é a bahia) são o mesmo objetos…
note que é igualdade direta, é pra saber se é a mesma referencia, e no caso da true, pois ele popula todos com o mesmo objeto…

public static void main(String[] args) { List&lt;Cidade&gt; cidades = REPOSITORIES.findAll(Cidade.class); EntityUtils.SESSION.close(); System.out.println(cidades.get(6).getEstado() == cidades.get(7).getEstado()); }
esse gera 1 erro, pois eu fechei a sessão e depois tentei acessar um método LAZY, gerando uma exceção, por não conseguir carregar…

mas pq vc ker fechar a sessão ? antes de usar os dados ??

e se kizer um gato… public static void main(String[] args) { List&lt;Cidade&gt; cidades = REPOSITORIES.findAll(Cidade.class); List&lt;Estado&gt; estados = REPOSITORIES.findAll(Estado.class); EntityUtils.SESSION.close(); System.out.println(cidades.get(6).getEstado() == cidades.get(7).getEstado()); }
bom nesse caso ai, vai funcionar, pq todos os estados foram carregados, e o Hibernate usa uma unica instancia pra tudo…

No meu projeto existe 1 instancia publica aberta 100% do tempo, uso ela pra consultas, e quando preciso de tranzações abro outra sessão pra fazer a tranzação…

C

vlw demais pela ajuda lavieri
to aprendendo muito nesse topico

eu não entendo muito do controle da sessao do hibernate ainda
eu utilizo o entitymanager… eu o obtenho desta forma

@PersistenceContext(name = "cadastroPersistenceUnit", unitName = "cadastroPersistenceUnit")
	private EntityManager entityManager;

	@Override
	public Session getSession() {
		return ((HibernateEntityManager) getEntityManager()).getSession();
	}

	public EntityManager getEntityManager() {
		if (entityManager == null)
			throw new IllegalStateException("EntityManager não foi inicializado para utilização dos objetos DAO.");

		return entityManager;
	}

pelo q li o entitymanager controla a sessao pra mim sempre q eu chamo um metodo de persistencia

soh mais uma coisa
agora q puz tudo LAZY, consegui arrumar as consultas
soh q por incrivel q pareca deu mais um erro de LazyInitializationException, mas dessa vez quando eu fui salvar um Colaborador

se eu salvo o Colaborador sem informar o EstadoCivil ou a Escolaridade ele salva ok
mas agora se eu informar qualquer um dos dois objetos… ele dah o LazyInitializationException… soh na hora de salvar
nao entendi por que
sendo q passo um novo EstadoCivil completo, com ID e demais atributos para o Colaborador antes de salvar

Lavieri

bom esse problema ai é o que te falei ^^ … ele da quando a sessão é fechada… e depois vc tenta abrir uma propriedade LAZY…

uma coisa que não sei se ficou claro la no inicio… o que não da pra fazer com EAGER é ter multiplas BAGS … ou seja… vc não pode ter VARIOS LISTS ou SETS com eager no mesmo objeto… (e se não me engano, so não pode pra mesma propriedade, ou seja, pro mesmo mappedBy) … propriedade do tipo 1-1, posso estar enganado, mas pelo que me lembro vc pode usar EAGER…

eu gosto da minha sessao publica, que mantenho em um lugar static do programa ^^ gosta dela, e a uso sempre pra consulta, isso favorece, principalmente pq ele via fazendo cache, do que ja foi aberto, e não refaz consulta no banco se não for preciso…

EntityManager do Hibernate é só um adptador, da Interface EntityManager do JPA para a Session do hibernate, mas na essencia ele é uma Session

C

entao mas agora tah dando a lazy excetpion na hora de salvar
e salvar o ManyToOne EstadoCivil dentro de Colaborador

tudo tah funfando agora… exceto isso
muito estranho pq passo um EstadoCivil completo (faco um findById no EstadoCivil) e em seguida passo pro colaborador.setEstadoCivil(estadoCivilEncontradoPeloFindById)

ai qd salvo o colaborador… ele dah pau

Lavieri

entre essas 2 etapas a sessão ta sendo fechada… por isso da o erro

como a propriedade não é uma lista, vc pode usar EAGER

C

resolvi velho
fiz o seguinte

@ManyToOne(fetch = FetchType.LAZY, cascade = { CascadeType.PERSIST, CascadeType.MERGE })

pronto… agora minha persistencia tah tudo ok
vc ve algum problema em setar os @ManyTOOne como CascadeType.PERSIST, CascadeType.MERGE ?

putz esse tópico rendeu ein!!!
vlw demais pela ajuda
marcando como resolvido
:slight_smile:

Lavieri

eu sempre uso MARGE, PERSIST e REFRESH ...

REMOVE, quando são coisas 1-1 tipo

Pessoa -> PessoaEndereço .... ai incluo o cascade 1-1

na maioria dos outros casos uso os 3 cascades....

assim posso fazer coisas do tipo

Pais p = new Pais(&quot;Brasil&quot;);
Estado paraiba = p.addEstado(&quot;Paraíba&quot;);
paraiba.addCidade(&quot;João Pessoa&quot;);

REPOSITORIES.persist(p); //pronto ja salvou tudo!

tudo bem q eu podia ter feito assim

Cidade joaoPessoa = new Pais(&quot;Brasil&quot;).addEsado(&quot;Paraiba&quot;).addCidade(&quot;João Pessoa&quot;);
REPOSITORIES.persist(joaoPessoa); //aki tb salva tudo, em cascade ^^
Criado 24 de março de 2009
Ultima resposta 26 de mar. de 2009
Respostas 22
Participantes 3