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

Что добавляет @Repository

1.0 Junior🔥 151 комментариев
#Spring Framework

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

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

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

# Что добавляет аннотация @Repository

Краткий ответ

@Repository — это специализированная аннотация Spring, которая:

  1. Регистрирует класс как компонент в контейнере Spring (как @Component)
  2. Добавляет трансляцию исключений БД в специфичные для Spring исключения
  3. Указывает на назначение класса — работа с данными
  4. Позволяет инжектировать repository в другие компоненты

Иерархия аннотаций Spring Stereotype

@Component  ← базовая аннотация
    ↓
┌─────────────────────────────────────────┐
│  @Repository  @Service  @Controller      │
│  (DATA)       (LOGIC)   (WEB)            │
└─────────────────────────────────────────┘

Все три — это специализированные версии @Component, но каждая имеет дополнительные возможности.

1. Регистрация в контейнере Spring

Без @Repository

public class UserRepository {
    public User findById(Long id) {
        // ...
    }
}

// ❌ Spring не знает об этом классе
// Невозможно внедрить через @Autowired

С @Repository

@Repository
public class UserRepository {
    public User findById(Long id) {
        // ...
    }
}

// ✅ Spring регистрирует этот класс
// Можно внедрить через @Autowired

Использование

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;  // Spring внедрит @Repository
    
    public User getUserById(Long id) {
        return userRepository.findById(id);
    }
}

2. Трансляция исключений (Exception Translation)

Это ГЛАВНОЕ преимущество @Repository.

Проблема без @Repository

// Без @Repository
public class PlainUserRepository {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public User findById(Long id) {
        try {
            // JdbcTemplate выбросит DataAccessException
            return jdbcTemplate.queryForObject(
                "SELECT * FROM users WHERE id = ?",
                new Object[]{id},
                new UserRowMapper()
            );
        } catch (DataAccessException e) {
            // ❌ Выбросится специфичное для Spring исключение
            // Но это всё ещё зависимость от Spring
            throw e;
        }
    }
}

Решение с @Repository

@Repository
public class UserRepository {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public User findById(Long id) {
        // Spring автоматически переводит исключения БД
        // в стандартные DataAccessException иерархии
        return jdbcTemplate.queryForObject(
            "SELECT * FROM users WHERE id = ?",
            new Object[]{id},
            new UserRowMapper()
        );
    }
}

// Spring автоматически перехватывает исключения и переводит их

Как работает трансляция

Исключение БД              →  Spring DataAccessException
────────────────────────────────────────────────────
SQLException               →  DataAccessResourceFailureException
SQLIntegrityConstraintViolationException → DataIntegrityViolationException
BadSqlGrammarException     →  InvalidDataAccessResourceUsageException
CancellationException      →  QueryTimeoutException

Иерархия исключений DataAccessException

DataAccessException (checked → unchecked!)
    ├── DataIntegrityViolationException (нарушение констреинтов)
    ├── DataAccessResourceFailureException (нет доступа к БД)
    ├── InvalidDataAccessResourceUsageException (неверный SQL)
    ├── QueryTimeoutException (timeout запроса)
    ├── PermissionDeniedDataAccessException (нет прав)
    └── ... другие

3. Пример трансляции исключений

@Repository
public class UserRepository {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public void createUser(User user) {
        try {
            jdbcTemplate.update(
                "INSERT INTO users (email, name) VALUES (?, ?)",
                user.getEmail(), user.getName()
            );
        } catch (DataIntegrityViolationException e) {
            // ✅ Spring перевёл SQLException в специфичное исключение
            // Это означает нарушение уникального ключа (email уже существует)
            throw new UserAlreadyExistsException("Email already registered", e);
        }
    }
}

@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    public void registerUser(UserRegistrationRequest request) {
        try {
            userRepository.createUser(new User(request.getEmail(), request.getName()));
        } catch (DataIntegrityViolationException e) {
            // Можем ловить специфичное исключение
            logger.warn("Попытка зарегистрировать существующий email");
        }
    }
}

4. @Repository с Spring Data JPA

When using Spring Data JPA, @Repository becomes even more powerful:

// ✅ Spring Data Repository
@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // Spring автоматически:
    // 1. Создаёт реализацию
    // 2. Переводит исключения
    // 3. Управляет транзакциями
    
    User findByEmail(String email);
    
    @Query("SELECT u FROM User u WHERE u.active = true")
    List<User> findActiveUsers();
}

