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

Как входил в базу данных

2.0 Middle🔥 161 комментариев
#Основы Java

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

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

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

# Как входил в базу данных

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

В Java входили в БД через JDBC (Java Database Connectivity) API, который обеспечивал унифицированный способ подключения к различным базам данных (MySQL, PostgreSQL, Oracle, SQL Server и т.д.). Позже появились ORM фреймворки (Hibernate, JPA), которые упростили работу с БД.

Историческое развитие

1. Raw JDBC (Базовый способ, используется до сих пор)

import java.sql.*;

public class JdbcExample {
    public static void main(String[] args) {
        // 1. Загружаем драйвер БД
        try {
            Class.forName("org.postgresql.Driver");  // PostgreSQL
            // или
            Class.forName("com.mysql.cj.jdbc.Driver");  // MySQL
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        
        // 2. Получаем соединение с БД
        String url = "jdbc:postgresql://localhost:5432/mydb";
        String user = "postgres";
        String password = "password";
        
        try (Connection conn = DriverManager.getConnection(url, user, password)) {
            // 3. Выполняем запросы
            
            // SELECT
            String selectSql = "SELECT * FROM users WHERE id = ?";
            try (PreparedStatement stmt = conn.prepareStatement(selectSql)) {
                stmt.setInt(1, 1);  // Подставляем параметр
                
                try (ResultSet rs = stmt.executeQuery()) {
                    while (rs.next()) {
                        int id = rs.getInt("id");
                        String name = rs.getString("name");
                        String email = rs.getString("email");
                        
                        System.out.println("ID: " + id + ", Name: " + name + ", Email: " + email);
                    }
                }
            }
            
            // INSERT
            String insertSql = "INSERT INTO users (name, email) VALUES (?, ?)";
            try (PreparedStatement stmt = conn.prepareStatement(insertSql)) {
                stmt.setString(1, "John Doe");
                stmt.setString(2, "john@example.com");
                int rowsInserted = stmt.executeUpdate();
                System.out.println("Rows inserted: " + rowsInserted);
            }
            
        } catch (SQLException e) {
            e.printStackTrace();
        }
    }
}

Проблемы с raw JDBC:

// Много boilerplate кода
// Нужно закрывать ресурсы (Connection, Statement, ResultSet)
// Ручное маппирование результатов на объекты
// Подвержено SQL injection если не использовать PreparedStatement
// Повторяющийся код в разных местах

2. Connection Pooling (Оптимизация JDBC)

// Было (медленно): создавать новое соединение для каждого запроса
Connection conn = DriverManager.getConnection(url, user, password);
// ... используем conn
conn.close();  // Коннекция закрывается, но переоткрывается снова!

// Стало (быстро): использовать пул соединений
// HikariCP (самый популярный пул)

import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

HikariConfig config = new HikariConfig();
config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");
config.setUsername("postgres");
config.setPassword("password");
config.setMaximumPoolSize(20);  // Макс 20 одновременных соединений

HikariDataSource dataSource = new HikariDataSource(config);

// Теперь берём соединение из пула
Connection conn = dataSource.getConnection();
// Используем
conn.close();  // Возвращаем в пул, не закрываем действительно!

3. ORM фреймворки (Modern approach)

// Hibernate / JPA
// Автоматическое маппирование объектов на таблицы

@Entity
@Table(name = "users")
public class User {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    
    @Column(name = "name")
    private String name;
    
    @Column(name = "email")
    private String email;
    
    // getters/setters
}

// Repository паттерн скрывает детали
@Repository
public class UserRepository {
    @Autowired
    private EntityManager em;
    
    public User findById(Long id) {
        return em.find(User.class, id);
    }
    
    public void save(User user) {
        em.persist(user);
    }
}

// Используем
User user = userRepository.findById(1L);
user.setEmail("newemail@example.com");
userRepository.save(user);  // Автоматически выполнит UPDATE

Полная история подключения

Этап 1: JDBC (1997)

// Самый базовый уровень
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(
    "jdbc:mysql://localhost:3306/db", 
    "user", 
    "password"
);

Этап 2: Connection Pooling (2000-е)

// Apache Commons DBCP, C3P0, HikariCP
DataSource dataSource = new HikariDataSource(config);
Connection conn = dataSource.getConnection();

Этап 3: JDBC DAO паттерн

// Обёртки над JDBC для упрощения
public abstract class BaseDAO<T> {
    protected DataSource dataSource;
    
    protected ResultSet query(String sql) throws SQLException {
        Connection conn = dataSource.getConnection();
        return conn.createStatement().executeQuery(sql);
    }
}

Этап 4: ORM фреймворки (2005+)

// Hibernate, TopLink
public interface UserRepository extends JpaRepository<User, Long> {
    User findByEmail(String email);
}

Этап 5: Spring Data (2010+)

// Максимально упрощённый доступ
@Query("SELECT u FROM User u WHERE u.email = ?1")
User findByEmail(String email);

Практический пример: от JDBC к JPA

Вариант 1: Raw JDBC

public class JdbcUserRepository {
    private DataSource dataSource;
    
    public User findById(Long id) throws SQLException {
        String sql = "SELECT * FROM users WHERE id = ?";
        
        try (Connection conn = dataSource.getConnection();
             PreparedStatement stmt = conn.prepareStatement(sql)) {
            stmt.setLong(1, id);
            
            try (ResultSet rs = stmt.executeQuery()) {
                if (rs.next()) {
                    return mapToUser(rs);
                }
            }
        }
        return null;
    }
    
