composed primary key using hibernate with spring data jpa and mysql

zak zak :

I'm trying to test a composed primary key, unfortunatly something is running incorrectly.

The Course class that contains the composed primary key

@Entity(name = "Course")
public class Course {
    @EmbeddedId
    private PkCourse pkCourse;

    public Course() {}

    public Course( PkCourse pkCourse) {
        this.pkCourse = pkCourse;
    }
    public PkCourse getPkCourse() {
        return pkCourse;
    }
    public void setPkCourse( PkCourse pkCourse ) {
        this.pkCourse = pkCourse;
    }
}

the class that represent a composed primary key

@Embeddable
public class PkCourse implements Serializable {

    @Column(name = "courseName")
    private String courseName;

    @Column(name = "courseGrade")
    private Integer grade;

    PkCourse(){
    }
    public PkCourse( String courseName, Integer grade ) {
        this.courseName = courseName;
        this.grade = grade;
    }

    public String getCourseName() {
        return courseName;
    }
    public void setCourseName( String courseName ) {
        this.courseName = courseName;
    }

    public Integer getGrade() {
        return grade;
    }
    public void setGrade( Integer grade ) {
        this.grade = grade;
    }
    @Override
    public boolean equals( Object o ) {
        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        PkCourse pkCourse = (PkCourse) o;
        return courseName.equals(pkCourse.courseName) &&
                Objects.equals(grade, pkCourse.grade);
    }
    @Override
    public int hashCode() {
        return Objects.hash(courseName, grade);
    }
}

My testing program

private void composedPrimaryKey() {
        PkCourse composedPK = new PkCourse("database", 3);
        Course course = new Course(composedPK);
        courseRepository.save(course);
    }

my jpa repository

public interface CourseRepository extends JpaRepository<Course, PkCourse> {
}

The exception I'm getting

Caused by: org.springframework.dao.InvalidDataAccessResourceUsageException: could not extract ResultSet; SQL [n/a]; nested exception is org.hibernate.exception.SQLGrammarException: could not extract ResultSet

Caused by: java.sql.SQLSyntaxErrorException: Table 'company.course' doesn't exist

The full trace

