Como mapear no JPA TABLE_PER_CLASS herança para [columns] e [sequences] diferentes?

7 respostas
F
Olá, estou tendo dificuldade em fazer o seguinte mapeamento, onde tenho uma tabela por subclasse, usando JPA 2. Segue classe abstrata(superclasse) abaixo:
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class FieldDefinition extends BaseEntity {
	@Id
	@GeneratedValue(strategy=GenerationType.SEQUENCE)
	protected Integer id;
}
e na subclasse:
@Entity
@Table(name="integer_fields")
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
@AttributeOverride(name="id", column=@Column(name="integer_field_id") )
@SequenceGenerator(name="generator", sequenceName="integer_fields_integer_field_id_seq")
public class IntegerField extends FieldDefinition {

	
}
no caso o atributo id é compartilhado entre as subclasses, e estas tem chave primárias diferentes, e consequente nome se sequences diferentes. do jeito que está acima, ganho o seguinte erro:
org.springframework.dao.InvalidDataAccessResourceUsageException: could not get next sequence value; SQL [select nextval ('hibernate_sequence')]; nested exception is org.hibernate.exception.SQLGrammarException: could not get next sequence value
alguém saberia como resolver mapeando a partir da subclasse?

7 Respostas

F

tinha visto algumas abordagens que mapeia a superclasse com @MappedSuperclass, onde na classe FieldDefinition ao invés de usar @Entity, usar @MappedSuperclass, mas isso implicará no seguinte erro abaixo, pois eu tenho outra classe, que tem um relacionamento @OneToMany para FieldDefinition

classe InfoRequest, que tem relacionamento OneToMany com FieldDefinition(abstrata)

@Entity @Table(name="info_requesters") public class InfoRequest { ... @OneToMany(cascade=CascadeType.ALL) @JoinColumn(name="info_request_id") private Set<FieldDefinition> fieldDefinition; }
erro ao tentar modificar classe FieldDefinition, usando @MappedSuperclass, ao invés de @Entity

resumindo, apenas estou realçando o porque não usei @MappedSuperclass para mapear a superclasse, FieldDefinition, apesar de este não ser o erro em específico, permanecendo o primeiro acima.

O

Lá no seu Set, você pode mapear com @CollectionOfElements.

@CollectionOfElements
     @JoinTable(name="fieldDefinitionTable",
     joinColumns=@JoinColumn(name="ID_ENTIDADE_DONA_DA_LISTA",
            referencedColumnName="ID_FIELD_DEFINITION"))
     private Set<FieldDefinition> fieldDefinition;

Tenta aí

P

eu acredito que não exista a necessidade de mapear novamente isso

@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)

na classe IntegerField

caso você esteja com problema com o atributo ID ser utilizado em várias tabelas, você pode utilizar um @TableGenerator que controla e distribui os ID para todas as tabelas que extendem essa classe.

você ja tentou isso?

F

oddy.silva:
Lá no seu Set<T>, você pode mapear com @CollectionOfElements.

@CollectionOfElements @JoinTable(name="fieldDefinitionTable", joinColumns=@JoinColumn(name="ID_ENTIDADE_DONA_DA_LISTA", referencedColumnName="ID_FIELD_DEFINITION")) private Set&lt;FieldDefinition&gt; fieldDefinition;Tenta aí


entendi, mas eu não poderia utilizar CollectionOfElements, porque a classe FieldDefinition é uma classe abstrata que não está mapeada para uma tabela.

[quote=pinheiro.cc]eu acredito que não exista a necessidade de mapear novamente isso

é vero, efeito copy-paste aqui no tópico. ignorem!

não tentei porque inicialmente não tinha achado que se aplicava ao meu caso por conta das sequences serem diferentes.

F

para explicar melhor o caso tenho o seguinte cenário no modelo de classe e banco, respectivamente

obs: analisando a priori estes modelos individualmente, parece uma idéia inútil, mas removi bastantes informações afim de facilitar entendimento, como idéia básica para mapear estes relacionamentos.

F

fazendo uns testes a força bruta, resgistrando tentativa falha, ele gerou o seguinte jpql

