Atualização de objeto transiente

3 respostas
D

Estou brigando com esse problema alguns dias já.

Tenho duas classes persistentes conforme descritas abaixo, e mapeadas
para as tabelas correspondentes.

A classe Person possui os atributos "fieldA" e "fieldB" que identificam um registro em 'persons' exclusivamente. Mas estou tendo problemas com o identificador (id).

Se eu tenho um objeto Person transiente e quero salvá-lo no banco eu preciso antes verificar se já não existe um registro no banco cujos atributos
"fieldA" e "fieldB" coincidam. No caso de já existir eu preciso apenas atualizar aquele registro baseado na instância do objeto que eu recebo no argumento do método save(Person). Se não existir, um novo Person será salvo no banco de dados.

O comportamento que eu estou observando, com a implementação atual do método save(Person) é: ele atualiza o registro na tabela 'persons' corretamente mas replica os filhos (sons) na tabela 'sons'. Não tenho idéia do que fazer para corrigir o problema.

Seguem as tabelas, mapeamentos (JPA) e a implementação do método save(Person).

TABLE persons (
   id       INT ...,
   name     VARCHAR(40),
   a_field  VARCHAR(10),
   b_field  VARCHAR(10)
)

TABLE sons (
   id         INT ...,
   person_id  INT REFERENCES persons(id),
   name       VARCHAR(40)
)

Mapeamento:

@Entity
@Table(name="persons")
class Person {
 
   // ...

   @Id
   @GeneratedValue(strategy=GenerationType.IDENTITY)
   public Long getId() {
      return this.id;
   }

   @Column(name="name", length=40, nullable=false)
   public String getName() {
      return this.name;
   }

   @Column(name="a_field", length=10, nullable=false)
   public String getFieldA() {
      return this.fieldA;
   }

   @Column(name="b_field", length=10, nullable=false)
   public String getFieldB() {
      return this.fieldB;
   }   
 
   @OneToMany(cascade=CascadeType.ALL, mappedBy="person")
   public Set<Son> getSons() {
      return this.sons;
   }

}

@Entity
@Table(name="sons")
public class Son {

   // ...

   @Id
   @GeneratedValue(strategy=GenerationType.IDENTITY)
   public Long getId() {
      return this.id;
   }

   @Column(name="name", length=40, nullable=false)
   public String getName() {
      return this.name;
   }

   @ManyToOne(cascade=CascadeType.ALL)
   @JoinColumn(name="person_id")
   public Person getPerson() {
      return this.person;
   }

}

E a implementação do método save(Person):

public void save(Person person) throws HibernateException {

   Session session = ...
   session.beginTransaction();

   Person aperson = (Person)session.createCriteria(Person.class)
             .add(Restrictions.eq("fieldA", person.getFieldA()))
             .add(Restrictions.eq("fieldB", person.getFieldB()))
             .uniqueResult();

   if (aperson != null) {
      person.setId(aperson.getId());
      session.evict(aperson);
   }

   session.saveOrUpdate(person);
   session.getTransaction().commit();
 
}

Na verdade o título deveria ser "Atualização através de um objeto transiente".

3 Respostas

_fs

O argumento “person” passado para o método save() possui os fillhos populados? Se sim, estes filhos estão com a PK populada?

marciocamurati

Só complementando o comportamento que você espera é que caso existam elementos populados em Sons ocorra um “sincronismo” ou seja apagar os diferentes, atualizar os com o mesmo ID e inserir os novos?

[]s

D

Rapaz!!! Será?
É, na prática sim… os IDs não estão definidos nos filhos. É que o objeto “Person” (já populado com os "Son"s) vem de uma fonte que não tem idéia de persistência. Por isso não tenho IDs. Na verdade eu quero mesmo um sincronismo (citação abaixo). Mas já funciona muito bem, se todos os "Son"s forem excluídos antes de os novos serem incluídos. Eu tentei isso usando um session.delete(aperson) e um session.evict(aperson) mas, claro, não funcionou.

Exato!
Conforme disse acima, se eu conseguir remover os "Son"s antes de atualizar o “Person” que contém outros "Son"s (ou os mesmos, semanticamente) já fica jóia.

Criado 20 de junho de 2008
Ultima resposta 20 de jun. de 2008
Respostas 3
Participantes 3