0
votes

I have an Account class for a job search application I am building which contains a Linked List of Education instances (just university name, gpa, etc). I would like to have an HTTP Post request to add an element to that list:

@PostMapping("/education/{id}")
    void newAccount(@RequestBody Education newEducation, @PathVariable Long id) {
        Account a = repository.findById(id).orElseThrow(() -> new AccountNotFoundException(id));
        a.addEducation(newEducation);
    }

This is the account class:


import java.util.LinkedList;
import java.util.Objects;

import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;

@Entity
public class Account {
    private @Id @GeneratedValue Long id;
    private String name;
    private String email;
    private String phone;
    private LinkedList<Education> education;
    Account() {}
    Account(String name, String email, String phone) {
        education = new LinkedList<>();
        this.setName(name);
        this.setEmail(email);
        this.setPhone(phone);
    }
    public Long getId() {
        return id;
    }
    public void setId(Long id) {
        this.id=id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public String getEmail() {
        return email;
    }
    public void setEmail(String email) {
        this.email = email;
    }
    public String getPhone() {
        return phone;
    }
    public void setPhone(String phone) {
        this.phone = phone;
    }
    public LinkedList<Education> getEducation() {
        return education;
    }
    public void addEducation(Education e) {
        education.add(e);
    }
    @Override
    public boolean equals(Object o) {
        if (this==o)
            return true;
        if (!(o instanceof Account))
            return false;
        Account acc = (Account)o;
        return name.equals(acc.name);
    }
    @Override
    public int hashCode() {
        return Objects.hash(this.id, this.name);
    }
    @Override
    public String toString() {
        String edu="";
        if (!this.education.isEmpty())
        {
            edu+=", \"Education: [";
            for (Education e: this.education)
            {
                edu+=e.toString();
            }
            edu+="]";
        }
        String s = "Account{";
        
        s += String.format("\"id\": \"%s\", ", this.id);
        s += String.format("\"name\": \"%s\", ", this.name);
        s += String.format("\"email\": \"%s\", ", this.email);
        s += String.format("\"phone\": \"%s\"", this.phone);
        s += edu;
        
        s += "}";
        return s;
    }
}

However, when I actually send a post request with education data (for an already existing user), it does not post it. It does print the Account a properly (with the added education) but does not actually update the database.

Additionally, this is another class I have:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.CommandLineRunner;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class LoadDatabase {
    private static final Logger log = 
            LoggerFactory.getLogger(LoadDatabase.class);
    
    @Bean
    CommandLineRunner initDatabase(AccountRepository repository) {
        return args -> {
            log.info("Preloading " + repository.save(new Account("Alex", "[email protected]", "214")));
            Account a= new Account("Bob", "[email protected]", "972");
            //a.addEducation(new Education("B.S.", "Uni", 3.64));
            //log.info("Preloading " + repository.save(a));
            
        };
    }

}

Uncommenting a.addEducation and log.info causes a runtime error (keeping either one of them would not trigger that):

java.lang.IllegalStateException: Failed to execute CommandLineRunner at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:798) ~[spring-boot-2.3.4.RELEASE.jar:2.3.4.RELEASE] at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:779) ~[spring-boot-2.3.4.RELEASE.jar:2.3.4.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:322) ~[spring-boot-2.3.4.RELEASE.jar:2.3.4.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1237) ~[spring-boot-2.3.4.RELEASE.jar:2.3.4.RELEASE] at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226) ~[spring-boot-2.3.4.RELEASE.jar:2.3.4.RELEASE] at com.project933.Project933Application.main(Project933Application.java:10) ~[classes/:na] Caused by: org.springframework.orm.jpa.JpaSystemException: could not serialize; nested exception is org.hibernate.type.SerializationException: could not serialize at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:353) ~[spring-orm-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:255) ~[spring-orm-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:528) ~[spring-orm-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61) ~[spring-tx-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242) ~[spring-tx-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:154) ~[spring-tx-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:178) ~[spring-data-jpa-2.3.4.RELEASE.jar:2.3.4.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:95) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE] at com.sun.proxy.$Proxy90.save(Unknown Source) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na] at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:344) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:205) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE] at com.sun.proxy.$Proxy64.save(Unknown Source) ~[na:na] at com.project933.LoadDatabase.lambda$0(LoadDatabase.java:19) ~[classes/:na] at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:795) ~[spring-boot-2.3.4.RELEASE.jar:2.3.4.RELEASE] ... 5 common frames omitted Caused by: org.hibernate.type.SerializationException: could not serialize at org.hibernate.internal.util.SerializationHelper.serialize(SerializationHelper.java:119) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.internal.util.SerializationHelper.serialize(SerializationHelper.java:144) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.internal.util.SerializationHelper.clone(SerializationHelper.java:75) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.type.descriptor.java.SerializableTypeDescriptor$SerializableMutabilityPlan.deepCopyNotNull(SerializableTypeDescriptor.java:41) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.type.descriptor.java.SerializableTypeDescriptor$SerializableMutabilityPlan.deepCopyNotNull(SerializableTypeDescriptor.java:32) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.type.descriptor.java.MutableMutabilityPlan.deepCopy(MutableMutabilityPlan.java:35) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.type.AbstractStandardBasicType.deepCopy(AbstractStandardBasicType.java:308) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.type.AbstractStandardBasicType.deepCopy(AbstractStandardBasicType.java:304) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.type.TypeHelper.deepCopy(TypeHelper.java:55) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:279) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:193) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:135) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:185) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:128) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:55) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:102) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:721) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:707) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na] at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:314) ~[spring-orm-5.2.9.RELEASE.jar:5.2.9.RELEASE] at com.sun.proxy.$Proxy87.persist(Unknown Source) ~[na:na] at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:554) ~[spring-data-jpa-2.3.4.RELEASE.jar:2.3.4.RELEASE] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na] at org.springframework.data.repository.core.support.ImplementationInvocationMetadata.invoke(ImplementationInvocationMetadata.java:72) ~[spring-data-commons-2.3.4.RELEASE.jar:2.3.4.RELEASE] at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:382) ~[spring-data-commons-2.3.4.RELEASE.jar:2.3.4.RELEASE] at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:205) ~[spring-data-commons-2.3.4.RELEASE.jar:2.3.4.RELEASE] at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:549) ~[spring-data-commons-2.3.4.RELEASE.jar:2.3.4.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:155) ~[spring-data-commons-2.3.4.RELEASE.jar:2.3.4.RELEASE] at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:130) ~[spring-data-commons-2.3.4.RELEASE.jar:2.3.4.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80) ~[spring-data-commons-2.3.4.RELEASE.jar:2.3.4.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:367) ~[spring-tx-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:118) ~[spring-tx-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.9.RELEASE.jar:5.2.9.RELEASE] at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) ~[spring-tx-5.2.9.RELEASE.jar:5.2.9.RELEASE] ... 21 common frames omitted Caused by: java.io.NotSerializableException: com.project933.Education at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1185) ~[na:na] at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:349) ~[na:na] at java.base/java.util.LinkedList.writeObject(LinkedList.java:1135) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:na] at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:na] at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:na] at java.base/java.lang.reflect.Method.invoke(Method.java:566) ~[na:na] at java.base/java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1145) ~[na:na] at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1497) ~[na:na] at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1433) ~[na:na] at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1179) ~[na:na] at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:349) ~[na:na] at org.hibernate.internal.util.SerializationHelper.serialize(SerializationHelper.java:115) ~[hibernate-core-5.4.21.Final.jar:5.4.21.Final] ... 63 common frames omitted

