如何避免 java.util.ConcurrentModificationException
How to avoid java.util.ConcurrentModificationException
我在此应用程序中使用 Hibernate。我正在尝试将数据从数据库调用到 jTable。当数据库为空代码正在编译但是当我将数据添加到 mysql table 程序抛出 java.util.ConcurrentModificationException.
public FrmMain() {
initialize();
getData();
}
public void getData() {
model = (DefaultTableModel) tblBookList.getModel();
List<Book> books = new ArrayList<>(bookManager.getBook());
ListIterator<Book> listIterator = books.listIterator();
while(listIterator.hasNext()) {
Book book = listIterator.next();
Object[] row = { book.getName(), book.getAuthor(), book.getPublisher(),
book.getPage(), book.getTranslator(), book.getPublishYear(), book.getType()
};
model.addRow(row);
}
}
我的数据访问代码。也许问题就在这里
public class MySqlBookDal implements IBookDal {
SessionFactory factory = new Configuration()
.configure("mysqlHibernate.cfg.xml")
.addAnnotatedClass(Book.class)
.buildSessionFactory();
Session session = factory.getCurrentSession();
public List<Book> select() {
List<Book> books = new CopyOnWriteArrayList<Book>();
try {
session.beginTransaction();
books = session.createQuery("from Book").getResultList();
for(Book book: books) {
books.add(book);
}
session.getTransaction().commit();
} finally {
factory.close();
}
return books;
}
有人可以帮助我吗?
堆栈跟踪:
ERROR: Connection leak detected: there are 1 unclosed connections upon
shutting down pool jdbc:mysql://localhost:3306/book?useUnicode=true&useLegacyDatetimeCode=false&serverTimezone=Turkey
java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
at com.github.bookManagementSystem.dataAccess.MySqlBookDal.select(MySqlBookDal.java:32)
at com.github.bookManagementSystem.business.BookManager.getBook(BookManager.java:20)
at com.github.bookManagementSystem.FrmMain.getData(FrmMain.java:98)
at com.github.bookManagementSystem.FrmMain.<init>(FrmMain.java:90)
at com.github.bookManagementSystem.FrmMain.run(FrmMain.java:76)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:316)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
at java.desktop/java.awt.EventQueue.run(EventQueue.java:721)
at java.desktop/java.awt.EventQueue.run(EventQueue.java:715)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
尝试使用 CopyOnWriteArrayList
而不是 ArrayList
。这将允许并发修改。但请注意,如果您经常更改列表,这不是很有效。不过,如果你大部分时间都在阅读,那应该没问题。
您正在用另一个列表替换 books
的值,然后循环尝试将新列表 books
附加到自身 - 因此出现 ConcurrentModificationException。
假设 getResultList() returns 一个 List 所有你需要做的就是将结果直接附加到书上而不需要 re-assignment:
books.addAll(session.createQuery("from Book").getResultList());
// books = session.createQuery("from Book").getResultList();
// for(Book book: books) {
// books.add(book);
// }
ConcurrentModificationException
被抛出,因为您将元素添加到您正在迭代的同一列表中。这里:
for(Book book : books) {
books.add(book);
}
此代码错误。
您可以 return 由 Hibernate return 编辑 List
实例,或者创建一个新列表。
您不需要使用 CopyOnWriteArrayList
!这是一个非常昂贵的数据结构。您的错误与同时修改和访问列表的不同线程无关。此外,您不需要针对数据库启动显式事务,因为您只是在读取。整个 select
方法可以完美地简化为:
public List<Book> select() {
return new ArrayList<>(session.createQuery("from Book").getResultList());
}
您也不需要关闭 factory
。
@DuncG 有解决您眼前问题的方法。
假设您通过 start.spring.io 下载所需的依赖项,您的代码可能会通过 Spring Boot 简化为如下所示:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface BookRepository extends JpaRepository<Book, Long> {
}
为您服务class:
@Autowired //or use constructor injection
BookRepository BookRepository;
public List<Book> findAll() {
return bookRepository.findAll();
}
JpaRepository
继承了 findAll()
方法以及 CRUD 存储库的其他非常常见的行为。有关详细信息,请参阅 docs。
我在此应用程序中使用 Hibernate。我正在尝试将数据从数据库调用到 jTable。当数据库为空代码正在编译但是当我将数据添加到 mysql table 程序抛出 java.util.ConcurrentModificationException.
public FrmMain() {
initialize();
getData();
}
public void getData() {
model = (DefaultTableModel) tblBookList.getModel();
List<Book> books = new ArrayList<>(bookManager.getBook());
ListIterator<Book> listIterator = books.listIterator();
while(listIterator.hasNext()) {
Book book = listIterator.next();
Object[] row = { book.getName(), book.getAuthor(), book.getPublisher(),
book.getPage(), book.getTranslator(), book.getPublishYear(), book.getType()
};
model.addRow(row);
}
}
我的数据访问代码。也许问题就在这里
public class MySqlBookDal implements IBookDal {
SessionFactory factory = new Configuration()
.configure("mysqlHibernate.cfg.xml")
.addAnnotatedClass(Book.class)
.buildSessionFactory();
Session session = factory.getCurrentSession();
public List<Book> select() {
List<Book> books = new CopyOnWriteArrayList<Book>();
try {
session.beginTransaction();
books = session.createQuery("from Book").getResultList();
for(Book book: books) {
books.add(book);
}
session.getTransaction().commit();
} finally {
factory.close();
}
return books;
}
有人可以帮助我吗? 堆栈跟踪:
ERROR: Connection leak detected: there are 1 unclosed connections upon
shutting down pool jdbc:mysql://localhost:3306/book?useUnicode=true&useLegacyDatetimeCode=false&serverTimezone=Turkey
java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
at com.github.bookManagementSystem.dataAccess.MySqlBookDal.select(MySqlBookDal.java:32)
at com.github.bookManagementSystem.business.BookManager.getBook(BookManager.java:20)
at com.github.bookManagementSystem.FrmMain.getData(FrmMain.java:98)
at com.github.bookManagementSystem.FrmMain.<init>(FrmMain.java:90)
at com.github.bookManagementSystem.FrmMain.run(FrmMain.java:76)
at java.desktop/java.awt.event.InvocationEvent.dispatch(InvocationEvent.java:316)
at java.desktop/java.awt.EventQueue.dispatchEventImpl(EventQueue.java:770)
at java.desktop/java.awt.EventQueue.run(EventQueue.java:721)
at java.desktop/java.awt.EventQueue.run(EventQueue.java:715)
at java.base/java.security.AccessController.doPrivileged(AccessController.java:391)
at java.base/java.security.ProtectionDomain$JavaSecurityAccessImpl.doIntersectionPrivilege(ProtectionDomain.java:85)
at java.desktop/java.awt.EventQueue.dispatchEvent(EventQueue.java:740)
at java.desktop/java.awt.EventDispatchThread.pumpOneEventForFilters(EventDispatchThread.java:203)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForFilter(EventDispatchThread.java:124)
at java.desktop/java.awt.EventDispatchThread.pumpEventsForHierarchy(EventDispatchThread.java:113)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:109)
at java.desktop/java.awt.EventDispatchThread.pumpEvents(EventDispatchThread.java:101)
at java.desktop/java.awt.EventDispatchThread.run(EventDispatchThread.java:90)
尝试使用 CopyOnWriteArrayList
而不是 ArrayList
。这将允许并发修改。但请注意,如果您经常更改列表,这不是很有效。不过,如果你大部分时间都在阅读,那应该没问题。
您正在用另一个列表替换 books
的值,然后循环尝试将新列表 books
附加到自身 - 因此出现 ConcurrentModificationException。
假设 getResultList() returns 一个 List 所有你需要做的就是将结果直接附加到书上而不需要 re-assignment:
books.addAll(session.createQuery("from Book").getResultList());
// books = session.createQuery("from Book").getResultList();
// for(Book book: books) {
// books.add(book);
// }
ConcurrentModificationException
被抛出,因为您将元素添加到您正在迭代的同一列表中。这里:
for(Book book : books) {
books.add(book);
}
此代码错误。
您可以 return 由 Hibernate return 编辑 List
实例,或者创建一个新列表。
您不需要使用 CopyOnWriteArrayList
!这是一个非常昂贵的数据结构。您的错误与同时修改和访问列表的不同线程无关。此外,您不需要针对数据库启动显式事务,因为您只是在读取。整个 select
方法可以完美地简化为:
public List<Book> select() {
return new ArrayList<>(session.createQuery("from Book").getResultList());
}
您也不需要关闭 factory
。
@DuncG 有解决您眼前问题的方法。
假设您通过 start.spring.io 下载所需的依赖项,您的代码可能会通过 Spring Boot 简化为如下所示:
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
@Repository
public interface BookRepository extends JpaRepository<Book, Long> {
}
为您服务class:
@Autowired //or use constructor injection
BookRepository BookRepository;
public List<Book> findAll() {
return bookRepository.findAll();
}
JpaRepository
继承了 findAll()
方法以及 CRUD 存储库的其他非常常见的行为。有关详细信息,请参阅 docs。