LockModeType.PESSIMISTIC_WRITE给出:当前没有事务处于活动状态

时间:2012-08-03 22:41:17

标签: exception jpa transactions locking jpql

冗余行显示在数据库中:

mysql> 
mysql> 
mysql> USE usenet;SHOW TABLES;DESCRIBE ARTICLE;DESCRIBE NEWSGROUP;SELECT * FROM NEWSGROUP;
Database changed
+------------------+
| Tables_in_usenet |
+------------------+
| ARTICLE          |
| NEWSGROUP        |
+------------------+
2 rows in set (0.00 sec)

+---------------+------------+------+-----+---------+----------------+
| Field         | Type       | Null | Key | Default | Extra          |
+---------------+------------+------+-----+---------+----------------+
| ID            | bigint(20) | NO   | PRI | NULL    | auto_increment |
| MESSAGENUMBER | int(11)    | YES  |     | NULL    |                |
| NEWSGROUP_ID  | bigint(20) | YES  | MUL | NULL    |                |
+---------------+------------+------+-----+---------+----------------+
3 rows in set (0.00 sec)

+-----------+--------------+------+-----+---------+----------------+
| Field     | Type         | Null | Key | Default | Extra          |
+-----------+--------------+------+-----+---------+----------------+
| ID        | bigint(20)   | NO   | PRI | NULL    | auto_increment |
| NEWSGROUP | varchar(255) | YES  |     | NULL    |                |
+-----------+--------------+------+-----+---------+----------------+
2 rows in set (0.00 sec)

+----+-------------------------------+
| ID | NEWSGROUP                     |
+----+-------------------------------+
|  1 | gwene.com.androidcentral      |
|  2 | gwene.com.androidcentral      |
|  3 | gwene.com.blogspot.emacsworld |
|  4 | gwene.com.blogspot.googlecode |
|  5 | gwene.com.blogspot.googlecode |
|  6 | gwene.com.economist           |
|  7 | gwene.com.economist           |
+----+-------------------------------+
7 rows in set (0.00 sec)

mysql> 

NEWSGROUP.newsgroup应具有唯一值。我有理由确定我需要在Article构造函数中锁定数据库:

public Article(Message message, Folder folder) {
    messageNumber = message.getMessageNumber();
    EntityManagerFactory emf;
    EntityManager em;
    emf = Persistence.createEntityManagerFactory("USENETPU");
    em = emf.createEntityManager();
    String fullNewsgroupName = folder.getFullName();
    TypedQuery<Newsgroup> query = em.createQuery("SELECT n FROM Newsgroup n WHERE n.newsgroup = :newsGroupParam", Newsgroup.class);
    query.setParameter("newsGroupParam", fullNewsgroupName);
    em.lock(query, LockModeType.PESSIMISTIC_WRITE);
    try {
        newsgroup = query.getSingleResult();
        LOG.info("found " + query.getSingleResult()); //ok
    } catch (javax.persistence.NoResultException e) {
        newsgroup = new Newsgroup(folder);
        LOG.info(e + "\ncould not find " + fullNewsgroupName); //ok
    } catch (NonUniqueResultException e) {
        LOG.info(e + "\nshould never happen\t" + fullNewsgroupName); //not ok
    }
}

但是,该锁定导致:

run:
DEBUG: nntp: newsrc loading /home/thufir/.newsrc
DEBUG: nntp: newsrc load: 5 groups in 35ms
[EL Info]: 2012-08-03 15:35:28.386--ServerSession(17944810)--EclipseLink, version: Eclipse Persistence Services - 2.3.0.v20110604-r9504
[EL Info]: 2012-08-03 15:35:29.526--ServerSession(17944810)--file:/home/thufir/NetBeansProjects/USENET/build/classes/_USENETPU login successful
Aug 03, 2012 3:35:30 PM net.bounceme.dur.usenet.driver.FetchBean <init>
INFO: [gwene.com.androidcentral, gwene.com.blogspot.emacsworld, gwene.com.blogspot.googlecode, gwene.com.blogspot.googlereader, gwene.com.economist]
Aug 03, 2012 3:35:31 PM net.bounceme.dur.usenet.driver.FetchBean main
SEVERE: null
javax.persistence.TransactionRequiredException: 
Exception Description: No transaction is currently active
    at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionWrapper.throwCheckTransactionFailedException(EntityTransactionWrapper.java:113)
    at org.eclipse.persistence.internal.jpa.transaction.EntityTransactionWrapper.checkForTransaction(EntityTransactionWrapper.java:50)
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.checkForTransaction(EntityManagerImpl.java:1776)
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.lock(EntityManagerImpl.java:1617)
    at org.eclipse.persistence.internal.jpa.EntityManagerImpl.lock(EntityManagerImpl.java:1593)
    at net.bounceme.dur.usenet.model.Article.<init>(Article.java:34)
    at net.bounceme.dur.usenet.driver.FetchBean.<init>(FetchBean.java:41)
    at net.bounceme.dur.usenet.driver.FetchBean.main(FetchBean.java:21)

