-1

I'm trying to learn the basics of JPA 2, using Hibernate, for a project. One thing I'm not sure on is how to do composite keys correctly. For some reason, when I simply print out the values of my table, I get them twice.

The table itself is:

Table: Anim_Type

CrashNumber RecordNumber AnimalType
1000        1            2
1000        1            10

In my main class, I have a @OneToMany relationship between it (Crash) and AnimalTypes:

@OneToMany(fetch=FetchType.EAGER, cascade=CascadeType.ALL)
@JoinColumn(name = "CRASH_NUMBER")
public List<AnimalType> getAnimalTypes() {
    return animalTypes;
}

The AnimalType @Entity should have a primary key of all 3 columns. Here's AnimalType class:

@Entity
@Table(name = "ANIM_TYPE")
public class AnimalType {

private AnimalTypePK pk;

@EmbeddedId
@AttributeOverrides( {
    @AttributeOverride(name = "crashNum", column = @Column(name = "CRASH_NUMBER")),
    @AttributeOverride(name = "recordNum", column = @Column(name = "RECORD_NUMBER")),
    @AttributeOverride(name = "animalType", column = @Column(name = "ANIMAL_TYPE"))
})
public AnimalTypePK getPk() {
    return pk;
}

public void setPk(AnimalTypePK pk) {
    this.pk = pk;
}

public void print() {
    System.out.println("AnimalType: " + pk.getCrashNum() + " " + pk.getRecordNum() + " " + pk.getAnimalType());
}


}

And my AnimalTypePK class is:

@Embeddable
public class AnimalTypePK implements Serializable {

private static final long serialVersionUID = 1L;
private int crashNum;
private int recordNum;
private int animalType;

// getters and setters, plus hashCode() and equals(Object).
// no other JPA annotations in this class

}

When I use the above code, things work, but when I print the AnimalTypes, the 2 rows in the table are repeated:

Animal Types

AnimalType: 1000 1 10
AnimalType: 1000 1 2
AnimalType: 1000 1 10
AnimalType: 1000 1 2

And I don't understand why?

Can anyone help?

Secondly, I use the @AttributeOverrides annotation in AnimalType to tell Hibernate the column names. Is this the correct way to do this?

Thanks!

Chris

EDIT: I did see the flagged post earlier, and followed the accepted answer's method, and also putting the @Column into the AnimalTypePK (taking out @AttributeOverrides in AnimalType). But when I try it, I get an Exception:

Exception in thread "main"      org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:242)
at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:225)
at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:417)
at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:59)
at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:213)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:147)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:131)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:92)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:208)
at com.sun.proxy.$Proxy34.findOne(Unknown Source)
at com.dbprototype.services.CrashService.readCrash(CrashService.java:28)
at com.dbprototype.services.CrashService$$FastClassBySpringCGLIB$$473ea387.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:204)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:718)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:157)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:654)
at com.dbprototype.services.CrashService$$EnhancerBySpringCGLIB$$a311f3d6.readCrash(<generated>)
at com.dbprototype.main.DBTest.main(DBTest.java:20)
Caused by: org.hibernate.exception.SQLGrammarException: could not extract ResultSet
at org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:63)
at org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:109)
at org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:95)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:79)
at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.getResultSet(AbstractLoadPlanBasedLoader.java:434)
at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeQueryStatement(AbstractLoadPlanBasedLoader.java:186)
at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:121)
at org.hibernate.loader.plan.exec.internal.AbstractLoadPlanBasedLoader.executeLoad(AbstractLoadPlanBasedLoader.java:86)
at org.hibernate.loader.entity.plan.AbstractLoadPlanBasedEntityLoader.load(AbstractLoadPlanBasedEntityLoader.java:167)
at org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:3955)
at org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:508)
at org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:478)
at org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:219)
at org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:278)
at org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:121)
at org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:89)
at org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1129)
at org.hibernate.internal.SessionImpl.access$2600(SessionImpl.java:164)
at org.hibernate.internal.SessionImpl$IdentifierLoadAccessImpl.load(SessionImpl.java:2696)
at org.hibernate.internal.SessionImpl.get(SessionImpl.java:975)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:1075)
at org.hibernate.jpa.spi.AbstractEntityManagerImpl.find(AbstractEntityManagerImpl.java:1039)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:293)
at com.sun.proxy.$Proxy29.find(Unknown Source)
at org.springframework.data.jpa.repository.support.SimpleJpaRepository.findOne(SimpleJpaRepository.java:235)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(Unknown Source)
at java.lang.reflect.Method.invoke(Unknown Source)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.executeMethodOn(RepositoryFactorySupport.java:483)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:468)
at org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:440)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:99)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:281)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:96)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:179)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:136)
... 19 more
Caused by: java.sql.SQLSyntaxErrorException: ORA-00904: "ANIMALTYPE1_"."RECORDNUM": invalid identifier

