Search code examples
jpaspring-bootone-to-manyinformixcomposite-key

Composite Key with one Foreign Key OneToMany Spring Boot JPA Informix


This is the current structure of the Informix Database:

Table: msg_body 
        id (PK)
        kanal
        sender
        ...

Table: msg_user
        id (PK, FK)
        empfaenger (PK)
        datum
        ...

A msg_user has one or more msg_bodies.

msg_body domain class:

import javax.persistence.*;
import java.io.Serializable;
import java.sql.Timestamp;

@Entity
@Table(name="msg_body")
public class MessageBody implements Serializable {
    @Id
    @Column(name="id")
    private int mb_id;
    @Column(name="kanal")
    private String channel;
    @Column(name="gueltig_von")
    private Timestamp validFrom;
    @Column(name="gueltig_bis")
    private Timestamp validTo;
    @Column(name="sender")
    private String sender;
    @Column(name="erstell_datum")
    private Timestamp createDate;
    @Column(name="prioritaet")
    private int priority;
    @Column(name="nachricht")
    private String message;
    @Column(name="info")
    private String info;
    @Column(name="gueltig")
    private char valid;

    @ManyToOne
    private MessageUser messageUser;

    //getter  + setter
}

msg_user domain class:

import java.io.Serializable;
import java.sql.Timestamp;
import java.util.HashSet;
import java.util.Set;
import javax.persistence.*;

@Entity
@Table(name="msg_user")
public class MessageUser implements Serializable {
    @EmbeddedId
    UserId mu_id;

    @MapsId("mb_id")
    @JoinColumn(name = "mu_id", referencedColumnName = "id")
    @OneToMany
    private Set<MessageBody> msg_bodies = new HashSet<>();

    @Column(name = "datum")
    private Timestamp receiverDate;
    @Column(name = "versuch")
    private Timestamp tryTime;
    @Column(name = "versuch_anzahl")
    private int tryAmount;
    @Column(name = "status")
    private int state;
    @Column(name = "ip")
    private char ip;

    // getter + setter + hashcode + equals
}

Composite Key Class:

import java.io.Serializable;
import javax.persistence.*;

@Embeddable
public class UserId implements Serializable
{
    @Column(name="id")
    private int mb_id;
    @Column(name="empfaenger")
    private String receiver;

    // getter + setter + hashcode + equals
}

Application class:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.context.web.SpringBootServletInitializer;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;

@Configuration
@ComponentScan("at.company.badge_service")
@EnableAutoConfiguration
@EnableJpaRepositories(basePackages = {"at.company.badge_service.repository"})

public class Application extends SpringBootServletInitializer
{
    public static void main (String[] args)
    {
        SpringApplication.run(Application.class, args);
    }
}

The goal is to run this statement:

public interface BARepository extends JpaRepository<MessageBody, Long>
{
    @Query( "SELECT COUNT(mu.id) FROM MessageUser mu WHERE mu.receiver = :username")
    Long countBA(@Param("username") String username);
}

application.properties file:

spring.main.show-banner=false
security.basic.enabled=false
spring.thymeleaf.cache=false

# JACKSON
spring.jackson.mapper.default-view-inclusion=true
#spring.jackson.deserialization.fail-on-unknown-properties=false

security.user.name: username
security.user.password: password

spring.view.prefix: /WEB-INF/views/
spring.view.suffix: .jsp

# DATABASE
spring.jpa.database: INFORMIX
spring.jpa.hibernate.ddl-auto: validate
spring.jpa.properties.hibernate.default_batch_fetch_size: 100
spring.datasource.jndi-name=jdbc/msgdb