Could anyone please explain why I am having problems with inserting the Education instances into the Account database? I am using JpaRepository.

Thanks!

edit: This is the Education class:

public class Education {
    private String degree;
    private String institute;
    private double gpa;
    Education(String degree, String institute, double gpa) {
        this.setDegree(degree);
        this.setInstitute(institute);
        this.setGpa(gpa);
    }
    public String getDegree() {
        return degree;
    }
    public void setDegree(String degree) {
        this.degree = degree;
    }
    public String getInstitute() {
        return institute;
    }
    public void setInstitute(String institute) {
        this.institute = institute;
    }
    public double getGpa() {
        return gpa;
    }
    public void setGpa(double gpa) {
        this.gpa = gpa;
    }
    @Override
    public String toString() {
        String s = "{";
        s += s += String.format("\"degree\": \"%s\"", this.degree);
        s += String.format("\"institute\": \"%s\"", this.institute);
        s += String.format("\"gpa\": %.2f", this.gpa);
        s += "}";
        return s;
    }
}

1

1 Answers

0
votes

https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/util/List.html

The problem is, as long as your list is dynamic, it can't be serialized, but it needs to be serialized to be able to transmit to the database. So you have to make sure that your list is serializable. This is achieved by "The List.of and List.copyOf static factory methods provide a convenient way to create unmodifiable lists." So it means, before you send your object to the database, you've to make sure that your list objects are unmodifable/not dynamic.