Problema com persistencia em mapeamento com hierarquia

Galera, estou com um pepino aqui há algumas horas e agora estou pedindo socorro. Há algo que não estou percebendo e gostaria da ajuda de vocês.

Segue meus código e o problema.



@Entity
@Table(name="erp_person")
public class Person implements MyEntity{
	private static final long serialVersionUID = 1L;
	
	@EmbeddedId
	private PersonId id;
	
	@Column
	private String name;

	@OneToOne(fetch=FetchType.LAZY, cascade={CascadeType.MERGE, CascadeType.PERSIST})
	@PrimaryKeyJoinColumn
	private Document document;


        public void addDocumentCompany(String _CNPJ, String _stateRegistration,
			String _fantasyName, String _director, String _businessType){
		document = new DocumentCompany(this, _CNPJ, _stateRegistration, 
				_fantasyName, _director, _businessType);
		type = PersonType.company;
	}
	
	public void addDocumentIndividual(String _CPF, String _RG, GenderType _gender,
			String _maritalStatus, String _nationality, Date _dateOfBirth,
			String _father, String _mother){
		document = new DocumentPerson(this, _CPF, _RG, _gender, _maritalStatus, 
				_nationality, _dateOfBirth, _father, _mother);
		type = PersonType.individual;
	}

...
}


@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class Document implements MyValueObject{
	private static final long serialVersionUID = 1L;
	
	@EmbeddedId
	@AttributeOverride(name="id", column=@Column(name="idperson"))
	protected PersonId idperson;
	
	public Document(Person _person){
		if (_person != null)
			idperson = (PersonId) _person.getIdentity();
	}

}

@Entity
@Table(name="erp_persondoccompany")
@DiscriminatorValue(value="company")
public class DocumentCompany extends Document {
	private static final long serialVersionUID = 1L;
	private String cnpj;
	private String ie;
	private String fantasyName;
	private String director;
	private String activity;
	
	protected DocumentCompany() {
		super(null);
	}
 ...
}


@Entity
@Table(name="erp_persondocindividual")
public class DocumentPerson extends Document {
	public enum GenderType { 
		male, female
	}
	private static final long serialVersionUID = 1L;
	private String cpf;
	private String rg;
	private String maritalStatus;
	private String nationality;
	private String father;
	private String mother;
	
	@Enumerated(EnumType.STRING)
	private GenderType gender;
	
	@Temporal(TemporalType.DATE)
	private Date birthday;
	
	protected DocumentPerson() {
		super(null);
	}

...
}

Tudo ia bem até que eu resolvi mudar um cliente de pessoa fisica para juridica, veio o erro que não consegui resolver.

a different object with the same identifier value was already associated with the session: [br.com.mysoft.myperson.domain.person.DocumentPerson#br.com.mysoft.myperson.domain.person.PersonId@1]
...

Veja só. Tudo está funcionando, inclusive as alterações e exclusões. O que não está funcionando é somente quando eu mudo de um tipo de documento para o outro(de o1:DocumentCompany para a o2:DocumentIndividual). O que estou fazendo de errado?

Desde já, obrigado!

Será que não faltou o @DiscriminatorValue da classe DocumentPerson?

Segundo a literatura para este tipo de mapeamento não há esta necessidade.

Qual a razão então de haver um @DiscriminatorValue na classe DocumentCompany e não haver na DocumentPerson?

Desculpe-me é erro meu ao tentar fazer testes. Já estou corrigindo!
Mas isto não resolve o problema.

Obrigado.

Tente usar a estratégia de mapeamento JOINED. A estratégia TABLE_PER_CLASS é a que costuma dar maiores problemas de associação.

[code]@Inheritance(strategy=InheritanceType.JOINED)
public abstract class Document {

}

@Entity
public class DocumentCompany extends Document {

}

@Entity
public class DocumentPerson extends Document {

}[/code]

O problema deste método é que a necessidade de ter uma tabela única para a junção dos dados, o que não é o correto para esta modelagem.

Bom, analisando os fatos verifiquei que o problema esta na criação de um novo objeto para o atributo documento.
Quando crio um novo atributo para documento sendo que o tipo de documento é outro ocorre o problema.

  public void addDocumentIndividual(String _CPF, String _RG, GenderType _gender,  
             String _maritalStatus, String _nationality, Date _dateOfBirth,  
             String _father, String _mother){  
         document = new DocumentPerson(this, _CPF, _RG, _gender, _maritalStatus,   
                 _nationality, _dateOfBirth, _father, _mother);  
         type = PersonType.individual;  
  }  

Ou seja, quando o atributo documento já possui uma referência de DocumentCompany ao tentar alterar para DocumentPerson é lançado o erro.

Alguma sugestão de resolução??

A estratégia JOINED não mantém uma única tabela. Ela mantém uma tabela para cada classe que faz parte da hierarquia, e faz a junção um-pra-um relacionando as chaves primárias de cada tabela. Se existem 3 classes - a classe mãe e suas duas herdeiras - teremos então 3 tabelas.

Se você quer manter apenas uma tabela, então use SINGLE_TABLE.

Cara você tem razão. Desculpe-me pela confusão… a cabeça não é mais a mesma.
Vc falando em Join e eu pensando em tabelas unicas, nada haver.

Bom o fato é que estou lendo dois capitulos do hibernate e nada me diz como resolver isto.
Eu preciso fazer uma associação com uma classe abstrata (ou interface) e a única maneira que encontrei foi esta.

Na literatura diz que estou fazendo tudo correto, mas que este tipo de mapeamento é problemático.
A verdade é que segui todas as recomendações do capitulo 5 e 7 do Hibernate, do Bauer. (java persistence com hibernate) e tudo me leva ao mesmo erro. Isto me leva a crer que não é um problema de mapeamento e sim de contrução de objeto.

Veja o meu post anterior.

Como você está salvando o objeto? Está usando Session.saveOrUpdate()? Caso positivo, tente trocar por um merge().

O que estou pensando agora é:

Em Person há um atributo de uma classe-entidade abstrata (Document). Este atributo recebe em tempo de execução seu verdadeiro tipo, ou DocumentPerson ou DocumentCompany.

Bom… então como fazer uma alteração de tipo em tempo de execução (mudar o atributo de DocumentPerson para DocumentCompany), sendo que o id é compartilhado de Person e isto fara com que o Hibernate verifique que existem dois objeto DIFERENTES com o mesmo id?

Tem mais… o hibernate precisa, além disto, resolver o fato de que um objeto deve deixar de ser persistente em detrimento do outro.

Eu vou tentar mais 1 hora e depois vou para o plano B.

Qual o plano B? Trocar pra JOINED? :mrgreen:

Não… Usar JDBC puro e simples!
Joined não funcionou tb.

Baseado nestas instruções http://stackoverflow.com/questions/217831/how-to-use-hibernate-any-related-annotations (e no capitulo 7) do livro do Bauer, foi uma otima solução de mapeamento, mas não resolve a atualização do objeto.

Então já vi que é final de festa.

Vlw!