    private User mapToUser(ResultSet rs) throws SQLException {
        User user = new User();
        user.setId(rs.getLong("id"));
        user.setName(rs.getString("name"));
        user.setEmail(rs.getString("email"));
        return user;
    }
}

Вариант 2: Hibernate

@Repository
public class HibernateUserRepository {
    @Autowired
    private SessionFactory sessionFactory;
    
    public User findById(Long id) {
        Session session = sessionFactory.openSession();
        try {
            return session.get(User.class, id);  // Автоматическое маппирование!
        } finally {
            session.close();
        }
    }
}

Вариант 3: Spring Data JPA (лучший способ)

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    User findByEmail(String email);  // Производит SQL автоматически!
}

// Использование
@Service
public class UserService {
    @Autowired
    private UserRepository userRepository;
    
    public User getUser(Long id) {
        return userRepository.findById(id).orElse(null);
    }
}

Конфигурация в Spring Boot

# application.yml
spring:
  datasource:
    url: jdbc:postgresql://localhost:5432/mydb
    username: postgres
    password: password
    hikari:
      maximum-pool-size: 20
      minimum-idle: 5
      connection-timeout: 20000
  
  jpa:
    hibernate:
      ddl-auto: update  # create, update, validate, none
    show-sql: true  # Логировать SQL запросы
    properties:
      hibernate:
        dialect: org.hibernate.dialect.PostgreSQL10Dialect
        format_sql: true
// Java конфигурация (если не используем application.yml)
@Configuration
public class DataSourceConfig {
    
    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:postgresql://localhost:5432/mydb");
        config.setUsername("postgres");
        config.setPassword("password");
        config.setMaximumPoolSize(20);
        config.setMinimumIdle(5);
        return new HikariDataSource(config);
    }
    
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

Типичная ошибка: не закрывать ресурсы

// ❌ НЕПРАВИЛЬНО: утечка памяти
public User getUser(long id) throws SQLException {
    Connection conn = dataSource.getConnection();
    PreparedStatement stmt = conn.prepareStatement("SELECT * FROM users WHERE id = ?");
    stmt.setLong(1, id);
    ResultSet rs = stmt.executeQuery();
    
    User user = null;
    if (rs.next()) {
        user = new User(rs.getInt("id"), rs.getString("name"));
    }
    // Забыли закрыть rs, stmt, conn!
    return user;
}

// ✅ ПРАВИЛЬНО: try-with-resources
public User getUser(long id) throws SQLException {
    String sql = "SELECT * FROM users WHERE id = ?";
    
    try (Connection conn = dataSource.getConnection();
         PreparedStatement stmt = conn.prepareStatement(sql)) {
        stmt.setLong(1, id);
        
        try (ResultSet rs = stmt.executeQuery()) {
            if (rs.next()) {
                return new User(rs.getInt("id"), rs.getString("name"));
            }
        }
    }
    return null;
}

Драйверы БД

<!-- pom.xml -->
<dependencies>
    <!-- PostgreSQL -->
    <dependency>
        <groupId>org.postgresql</groupId>
        <artifactId>postgresql</artifactId>
        <version>42.7.0</version>
    </dependency>
    
    <!-- MySQL -->
    <dependency>
        <groupId>com.mysql</groupId>
        <artifactId>mysql-connector-j</artifactId>
        <version>8.1.0</version>
    </dependency>
    
    <!-- Oracle -->
    <dependency>
        <groupId>com.oracle.database.jdbc</groupId>
        <artifactId>ojdbc11</artifactId>
        <version>21.9.0.0</version>
    </dependency>
    
    <!-- H2 (in-memory для тестов) -->
    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <version>2.1.214</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Эволюция в Spring Framework

JDBC (1997)
    ↓
Spring Framework (2003) → JdbcTemplate
    ↓
Hibernate (2001) → JPA (2006)
    ↓
Spring Data (2010) → JpaRepository
    ↓
Reactive (2017) → R2DBC (асинхронный JDBC)
// Spring Data с Reactive
@Repository
public interface UserRepository extends ReactiveCrudRepository<User, Long> {
    Mono<User> findByEmail(String email);  // Асинхронный результат
}

Итоговая сравнительная таблица

ПодходУровеньПростотаКонтрольПроизводительность
Raw JDBCНизкийНизкаяВысокийХорошая
JdbcTemplateНизкийСредняяВысокийХорошая
HibernateВысокийВысокаяНизкийЗависит
Spring DataВысокийОчень высокаяНизкийЗависит
MyBatisСреднийСредняяОчень высокийОтличная

Для интервью

"Входили в БД через JDBC API, который предоставляет унифицированный доступ ко всем базам данных. Сначала это был raw JDBC - нужно было вручную загружать драйвер, открывать соединение, выполнять запросы и закрывать ресурсы.

Затем появились оптимизации:

  1. Connection Pooling (HikariCP) - переиспользование соединений
  2. ORM фреймворки (Hibernate, JPA) - автоматическое маппирование объектов на таблицы
  3. Spring Data - декларативные репозитории

Современный подход - использовать Spring Data JPA с аннотациями, что скрывает всю сложность и обеспечивает лучший код и производительность.