private static boolean isLockingException(final Throwable t, final boolean recurse) { if (t instanceof LockingException || t instanceof PessimisticLockException) { return true; } if (t instanceof SQLException) { final SQLException e = (SQLException) t; return e.getErrorCode() == ER_LOCK_WAIT_TIMEOUT || ST_LOCK.equals(e.getSQLState()); } if (recurse) { for (final Throwable thr : ExceptionUtils.getThrowables(t)) { if (isLockingException(thr, false)) { return true; } } } return false; }
@Override public int count(ConnectorInstance connectorInstance) { int attempts = 0; while (true) { try { String ql = "SELECT COUNT(r.id) FROM Record r WHERE r.connectorInstance = :connectorInstance"; Query query = this.getEntityManager().createQuery(ql); query.setParameter("connectorInstance", connectorInstance); return ((Number) query.getSingleResult()).intValue(); } catch (PessimisticLockException e) { attempts++; if (attempts > 100) { throw e; } } } }
/** * A strategy method to lock an object with an exclusive lock so that it can * be processed * * @param entity the entity to be locked * @param entityManager entity manager * @return true if the entity was locked */ protected boolean lockEntity(Object entity, EntityManager entityManager) { if (!getEndpoint().isConsumeLockEntity()) { return true; } try { LOG.debug("Acquiring exclusive lock on entity: {}", entity); if (isSkipLockedEntity()) { entityManager.lock(entity, lockModeType, NOWAIT); } else { entityManager.lock(entity, lockModeType); } return true; } catch (Exception e) { if (LOG.isDebugEnabled()) { LOG.debug("Failed to achieve lock on entity: " + entity + ". Reason: " + e, e); } if (e instanceof PessimisticLockException || e instanceof OptimisticLockException) { //transaction marked as rollback can't continue gracefully throw (PersistenceException) e; } //TODO: Find if possible an alternative way to handle results of native queries. //Result of native queries are Arrays and cannot be locked by all JPA Providers. if (entity.getClass().isArray()) { return true; } return false; } }
private int count(RecordCollection collection, Boolean updateIndex, Boolean deleted) { int attempts = 0; while (true) { try { String ql = "SELECT COUNT(r.id) FROM Record r WHERE r.connectorInstance.recordCollection = :recordCollection"; if (Boolean.TRUE.equals(updateIndex)) { ql += " AND updateIndex = :updateIndex AND (excluded = false OR excluded = null)"; } if (Boolean.TRUE.equals(deleted)) { ql += " AND deleted = :deleted"; } Query query = this.getEntityManager().createQuery(ql); query.setParameter("recordCollection", collection); if (updateIndex != null && Boolean.TRUE.equals(updateIndex)) { query.setParameter("updateIndex", updateIndex); } if (deleted != null && Boolean.TRUE.equals(deleted)) { query.setParameter("deleted", deleted); } return ((Number) query.getSingleResult()).intValue(); } catch (PessimisticLockException e) { attempts++; if (attempts > 100) { throw e; } } } }
/** * Convert the given runtime exception to an appropriate exception from the * {@code org.springframework.dao} hierarchy. * Return null if no translation is appropriate: any other exception may * have resulted from user code, and should not be translated. * <p>The most important cases like object not found or optimistic locking failure * are covered here. For more fine-granular conversion, JpaTransactionManager etc * support sophisticated translation of exceptions via a JpaDialect. * @param ex runtime exception that occurred * @return the corresponding DataAccessException instance, * or {@code null} if the exception should not be translated */ public static DataAccessException convertJpaAccessExceptionIfPossible(RuntimeException ex) { // Following the JPA specification, a persistence provider can also // throw these two exceptions, besides PersistenceException. if (ex instanceof IllegalStateException) { return new InvalidDataAccessApiUsageException(ex.getMessage(), ex); } if (ex instanceof IllegalArgumentException) { return new InvalidDataAccessApiUsageException(ex.getMessage(), ex); } // Check for well-known PersistenceException subclasses. if (ex instanceof EntityNotFoundException) { return new JpaObjectRetrievalFailureException((EntityNotFoundException) ex); } if (ex instanceof NoResultException) { return new EmptyResultDataAccessException(ex.getMessage(), 1, ex); } if (ex instanceof NonUniqueResultException) { return new IncorrectResultSizeDataAccessException(ex.getMessage(), 1, ex); } if (ex instanceof QueryTimeoutException) { return new org.springframework.dao.QueryTimeoutException(ex.getMessage(), ex); } if (ex instanceof LockTimeoutException) { return new CannotAcquireLockException(ex.getMessage(), ex); } if (ex instanceof PessimisticLockException) { return new PessimisticLockingFailureException(ex.getMessage(), ex); } if (ex instanceof OptimisticLockException) { return new JpaOptimisticLockingFailureException((OptimisticLockException) ex); } if (ex instanceof EntityExistsException) { return new DataIntegrityViolationException(ex.getMessage(), ex); } if (ex instanceof TransactionRequiredException) { return new InvalidDataAccessApiUsageException(ex.getMessage(), ex); } // If we have another kind of PersistenceException, throw it. if (ex instanceof PersistenceException) { return new JpaSystemException((PersistenceException) ex); } // If we get here, we have an exception that resulted from user code, // rather than the persistence provider, so we return null to indicate // that translation should not occur. return null; }
@Override public void deleteAutomaticRecordTags(RecordCollection collection, Date newStartTaggingDate) { int attempts = 0; while (true) { try { String sqlTag; if (newStartTaggingDate == null) { sqlTag = "DELETE FROM RecordTag WHERE manual=? AND record_id IN" + " (SELECT r.id FROM Record r, ConnectorInstance ci, RecordCollection rc" + " WHERE r.connectorInstance_id=ci.id AND ci.recordCollection_id=rc.id AND rc.id=?)"; } else { sqlTag = "DELETE FROM RecordTag WHERE manual=? AND record_id IN" + " (SELECT r.id FROM Record r, ConnectorInstance ci, RecordCollection rc " + "WHERE r.connectorInstance_id=ci.id AND ci.recordCollection_id=rc.id AND rc.id=? AND" + " (r.lastAutomaticTagging > ? OR r.lastAutomaticTagging IS NULL))"; } Query tagQuery = getEntityManager().createNativeQuery(sqlTag); tagQuery.setParameter(1, Boolean.FALSE); tagQuery.setParameter(2, collection.getId()); if (newStartTaggingDate != null) { tagQuery.setParameter(3, newStartTaggingDate); } tagQuery.executeUpdate(); String sqlRecord; if (newStartTaggingDate == null) { sqlRecord = "UPDATE Record r SET r.lastAutomaticTagging = null WHERE connectorInstance_id IN" + " (SELECT ci.id FROM ConnectorInstance ci, RecordCollection rc WHERE ci.recordCollection_id=rc.id AND rc.id=?)"; } else { sqlRecord = "UPDATE Record r SET r.lastAutomaticTagging = null WHERE connectorInstance_id IN" + " (SELECT ci.id FROM ConnectorInstance ci, RecordCollection rc WHERE ci.recordCollection_id=rc.id AND" + " rc.id=?) AND (r.lastAutomaticTagging > ? OR r.lastAutomaticTagging IS NULL)"; } Query recordQuery = getEntityManager().createNativeQuery(sqlRecord); recordQuery.setParameter(1, collection.getId()); if (newStartTaggingDate != null) { recordQuery.setParameter(2, newStartTaggingDate); } recordQuery.executeUpdate(); break; } catch (PessimisticLockException e) { attempts++; if (attempts > 100) { throw e; } } } }