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

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

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

  1. @Getter
  2. @Setter
  3. @RequiredArgsConstructor
  4. @SuperBuilder
  5. @NoArgsConstructor
  6. @Entity
  7. @Table(name = "account")
  8. public class Account {
  9. @Id
  10. @GeneratedValue(strategy = GenerationType.IDENTITY)
  11. @Column(name = "account_id", unique = true, nullable = false)
  12. private int id;
  13. @NonNull
  14. @Column(nullable = false)
  15. private String username;
  16. @NonNull
  17. @Column(nullable = false)
  18. private String password;
  19. @NonNull
  20. @Builder.Default
  21. @OneToMany(mappedBy = "account", orphanRemoval = true, cascade = CascadeType.ALL)
  22. private Set<Folder> folders = new HashSet<>();
  23. //There are other properties such as: smtpPort, inServerType(pop3 or imap), inServerPort...all primitives
  24. }

文件夹

  1. @Getter
  2. @Setter
  3. @SuperBuilder
  4. @NoArgsConstructor
  5. @Entity
  6. public class Folder {
  7. @Id
  8. @GeneratedValue(strategy = GenerationType.IDENTITY)
  9. @Column(name = "folder_id", unique = true, nullable = false)
  10. private int id;
  11. @Column(name = "full_folder_name", nullable = true)
  12. private String fullFolderName; //this is name of Folder on email server
  13. @Column(name = "folder_name", nullable = true)
  14. private String folderName;
  15. @Column(name = "folder_url", nullable = true)
  16. private String folderUrl;
  17. @Builder.Default
  18. @OneToMany(fetch = FetchType.EAGER, mappedBy = "parent", orphanRemoval = true)
  19. private Set<Folder> children = new HashSet<Folder>();
  20. @Builder.Default
  21. @OneToMany(fetch = FetchType.EAGER, mappedBy = "folder", orphanRemoval = true)
  22. @Column(nullable = false)
  23. private Set<Message> messages = new HashSet<>();
  24. @ManyToOne(fetch = FetchType.EAGER, cascade = CascadeType.ALL)
  25. @JoinColumn(name = "parent_id", referencedColumnName = "folder_id", nullable = true)
  26. private Folder parent;
  27. @ManyToOne
  28. private Account account;
  29. }

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

  1. public void fetchInitialFolderStructure(@NonNull Account account) {
  2. Runnable thread = () -> {
  3. //Folder rootFolder = null;
  4. try {
  5. Store store = getStore(account);
  6. if (store.isConnected()) {
  7. System.out.println("ok");
  8. javax.mail.Folder root = store.getDefaultFolder();
  9. final Folder rootFolder = folderService.save(Folder.builder()
  10. .fullFolderName(root.getFullName())
  11. .folderName(root.getName())
  12. .folderUrl(root.getURLName().toString())
  13. .children(new HashSet<Folder>())
  14. .messages(new HashSet<Message>())
  15. .account(account)
  16. .build());
  17. Arrays.stream(root.list("%")).forEach(jmailFolder -> {
  18. try {
  19. Folder levelFolder = Folder.builder()
  20. .fullFolderName(jmailFolder.getFullName())
  21. .folderName(jmailFolder.getName())
  22. .folderUrl(jmailFolder.getURLName().toString())
  23. .children(new HashSet<Folder>())
  24. .messages(new HashSet<Message>())
  25. .parent(rootFolder)
  26. .account(account)
  27. .build();
  28. levelFolder = folderService.save(levelFolder);
  29. dumpFolder(jmailFolder, levelFolder, account);
  30. } catch (MessagingException e) {
  31. e.printStackTrace();
  32. }
  33. });
  34. } else {
  35. throw new IllegalArgumentException("Cannot connect to Store!");
  36. }
  37. store.close();
  38. } catch (NoSuchProviderException e) {
  39. e.printStackTrace();
  40. } catch (MessagingException e) {
  41. e.printStackTrace();
  42. }
  43. };
  44. new Thread(thread).start();
  45. }

