Sou recente em Java. Estou trabalhando com a tecnologia “Hibernate 3” e estou tendo problemas na hora de executar uma (ou várias) inserções numa tabela NxN, segue o esquema abaixo:
Author (pk, Description)
Work (pk, Description)
Author_Work (pk, fkAuthor, fkWork)
OBS: É necessário que não exista a junção dos valores das foreign keys em uma primary keys na tabela Author_Work!
O Problema: Antes de adicionar as tags “set” em ambos os mapeamentos, tudo estava perfeito. Depois de adicionar estas tags nos mapeamentos e os métodos getters e setters nas classes Author.java e Sector.java, estou tendo problemas em tempo de execução. Segundo o console do java, estou tendo problemas com a chave primária da tabela “Author_Work”.
O mapeamento XML que utilizo para classe Author é o seguinte:
Vale lembrar que estou tendo sucesso na hora de executar os códigos se eu não utilizar as tags “set” no mapeamento. Não acho que seria uma boa prática de programação utilizar um mapeamento exclusivo para Author_Work, Sujestões?
Este problema acima foi um caso hipotético que bolei para todos entederem meu problema, meu código é bem parecido. Mas, basicamente, você deverá substituir as entidades/classes Author por Sector, Work por Contact e Author_Work por SectorContact. As chaves primárias são nomeadas como “pk” e as chaves estrangeiras como fkSector/fkContact.
A tabela intermediária SectorContact contém (pk, fkContact, fkSector)
Segue abaixo a StackTrace do meu código:
2005-08-05 08:55:41,187 ERROR JDBCExceptionReporter -> Entrada em lote 0 insert into sectorcontact (fkSector, fkContact) values ( foi abortada. Chame getNextException() para ver a causa.
2005-08-05 08:55:41,187 ERROR JDBCExceptionReporter -> ERROR: null value in column "pk" violates not-null constraint
2005-08-05 08:55:41,203 ERROR AbstractFlushingEventListener -> Could not synchronize database state with session
org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:63)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:181)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:226)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:140)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:274)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:730)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:324)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:86)
at com.pacto4.dao.DAO.save(DAO.java:106)
at com.pacto4.dao.person.Teste.main(Teste.java:65)
Caused by: Entrada em lote 0 insert into sectorcontact (fkSector, fkContact) values ( foi abortada. Chame getNextException() para ver a causa.
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc2Statement.java:107)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:57)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:174)
... 9 more
Exception in thread "main" org.hibernate.HibernateException: org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at com.pacto4.dao.DAO.save(DAO.java:109)
at com.pacto4.dao.person.Teste.main(Teste.java:65)
Caused by: org.hibernate.exception.ConstraintViolationException: Could not execute JDBC batch update
at org.hibernate.exception.SQLStateConverter.convert(SQLStateConverter.java:63)
at org.hibernate.exception.JDBCExceptionHelper.convert(JDBCExceptionHelper.java:43)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:181)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:226)
at org.hibernate.engine.ActionQueue.executeActions(ActionQueue.java:140)
at org.hibernate.event.def.AbstractFlushingEventListener.performExecutions(AbstractFlushingEventListener.java:274)
at org.hibernate.event.def.DefaultFlushEventListener.onFlush(DefaultFlushEventListener.java:27)
at org.hibernate.impl.SessionImpl.flush(SessionImpl.java:730)
at org.hibernate.impl.SessionImpl.managedFlush(SessionImpl.java:324)
at org.hibernate.transaction.JDBCTransaction.commit(JDBCTransaction.java:86)
at com.pacto4.dao.DAO.save(DAO.java:106)
... 1 more
Caused by: Entrada em lote 0 insert into sectorcontact (fkSector, fkContact) values ( foi abortada. Chame getNextException() para ver a causa.
at org.postgresql.jdbc2.AbstractJdbc2Statement.executeBatch(AbstractJdbc2Statement.java:107)
at org.hibernate.jdbc.BatchingBatcher.doExecuteBatch(BatchingBatcher.java:57)
at org.hibernate.jdbc.AbstractBatcher.executeBatch(AbstractBatcher.java:174)
... 9 more
Não, o problema não é a tabela. O problema é o mapeamento do Hibernate que não sei como fazer para este caso. Tal mapeamento não é exemplificado no manual no site do Hibernate, não tenho pistas de como faze-lo pelo DTD, nem é comentado na API Javadoc.
OBS: A pk da tabela intermediária realmente é uma sequence.
Eu estava enganado, fui consultar o script do banco e realmente não tem sequences nas tabelas. Isso foi decidido por motivos de compatibilidade. Notem que nos mapeamentos é o Hibernate que está responsável de cuidar dos auto-incrementos. Estamos progredindo… mas ainda não sei como tratar isso, sequences no banco estão fora de questão, devemos utilizar o auto-incremento do hibernate!
Eu até poderia fazer tal relacionamento com esta tabela, mas no banco tenho relacionamentos mais complexos onde até 10 tabelas são plugadas numa tabela intermediária. Nestes casos, é importante ter uma pk para a tabela intermediária. isto implica em performance e, ao mesmo tempo, facilidade de uso na hora de importar a chave primária desta tabela intermediária.
Eu acredito que o Hibernate tenha solução para isso, mas não estou achando nada na documentação nem no DTD. Também acredito que deva existir algo na tag “set” ou “id” que resolva esta situação.
Cara, o problema está na PK da tabela de união. Dependendo do tipo de ID declarado o Hibernate gerará um query diferente.
Por exemplo, usamos o SQL Server e a maioria das tabelas usa Identity. Sendo assim o Hibernate omite o ID em todas as queries de inserção.
O mesmo não ocorreia com um ID declarado como “assigned” por exemplo.
Portanto estude o comportamento do banco e depois os generators do Hibernate. Então decida a melhor forma.
Após pesquisar inúmeras vezes o manual do Hibernate, achei o seguinte
trecho:
6.7. Using an <idbag>
If you've fully embraced our view that composite keys are a bad thing
and that entities should have synthetic identifiers (surrogate keys), then
you might find it a bit odd that the many to many associations and
collections of values that we've shown so far all map to tables with
composite keys! Now, this point is quite arguable; a pure association table
doesn't seem to benefit much from a surrogate key (though a collection of
composite values might). Nevertheless, Hibernate provides a feature that
allows you to map many to many associations and collections of values to
a table with a surrogate key.
The <idbag> element lets you map a List (or Collection) with bag
semantics.
<idbag name="lovers" table="LOVERS" lazy="true">
<collection-id column="ID" type="long">
<generator class="hilo"/>
</collection-id>
<key column="PERSON1"/>
<many-to-many column="PERSON2" class="eg.Person" outer-join="true"/>
</idbag>
As you can see, an <idbag> has a synthetic id generator, just like an
entity class! A different surrogate key is assigned to each collection row.
Hibernate does not provide any mechanism to discover the surrogate key
value of a particular row, however.
Note that the update performance of an <idbag> is much better than a
regular <bag>! Hibernate can locate individual rows efficiently and update
or delete them individually, just like a list, map or set.
In the current implementation, the native identifier generation strategy is
not supported for <idbag> collection identifiers.
Esta é a solução para o problema, utilizei o trecho do mapeamento acima
substituindo os nome das classes, atributos e a tag generator
(class=“increment”) para minha realidade. Tudo funciona perfeito agora!