> java.lang.IllegalStateException: Failed to execute CommandLineRunner
>   at
> org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:783)
> [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]     at
> org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:764)
> [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]     at
> org.springframework.boot.SpringApplication.run(SpringApplication.java:319)
> [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]     at
> org.springframework.boot.SpringApplication.run(SpringApplication.java:1214)
> [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]     at
> org.springframework.boot.SpringApplication.run(SpringApplication.java:1203)
> [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]     at
> jpa.hibearnate.example.hibernateDemo4.HibernateDemoApplication.main(HibernateDemoApplication.java:26)
> [classes/:na]     at sun.reflect.NativeMethodAccessorImpl.invoke0(Native
> Method) ~[na:1.8.0_131]   at
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
> ~[na:1.8.0_131]   at
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> ~[na:1.8.0_131]   at java.lang.reflect.Method.invoke(Method.java:498)
> ~[na:1.8.0_131]   at
> org.springframework.boot.devtools.restart.RestartLauncher.run(RestartLauncher.java:49)
> [spring-boot-devtools-2.1.7.RELEASE.jar:2.1.7.RELEASE] Caused by:
> 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:279)
> ~[spring-orm-5.1.9.RELEASE.jar:5.1.9.RELEASE]     at
> org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:253)
> ~[spring-orm-5.1.9.RELEASE.jar:5.1.9.RELEASE]     at
> org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:527)
> ~[spring-orm-5.1.9.RELEASE.jar:5.1.9.RELEASE]     at
> org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
> ~[spring-tx-5.1.9.RELEASE.jar:5.1.9.RELEASE]  at
> org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
> ~[spring-tx-5.1.9.RELEASE.jar:5.1.9.RELEASE]  at
> org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:153)
> ~[spring-tx-5.1.9.RELEASE.jar:5.1.9.RELEASE]  at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
> ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]     at
> org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:144)
> ~[spring-data-jpa-2.1.10.RELEASE.jar:2.1.10.RELEASE]  at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
> ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]     at
> org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$ExposeRepositoryInvocationInterceptor.invoke(CrudMethodMetadataPostProcessor.java:364)
> ~[spring-data-jpa-2.1.10.RELEASE.jar:2.1.10.RELEASE]  at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
> ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]     at
> org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93)
> ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]     at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
> ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]     at
> org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61)
> ~[spring-data-commons-2.1.10.RELEASE.jar:2.1.10.RELEASE]  at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
> ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]     at
> org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
> ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]     at
> com.sun.proxy.$Proxy71.save(Unknown Source) ~[na:na]  at
> jpa.hibearnate.example.hibernateDemo4.HibernateDemoApplication.composedPrimaryKey(HibernateDemoApplication.java:37)
> [classes/:na]     at
> jpa.hibearnate.example.hibernateDemo4.HibernateDemoApplication.run(HibernateDemoApplication.java:31)
> [classes/:na]     at
> org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:780)
> [spring-boot-2.1.7.RELEASE.jar:2.1.7.RELEASE]     ... 10 common frames
> omitted Caused by: org.hibernate.exception.SQLGrammarException: could
> not extract ResultSet     at
> org.hibernate.exception.internal.SQLExceptionTypeDelegate.convert(SQLExceptionTypeDelegate.java:63)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.exception.internal.StandardSQLExceptionConverter.convert(StandardSQLExceptionConverter.java:42)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:113)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.engine.jdbc.spi.SqlExceptionHelper.convert(SqlExceptionHelper.java:99)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:69)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.loader.Loader.getResultSet(Loader.java:2167)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1930)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.loader.Loader.executeQueryStatement(Loader.java:1892)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.loader.Loader.doQuery(Loader.java:937)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:340)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:310)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.loader.Loader.loadEntity(Loader.java:2281)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:64)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.loader.entity.AbstractEntityLoader.load(AbstractEntityLoader.java:54)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.persister.entity.AbstractEntityPersister.load(AbstractEntityPersister.java:4273)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.event.internal.DefaultLoadEventListener.loadFromDatasource(DefaultLoadEventListener.java:511)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.event.internal.DefaultLoadEventListener.doLoad(DefaultLoadEventListener.java:481)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.event.internal.DefaultLoadEventListener.load(DefaultLoadEventListener.java:222)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.event.internal.DefaultLoadEventListener.proxyOrLoad(DefaultLoadEventListener.java:281)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.event.internal.DefaultLoadEventListener.doOnLoad(DefaultLoadEventListener.java:124)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.event.internal.DefaultLoadEventListener.onLoad(DefaultLoadEventListener.java:92)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.internal.SessionImpl.fireLoad(SessionImpl.java:1257)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.internal.SessionImpl.access$2000(SessionImpl.java:208)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.internal.SessionImpl$IdentifierLoadAccessImpl.doLoad(SessionImpl.java:2881)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.internal.SessionImpl$IdentifierLoadAccessImpl.load(SessionImpl.java:2855)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.internal.SessionImpl.get(SessionImpl.java:1098)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.event.internal.DefaultMergeEventListener.entityIsDetached(DefaultMergeEventListener.java:290)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:170)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:69)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:901)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> org.hibernate.internal.SessionImpl.merge(SessionImpl.java:887)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]   at
> sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> ~[na:1.8.0_131]   at
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
> ~[na:1.8.0_131]   at
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> ~[na:1.8.0_131]   at java.lang.reflect.Method.invoke(Method.java:498)
> ~[na:1.8.0_131]   at
> org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:309)
> ~[spring-orm-5.1.9.RELEASE.jar:5.1.9.RELEASE]     at
> com.sun.proxy.$Proxy69.merge(Unknown Source) ~[na:na]     at
> org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:538)
> ~[spring-data-jpa-2.1.10.RELEASE.jar:2.1.10.RELEASE]  at
> sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
> ~[na:1.8.0_131]   at
> sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
> ~[na:1.8.0_131]   at
> sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
> ~[na:1.8.0_131]   at java.lang.reflect.Method.invoke(Method.java:498)
> ~[na:1.8.0_131]   at
> org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:359)
> ~[spring-data-commons-2.1.10.RELEASE.jar:2.1.10.RELEASE]  at
> org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200)
> ~[spring-data-commons-2.1.10.RELEASE.jar:2.1.10.RELEASE]  at
> org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:644)
> ~[spring-data-commons-2.1.10.RELEASE.jar:2.1.10.RELEASE]  at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
> ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]     at
> org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:608)
> ~[spring-data-commons-2.1.10.RELEASE.jar:2.1.10.RELEASE]  at
> org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$invoke$3(RepositoryFactorySupport.java:595)
> ~[spring-data-commons-2.1.10.RELEASE.jar:2.1.10.RELEASE]  at
> org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:595)
> ~[spring-data-commons-2.1.10.RELEASE.jar:2.1.10.RELEASE]  at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
> ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]     at
> org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59)
> ~[spring-data-commons-2.1.10.RELEASE.jar:2.1.10.RELEASE]  at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
> ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]     at
> org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:295)
> ~[spring-tx-5.1.9.RELEASE.jar:5.1.9.RELEASE]  at
> org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98)
> ~[spring-tx-5.1.9.RELEASE.jar:5.1.9.RELEASE]  at
> org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
> ~[spring-aop-5.1.9.RELEASE.jar:5.1.9.RELEASE]     at
> org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139)
> ~[spring-tx-5.1.9.RELEASE.jar:5.1.9.RELEASE]  ... 24 common frames
> omitted Caused by: java.sql.SQLSyntaxErrorException: Table
> 'company.course' doesn't exist    at
> com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:120)
> ~[mysql-connector-java-8.0.17.jar:8.0.17]     at
> com.mysql.cj.jdbc.exceptions.SQLError.createSQLException(SQLError.java:97)
> ~[mysql-connector-java-8.0.17.jar:8.0.17]     at
> com.mysql.cj.jdbc.exceptions.SQLExceptionsMapping.translateException(SQLExceptionsMapping.java:122)
> ~[mysql-connector-java-8.0.17.jar:8.0.17]     at
> com.mysql.cj.jdbc.ClientPreparedStatement.executeInternal(ClientPreparedStatement.java:953)
> ~[mysql-connector-java-8.0.17.jar:8.0.17]     at
> com.mysql.cj.jdbc.ClientPreparedStatement.executeQuery(ClientPreparedStatement.java:1003)
> ~[mysql-connector-java-8.0.17.jar:8.0.17]     at
> com.zaxxer.hikari.pool.ProxyPreparedStatement.executeQuery(ProxyPreparedStatement.java:52)
> ~[HikariCP-3.2.0.jar:na]  at
> com.zaxxer.hikari.pool.HikariProxyPreparedStatement.executeQuery(HikariProxyPreparedStatement.java)
> ~[HikariCP-3.2.0.jar:na]  at
> org.hibernate.engine.jdbc.internal.ResultSetReturnImpl.extract(ResultSetReturnImpl.java:60)
> ~[hibernate-core-5.3.10.Final.jar:5.3.10.Final]

