← Назад к вопросам

Что такое DataAccessException?

2.0 Middle🔥 141 комментариев
#Spring Framework

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI23 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

DataAccessException в Spring Framework

DataAccessException — это корневое unchecked исключение в Spring Framework для обработки ошибок доступа к данным. Это главное средство абстрагирования специфических SQL исключений от конкретных БД.

Проблема без DataAccessException

Разные БД выбрасывают разные исключения при одних и тех же ошибках:

// ❌ Без Spring — зависимость от конкретной БД
try {
    // JDBC код
    statement.executeUpdate(query);
} catch (SQLException ex) {
    // SQLState код зависит от БД:
    // PostgreSQL: 23505 для UNIQUE violation
    // MySQL: 1062 для duplicate entry
    // Oracle: ORA-00001
    if (ex.getErrorCode() == 23505) {
        // обработка duplicate
    }
}

Это затрудняет переход между БД и усложняет код.

Решение: Spring DataAccessException

Spring преобразует database-specific исключения в hierarchical runtime исключения:

// ✅ С Spring — независимо от БД
@Repository
public class UserRepository {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public void createUser(String email) {
        try {
            String sql = "INSERT INTO users (email) VALUES (?)";
            jdbcTemplate.update(sql, email);
        } catch (DataIntegrityViolationException ex) {
            // Сработает для любой БД при UNIQUE violation
            throw new UserEmailAlreadyExistsException(
                "Email already registered", ex
            );
        }
    }
}

Иерархия DataAccessException

Spring предоставляет специализированные исключения для разных сценариев:

Основные типы:

  1. DataIntegrityViolationException — нарушение целостности:

    • UNIQUE constraint violation
    • FOREIGN KEY violation
    • NOT NULL violation
  2. DataAccessResourceFailureException — проблема с ресурсом:

    • Нет соединения с БД
    • Connection pool истощен
  3. InvalidDataAccessApiUsageException — неправильное использование API:

    • Неправильный SQL синтаксис
    • Неправильное количество параметров
  4. PermissionDeniedDataAccessException — отсутствие прав доступа

  5. BadSqlGrammarException — синтаксическая ошибка в SQL

  6. OptimisticLockingFailureException — конфликт при оптимистичной блокировке

Практические примеры

Пример 1: Обработка UNIQUE constraint

@Service
public class UserService {
    @Autowired
    private UserRepository repository;
    
    public UserDto createUser(CreateUserRequest request) {
        try {
            User user = new User(request.getEmail());
            repository.save(user);
            return UserDto.from(user);
        } catch (DataIntegrityViolationException ex) {
            // Spring преобразовал SQLException в специализированное исключение
            throw new ApplicationException(
                "User with this email already exists",
                ErrorCode.EMAIL_ALREADY_EXISTS
            );
        }
    }
}

Пример 2: Обработка проблемы соединения

@Repository
public class OrderRepository extends JpaRepository<Order, Long> {
    
    public List<Order> findActiveOrders() {
        try {
            return findByStatusActive();
        } catch (DataAccessResourceFailureException ex) {
            // БД недоступна — можно запросить fallback сервис
            log.error("Database unavailable, using cache", ex);
            return cacheService.getActiveOrders();
        }
    }
}

Пример 3: JPA и DataAccessException

@Service
public class ProductService {
    @Autowired
    private ProductRepository repository;
    @Autowired
    private PlatformTransactionManager transactionManager;
    
    @Transactional
    public void updateProduct(Long id, UpdateProductRequest request) {
        Product product = repository.findById(id)
            .orElseThrow(() -> new EntityNotFoundException("Product not found"));
        
        try {
            product.setName(request.getName());
            product.setPrice(request.getPrice());
            repository.save(product);
        } catch (DataIntegrityViolationException ex) {
            throw new ValidationException("Invalid product data", ex);
        } catch (DataAccessException ex) {
            // Generic обработка любой BD ошибки
            throw new DataAccessException("Failed to update product", ex);
        }
    }
}

Пример 4: Custom SQLExceptionTranslator

@Configuration
public class DataAccessConfig {
    
    @Bean
    public SQLExceptionTranslator customExceptionTranslator() {
        return (task, sql, ex) -> {
            // Custom логика преобразования SQLException
            if (ex.getMessage().contains("deadlock")) {
                return new DeadlockLoserDataAccessException(
                    "Deadlock detected", ex
                );
            }
            return null;  // Использовать default транслятор
        };
    }
}

Когда используется DataAccessException

JdbcTemplate:

@Repository
public class UserJdbcRepository {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public User findById(Long id) {
        try {
            return jdbcTemplate.queryForObject(
                "SELECT * FROM users WHERE id = ?",
                new UserRowMapper(),
                id
            );
        } catch (EmptyResultDataAccessException ex) {
            return null;  // не найден
        }
    }
}

Spring Data JPA:

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // JPA также использует DataAccessException
    // RuntimeException от Hibernate оборачивается в DataAccessException
}

Глобальная обработка

@RestControllerAdvice
public class GlobalExceptionHandler {
    
    @ExceptionHandler(DataAccessException.class)
    public ResponseEntity<ErrorResponse> handleDataAccessException(
            DataAccessException ex) {
        
        ErrorResponse response = new ErrorResponse();
        response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
        
        if (ex instanceof DataIntegrityViolationException) {
            response.setMessage("Data integrity violation");
        } else if (ex instanceof DataAccessResourceFailureException) {
            response.setMessage("Database temporarily unavailable");
        } else {
            response.setMessage("Database error occurred");
        }
        
        log.error("DataAccessException", ex);
        return ResponseEntity
            .status(HttpStatus.INTERNAL_SERVER_ERROR)
            .body(response);
    }
}

Best Practices

1. Не ловите общее DataAccessException без причины

// ❌ Плохо
try {
    repository.save(entity);
} catch (DataAccessException ex) {
    // слишком общая обработка
}

// ✅ Хорошо
try {
    repository.save(entity);
} catch (DataIntegrityViolationException ex) {
    // Специфичная обработка
}

2. Логируй ошибки

catch (DataAccessException ex) {
    log.error("Database operation failed", ex);  // with stacktrace
    throw new ApplicationException("Operation failed", ex);
}

3. Используй @Transactional для управления транзакциями

@Transactional
public void complexOperation() {
    // Spring автоматически откатит при DataAccessException
}

DataAccessException — это критичный механизм Spring для портируемости и надёжности database операций.

Что такое DataAccessException? | PrepBro