Hibernate: Protegendo relacionamento bi-direcional [RESOLVIDO]

16 respostas
saoj

No tutorial do Hibernate ele explica como criar e proteger relacionamentos bi-direcionais. Só ficou a seguinte dúvida:

Pessoas tem eventos e Eventos tem pessoas.

private Set participants = new HashSet();
    
    protected void setParticipants(Set participants) {
        this.participants = participants;
    }
    
    protected Set getParticipants() {
        return participants;
    }
    
    public void addParticipant(Person p) {
        participants.add(p);
        p.events.add(this); // é! isso funciona para protected...
    }
    
    public void delParticipant(Person p) {
        participants.remove(p);
        p.events.remove(this);
    }

Beleza. Não tem como ninguém modificar o set participants sem passar pelo add e pelo del.

Só que eu quero ter acesso ao Set para listar os participantes, isto é, quero ter um método publico getParticipants(). Só que aí a segurança é perdida, pois qualquer um poderia meter um participante no set depois de charmar getParticipants().

Soluções:

  1. No getParticipants() fazer return Collections.unmodifiableSet(participants); (NAO FUNCIONA NO HIBERNATE!)

Erro: Exception in thread “main” org.hibernate.AssertionFailure: collection was not processed by flush()

  1. No getParticipants() dar um clone no Set antes de retornar. Não testei isso, mas se a lista for muito grande isso deve ser uma bosta. Ainda mais que o Hibernate tá acessando essa função toda hora!

  2. Criar um outro método público e deixar o getParticipants() privado mesmo. Ex:

public Set getParticipantsSet() {
    return Collections.unmodifiableSet(participants);
}

Mas aí o nome vai está fora do padrão JavaBean e vou ter que fazer: user.participantsSet na camada view.

  1. Deixar aberto mesmo e confiar que ninguem vai adicionar um participante por fora.

Qual é a melhor solução: 1), 2), 3) ou 4) ???

16 Respostas

Thiago_Senna

Só encontrei um problema nesta abordagem!
Melhor, é uma crítica…

Suponha que vc cria uma uma classe que extenda ArrayList, chamada MeuArrayList… e tendo o seguinte código

MeuArrayList participantes = new MeuArrayList ();

public List getParticipantsSet() {
     return Collections.unmodifiableList(participants);
 }

Quando vc tentar fazer isso:

MeuArrayList participantes = (MeuArrayList ) meuObj.getParticipantesList();

Será lançado um ClassCastException!

Talvez nem precise criar o MeuArraYList… acho que se vc fazer qualquer tipo de cas com uma lista que não pode ser modificado sempre acontecerá este erro!

Abraços!
Thiago Senna

saoj

Pelo que entendi a desvantagem de usar unmodifiedSet é que ele sempre vai retornar um Set e nunca um MySet.

Beleza, valeu pela dica pois eu realmente nao sabia disso, mas a questão não é essa. :slight_smile:

A questão é como proteger o set ???

1), 2), 3) ou 4 ?

Thiago_Senna
  1. Retorna apenas o Iterator dos participantes.

Thiago Senna

saoj

Boa idéia. Mas tem dois problemas que vc não pensou:

  1. Vou ter que mudar de qualquer jeito o nome da função, fugindo do padrão. (Ex: getParticipantsIterator())

  2. E principalmente iterator tem remove() ! :wink:

Thiago_Senna

humm… entendi…

entaum eu ficaria com a opção 4!

louds

Isso é uma opção do hibernate mesmo. Você tem que escolher de qual lado atualizar. Isso é para não violar as espectativas que temos dos pojos.

Eu tenho o habito de escrever unit tests e evitar usar qualquer API do hibernate nos meus POJOs. Usando apenas java.util não vou ter como garantir que as relações bidirecionais sejam preservadas sem escrever toneladas de código.

Bele, não faço isso e deixo meus pojos durante os testes sem isso. Quando eles estiverem sendo usados pelo hibernate, e tivessemos relacionamentos bidirecionais, ia dar um porrada de problemas.

_fs

Coloca access=“field” para esta propriedade e seja feliz.

saoj

:shock: :shock: :shock:

Fui feliz !

:lol:

louds

Sérgio, só tome cuidado com qual lado da tua relação atualiza ela. E nunca coloque os dois para fazer isso.

Thiago_Senna

Não estou usando hibernate, mas deve ser por isso que ontem meus testes unitários dos DAO’s pularam de 3 segundos para 96 segundos! :smiley:

jack_ganzha

Oi, louds, por que não?

valeuz…

Thiago_Senna

bom… naum sei se te ajudaria jack, mas comigo foi assim…

empresa carregava servicço prestado, servicoprestado carregava empresa, empresa carrega servico prestado, servicoprestado carrega empresa, empresa carregava servicço prestado, servicoprestado carregava empresa, empresa carrega servico prestado, servicoprestado carrega empresa, empresa carregava servicço prestado, servicoprestado carregava empresa, empresa carrega servico prestado, servicoprestado carrega empresa, empresa carregava servicço prestado, servicoprestado carregava empresa, empresa carrega servico prestado, servicoprestado carrega empresa, empresa carregava servicço prestado, servicoprestado carregava empresa, empresa carrega servico prestado, servicoprestado carrega empresa, empresa carregava servicço prestado, servicoprestado carregava empresa, empresa carrega servico prestado, servicoprestado carrega empresa.

É isso né???

louds

Não thiago, isso é por que você não está usando first level caching :wink:

O problema é o seguinte:

A.getBs().add(B);
B.getAs().add(A);

Isso vai gerar:

//atualizando relacionamentos de A
insert into AxB(A.id, B.id)
//atualizando relacionamentos de B
insert into AxB(A.id, B.id)

saoj

Estou inserindo em ambas as listas em ambos os lados, obviamente sem colocar a coisa num loop infinito.

Não devo fazer isso ???

No tutorial do hibernate ele diz que não há problema nisso.

???

louds

Não tem problema sergio, se você seguir as recomendações anteriores do tutorial, e vc seguiu pelo visto. Ele não fala para nunca colocar os dois lados de um relacionamento cuidando de atualizá-lo?

saoj

Valeu pelas dicas Louds!

Por um momento pensei que ele fizesse dois inserts na tabela N-N.

Veja o que o tutorial fala:

Do link: http://www.hibernate.org/hib_docs/v3/reference/en/html/tutorial.html

Criado 14 de julho de 2005
Ultima resposta 14 de jul. de 2005
Respostas 16
Participantes 5