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

В чем разница между JDBCTemplate и Criteria API?

1.7 Middle🔥 131 комментариев
#ORM и Hibernate#Spring Boot и Spring Data

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

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

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

Разница между JdbcTemplate и Criteria API

Эти два подхода к работе с данными в Spring предоставляют разные уровни абстракции для взаимодействия с базой данных. Каждый имеет свои преимущества и недостатки.

JdbcTemplate — Low-Level обработка SQL

JdbcTemplate — это низкоуровневый инструмент Spring для работы с JDBC. Вы пишете сырой SQL и управляете параметрами вручную.

@Repository
public class UserRepository {
    private final JdbcTemplate jdbcTemplate;
    
    public UserRepository(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    
    public List<User> findByAge(int age) {
        String sql = "SELECT * FROM users WHERE age > ?";
        return jdbcTemplate.query(
            sql,
            new Object[]{age},
            new UserRowMapper()
        );
    }
    
    public User findById(Long id) {
        String sql = "SELECT * FROM users WHERE id = ?";
        return jdbcTemplate.queryForObject(
            sql,
            new Object[]{id},
            new UserRowMapper()
        );
    }
    
    public void insert(User user) {
        String sql = "INSERT INTO users (name, email, age) VALUES (?, ?, ?)";
        jdbcTemplate.update(sql, user.getName(), user.getEmail(), user.getAge());
    }
}

// RowMapper для преобразования результатов
class UserRowMapper implements RowMapper<User> {
    @Override
    public User mapRow(ResultSet rs, int rowNum) throws SQLException {
        return new User(
            rs.getLong("id"),
            rs.getString("name"),
            rs.getString("email"),
            rs.getInt("age")
        );
    }
}

Характеристики JdbcTemplate:

  • SQL-ориентированность: Вы полностью контролируете SQL
  • Низкоуровневость: Нужно самому управлять параметрами и маппингом
  • Быстрота: Минимальный overhead
  • Опасность: SQL injection если не использовать параметризованные запросы

Criteria API — Object-Oriented запросы

Criteria API — это часть JPA, которая позволяет строить запросы программно, без сырого SQL.

@Repository
public class UserRepository {
    private final EntityManager entityManager;
    
    public UserRepository(EntityManager entityManager) {
        this.entityManager = entityManager;
    }
    
    public List<User> findByAge(int age) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<User> query = cb.createQuery(User.class);
        Root<User> user = query.from(User.class);
        
        query.select(user)
             .where(cb.greaterThan(user.get("age"), age));
        
        return entityManager.createQuery(query).getResultList();
    }
    
    public List<User> findByAgeAndEmail(int age, String email) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<User> query = cb.createQuery(User.class);
        Root<User> user = query.from(User.class);
        
        query.select(user)
             .where(
                 cb.and(
                     cb.greaterThan(user.get("age"), age),
                     cb.like(user.get("email"), email + "%")
                 )
             );
        
        return entityManager.createQuery(query).getResultList();
    }
    
    public List<User> findSorted(int age) {
        CriteriaBuilder cb = entityManager.getCriteriaBuilder();
        CriteriaQuery<User> query = cb.createQuery(User.class);
        Root<User> user = query.from(User.class);
        
        query.select(user)
             .where(cb.greaterThan(user.get("age"), age))
             .orderBy(cb.asc(user.get("name")));
        
        return entityManager.createQuery(query).getResultList();
    }
}

Характеристики Criteria API:

  • Type-safety: Ошибки в имена полей ловятся при компиляции
  • Dynamically built queries: Запросы строятся программно
  • Reusability: Условия можно комбинировать
  • Сложность: Более многословно, чем JPQL

Сравнение — Простой поиск

JdbcTemplate:

String sql = "SELECT * FROM users WHERE age > ? AND status = ?";
List<User> users = jdbcTemplate.query(
    sql,
    new Object[]{minAge, "ACTIVE"},
    new UserRowMapper()
);

Criteria API:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<User> query = cb.createQuery(User.class);
Root<User> user = query.from(User.class);

query.select(user)
     .where(
         cb.and(
             cb.greaterThan(user.get("age"), minAge),
             cb.equal(user.get("status"), "ACTIVE")
         )
     );

List<User> users = entityManager.createQuery(query).getResultList();

Сравнение — Сложный запрос

JdbcTemplate:

String sql = """
    SELECT u.id, u.name, u.email, COUNT(o.id) as order_count
    FROM users u
    LEFT JOIN orders o ON u.id = o.user_id
    WHERE u.age > ? AND u.status = ?
    GROUP BY u.id, u.name, u.email
    HAVING COUNT(o.id) > ?
    ORDER BY order_count DESC
""";

List<Map<String, Object>> results = jdbcTemplate.queryForList(
    sql,
    minAge, "ACTIVE", minOrders
);

Criteria API:

CriteriaBuilder cb = entityManager.getCriteriaBuilder();
CriteriaQuery<Object[]> query = cb.createQuery(Object[].class);
Root<User> user = query.from(User.class);
Join<User, Order> orders = user.leftJoin("orders");

query.select(cb.array(
    user.get("id"),
    user.get("name"),
    user.get("email"),
    cb.count(orders.get("id"))
))
.where(
    cb.and(
        cb.greaterThan(user.get("age"), minAge),
        cb.equal(user.get("status"), "ACTIVE")
    )
)
.groupBy(user.get("id"), user.get("name"), user.get("email"))
.having(cb.greaterThan(cb.count(orders.get("id")), minOrders))
.orderBy(cb.desc(cb.count(orders.get("id"))));

List<Object[]> results = entityManager.createQuery(query).getResultList();

Преимущества и недостатки

JdbcTemplate:

  • Преимущества: контроль, производительность, простота для простых запросов
  • Недостатки: SQL injection (если не осторожный), код не type-safe, много boilerplate

Criteria API:

  • Преимущества: type-safe, защита от SQL injection, reusable conditions, динамические запросы
  • Недостатки: многословность, более сложный синтаксис, медленнее для простых запросов

Современная альтернатива — QueryDSL

@Repository
public class UserRepository {
    private final JPAQueryFactory queryFactory;
    
    public UserRepository(JPAQueryFactory queryFactory) {
        this.queryFactory = queryFactory;
    }
    
    public List<User> findByAge(int age) {
        return queryFactory
            .selectFrom(QUser.user)
            .where(QUser.user.age.gt(age))
            .fetch();
    }
    
    public List<User> findByAgeAndStatus(int age, String status) {
        return queryFactory
            .selectFrom(QUser.user)
            .where(
                QUser.user.age.gt(age),
                QUser.user.status.eq(status)
            )
            .orderBy(QUser.user.name.asc())
            .fetch();
    }
}

QueryDSL более читаемый и type-safe, чем Criteria API.

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

JdbcTemplate:

  • Простые CRUD операции
  • Высокопроизводительные запросы
  • Сложный SQL с использованием специфических функций БД

Criteria API / QueryDSL:

  • Динамические запросы, строящиеся программно
  • Type-safe код
  • Сложная бизнес-логика с множеством условий

Выводы

JdbcTemplate — это низкоуровневый инструмент для контролируемого управления SQL. Criteria API предоставляет объектно-ориентированный способ построения запросов. Выбор зависит от сложности запросов, требований к производительности и предпочтений разработчика. Для современных проектов часто используют QueryDSL как улучшенную альтернативу Criteria API.