Комментарии (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 — это:
- ✅ Стандартный API для работы с БД в Java
- ✅ Требует явного управления connections/statements
- ✅ Подвержен ошибкам (утечки, SQL injection)
- ✅ Очень низкий уровень абстракции
Практика:
- Используй JdbcTemplate вместо raw JDBC
- Или лучше — используй JPA/Hibernate
- Всегда используй PreparedStatement (защита от injection)
- Используй пул соединений (HikariCP)
- Закрывай ресурсы через try-with-resources
- Логируй SQL запросы в development (показывает реальные запросы к БД)