JDBC API
- Пример создания соединения
- Statement - создание SQL-выражений
- PreparedStatement
- Выполнение SQL-выражений
HikariConfig config = new HikariConfig(); // Создание и заполнение конфига соединения
config.setJdbcUrl(DB_URL);
config.setUsername(DB_USERNAME);
config.setPassword(DB_PASSWORD);
HikariDataSource hikariDataSource = new HikariDataSource(config); // Создание пула соединений
if (hikariDataSource.getConnection() == null) {
throw new SQLException("Database connection failed");
}
return hikariDataSource.getConnection(); // Возврат соединения типа Connection
Для выполнения команд используется объект класса Statement
.
Statement
- базовый, предназначен для выполнения простых запросов и извлечения результатов.PreparedStatement
- позволяет добавлять в запросы входные параметры; добавлять методы управления входными папаметрами.CallableStatement
- используется для вызовов хранимых процедур; добавляет методы для манипуляции выходными параметрами.
Connection connection = DriverManager.getConnection(url, username, password);
Statement statement = connection.createStatement();
executeQuery
- используется в запросахв запросах, результатом которых является один единственный набор значений, таких как запросов типа SELECT.executeUpdate
- следует использовать для INSERT, UPDATE, DELETE - результат - количество измененных строк таблицы.CREATE TABLE
,DROP TABLE
- возвращаемое значениие 0.execute
- редко используется, в случаях, когда операторы SQL возвращают более одного набора данных, более одного счетчика обновлений или и то, и другое.
ResultSet resultSet = statement.executeQuery("SElECT * FROM chat.messages WHERE id = " + id);
if(resultSet.next()){
long authorId=resultSet.getInt("author");
long roomId=resultSet.getInt("chatroom");
long messageId=resultSet.getInt("id");
}
// Проверить, если результате выполнения запроса не нашлось ни одной строки
if (!resultSet.next()) {
throw new NotSavedSubEntityException();
} else {
String name = resultSet.getString("name");
if (!chatroom.getName().equals(name)) {
throw new NotSavedSubEntityException();
}
}
PreparedStatement предварительно компилирует запросы, которые могут содержать входные параметры обозначенные символом
?
, вместо которых определяются значения.
Нумерация
?
начинается с 1.
Предотвращает уязвимость к SQL-инъекциям
PreparedStatement pstmt = null;
//~~~ Чтение таблицы БД ~~~
pstmt = connection.prepareStatement(
"SELECT * FROM GOODS where id > ? and id < ?");
// Определяем значения параметров
pstmt.setInt(1, 2);
pstmt.setInt(2, 10);
// Выполнение запроса
ResultSet rs = preparedStatement.executeQuery();
// Вывод результата запроса
while (rs.next()) {
System.out.println("" + rs.getRow() + ". " +
"id = " + result2.getInt("id") +
", name = '" + rs.getString("name"));
}
//~~~ Запись в таблицу БД ~~~
pstmt = connection.prepareStatement(
"INSERT INTO GOODS(name) values(?)");
pstmt.setString(1, "Кофе");
pstmt.executeUpdate();
Зависимость Notes.Spring jdbc
Обертка вокруг JDBC API, которая предоставляется Notes.Spring Framework
- Сокращает код за счет отсутствия создания
Connection
,Statement
, работы сResultSet
. - Предотвращает дублирование кода
- Отсутствие неинформативного
SQLException
@Bean
public DataSource dataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("org.postgresql.Driver");
dataSource.setUrl("jdbc:postgresql://localhost:5432/springcourse");
dataSource.setUsername("postgres");
dataSource.setPassword("1234");
return dataSource;
}
@Bean
public JdbcTemplate jdbcTemplate() {
return new JdbcTemplate(dataSource());
}
Спринг внедрит зависимость с помощью аннотации @Autowired
.
private JdbcTemplate jdbcTemplate;
@Autowired
public PersonDAO(JdbcTemplate jdbcTemplate) {
this.jdbcTemplate = jdbcTemplate;
}
RowMapper - объект, отображающий строки результата запроса в POJO-объекты указанного класса.
public class PersonMapper implements RowMapper<Person> {
@Override
public Person mapRow(ResultSet rs, int rowNum) throws SQLException {
Person person = new Person();
person.setId(rs.getInt("id"));
person.setName(rs.getString("name"));
person.setEmail(rs.getString("email"));
person.setAge(rs.getInt("age"));
return person;
}
}
Класс реализовывает интерфейс RowMapper. Создаем новый объект, помещаем значения из resultSet
.
В случае совпадения имени поля и имени ячейки таблицы можно использовать BeanPropertyRowMapper.
```java
public List<Person> index() {
return jdbcTemplate.query("SELECT * FROM Person", new BeanPropertyRowMapper<>(Person.class));
}
Показать всех пользователей из таблицы.
public List<Person> index() {
return jdbcTemplate.query("SELECT * FROM Person", new PersonMapper());
}
Показать пользователя c конкретным id.
new Object[]{id}
- массив значений, которые должны быть подставлены вместо ?
в preparedStatement
.
(в данном примере - это id).
public Person show(int id) {
return jdbcTemplate.query("SELECT * FROM Person WHERE id=?", new Object[]{id}, new PersonMapper())
.stream().findAny().orElse(null);
}
Сохранение пользователя
public void save(Person person) {
jdbcTemplate.update("INSERT INTO Person(name, age, email) VALUES(?, ?, ?)", person.getName(), person.getAge(), person.getEmail());
}
Обновить пользователя
public void update(int id, Person updatedPerson) {
jdbcTemplate.update("UPDATE Person SET name=?, age=?, email=? WHERE id=?", updatedPerson.getName(), updatedPerson.getAge(),
updatedPerson.getEmail(), id);
}
Удалить пользователя
public void delete(int id) {
jdbcTemplate.update("DELETE FROM Person WHERE id=?", id);
}
Механизм упаковки множества записей в пакет и отправка его в БД.
Снижает нагрузку на бд за счет отправки множества запросов и получения ответов на них в виде одного пакета. БД может распараллеливать оперции вставки на несколько потоков.
jdbcTemplate.batchUpdate("INSERT INTO person(name, age, email) VALUES (?, ?, ?)",
new BatchPreparedStatementSetter() {
@Override
public void setValues(PreparedStatement preparedStatement, int i) throws SQLException {
preparedStatement.setString(1, people.get(i).getName());
preparedStatement.setInt(2, people.get(i).getAge());
preparedStatement.setString(3, people.get(i).getEmail());
}
@Override
public int getBatchSize() {
return people.size();
}
});