在新线程的递归函数中分离实体?

irlmq6kh  于 2021-07-23  发布在  Java
关注(0)|答案(0)|浏览(173)

我在用 Spring Boot , Hibernate 以及 JavaMail 要创建的库 Email Client 项目(类似于 Thunderbird )为了我的学校项目。我很难坚持下去 Folder 实体。
以下是我拥有的大多数实体:
帐户

@Getter
@Setter
@RequiredArgsConstructor
@SuperBuilder
@NoArgsConstructor
@Entity
@Table(name = "account")
public class Account {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "account_id", unique = true, nullable = false)
    private int id;

    @NonNull
    @Column(nullable = false)
    private String username;

    @NonNull
    @Column(nullable = false)
    private String password;

    @NonNull
    @Builder.Default
    @OneToMany(mappedBy = "account", orphanRemoval = true, cascade = CascadeType.ALL)
    private Set<Folder> folders = new HashSet<>();

    //There are other properties such as: smtpPort, inServerType(pop3 or imap), inServerPort...all primitives

}

文件夹

@Getter
@Setter
@SuperBuilder
@NoArgsConstructor
@Entity
public class Folder {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    @Column(name = "folder_id", unique = true, nullable = false)
    private int id;

    @Column(name = "full_folder_name", nullable = true)
    private String fullFolderName; //this is name of Folder on email server

    @Column(name = "folder_name", nullable = true)
    private String folderName;

    @Column(name = "folder_url", nullable = true)
    private String folderUrl;

    @Builder.Default
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "parent", orphanRemoval = true)
    private Set<Folder> children = new HashSet<Folder>();

    @Builder.Default
    @OneToMany(fetch = FetchType.EAGER, mappedBy = "folder", orphanRemoval = true)
    @Column(nullable = false)
    private Set<Message> messages = new HashSet<>();

    @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
    @JoinColumn(name = "parent_id", referencedColumnName = "folder_id", nullable = true)
    private Folder parent;

    @ManyToOne
    private Account account;
}

我希望能够从任何电子邮件服务器(如gmail、yahoo等)获取初始文件夹结构,并将该结构存储到我的数据库中(下一步是为每个服务器获取消息) Folder s。这个想法是从一个单独的线程获取文件夹结构,我不希望我的前端等待。我获取文件夹结构的代码工作正常,我获取所有文件夹层次结构,但仅从主线程获取。在我创建之后 Account 实体并将其持久化,只有这样我才能传递那个新引用(它确实有 Id )到以下助手方法。

public void fetchInitialFolderStructure(@NonNull Account account)  {    
        Runnable thread = () -> {
            //Folder rootFolder = null;
            try {
                Store store = getStore(account);

                if (store.isConnected()) {
                    System.out.println("ok");
                    javax.mail.Folder root = store.getDefaultFolder();
                    final Folder rootFolder = folderService.save(Folder.builder()
                            .fullFolderName(root.getFullName())
                            .folderName(root.getName())
                            .folderUrl(root.getURLName().toString())
                            .children(new HashSet<Folder>())
                            .messages(new HashSet<Message>())
                            .account(account)
                            .build());

                    Arrays.stream(root.list("%")).forEach(jmailFolder -> {

                        try {

                            Folder levelFolder = Folder.builder()
                                    .fullFolderName(jmailFolder.getFullName())
                                    .folderName(jmailFolder.getName())
                                    .folderUrl(jmailFolder.getURLName().toString())
                                    .children(new HashSet<Folder>())
                                    .messages(new HashSet<Message>())
                                    .parent(rootFolder)
                                    .account(account)
                                    .build();
                            levelFolder = folderService.save(levelFolder);
                            dumpFolder(jmailFolder, levelFolder, account);
                        } catch (MessagingException e) {
                            e.printStackTrace();
                        } 

                    });

                } else {
                    throw new IllegalArgumentException("Cannot connect to Store!");
                }

                store.close();
            } catch (NoSuchProviderException e) {
                e.printStackTrace();
            } catch (MessagingException e) {
                e.printStackTrace();
            } 
        };
        new Thread(thread).start();

    }

下面是递归方法:

@Transactional
private void dumpFolder(javax.mail.Folder jmailFolder, Folder folder, Account account) {
    try {
        if ((jmailFolder.getType() & javax.mail.Folder.HOLDS_FOLDERS) != 0) {

                Arrays.stream(jmailFolder.list()).forEach(f -> {
                    Folder childFolder = null;

                    try {
                        childFolder = Folder.builder()
                                        .fullFolderName(jmailFolder.getFullName())
                                        .folderName(jmailFolder.getName())
                                        .folderUrl(jmailFolder.getURLName().toString())
                                        .children(new HashSet<Folder>())
                                        .messages(new HashSet<Message>())
                                        .parent(folder)
                                        .account(account)
                                        .build();
                        } catch (MessagingException e) {

                                e.printStackTrace();
                        }

                    folderService.save(childFolder);
                    dumpFolder(f, childFolder, account);

                });
        }
    } catch (MessagingException e) {
        e.printStackTrace();
    }

}

有趣的是,可能有人会对如何以不同的方式执行递归有一个想法,那就是 list("%") 调用的方法 root :

在设置new runnable并启动new thread之前,我设法获取整个层次结构,但是对于new thread,我得到以下异常:

Exception in thread "Thread-19" org.springframework.dao.InvalidDataAccessApiUsageException: detached entity passed to persist: com.uns.ac.rs.emailclient.model.Folder; nested exception is org.hibernate.PersistentObjectException: detached entity passed to persist: com.uns.ac.rs.emailclient.model.Folder
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:297)
    at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:233)
    at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:551)
    at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
    at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:174)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
    at com.sun.proxy.$Proxy135.save(Unknown Source)
    at com.uns.ac.rs.emailclient.service.impl.FolderServiceImpl.save(FolderServiceImpl.java:18)
    at com.uns.ac.rs.emailclient.service.helper.JavaxMailHelper.lambda$2(JavaxMailHelper.java:101)
    at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
    at com.uns.ac.rs.emailclient.service.helper.JavaxMailHelper.dumpFolder(JavaxMailHelper.java:83)
    at com.uns.ac.rs.emailclient.service.helper.JavaxMailHelper.lambda$1(JavaxMailHelper.java:61)
    at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
    at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
    at com.uns.ac.rs.emailclient.service.helper.JavaxMailHelper.lambda$0(JavaxMailHelper.java:47)
    at java.base/java.lang.Thread.run(Thread.java:832)
Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.uns.ac.rs.emailclient.model.Folder
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:120)
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:113)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:744)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:712)
    at org.hibernate.engine.spi.CascadingActions$7.cascade(CascadingActions.java:298)
    at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:492)
    at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:416)
    at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:218)
    at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:151)
    at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:427)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:264)
    at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:193)
    at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:123)
    at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:185)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:128)
    at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:55)
    at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:102)
    at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:720)
    at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:706)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:311)
    at com.sun.proxy.$Proxy132.persist(Unknown Source)
    at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:557)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:564)
    at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:289)
    at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137)
    at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121)
    at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:524)
    at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285)
    at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:531)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:156)
    at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:131)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
    at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
    at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
    at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
    at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
    ... 17 more

所以问题是如何从一个单独的线程启动递归函数而不让hibernate发疯(通俗地说)?我不太精通hibernate和并发,所以任何帮助都是非常受欢迎的。抱歉,我的帖子太长了。

暂无答案!

目前还没有任何答案,快来回答吧!

相关问题