ErrorCode NullpointerException:

    2015-04-24 09:30:04,702 [RMI TCP Connection(3)-127.0.0.1] ERROR o.s.boot.SpringApplication - Application startup failed
    org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NullPointerException
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1566) ~[spring-beans-4.1.5.RELEASE.jar:4.1.5.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:539) ~[spring-beans-4.1.5.RELEASE.jar:4.1.5.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:476) ~[spring-beans-4.1.5.RELEASE.jar:4.1.5.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:303) ~[spring-beans-4.1.5.RELEASE.jar:4.1.5.RELEASE]
        at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.1.5.RELEASE.jar:4.1.5.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:299) ~[spring-beans-4.1.5.RELEASE.jar:4.1.5.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:194) ~[spring-beans-4.1.5.RELEASE.jar:4.1.5.RELEASE]
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110) [na:1.7.0_02]
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603) [na:1.7.0_02]
        at java.lang.Thread.run(Thread.java:722) [na:1.7.0_02]
    Caused by: java.lang.NullPointerException: null
    Apr 24, 2015 9:30:04 AM org.apache.catalina.core.ContainerBase addChildInternal
        at org.hibernate.cfg.annotations.CollectionBinder.bindCollectionSecondPass(CollectionBinder.java:1460) ~[hibernate-core-4.3.7.Final.jar:4.3.7.Final]
        at org.hibernate.cfg.annotations.CollectionBinder.bindOneToManySecondPass(CollectionBinder.java:864) ~[hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    Schwerwiegend: ContainerBase.addChild: start: 
        at org.hibernate.cfg.annotations.CollectionBinder.bindStarToManySecondPass(CollectionBinder.java:779) ~[hibernate-core-4.3.7.Final.jar:4.3.7.Final]
    org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/badge_service]]
        at org.hibernate.cfg.annotations.CollectionBinder$1.secondPass(CollectionBinder.java:728) ~[hibernate-core-4.3.7.Final.jar:4.3.7.Final]at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:901)
        at org.hibernate.cfg.Configuration.secondPassCompile(Configuration.java:1426) ~[hibernate-core-4.3.7.Final.jar:4.3.7.Final]

        at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:618)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1625) ~[spring-beans-4.1.5.RELEASE.jar:4.1.5.RELEASE]
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1562) ~[spring-beans-4.1.5.RELEASE.jar:4.1.5.RELEASE]
        at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:565)
        ... 58 common frames omitted
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
        at java.lang.Thread.run(Thread.java:722)
    Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in class path resource [org/springframework/boot/autoconfigure/orm/jpa/HibernateJpaAutoConfiguration.class]: Invocation of init method failed; nested exception is java.lang.NullPointerException
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1566)
        at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
        ... 40 more
    Caused by: java.lang.NullPointerException
        at org.hibernate.cfg.annotations.CollectionBinder.bindCollectionSecondPass(CollectionBinder.java:1460)
        at org.hibernate.cfg.annotations.CollectionBinder.bindOneToManySecondPass(CollectionBinder.java:864)
        at org.springframework.orm.jpa.vendor.SpringHibernateJpaPersistenceProvider.createContainerEntityManagerFactory(SpringHibernateJpaPersistenceProvider.java:60)
        at org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean.createNativeEntityManagerFactory(LocalContainerEntityManagerFactoryBean.java:343)
        at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.afterPropertiesSet(AbstractEntityManagerFactoryBean.java:318)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.invokeInitMethods(AbstractAutowireCapableBeanFactory.java:1625)
        at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1562)
        ... 58 more

    Apr 24, 2015 9:30:04 AM org.apache.tomcat.util.modeler.BaseModelMBean invoke
    Schwerwiegend: Exception invoking method manageApp
    java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/badge_service]]
        at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:904)
        at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:877)
        at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:649)
        at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:618)
        at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:565)
        at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:553)
        at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:808)
        at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:667)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
        at java.lang.Thread.run(Thread.java:722)

    Apr 24, 2015 9:30:04 AM org.apache.tomcat.util.modeler.BaseModelMBean invoke
    Schwerwiegend: Exception invoking method createStandardContext
    javax.management.RuntimeOperationsException: Exception invoking method manageApp
        at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:309)
        at com.sun.jmx.interceptor.DefaultMBeanServerInterceptor.invoke(DefaultMBeanServerInterceptor.java:819)
        at com.sun.jmx.mbeanserver.JmxMBeanServer.invoke(JmxMBeanServer.java:791)
        at org.apache.catalina.mbeans.MBeanFactory.createStandardContext(MBeanFactory.java:618)
        at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1110)
        at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:603)
        at java.lang.Thread.run(Thread.java:722)
    Caused by: java.lang.IllegalStateException: ContainerBase.addChild: start: org.apache.catalina.LifecycleException: Failed to start component [StandardEngine[Catalina].StandardHost[localhost].StandardContext[/badge_service]]
        at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:904)
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
        at java.lang.reflect.Method.invoke(Method.java:601)
        at org.apache.tomcat.util.modeler.BaseModelMBean.invoke(BaseModelMBean.java:301)
        ... 31 more

