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

Как Java работает с JDBC

2.0 Middle🔥 111 комментариев
#Базы данных и SQL

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

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

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

Ответ

JDBC (Java Database Connectivity) — это **стандартный API в Java для подключения к базам данных и выполнения SQL запросов**. Это мост между Java приложением и БД.

Архитектура JDBC

Java Application
        ↓
    JDBC API (java.sql.*)
        ↓
    JDBC Driver Manager
        ↓
  JDBC Driver (PostgreSQL, MySQL, Oracle, и т.д.)
        ↓
  Database Server

Основные компоненты JDBC

1. Driver — драйвер БД

// Каждая БД имеет свой драйвер
// PostgreSQL
Class.forName("org.postgresql.Driver");

// MySQL
Class.forName("com.mysql.cj.jdbc.Driver");

// Oracle
Class.forName("oracle.jdbc.OracleDriver");

2. Connection — соединение с БД

import java.sql.*;

public class JdbcExample {
    public static void main(String[] args) {
        // Классический способ (плохо)
        try {
            Class.forName("org.postgresql.Driver");
            Connection conn = DriverManager.getConnection(
                "jdbc:postgresql://localhost:5432/mydb",
                "user",
                "password"
            );
            System.out.println("Connected!");
            conn.close();
        } catch (ClassNotFoundException | SQLException e) {
            e.printStackTrace();
        }
    }
}

3. Statement — выполнение SQL

// ❌ Простой Statement (уязвим к SQL injection)
Statement stmt = conn.createStatement();
String query = "SELECT * FROM users WHERE id = " + userId;  // ОПАСНО!
ResultSet rs = stmt.executeQuery(query);

// ✅ PreparedStatement (защищённый)
String query = "SELECT * FROM users WHERE id = ?";
PreparedStatement pstmt = conn.prepareStatement(query);
pstmt.setInt(1, userId);  // Параметр на месте ?
ResultSet rs = pstmt.executeQuery();

4. ResultSet — результаты запроса

public class UserDAO {
    private DataSource dataSource;
    
    public User getUserById(Long id) throws SQLException {
        String query = "SELECT id, name, email, age FROM users WHERE id = ?";
        
        try (Connection conn = dataSource.getConnection();
             PreparedStatement pstmt = conn.prepareStatement(query)) {
            
            pstmt.setLong(1, id);
            
            try (ResultSet rs = pstmt.executeQuery()) {
                if (rs.next()) {
                    return new User(
                        rs.getLong("id"),
                        rs.getString("name"),
                        rs.getString("email"),
                        rs.getInt("age")
                    );
                }
            }
        }
        return null;
    }
}

Полный пример JDBC операций

CREATE (INSERT)

public void createUser(User user) throws SQLException {
    String query = "INSERT INTO users (name, email, age) VALUES (?, ?, ?)";
    
    try (Connection conn = dataSource.getConnection();
         PreparedStatement pstmt = conn.prepareStatement(query)) {
        
        pstmt.setString(1, user.getName());
        pstmt.setString(2, user.getEmail());
        pstmt.setInt(3, user.getAge());
        
        int rowsInserted = pstmt.executeUpdate();
        System.out.println("Rows inserted: " + rowsInserted);
    }
}

READ (SELECT)

public List<User> getAllUsers() throws SQLException {
    String query = "SELECT id, name, email, age FROM users";
    List<User> users = new ArrayList<>();
    
    try (Connection conn = dataSource.getConnection();
         Statement stmt = conn.createStatement();
         ResultSet rs = stmt.executeQuery(query)) {
        
        while (rs.next()) {
            users.add(new User(
                rs.getLong("id"),
                rs.getString("name"),
                rs.getString("email"),
                rs.getInt("age")
            ));
        }
    }
    return users;
}

UPDATE

public void updateUser(User user) throws SQLException {
    String query = "UPDATE users SET name = ?, email = ? WHERE id = ?";
    
    try (Connection conn = dataSource.getConnection();
         PreparedStatement pstmt = conn.prepareStatement(query)) {
        
        pstmt.setString(1, user.getName());
        pstmt.setString(2, user.getEmail());
        pstmt.setLong(3, user.getId());
        
        int rowsUpdated = pstmt.executeUpdate();
        System.out.println("Rows updated: " + rowsUpdated);
    }
}

DELETE

public void deleteUser(Long id) throws SQLException {
    String query = "DELETE FROM users WHERE id = ?";
    
    try (Connection conn = dataSource.getConnection();
         PreparedStatement pstmt = conn.prepareStatement(query)) {
        
        pstmt.setLong(1, id);
        
        int rowsDeleted = pstmt.executeUpdate();
        System.out.println("Rows deleted: " + rowsDeleted);
    }
}

Транзакции (Transactions)

public void transferMoney(Long fromAccountId, Long toAccountId, BigDecimal amount) 
        throws SQLException {
    
    try (Connection conn = dataSource.getConnection()) {
        // Отключаем автокоммит
        conn.setAutoCommit(false);
        
        try {
            // Снимаем со счёта
            updateAccountBalance(conn, fromAccountId, amount.negate());
            
            // Добавляем на счёт
            updateAccountBalance(conn, toAccountId, amount);
            
            // Коммитим обе операции вместе
            conn.commit();
            System.out.println("Transfer successful");
            
        } catch (SQLException e) {
            // Откатываем обе операции если произошла ошибка
            conn.rollback();
            System.out.println("Transfer failed, rolled back");
            throw e;
        }
    }
}