BUILD SUCCESSFUL (total time: 16 seconds)

而评论它给出了正常的运行:

run:
DEBUG: nntp: newsrc loading /home/thufir/.newsrc
DEBUG: nntp: newsrc load: 5 groups in 14ms
[EL Info]: 2012-08-03 15:36:28.103--ServerSession(17944810)--EclipseLink, version: Eclipse Persistence Services - 2.3.0.v20110604-r9504
[EL Info]: 2012-08-03 15:36:29.186--ServerSession(17944810)--file:/home/thufir/NetBeansProjects/USENET/build/classes/_USENETPU login successful
Aug 03, 2012 3:36:29 PM net.bounceme.dur.usenet.driver.FetchBean <init>
INFO: [gwene.com.androidcentral, gwene.com.blogspot.emacsworld, gwene.com.blogspot.googlecode, gwene.com.blogspot.googlereader, gwene.com.economist]
Aug 03, 2012 3:36:31 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.androidcentral
Aug 03, 2012 3:36:31 PM net.bounceme.dur.usenet.model.Article <init>
INFO: found gwene.com.androidcentral
Aug 03, 2012 3:36:31 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.androidcentral
Aug 03, 2012 3:36:31 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.androidcentral
Aug 03, 2012 3:36:31 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.androidcentral
Aug 03, 2012 3:36:31 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.androidcentral
Aug 03, 2012 3:36:31 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.androidcentral
Aug 03, 2012 3:36:31 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.androidcentral
Aug 03, 2012 3:36:31 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.blogspot.emacsworld
Aug 03, 2012 3:36:31 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.blogspot.googlecode
Aug 03, 2012 3:36:32 PM net.bounceme.dur.usenet.model.Article <init>
INFO: found gwene.com.blogspot.googlecode
Aug 03, 2012 3:36:32 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.blogspot.googlecode
Aug 03, 2012 3:36:32 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.blogspot.googlecode
Aug 03, 2012 3:36:32 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.blogspot.googlecode
Aug 03, 2012 3:36:32 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.blogspot.googlecode
Aug 03, 2012 3:36:32 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.blogspot.googlecode
Aug 03, 2012 3:36:32 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.blogspot.googlecode
Aug 03, 2012 3:36:32 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NoResultException: getSingleResult() did not retrieve any entities.
could not find gwene.com.economist
Aug 03, 2012 3:36:33 PM net.bounceme.dur.usenet.model.Article <init>
INFO: found gwene.com.economist
Aug 03, 2012 3:36:33 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.economist
Aug 03, 2012 3:36:33 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.economist
Aug 03, 2012 3:36:33 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.economist
Aug 03, 2012 3:36:33 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.economist
Aug 03, 2012 3:36:33 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.economist
Aug 03, 2012 3:36:33 PM net.bounceme.dur.usenet.model.Article <init>
INFO: javax.persistence.NonUniqueResultException: More than one result was returned from Query.getSingleResult()
should never happen gwene.com.economist
Aug 03, 2012 3:36:33 PM net.bounceme.dur.usenet.driver.FetchBean <init>
INFO: **************************done
BUILD SUCCESSFUL (total time: 16 seconds)

只有Article构造函数才会实例化Newsgroup实体;目前,表格生成策略为drop and create

如何在此方案中获取锁定以防止重复?

2 个答案:

答案 0 :(得分:1)

您需要在执行锁定查询之前启动事务。您只能在数据库事务的上下文中锁定某些内容。

答案 1 :(得分:0)

就我而言,我使用jpa存储库和查询提示来获得乐观悲观的写锁。通常,我将在服务层上使用@transactional批注,但不适用于以下存储库设置。因此,在使用存储库之前,我必须显式启动事务。也许这对您有帮助。

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

    /**
     * "The lock acquisition request skips the already locked rows.
     * It uses a SELECT …​ FOR UPDATE SKIP LOCKED in Oracle and PostgreSQL 9.5,
     * or SELECT …​ with (rowlock, updlock, readpast) in SQL Server."
     */
    String UPGRADE_SKIPLOCKED = "-2";

    @Lock(value = LockModeType.PESSIMISTIC_WRITE) // adds 'FOR UPDATE' statement
    @QueryHints({@QueryHint(name = "javax.persistence.lock.timeout", value = UPGRADE_SKIPLOCKED)})
    Book findFirst();
}

和服务:

import org.springframework.stereotype.Service;
import org.springframework.transaction.PlatformTransactionManager;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

@Service
public class BookService {

    private final PlatformTransactionManager transactionManager;
    private final BookRepository bookRepository;

    public BookService(PlatformTransactionManager transactionManager, BookRepository bookRepository) {
        this.transactionManager = transactionManager;
        this.bookRepository = bookRepository;
    }

    public void doSomethingWithBook() {
        final DefaultTransactionDefinition transactionDefinition = new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
        TransactionStatus transaction = transactionManager.getTransaction(transactionDefinition);
        Book book = bookRepository.findFirst();
        // ....
        bookRepository.save(book);
        transactionManager.commit(transaction);
    }
}