【问题标题】:java.lang.NullPonterException when saving Spring Entity保存 Spring Entity 时出现 java.lang.NullPointerException
【发布时间】:2019-11-23 14:28:15
【问题描述】:

我尝试读取 ExcelFiles 并将数据保存在 postgres 数据库中。在某些文件中,我收到以下错误消息:

java.lang.NullPointerException: null 在 de.hspf.lectureservice.semesterLecture.SemesterLecture.hashCode(SemesterLecture.java:51) ~[classes/:na] 在 java.lang.Object.toString(Object.java:236) ~[na:1.8.0_161] 在 java.lang.String.valueOf(String.java:2994) ~[na:1.8.0_161] 在 java.lang.StringBuilder.append(StringBuilder.java:131) ~[na:1.8.0_161] 在 org.hibernate.pretty.MessageHelper.infoString(MessageHelper.java:57) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.NonUniqueObjectException.getMessage(NonUniqueObjectException.java:61) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:123) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:181) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.internal.ExceptionConverterImpl.convert(ExceptionConverterImpl.java:188) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:933) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.internal.SessionImpl.merge(SessionImpl.java:892) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.engine.spi.CascadingActions$6.cascade(CascadingActions.java:261) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:490) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:415) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:216) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.engine.internal.Cascade.cascadeCollectionElements(Cascade.java:523) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.engine.internal.Cascade.cascadeCollection(Cascade.java:455) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:418) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:216) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.engine.internal.Cascade.cascade(Cascade.java:149) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.event.internal.DefaultMergeEventListener.cascadeOnMerge(DefaultMergeEventListener.java:460) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.event.internal.DefaultMergeEventListener.entityIsPersistent(DefaultMergeEventListener.java:202) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:176) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.event.internal.DefaultMergeEventListener.onMerge(DefaultMergeEventListener.java:69) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.internal.SessionImpl.fireMerge(SessionImpl.java:900) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 org.hibernate.internal.SessionImpl.merge(SessionImpl.java:886) ~[hibernate-core-5.3.9.Final.jar:5.3.9.Final] 在 sun.reflect.GeneratedMethodAccessor88.invoke(未知来源)~[na:na] 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_161] 在 java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_161] 在 org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:308) ~[spring-orm-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 com.sun.proxy.$Proxy116.merge(Unknown Source) ~[na:na] 在 org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:492) ~[spring-data-jpa-2.1.6.RELEASE.jar:2.1.6.RELEASE] 在 sun.reflect.GeneratedMethodAccessor86.invoke(未知来源)~[na:na] 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_161] 在 java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_161] 在 org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:359) ~[spring-data-commons-2.1.6.RELEASE.jar:2.1.6.RELEASE] 在 org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:200) ~[spring-data-commons-2.1.6.RELEASE.jar:2.1.6.RELEASE] 在 org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:644) ~[spring-data-commons-2.1.6.RELEASE.jar:2.1.6.RELEASE] 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.doInvoke(RepositoryFactorySupport.java:608) ~[spring-data-commons-2.1.6.RELEASE.jar:2.1.6.RELEASE] 在 org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.lambda$invoke$3(RepositoryFactorySupport.java:595) ~[spring-data-commons-2.1.6.RELEASE.jar:2.1.6.RELEASE] 在 org.springframework.data.repository.core.support.RepositoryFactorySupport$QueryExecutorMethodInterceptor.invoke(RepositoryFactorySupport.java:595) ~[spring-data-commons-2.1.6.RELEASE.jar:2.1.6.RELEASE] 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:59) ~[spring-data-commons-2.1.6.RELEASE.jar:2.1.6.RELEASE] 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294) ~[spring-tx-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) ~[spring-tx-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:139) ~[spring-tx-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:138) ~[spring-data-jpa-2.1.6.RELEASE.jar:2.1.6.RELEASE] 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:93) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.data.repository.core.support.SurroundingTransactionDetectorMethodInterceptor.invoke(SurroundingTransactionDetectorMethodInterceptor.java:61) ~[spring-data-commons-2.1.6.RELEASE.jar:2.1.6.RELEASE] 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 com.sun.proxy.$Proxy126.save(未知来源)~[na:na] 在 de.hspf.lectureservice.lecture.LectureService.readExcelFile(LectureService.java:111) ~[classes/:na] 在 de.hspf.lectureservice.lecture.LectureService.readExcelFileFolder(LectureService.java:56) ~[classes/:na] 在 de.hspf.lectureservice.lecture.LectureService$$FastClassBySpringCGLIB$$c90e79de.invoke() ~[classes/:na] 在 org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218) ~[spring-core-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:749) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:294) ~[spring-tx-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:98) ~[spring-tx-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:688) ~[spring-aop-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 de.hspf.lectureservice.lecture.LectureService$$EnhancerBySpringCGLIB$$8c87ac4c.readExcelFileFolder() ~[classes/:na] 在 sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_161] 在 sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_161] 在 sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_161] 在 java.lang.reflect.Method.invoke(Method.java:498) ~[na:1.8.0_161] 在 org.springframework.context.event.ApplicationListenerMethodAdapter.doInvoke(ApplicationListenerMethodAdapter.java:261) ~[spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.context.event.ApplicationListenerMethodAdapter.processEvent(ApplicationListenerMethodAdapter.java:179) ~[spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.context.event.ApplicationListenerMethodAdapter.onApplicationEvent(ApplicationListenerMethodAdapter.java:142) ~[spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.context.event.SimpleApplicationEventMulticaster.doInvokeListener(SimpleApplicationEventMulticaster.java:172) ~[spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.context.event.SimpleApplicationEventMulticaster.invokeListener(SimpleApplicationEventMulticaster.java:165) ~[spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.context.event.SimpleApplicationEventMulticaster.multicastEvent(SimpleApplicationEventMulticaster.java:139) ~[spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:402) ~[spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.context.support.AbstractApplicationContext.publishEvent(AbstractApplicationContext.java:359) ~[spring-context-5.1.6.RELEASE.jar:5.1.6.RELEASE] 在 org.springframework.boot.context.event.EventPublishingRunListener.running(EventPublishingRunListener.java:105) ~[spring-boot-2.1.4.RELEASE.jar:2.1.4.RELEASE] 在 org.springframework.boot.SpringApplicationRunListeners.running(SpringApplicationRunListeners.java:78) ~[spring-boot-2.1.4.RELEASE.jar:2.1.4.RELEASE] 在 org.springframework.boot.SpringApplication.run(SpringApplication.java:332) [spring-boot-2.1.4.RELEASE.jar:2.1.4.RELEASE] 在 org.springframework.boot.SpringApplication.run(SpringApplication.java:1260) [spring-boot-2.1.4.RELEASE.jar:2.1.4.RELEASE] 在 org.springframework.boot.SpringApplication.run(SpringApplication.java:1248) [spring-boot-2.1.4.RELEASE.jar:2.1.4.RELEASE] 在 de.hspf.lectureservice.LectureServiceApplication.main(LectureServiceApplication.java:15) [classes/:na]

@Entity
public class SemesterLecture implements Serializable {
    @Id
    @ManyToOne
    @JsonIgnore
    @JoinColumn
    private Semester semester;

    @Id
    @ManyToOne
    @JoinColumn
    private Lecture lecture;

    private String grade;

    @OneToMany(cascade = CascadeType.ALL, orphanRemoval = true)
    private List<LectureDateTime> lectureDateTimes;

    public SemesterLecture(Lecture lecture, String grade) {
        this.lecture = lecture;
        this.grade = grade;
    }

    public SemesterLecture() {
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof SemesterLecture)) return false;
        SemesterLecture that = (SemesterLecture) o;
        return Objects.equals(semester.getTitle(), that.semester.getTitle()) &&
                Objects.equals(lecture.getTitle(), that.lecture.getTitle());
    }

@Override
    public int hashCode() {
        return Objects.hash(semester.getTitle(), lecture.getTitle(), grade);
    }
@EventListener(ApplicationReadyEvent.class)
    @Transactional
    public void readExcelFileFolder() throws IOException, InvalidFormatException, NotFoundException {
        LOG.info("Load Excel Files from folder...");
        FileFilter filter = new ExcelFileFilter ();
        Resource[] resourceFiles = resourcePatternResolver.getResources("classpath:excel-files/*.xls");
        for (Resource resource : resourceFiles) {
            readExcelFile(resource.getInputStream(), resource.getFilename());
        }
    }

    public void readExcelFile(InputStream inputStream, String fileName) throws IOException, InvalidFormatException, NotFoundException {
        Workbook workbook = WorkbookFactory.create(inputStream);
        DataFormatter dataFormatter = new DataFormatter();
        LOG.info("Read Excel File: " + fileName.substring(0, fileName.lastIndexOf('.')));

        Course existingCourse = courseRepository.findByCourseNumber(fileName.replace(" ", "").substring(0, fileName.lastIndexOf('.')));
        if(existingCourse == null){
            return;
        }
        LOG.info("Found course with title " + existingCourse.getCourseTitle());

        if(existingCourse.getSemesters().size() < 1){
            for(int i = 1; i < 8; i++){
                Semester semester = new Semester();
                semester.setTitle("Semester " + i);
                semester.setSemesterLectures(new ArrayList<SemesterLecture>());
                semester.setSemesterNumber(i);
                existingCourse.getSemesters().add(semesterRepository.save(semester));
            }
            LOG.info("Added " + existingCourse.getSemesters().size() + " Semesters");
        }

        for(int y = 0; y < 5; y++){
            Sheet sheet = workbook.getSheetAt(y);
            for (Row row: sheet) {
                String title = dataFormatter.formatCellValue(row.getCell(0));
                String modulNumber = dataFormatter.formatCellValue(row.getCell(1)).replace(" ", "");
                String language = dataFormatter.formatCellValue(row.getCell(2));
                int semesterNumber = 1;
                try{
                    semesterNumber = Integer.parseInt(dataFormatter.formatCellValue(row.getCell(3)).replace(".", ""));
                }catch (Exception e){}
                if(title instanceof String && modulNumber instanceof String && !title.isEmpty() && !modulNumber.isEmpty() && !title.startsWith("Module")){
                    try{
                        if(lectureRepository.existsByModulNumber(modulNumber.replace(" ", ""))){
                            Lecture lecture = lectureRepository.findByModulNumber(modulNumber.replace(" ", ""));
                            SemesterLecture sm = new SemesterLecture(lecture, "0");
                            sm.setSemester(existingCourse.getSemesters().get(semesterNumber-1));
                            sm.setLecture(lecture);
                            sm.setGrade("0");
                            existingCourse.getSemesters().get(semesterNumber-1).getSemesterLectures().add(sm);
                            LOG.info(sm.getLecture().getTitle() + sm.getSemester().getTitle());
                        }
                    } catch (NumberFormatException e) {
                        throw new NotFoundException("Lecture does not exist");
                    }
                }
            }
        }

        for(Semester semester : existingCourse.getSemesters()){
            semesterRepository.save(semester);
        }
        courseRepository.save(existingCourse);
        LOG.info("Saved all lectures from course " + existingCourse.getCourseTitle());
    }

在大多数抓取的数据中没有发生任何事情。 当我尝试保存学期对象时出现错误。 Excel 文件的结构如下:标题、模数、语言、学期号

【问题讨论】:

  • 你的哈希码方法是什么?
  • @Override public int hashCode() { return Objects.hash(semester.getTitle(), Lecture.getTitle(), Grade); }
  • 使用调试器,Objects.hash 中的值之一为空。 hash 对参数进行空点检查,但如果学期或讲座为空,它将在 getter 上抛出空指针
  • 所以你认为在一个学期的讲座中有一个讲座或学期为空?因为当我调试发生错误的课程时,一切看起来都很好......
  • 这是一个可能,我只是认为来自堆栈跟踪

标签: spring hibernate spring-boot spring-data-jpa


【解决方案1】:

根据堆栈跟踪,我认为您有两个问题:

  • 第一个,被第二个隐藏,是你试图保存一个 SemesterLecture 实例,它与一个已经存在的主 id 相同(即一对 SemesterLecture 的值相同)数据库,因此可以在堆栈跟踪中看到 NonUniqueObjectException
  • 第二个是 Spring,当尝试打印 NonUniqueObjectException 的消息时,通过调用 SemesterLecture 类的 hash 方法结束,例如,将字段学期或字段讲座设置为 null,因此 NullPointerException没问题。

我的建议:

首先,删除或注释 SemesterLecture 类的哈希方法。默认的就足够了,它可以让你看到你原来的异常。我的猜测是您的 Semester 或 Lecture 类的 hash 或 equals 方法中存在错误,导致 hibernate 将两个实体误认为是同一个。

【讨论】:

  • 你是对的。评论哈希方法后,我收到以下错误:org.springframework.dao.DataIntegrityViolationException:具有相同标识符值的不同对象已与会话关联。您知道我该如何解决吗?当我尝试添加自动生成的 id 时,spring 应该能够识别不同的对象,对吧?
  • @softwareUser 添加自动生成的 id 应该可以解决问题。使用业务价值作为主键通常被认为是一种不好的做法。但如果没有更多信息,我无法给你一个完美的答案。你将不得不尝试和学习:)
猜你喜欢
  • 2023-03-30
  • 2014-06-27
  • 2020-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多