private void updateAccountBalance(Connection conn, Long accountId, BigDecimal amount) 
        throws SQLException {
    String query = "UPDATE accounts SET balance = balance + ? WHERE id = ?";
    try (PreparedStatement pstmt = conn.prepareStatement(query)) {
        pstmt.setBigDecimal(1, amount);
        pstmt.setLong(2, accountId);
        pstmt.executeUpdate();
    }
}

Batch операции (скорость)

public void insertBatch(List<User> users) throws SQLException {
    String query = "INSERT INTO users (name, email, age) VALUES (?, ?, ?)";
    
    try (Connection conn = dataSource.getConnection();
         PreparedStatement pstmt = conn.prepareStatement(query)) {
        
        for (User user : users) {
            pstmt.setString(1, user.getName());
            pstmt.setString(2, user.getEmail());
            pstmt.setInt(3, user.getAge());
            pstmt.addBatch();  // Добавляем в batch
        }
        
        int[] results = pstmt.executeBatch();  // Выполняем всё за раз
        System.out.println("Inserted: " + results.length + " rows");
    }
}

Batch в 1000 раз быстрее чем 1000 отдельных INSERT'ов!

JDBC в Spring (предпочтительный способ)

@Configuration
public class DataSourceConfig {
    @Bean
    public DataSource dataSource() {
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:postgresql://localhost/mydb");
        config.setUsername("user");
        config.setPassword("password");
        config.setMaximumPoolSize(20);
        return new HikariDataSource(config);
    }
}

@Service
public class UserService {
    @Autowired
    private DataSource dataSource;  // Берём пул соединений
    
    public User getUserById(Long id) {
        String query = "SELECT * FROM users WHERE id = ?";
        
        // Spring JdbcTemplate упрощает JDBC
        return new JdbcTemplate(dataSource)
            .queryForObject(query, new Object[]{id}, 
                (rs, rowNum) -> new User(
                    rs.getLong("id"),
                    rs.getString("name"),
                    rs.getString("email")
                ));
    }
}

JdbcTemplate (упрощённый JDBC)

@Service
public class UserService {
    @Autowired
    private JdbcTemplate jdbcTemplate;
    
    public User getUserById(Long id) {
        // JdbcTemplate автоматически управляет connection и statement
        return jdbcTemplate.queryForObject(
            "SELECT * FROM users WHERE id = ?",
            new Object[]{id},
            (rs, rowNum) -> new User(
                rs.getLong("id"),
                rs.getString("name"),
                rs.getString("email")
            )
        );
    }
    
    public List<User> getAllUsers() {
        return jdbcTemplate.query(
            "SELECT * FROM users",
            (rs, rowNum) -> new User(
                rs.getLong("id"),
                rs.getString("name"),
                rs.getString("email")
            )
        );
    }
    
    public void updateUser(User user) {
        jdbcTemplate.update(
            "UPDATE users SET name = ?, email = ? WHERE id = ?",
            user.getName(),
            user.getEmail(),
            user.getId()
        );
    }
}

JPA/Hibernate (ещё выше абстракции)

// JPA скрывает JDBC совсем
@Entity
@Table(name = "users")
public class User {
    @Id
    private Long id;
    
    @Column
    private String name;
    
    @Column
    private String email;
}

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
    // JPA автоматически генерирует JDBC запросы
    List<User> findByEmailContaining(String email);
}

@Service
public class UserService {
    @Autowired
    private UserRepository repository;
    
    public User getUserById(Long id) {
        // Под капотом — JDBC запрос к БД
        return repository.findById(id).orElseThrow();
    }
}

Типичные проблемы и решения

Проблема 1: SQL Injection

// ❌ Уязвимо
String query = "SELECT * FROM users WHERE name = '" + userName + "'";

// ✅ Безопасно
String query = "SELECT * FROM users WHERE name = ?";
PreparedStatement pstmt = conn.prepareStatement(query);
pstmt.setString(1, userName);

Проблема 2: Утечки соединений

// ❌ Соединение не закроется при исключении
Connection conn = DriverManager.getConnection(url, user, pass);
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery(query);

// ✅ Try-with-resources (автоматически закроет)
try (Connection conn = dataSource.getConnection();
     PreparedStatement pstmt = conn.prepareStatement(query);
     ResultSet rs = pstmt.executeQuery()) {
    // Ресурсы автоматически закроются
}

Проблема 3: N+1 Query Problem

// ❌ 1 запрос + N запросов
List<User> users = repository.findAll();
for (User user : users) {
    List<Order> orders = orderRepository.findByUserId(user.getId());  // N раз!
}

// ✅ Один запрос с JOIN
List<User> users = repository.findAllWithOrders();  // FETCH JOIN

Итог

JDBC — это:

  1. ✅ Стандартный API для работы с БД в Java
  2. ✅ Требует явного управления connections/statements
  3. ✅ Подвержен ошибкам (утечки, SQL injection)
  4. ✅ Очень низкий уровень абстракции

Практика:

  • Используй JdbcTemplate вместо raw JDBC
  • Или лучше — используй JPA/Hibernate
  • Всегда используй PreparedStatement (защита от injection)
  • Используй пул соединений (HikariCP)
  • Закрывай ресурсы через try-with-resources
  • Логируй SQL запросы в development (показывает реальные запросы к БД)
Как Java работает с JDBC | PrepBro