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

Почему писал скрипты на XML, а не на SQL?

2.0 Middle🔥 141 комментариев
#ORM и Hibernate

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

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

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

Почему писал скрипты на XML, а не на SQL

Этот вопрос относится к истории проектов и технологических выборов. Я предполагаю, вы имеете в виду MyBatis или похожие инструменты, где маппинги часто писались на XML. Это был частый выбор в Java-приложениях нулевых и десятых годов.

Исторический контекст: MyBatis и другие ORM

1. Отделение SQL от кода

Основная мотивация была разделить бизнес-логику (Java) от запросов к БД (SQL):

<!-- MyBatis XML маппинг -->
<mapper namespace="com.example.UserMapper">
    <select id="selectUserById" resultType="User">
        SELECT id, name, email FROM users WHERE id = #{id}
    </select>
    
    <insert id="insertUser" parameterType="User">
        INSERT INTO users (name, email) VALUES (#{name}, #{email})
    </insert>
</mapper>

Соответствующий Java код:

public interface UserMapper {
    User selectUserById(long id);
    void insertUser(User user);
}

Преимущества:

  • DBA могли редактировать SQL без изменения кода
  • Легче тестировать SQL отдельно
  • Версионировать и отслеживать изменения запросов

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

XML позволял описывать сложную логику с условиями:

<select id="findUsers" resultType="User">
    SELECT * FROM users WHERE 1=1
    <if test="name != null">
        AND name LIKE CONCAT('%', #{name}, '%')
    </if>
    <if test="ageFrom != null">
        AND age >= #{ageFrom}
    </if>
    <if test="ageTo != null">
        AND age <= #{ageTo}
    </if>
    <if test="cities != null and cities.size > 0">
        AND city IN
        <foreach item="city" collection="cities" open="(" separator="," close=")">
            #{city}
        </foreach>
    </if>
</select>

Это было удобнее, чем строить SQL-строки в коде вручную.

3. Безопасность: защита от SQL-инъекций

XML маппинги автоматически экранировали переменные:

<!-- Безопасно! Параметр автоматически экранируется -->
<select id="selectByName">
    SELECT * FROM users WHERE name = #{name}
</select>

Вместо опасного кода:

// ОПАСНО - SQL-инъекция!
String query = "SELECT * FROM users WHERE name = '" + name + "'";

4. Переиспользование и модульность

XML позволял переиспользовать SQL-фрагменты:

<!-- Одна переиспользуемая часть -->
<sql id="userColumns">
    id, name, email, created_at
</sql>

<!-- Используется в разных запросах -->
<select id="selectById" resultType="User">
    SELECT <include refid="userColumns"/> FROM users WHERE id = #{id}
</select>

<select id="selectAll" resultType="User">
    SELECT <include refid="userColumns"/> FROM users
</select>

Историческая эволюция: почему это менялось

2000-2010: Расцвет XML

Когда Hibernate и MyBatis были популярны, XML казалась хорошим выбором:

  • Java еще не поддерживала аннотации эффективно
  • Конфигурация часто была в XML (Spring, Hibernate)
  • Инструменты IDE хорошо поддерживали XML

2010+: Переход на аннотации

Постепенно язык менялся:

  • Java добавила поддержку аннотаций
  • Spring начал поддерживать конфигурацию через аннотации
  • JPA стал стандартом с аннотациями
// Современный подход (JPA)
@Entity
@Table(name = "users")
public class User {
    @Id
    private Long id;
    
    @Column(name = "name")
    private String name;
}

2010-2020: Кастомные запросы в аннотациях

XML заменили на аннотации:

// Spring Data JPA
public interface UserRepository extends JpaRepository<User, Long> {
    @Query("SELECT u FROM User u WHERE u.name LIKE %:name%")
    List<User> findByNamePattern(@Param("name") String name);
    
    // Или через нативный SQL
    @Query(value = "SELECT * FROM users WHERE age > :age", nativeQuery = true)
    List<User> findByAgeGreaterThan(@Param("age") int age);
}

2020+: Полнофункциональные DSL

Современные подходы предлагают типобезопасные конструкции:

// jOOQ - типобезопасный SQL
List<User> users = dsl.select()
    .from(USERS)
    .where(USERS.AGE.greaterThan(18))
    .and(USERS.CITY.in("Moscow", "SPB"))
    .fetch()
    .into(User.class);

Почему сейчас редко используют XML

1. Шум и многословность

XML требовал много обслуживающего кода:

<mapper namespace="..." xmlns="..." xmlns:xsi="..." xsi:schemaLocation="...">
    <!-- Много бойлерплейта -->
</mapper>

2. IDE поддержка и рефакторинг

Инструменты лучше работают с Java-кодом:

  • Проверка типов во время компиляции
  • Автодополнение
  • Refactoring
  • Навигация по коду

3. Type-safety

Аннотации и DSL дают полную типизацию на этапе компиляции:

// Ошибка будет поймана на этапе компиляции
query.where(USERS.AGE.greaterThan("invalid"));  // Ошибка типа!

Когда XML все еще имеет смысл

Хотя это редко, XML может быть полезен в очень специфических случаях:

1. Очень сложные запросы

<!-- Многоуровневое соединение с условиями -->
<select id="complexQuery" resultType="ReportDto">
    SELECT u.id, u.name, COUNT(o.id) as order_count
    FROM users u
    LEFT JOIN orders o ON u.id = o.user_id
    <where>
        <if test="status != null">
            AND o.status = #{status}
        </if>
        <if test="dateFrom != null">
            AND o.created_at >= #{dateFrom}
        </if>
    </where>
    GROUP BY u.id, u.name
    HAVING COUNT(o.id) > #{minOrders}
    ORDER BY order_count DESC
</select>

Это проще, чем строить в коде.

2. Быстрая портативность между БД

Один маппинг может работать с разными БД (PostgreSQL, MySQL, Oracle):

<!-- PostgreSQL -->
<select id="selectNextId" databaseId="postgresql">
    SELECT nextval('users_seq')
</select>

<!-- MySQL -->
<select id="selectNextId" databaseId="mysql">
    SELECT AUTO_INCREMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME='users'
</select>

Современные лучшие практики

1. Spring Data JPA + Native Queries — для стандартных случаев

public interface UserRepository extends JpaRepository<User, Long> {
    @Query("SELECT u FROM User u WHERE u.age > :age")
    List<User> findAdults(@Param("age") int age);
}

2. jOOQ — для типобезопасных сложных запросов

var result = dsl.selectFrom(USERS)
    .where(USERS.AGE.between(18, 65))
    .orderBy(USERS.NAME)
    .fetch();

3. MyBatis (если нужна гибкость) — все еще существует, но с аннотациями

public interface UserMapper {
    @Select("SELECT * FROM users WHERE id = #{id}")
    User selectById(long id);
}

Заключение

XML-маппинги были логичным выбором в эпоху, когда Java не имела хороших механизмов для типизации и когда нужна была жёсткая изоляция SQL от кода. Современный Java предлагает гораздо лучшие инструменты: типизированные запросы, DSL, аннотации. Они обеспечивают безопасность типов, лучший рефакторинг и понятность кода, что делает их предпочтительными для новых проектов.