Hibernate: insert into owners (logo_id, name) values (?, ?)
Hibernate: select currval('owners_owner_id_seq')
Hibernate: insert into info_requesters (final_date, owner_id) values (?, ?)
Hibernate: select currval('info_requesters_info_request_id_seq')
Hibernate: select nextval ('integer_fields_integer_field_id_seq')
Hibernate: insert into integer_fields (label, required, info_request_id, integer_field_id) values (?, ?, ?, ?)
Hibernate: update FieldDefinition set info_request_id=? where integer_field_id=?

reparem que na última linha ele gerou uma operação de update para classe abstrata onde não deveria, apesar de na linha anterior ter feito um insert na tabela correta mapeada a partir da subclasse. segue como ficou mapeamento para gerar comportamento acima:

classe abstrata FieldDefinition ... Fiz propositalmente declaração dos campos na superclasse, pois não consegui rescrevê-los na subclasse demonstrado no 1º post deste tópico
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class FieldDefinition extends BaseEntity {

private static final long serialVersionUID = 8428299722798237491L;
	...	
	// sei que este mapeamento abaixo está errado na superclasse, mas não consegui específicá-los na subclasse, então fiz teste de força bruta que não deu certo
	@Id @Column(name="integer_field_id")
	@GeneratedValue(strategy=GenerationType.SEQUENCE, generator="generator")
	@SequenceGenerator(name="generator", sequenceName="integer_fields_integer_field_id_seq")
	protected Integer id;
}
subclasse IntegerField
@Entity
@Table(name="integer_fields")
public class IntegerField extends FieldDefinition {
	...
}
classe InfoRequest que possui relacionamento OneToMany com classe abstrata FieldDefinition, do tipo Set
@Entity
@Table(name="info_requesters")
public class InfoRequest extends BaseEntity {
	...
	@OneToMany(cascade=CascadeType.ALL)
	@JoinColumn(name="info_request_id")
	private Set<FieldDefinition> fieldDefinition;
}
segue abaixo como ficou chamada para metodo de teste do log acima identificado na geração do hql
@Test public void testBasic() {

	InfoRequest infoRequest = createInfoRequest(); // cria instancia apenas da entidade infoRequest
		
	IntegerField fieldAge = new IntegerField(); // criando item 
	fieldAge.setLabel("age");
	fieldAge.setRequired(true);
	fieldAge.setInfoRequest( infoRequest );
		
	infoRequest.add( fieldAge ); // adicionando item ao infoRequest
	infoRequestRepository.create( infoRequest ); // criando, 
		
	sync(); // aqui faz em.flush() e em.clear() ... e ele dá erro na chamada flush() no comentário abaixo
		
	assertTrue( true );
}

erro ocorre após executar linha de método acima sync()

Caused by: java.sql.BatchUpdateException: Entrada em lote 0 update FieldDefinition set info_request_id=107 where integer_field_id=1700 foi abortada. Chame getNextException para ver a causa. ERRO: relação "fielddefinition" não existe

obs: último acima informa uma coisa óbvia, faltando apenas fazer com que ele gere jpql para entidade certa. :?:

F

uma pergunta vocês sabem como faço para sobrescrever mapeamento da chave primária a partir da subclasse. não é sobrescrever associação ou atributo, é o @Id da superclasse.
no caso tentei fazê-lo da seguinte forma:

super classe FieldDefinition
@Entity
@Inheritance(strategy=InheritanceType.TABLE_PER_CLASS)
public abstract class FieldDefinition extends BaseEntity {

	@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
	protected Integer id;

	...

	// movi o atributo de infoRequest para subclasse, no caso IntegerField
	// fiz isto acima, porque ele gerava aquele update nada a ver em negrito como descrevi acima -> http://www.guj.com.br/posts/list/206452.java#1049305

}
subclasse IntegerField
@Entity
@Table(name="integer_fields")
@AttributeOverride(name="id", column=@Column(name="integer_field_id"))
public class IntegerField extends FieldDefinition {

	@Id @GeneratedValue(strategy=GenerationType.IDENTITY)
	protected Integer id;
	
	@ManyToOne
	@JoinColumn(name="info_request_id")
	private InfoRequest infoRequest;
}
fazendo isto recebo a seguinte exceção
Caused by: java.lang.ClassCastException: org.hibernate.mapping.UnionSubclass cannot be cast to org.hibernate.mapping.RootClass
vi no forum do hibernate sobre onde uma alternativa seria, tipo colocando uma anotação no método get, mas não funfou/funcionou
Criado 11 de maio de 2010
Ultima resposta 13 de mai. de 2010
Respostas 7
Participantes 3