I tried to find a solution on the web, but none of them worked in my case. Especially the foreign key in the composite key seems to be a rare problem. I'm well aware, that it would be much easier to add a surrogate key to the msg_user-table, but I have to solve exactly this problem, so I would be happy if someone could show me how to code the domain classes with the relations in the right way. If you need further information do not hesitate to ask for them! Many thanks in advance!


Solution

  • After a while I found myself a solution:

    MessageBody domainclass:

    import javax.persistence.*;
    import java.io.Serializable;
    import java.sql.Timestamp;
    import java.util.HashSet;
    import java.util.Set;
    
    @Entity
    @Table(name="msg_body")
    public class MessageBody implements Serializable
    {
        @Id
        @Column(name="id")
        private int msgb_id;
    
        @Column(name="kanal", columnDefinition = "char")
        private String channel;
    
        @Column(name="gueltig_von")
        private Timestamp validFrom;
    
        @Column(name="gueltig_bis")
        private Timestamp validTo;
    
        @Column(name="sender")
        private String sender;
    
        @Column(name="erstell_datum")
        private Timestamp createDate;
    
        @Column(name="prioritaet")
        private int priority;
    
        @Column(name="nachricht")
        private String message;
    
        @Column(name="info", columnDefinition = "lvarchar")
        private String info;
    
        @Column(name="gueltig")
        private char valid;
    
        @OneToMany
        @JoinColumn(name = "id")
        private Set<MessageUser> message_users = new HashSet<>();
    
        // getter  + setter
    }
    

    CompositeKey domainclass:

    import java.io.Serializable;
    import javax.persistence.*;
    
    @Embeddable
    public class MessageUserPK implements Serializable
    {
        private int mb_id;
        private String receiver;
    
        // getter + setter + hashcode + equals
    }
    

    MessageUser domainclass:

    import java.io.Serializable;
    import java.sql.Timestamp;
    import javax.persistence.*;
    
    @Entity
    @Table(name="msg_user")
    public class MessageUser implements Serializable
    {
        @EmbeddedId
        @AttributeOverrides
        ({
                @AttributeOverride(name = "mb_id", column = @Column(name = "id")),
                @AttributeOverride(name = "receiver", column = @Column(name = "empfaenger"))
        })
        MessageUserPK mu_id;
    
        @Column(name = "datum")
        private Timestamp receiverDate;
    
        @Column(name = "versuch")
        private Timestamp tryTime;
    
        @Column(name = "versuch_anzahl")
        private int tryAmount;
    
        @Column(name = "status")
        private int state;
    
        @Column(name = "ip")
        private char ip;
    
        @ManyToOne
        @MapsId("mb_id")
        @JoinColumn(name = "id")
        private MessageBody messageBody;
    
        // getter + setter + hashcode + equals
    }
    

    The relation is bidirectional as in the question, but I put the Collection in the MessageBody-Domainclass. After some other fixes like the "AttributOverrides" it worked. Hope I could help another one with the solution!