简体   繁体   中英

How to avoid java.util.ConcurrentModificationException

I'm using Hibernate in this application. I'm trying call data from database to jTable. When database is empty codes are compiling but when i add data to mysql table program throw 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);
      
    }
    
}

My data access codes. Maybe the problem is here

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;
}

Somebody can help me? Stack trace:

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$1.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$4.run(EventQueue.java:721)
    at java.desktop/java.awt.EventQueue$4.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)

You are replacing the value of books with another list then the loop tries to append the new list books into itself - hence ConcurrentModificationException.

Assuming that getResultList() returns a List all you need to do is append results directly to books without re-assignment:

books.addAll(session.createQuery("from Book").getResultList());

// books = session.createQuery("from Book").getResultList();
// for(Book book: books) {
//         books.add(book);
// }

Try using a CopyOnWriteArrayList instead of an ArrayList . This will allow for concurrent modification. But be warned, if you are changing the list a lot, this is not very efficient. If you are mostly reading, however, it should be fine.

ConcurrentModificationException is being thrown because you are adding elements to the same list you are iterating. Here:

for(Book book : books) {
    books.add(book);
}

This code is wrong.

You can either return the List instance returned by Hibernate, or create a new list.

You don't need to use a CopyOnWriteArrayList ! It is a very expensive datastructure. Your error has nothing to do with different threads modifiying and accessing the list at the same time. Besides, you don't need to start an explicit transaction against the database, because you are only reading. The whole select method could perfectly be simplified to:

public List<Book> select() {
    return new ArrayList<>(session.createQuery("from Book").getResultList());
}

You don't need to close the factory , either.

@DuncG has a solution to your immediate problem.

Your code may be simplified to something like the following with Spring Boot, assuming you download the needed dependencies through start.spring.io:

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface BookRepository extends JpaRepository<Book, Long> {
}

In your service class:

@Autowired //or use constructor injection
BookRepository BookRepository;

public List<Book> findAll() {
    return bookRepository.findAll();
}

JpaRepository inherits the findAll() method as well as other very common behaviors with CRUD repositories. See the docs for further detail.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM