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

Какой синтаксис можно использовать в @Query?

1.0 Junior🔥 101 комментариев
#Spring Boot и Spring Data

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

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

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

Что такое @Primary?

@Primary — это аннотация в Spring, которая указывает что это bean должен использоваться по умолчанию, когда есть несколько реализаций одного интерфейса и нет явного @Qualifier. Это решает конфликт автоматического связывания (autowiring) зависимостей.

Проблема: несколько реализаций

Конфликт при автоматическом внедрении

// Интерфейс
public interface PaymentService {
    void pay(double amount);
}

// Несколько реализаций
@Service
public class CreditCardPayment implements PaymentService {
    @Override
    public void pay(double amount) {
        System.out.println("Paying with credit card: " + amount);
    }
}

@Service
public class PayPalPayment implements PaymentService {
    @Override
    public void pay(double amount) {
        System.out.println("Paying with PayPal: " + amount);
    }
}

// Попытка внедрить
@RestController
public class OrderController {
    @Autowired
    private PaymentService paymentService;  // ОШИБКА!
    // Spring не знает какую реализацию использовать
    // NoUniqueBeanDefinitionException
}

Решение 1: @Primary

@Primary указывает какую реализацию использовать по умолчанию

// Интерфейс
public interface PaymentService {
    void pay(double amount);
}

// Primary реализация
@Service
@Primary  // используется по умолчанию
public class CreditCardPayment implements PaymentService {
    @Override
    public void pay(double amount) {
        System.out.println("Paying with credit card: " + amount);
    }
}

// Альтернативная реализация
@Service
public class PayPalPayment implements PaymentService {
    @Override
    public void pay(double amount) {
        System.out.println("Paying with PayPal: " + amount);
    }
}

// Теперь работает!
@RestController
public class OrderController {
    @Autowired
    private PaymentService paymentService;  // получит CreditCardPayment
    
    @PostMapping("/order")
    public void createOrder() {
        paymentService.pay(100);  // использует CreditCardPayment
    }
}

Решение 2: @Qualifier (альтернатива)

@Qualifier явно указывает какую реализацию использовать

// Интерфейс
public interface PaymentService {
    void pay(double amount);
}

// Реализация 1
@Service("creditCard")
public class CreditCardPayment implements PaymentService {
    @Override
    public void pay(double amount) {
        System.out.println("Paying with credit card: " + amount);
    }
}

// Реализация 2
@Service("paypal")
public class PayPalPayment implements PaymentService {
    @Override
    public void pay(double amount) {
        System.out.println("Paying with PayPal: " + amount);
    }
}

// Использование
@RestController
public class OrderController {
    @Autowired
    @Qualifier("creditCard")  // явно выбираем
    private PaymentService creditCardService;
    
    @Autowired
    @Qualifier("paypal")  // явно выбираем другую
    private PaymentService paypalService;
    
    @PostMapping("/order")
    public void createOrder(String paymentMethod) {
        if (paymentMethod.equals("cc")) {
            creditCardService.pay(100);
        } else if (paymentMethod.equals("paypal")) {
            paypalService.pay(100);
        }
    }
}

@Primary с @Bean

@Primary можно использовать с @Bean в @Configuration

// Интерфейс
public interface DataSource {
    Connection getConnection();
}

// Конфигурация
@Configuration
public class DataSourceConfig {
    @Bean
    @Primary  // по умолчанию
    public DataSource primaryDataSource() {
        return new PostgresDataSource("jdbc:postgresql://localhost/prod");
    }
    
    @Bean("backupDataSource")
    public DataSource backupDataSource() {
        return new PostgresDataSource("jdbc:postgresql://localhost/backup");
    }
}

// Использование
@Service
public class UserService {
    @Autowired
    private DataSource dataSource;  // получит primaryDataSource
    
    @Autowired
    @Qualifier("backupDataSource")
    private DataSource backupDb;  // явно выбираем резервную
}

Сравнение: @Primary vs @Qualifier

Аспект@Primary@Qualifier
ЦельУстанавливает defaultЯвно выбирает конкретную
Когда использоватьОдин bean нужен чащеРазные beans для разных целей
ЧитаемостьКод использования чищеЯвно видно что используется
КонфликтыРешает автоматическиТребует явного указания
ОшибкиNoUniqueBeanDefinitionExceptionNoSuchBeanDefinitionException

Практический пример: кэш

// Интерфейс
public interface CacheService {
    void put(String key, Object value);
    Object get(String key);
}

// Реализации
@Service
@Primary  // используется для простых случаев
public class InMemoryCache implements CacheService {
    private Map<String, Object> cache = new HashMap<>();
    
    @Override
    public void put(String key, Object value) {
        cache.put(key, value);
    }
    
    @Override
    public Object get(String key) {
        return cache.get(key);
    }
}

@Service("redisCache")
public class RedisCache implements CacheService {
    @Autowired
    private RedisTemplate redisTemplate;
    
    @Override
    public void put(String key, Object value) {
        redisTemplate.opsForValue().set(key, value);
    }
    
    @Override
    public Object get(String key) {
        return redisTemplate.opsForValue().get(key);
    }
}

// Использование
@Service
public class UserService {
    @Autowired
    private CacheService cache;  // получит InMemoryCache
    
    public User findUser(String id) {
        User user = (User) cache.get("user:" + id);
        if (user == null) {
            user = loadFromDb(id);
            cache.put("user:" + id, user);
        }
        return user;
    }
}

@Service
public class OrderService {
    @Autowired
    @Qualifier("redisCache")  // нужен масштабируемый кэш
    private CacheService cache;
    
    public Order findOrder(String id) {
        Order order = (Order) cache.get("order:" + id);
        if (order == null) {
            order = loadFromDb(id);
            cache.put("order:" + id, order);
        }
        return order;
    }
}

Наследование и @Primary

@Primary работает с наследованием

// Базовый класс
public class Vehicle {
    public void start() {
        System.out.println("Vehicle starting");
    }
}

// Наследники
@Service
@Primary
public class Car extends Vehicle {
    @Override
    public void start() {
        System.out.println("Car starting");
    }
}

@Service
public class Truck extends Vehicle {
    @Override
    public void start() {
        System.out.println("Truck starting");
    }
}

// Использование
@Service
public class TransportService {
    @Autowired
    private Vehicle vehicle;  // получит Car (@Primary)
    
    public void startJourney() {
        vehicle.start();  // Car starting
    }
}

Сложный пример: несколько интерфейсов

// Два интерфейса
public interface Database {
    void query(String sql);
}

public interface Cache {
    void set(String key, String value);
}

// Реализации
@Service
@Primary
public class DatabaseImpl implements Database {
    @Override
    public void query(String sql) { }
}

@Service
public class CacheImpl implements Cache {
    @Override
    public void set(String key, String value) { }
}

// @Primary работает отдельно для каждого интерфейса
@Service
public class UserRepository {
    @Autowired
    private Database db;  // получит DatabaseImpl
    
    @Autowired
    private Cache cache;  // получит CacheImpl
}

// Множественные реализации одного интерфейса
public interface Logger {
    void log(String message);
}

@Service
@Primary
public class ConsoleLogger implements Logger {
    @Override
    public void log(String message) { }
}

@Service("fileLogger")
public class FileLogger implements Logger {
    @Override
    public void log(String message) { }
}

@Service
public class Application {
    @Autowired
    private Logger logger;  // получит ConsoleLogger
    
    @Autowired
    @Qualifier("fileLogger")
    private Logger fileLogger;  // явно выбираем FileLogger
}

Лучшие практики

1. Используй @Primary для "common case"

@Service
@Primary  // большинство случаев
public class LocalCache implements CacheService { }

@Service
public class DistributedCache implements CacheService { }

2. Используй @Qualifier для специфичных случаев

@Service
public class DataProcessor {
    @Autowired  // default
    private CacheService cache;
    
    @Autowired
    @Qualifier("distributedCache")  // специфично для этого поля
    private CacheService clusterCache;
}

3. Комбинируй для ясности

@Configuration
public class DatabaseConfig {
    @Bean
    @Primary  // primary для DataSource
    public DataSource dataSource() { }
    
    @Bean
    public DataSource readOnlyDataSource() { }
    
    @Bean
    public DataSource analyticsDataSource() { }
}

4. НЕ переусложняй

// ❌ Плохо: множество @Primary 
@Primary
public class ServiceA implements MyService { }

@Primary
public class ServiceB implements MyService { }

// ✅ Хорошо: одна @Primary + @Qualifier для остальных
@Primary
public class ServiceA implements MyService { }

@Service
public class ServiceB implements MyService { }

Когда использовать @Primary

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

  • Есть одна "очевидная" реализация по умолчанию
  • 90% случаев используют одну реализацию
  • Это упрощает код в большинстве мест

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

  • Разные компоненты нужны в разных местах
  • Выбор зависит от условия/конфигурации
  • Нужна явность для понимания

Вывод

@Primary — это способ указать Spring какой bean использовать по умолчанию при наличии нескольких реализаций одного интерфейса. Это упрощает autowiring когда одна реализация используется чаще.

Ключевые моменты:

  • Только ОДНА реализация может быть @Primary
  • @Primary работает автоматически при autowiring
  • @Qualifier имеет приоритет над @Primary
  • Используй для "common case", @Qualifier для специфичных случаев

Это стандартный паттерн в Spring для управления несколькими реализациями интерфейсов.