下面是递归方法:

  1. @Transactional
  2. private void dumpFolder(javax.mail.Folder jmailFolder, Folder folder, Account account) {
  3. try {
  4. if ((jmailFolder.getType() & javax.mail.Folder.HOLDS_FOLDERS) != 0) {
  5. Arrays.stream(jmailFolder.list()).forEach(f -> {
  6. Folder childFolder = null;
  7. try {
  8. childFolder = Folder.builder()
  9. .fullFolderName(jmailFolder.getFullName())
  10. .folderName(jmailFolder.getName())
  11. .folderUrl(jmailFolder.getURLName().toString())
  12. .children(new HashSet<Folder>())
  13. .messages(new HashSet<Message>())
  14. .parent(folder)
  15. .account(account)
  16. .build();
  17. } catch (MessagingException e) {
  18. e.printStackTrace();
  19. }
  20. folderService.save(childFolder);
  21. dumpFolder(f, childFolder, account);
  22. });
  23. }
  24. } catch (MessagingException e) {
  25. e.printStackTrace();
  26. }
  27. }

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

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

  1. 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
  2. at org.springframework.orm.jpa.vendor.HibernateJpaDialect.convertHibernateAccessException(HibernateJpaDialect.java:297)
  3. at org.springframework.orm.jpa.vendor.HibernateJpaDialect.translateExceptionIfPossible(HibernateJpaDialect.java:233)
  4. at org.springframework.orm.jpa.AbstractEntityManagerFactoryBean.translateExceptionIfPossible(AbstractEntityManagerFactoryBean.java:551)
  5. at org.springframework.dao.support.ChainedPersistenceExceptionTranslator.translateExceptionIfPossible(ChainedPersistenceExceptionTranslator.java:61)
  6. at org.springframework.dao.support.DataAccessUtils.translateIfNecessary(DataAccessUtils.java:242)
  7. at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:152)
  8. at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
  9. at org.springframework.data.jpa.repository.support.CrudMethodMetadataPostProcessor$CrudMethodMetadataPopulatingMethodInterceptor.invoke(CrudMethodMetadataPostProcessor.java:174)
  10. at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
  11. at org.springframework.aop.interceptor.ExposeInvocationInterceptor.invoke(ExposeInvocationInterceptor.java:97)
  12. at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
  13. at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:215)
  14. at com.sun.proxy.$Proxy135.save(Unknown Source)
  15. at com.uns.ac.rs.emailclient.service.impl.FolderServiceImpl.save(FolderServiceImpl.java:18)
  16. at com.uns.ac.rs.emailclient.service.helper.JavaxMailHelper.lambda$2(JavaxMailHelper.java:101)
  17. at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
  18. at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
  19. at com.uns.ac.rs.emailclient.service.helper.JavaxMailHelper.dumpFolder(JavaxMailHelper.java:83)
  20. at com.uns.ac.rs.emailclient.service.helper.JavaxMailHelper.lambda$1(JavaxMailHelper.java:61)
  21. at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:948)
  22. at java.base/java.util.stream.ReferencePipeline$Head.forEach(ReferencePipeline.java:658)
  23. at com.uns.ac.rs.emailclient.service.helper.JavaxMailHelper.lambda$0(JavaxMailHelper.java:47)
  24. at java.base/java.lang.Thread.run(Thread.java:832)
  25. Caused by: org.hibernate.PersistentObjectException: detached entity passed to persist: com.uns.ac.rs.emailclient.model.Folder
  26. at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:120)
  27. at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:113)
  28. at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:744)
  29. at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:712)
  30. at org.hibernate.engine.spi.CascadingActions$7.cascade(CascadingActions.java:298)
  31. at org.hibernate.engine.internal.Cascade.cascadeToOne(Cascade.java:492)
  32. at org.hibernate.engine.internal.Cascade.cascadeAssociation(Cascade.java:416)
  33. at org.hibernate.engine.internal.Cascade.cascadeProperty(Cascade.java:218)
  34. at org.hibernate.engine.internal.Cascade.cascade(Cascade.java:151)
  35. at org.hibernate.event.internal.AbstractSaveEventListener.cascadeBeforeSave(AbstractSaveEventListener.java:427)
  36. at org.hibernate.event.internal.AbstractSaveEventListener.performSaveOrReplicate(AbstractSaveEventListener.java:264)
  37. at org.hibernate.event.internal.AbstractSaveEventListener.performSave(AbstractSaveEventListener.java:193)
  38. at org.hibernate.event.internal.AbstractSaveEventListener.saveWithGeneratedId(AbstractSaveEventListener.java:123)
  39. at org.hibernate.event.internal.DefaultPersistEventListener.entityIsTransient(DefaultPersistEventListener.java:185)
  40. at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:128)
  41. at org.hibernate.event.internal.DefaultPersistEventListener.onPersist(DefaultPersistEventListener.java:55)
  42. at org.hibernate.event.service.internal.EventListenerGroupImpl.fireEventOnEachListener(EventListenerGroupImpl.java:102)
  43. at org.hibernate.internal.SessionImpl.firePersist(SessionImpl.java:720)
  44. at org.hibernate.internal.SessionImpl.persist(SessionImpl.java:706)
  45. at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  46. at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
  47. at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  48. at java.base/java.lang.reflect.Method.invoke(Method.java:564)
  49. at org.springframework.orm.jpa.SharedEntityManagerCreator$SharedEntityManagerInvocationHandler.invoke(SharedEntityManagerCreator.java:311)
  50. at com.sun.proxy.$Proxy132.persist(Unknown Source)
  51. at org.springframework.data.jpa.repository.support.SimpleJpaRepository.save(SimpleJpaRepository.java:557)
  52. at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
  53. at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:64)
  54. at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
  55. at java.base/java.lang.reflect.Method.invoke(Method.java:564)
  56. at org.springframework.data.repository.core.support.RepositoryMethodInvoker$RepositoryFragmentMethodInvoker.lambda$new$0(RepositoryMethodInvoker.java:289)
  57. at org.springframework.data.repository.core.support.RepositoryMethodInvoker.doInvoke(RepositoryMethodInvoker.java:137)
  58. at org.springframework.data.repository.core.support.RepositoryMethodInvoker.invoke(RepositoryMethodInvoker.java:121)
  59. at org.springframework.data.repository.core.support.RepositoryComposition$RepositoryFragments.invoke(RepositoryComposition.java:524)
  60. at org.springframework.data.repository.core.support.RepositoryComposition.invoke(RepositoryComposition.java:285)
  61. at org.springframework.data.repository.core.support.RepositoryFactorySupport$ImplementationMethodExecutionInterceptor.invoke(RepositoryFactorySupport.java:531)
  62. at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
  63. at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.doInvoke(QueryExecutorMethodInterceptor.java:156)
  64. at org.springframework.data.repository.core.support.QueryExecutorMethodInterceptor.invoke(QueryExecutorMethodInterceptor.java:131)
  65. at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
  66. at org.springframework.data.projection.DefaultMethodInvokingMethodInterceptor.invoke(DefaultMethodInvokingMethodInterceptor.java:80)
  67. at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
  68. at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:123)
  69. at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:388)
  70. at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:119)
  71. at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
  72. at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
  73. ... 17 more

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

暂无答案!

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

相关问题