// Использование
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;  // Spring внедрит repository
    
    public User getUserByEmail(String email) {
        return userRepository.findByEmail(email);  // Работает как надо
    }
}

5. Сравнение стереотипов

@Component

@Component
public class GenericHelper {
    public static String format(String text) {
        return text.toUpperCase();
    }
}

// Использование
@Service
public class MyService {
    @Autowired
    private GenericHelper helper;  // Инжектируется
}

@Service

@Service
public class UserService {
    // Бизнес-логика
    public User createUser(UserRequest request) {
        User user = new User(request.getName());
        return userRepository.save(user);
    }
}

// Использование
@RestController
public class UserController {
    @Autowired
    private UserService userService;  // Инжектируется
}

@Repository

@Repository
public class UserRepository {
    // Доступ к данным + трансляция исключений
    public User findById(Long id) {
        return jdbcTemplate.queryForObject(...);
    }
}

// Использование
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;  // Инжектируется + трансляция
}

@Controller / @RestController

@RestController
@RequestMapping("/api/users")
public class UserController {
    // Web layer
    @GetMapping("/{id}")
    public User getUser(@PathVariable Long id) {
        return userService.getUserById(id);
    }
}

6. Когда использовать @Repository

✅ Используй когда:

// Класс работает с БД напрямую
@Repository
public class UserRepository {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public User findById(Long id) { }
    public void save(User user) { }
}

// Наследуешь от Spring Data интерфейса
@Repository
public interface UserRepository extends JpaRepository<User, Long> { }

// Используешь Hibernate Session
@Repository
public class ProductRepository {
    @Autowired
    private SessionFactory sessionFactory;
}

❌ Не используй когда:

// Это сервис, не repository
@Service  // ← Правильно
public class EmailService {
    public void sendEmail(String to, String message) { }
}

// Это контроллер, не repository
@RestController  // ← Правильно
public class OrderController { }

// Это утилита, не repository
@Component  // ← Правильно
public class DateUtils { }

7. Практический пример: Error Handling

@Repository
public class OrderRepository {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public Order findById(Long id) {
        try {
            return jdbcTemplate.queryForObject(
                "SELECT * FROM orders WHERE id = ?",
                new Object[]{id},
                new OrderRowMapper()
            );
        } catch (EmptyResultDataAccessException e) {
            // Нет результата
            return null;
        }
        // Spring автоматически переведёт другие исключения
    }
    
    public void update(Order order) {
        try {
            jdbcTemplate.update(
                "UPDATE orders SET status = ? WHERE id = ?",
                order.getStatus(), order.getId()
            );
        } catch (DataIntegrityViolationException e) {
            // Нарушение констреинта
            throw new OrderUpdateException("Cannot update order", e);
        }
    }
}

@Service
public class OrderService {
    @Autowired
    private OrderRepository orderRepository;
    
    public Order getOrder(Long id) {
        Order order = orderRepository.findById(id);
        if (order == null) {
            throw new OrderNotFoundException("Order not found");
        }
        return order;
    }
}

8. Различие между проверяемыми и непроверяемыми исключениями

// SQLException — проверяемое исключение (checked)
public void oldWayFindUser() throws SQLException {  // Нужен throws
    Connection conn = DriverManager.getConnection(url);
    // ...
}

// DataAccessException — непроверяемое исключение (unchecked)
@Repository
public class UserRepository {
    public User findById(Long id) {  // Не нужен throws!
        // Spring переводит SQLException (checked)
        // в DataAccessException (unchecked)
        return jdbcTemplate.queryForObject(...);
    }
}

// ✅ Это удобнее — не нужно объявлять throws везде

Итоги

  1. @Repository — специализированная версия @Component для data access
  2. Регистрация в контейнере — Spring создаёт bean и позволяет инжектировать
  3. Трансляция исключений — главное преимущество (SQLException → DataAccessException)
  4. DataAccessException — unchecked исключение, удобнее для обработки ошибок
  5. Spring Data — @Repository работает с JpaRepository и создаёт реализацию
  6. Правильное использование — @Repository для доступа к данным, @Service для бизнес-логики, @Controller для web
  7. Практически — трансляция исключений часто спасает жизнь при обработке ошибок БД