at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:450)
at oracle.jdbc.driver.T4CTTIoer.processError(T4CTTIoer.java:399)
at oracle.jdbc.driver.T4C8Oall.processError(T4C8Oall.java:1017)
at oracle.jdbc.driver.T4CTTIfun.receive(T4CTTIfun.java:655)
at oracle.jdbc.driver.T4CTTIfun.doRPC(T4CTTIfun.java:249)
at oracle.jdbc.driver.T4C8Oall.doOALL(T4C8Oall.java:566)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:215)
at oracle.jdbc.driver.T4CPreparedStatement.doOall8(T4CPreparedStatement.java:58)
at oracle.jdbc.driver.T4CPreparedStatement.executeForDescribe(T4CPreparedStatement.java:776)
at oracle.jdbc.driver.OracleStatement.executeMaybeDescribe(OracleStatement.java:897)
at oracle.jdbc.driver.OracleStatement.doExecuteWithTimeout(OracleStatement.java:1034)
at oracle.jdbc.driver.OraclePreparedStatement.executeInternal(OraclePreparedStatement.java:3820)
at oracle.jdbc.driver.OraclePreparedStatement.executeQuery(OraclePreparedStatement.java:3867)
at oracle.jdbc.driver.OraclePreparedStatementWrapper.executeQuery(OraclePreparedStatementWrapper.java:1502)
at org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:70)
... 59 more
Alan Hay
  • 22,665
  • 4
  • 56
  • 110
Chris
  • 391
  • 2
  • 4
  • 15
  • Possible duplicate of [How to create and handle composite primary key in JPA](http://stackoverflow.com/questions/13032948/how-to-create-and-handle-composite-primary-key-in-jpa) – jbx Jan 20 '16 at 01:51
  • Not sure where you are actually printing them from. But the right way to put the column names of the composite key is using the usual `@Column` annotations in the `AnimalTypePK` class. – jbx Jan 20 '16 at 01:52
  • @jbx See my Edit comments above. I did see that post, and basically it didn't work. Getting exception. – Chris Jan 20 '16 at 02:04
  • Are you sure the column names in the table are as you are saying? Why is the exception referring to `RECORDNUM` when your column is supposed to be `RECORD_NUMBER`? Can you update the full code properly and put the full details of the exception, and also the table structure of what you have in your Oracle database? Something doesn't seem to be matching. – jbx Jan 20 '16 at 02:08
  • Yes, the column names are correct. I've abbreviated them here, but checked my code, and the columns match. I had been assuming that the exception refers to RECORDNUM since that is the Java variable name for that column, hence some kind of mapping issue between the Java variable names & database columns. – Chris Jan 20 '16 at 02:16
  • I mean update the code properly. Put `@ColumnName(name="RECORD_NUMBER")` on top of the `recordNum` field etc. Clean it up so that people who see the question can understand what you are actually asking. We can't magically understand what you have in your code without seeing the full picture. Also show the code you are using to get the primary key and list the results that you are saying are duplicate. It could be a silly error there. – jbx Jan 20 '16 at 02:18
  • I will tomorrow. I'm away from my work computer right now. – Chris Jan 20 '16 at 03:34
  • The issue is most likely not with your composite key but with the eagerly fetched association. Try changing the collection type from List to Set **or** removing the eager fetch property. https://howtoprogramwithjava.com/how-to-fix-duplicate-data-from-hibernate-queries/ – Alan Hay Jan 20 '16 at 09:06
  • That worked Alan! Thanks. – Chris Jan 20 '16 at 15:27
  • @AlanHay One thing I'm not clear on is why using a Set works and using a List doesn't work. I did look at the article you mentioned, but still not clear on why. The example uses an optional join on the ManyToOne. But in my code I don't have any ManyToOne side, I was using unidirectional mapping. Or does Hibernate see that as an optional ManyToOne? Thanks again! – Chris Jan 20 '16 at 15:38
  • I have added an answer. – Alan Hay Jan 20 '16 at 16:05

1 Answers1

0

The issue has nothing to do with anything other than the fact you have used EAGER on the association.

You can find some further discussion here.

https://howtoprogramwithjava.com/how-to-fix-duplicate-data-from-hibernate-queries/

Hibernate Criteria returns children multiple times with FetchType.EAGER

Possible fixes are then to remove the Eager Fetch or to map this association using a Set rather than a List.

With regards to your follow up question as to why using a SET has fixed it....

Well, if we consider the below:

The main role of the persistence context is to make sure that a database entity object is represented by no more than one in-memory entity object within the same EntityManager. Every EntityManager manages its own persistence context. Therefore, a database object can be represented by different memory entity objects in different EntityManager instances. But retrieving the same database object more than once using the same EntityManager should always result in the same in-memory entity object.

http://www.objectdb.com/java/jpa/persistence/managed

Given the above statement, if we consider then your original duplicated List:

  • AnimalType: 1000 1 10
  • AnimalType: 1000 1 2
  • AnimalType: 1000 1 10
  • AnimalType: 1000 1 2

we would expect (assuming AnimalType has either default equals method or correctly implemented custom equals method) that

list.get(0).equals(list.get(2)) == true 

and

list.get(1).equals(list.get(3)) == true

As, of course, a Set cannot contain duplicates then the duplicate records are filtered out as a side effect of using a Set.

https://docs.oracle.com/javase/7/docs/api/java/util/Set.html

A collection that contains no duplicate elements. More formally, sets contain no pair of elements e1 and e2 such that e1.equals(e2)...

Community
  • 1
  • 1
Alan Hay
  • 22,665
  • 4
  • 56
  • 110
  • Thanks Alan! I had originally used Lazy loading, but that always gives me : Exception in thread "main" org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role: com.dbprototype.domain.Crash.animalTypes, could not initialize proxy - no Session. Not sure why. I just tried Lazy loading with both the Set and the List, gives same exception. Other than changing to Lazy loading, code is the same. – Chris Jan 20 '16 at 17:22
  • Well lazy loading problem is another issue entirely. You should accept this answer to your original question around duplicates and research further on lazy loading as this is something you really **must** understand if working with JPA. – Alan Hay Jan 20 '16 at 23:25