Edit1

I solved the problem of the Course table creation by replacing the Integer attribute grade by a String atttribute.

Edit2

My hibernate log:

> Hibernate: 
>     
>     drop table if exists course Hibernate: 
>     
>     create table course (
>        course_name varchar(255) not null,
>         course_grade integer not null,
>         primary key (course_name, course_grade)
>     ) engine=MyISAM 2019-08-15 09:36:34.238  WARN 11508 --- [  restartedMain] o.h.t.s.i.ExceptionHandlerLoggedImpl     :
> GenerationTarget encountered exception accepting command : Error
> executing DDL "
>     create table course (
>        course_name varchar(255) not null,
>         course_grade integer not null,
>         primary key (course_name, course_grade)
>     ) engine=MyISAM" via JDBC Statement
> 
> org.hibernate.tool.schema.spi.CommandAcceptanceException: Error
> executing DDL "
>     create table course (
>        course_name varchar(255) not null,
>         course_grade integer not null,
>         primary key (course_name, course_grade)
>     ) engine=MyISAM" via JDBC Statement
Caused by: java.sql.SQLSyntaxErrorException: Specified key was too long; max key length is 1000 bytes

Hibernate: 
    select
        course0_.course_name as course_n1_0_0_,
        course0_.course_grade as course_g2_0_0_ 
    from
        course course0_ 
    where
        course0_.course_name=? 
        and course0_.course_grade=?
2019-08-15 09:36:34.718 ERROR 11508 --- [  restartedMain] o.h.engine.jdbc.spi.SqlExceptionHelper   : Table 'company.course' doesn't exist

Could someone explains why it didn't work with two attribute with differents types?(String and Integer in my first case).

Edit 3

Also, I solved my problem by modifiying the annotation

@Column(name = "courseName")
private String courseName;

to

@Column(name = "courseName", length = 20)
private String courseName;

I tried this modification based on the exception line

Caused by: java.sql.SQLSyntaxErrorException: Specified key was too long; max key length is 1000 bytes

Could someone explains what is the magic behind that?

Tarun Lalwani :

If you look at the DDL

create table course (
  course_name varchar(255) not null,
  course_grade integer not null,
  primary key (course_name, course_grade)
) engine=MyISAM" via JDBC Statement

There is limit on size of value that is indexed in MyISAM tables, this default size is also dependent version of your MySQL

Now ideal way to create such table is not to use course_name and course_grade as a primary key but rather you should use a uuid or auto gen id and then put a unique index on a combination of course_name and course_grade.

In case you still face an issue with that, You can also try different fixes mentioned in threads referred in this answer

  1. SET @@global.innodb_large_prefix = 1;

  2. Upgrade to higher MySQL version 5.7+

  3. set GLOBAL storage_engine='InnoDb';

Referrences

#1071 - Specified key was too long; max key length is 1000 bytes

#1071 - Specified key was too long; max key length is 767 bytes

https://dba.stackexchange.com/questions/49913/specified-key-was-too-long-max-key-length-is-1000-bytes-in-mysql-5-6

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325532&siteId=1