В предыдущих блогах этой серии мы никогда не объясняли метод работы с базой данных, но в реальной работе почти все системы неотделимы от сохраняемости данных, поэтому очень важно освоить метод работы с базой данных.
В Spring существует много способов работы с базой данных.Мы можем использовать JDBC, Hibernate, MyBatis или другие среды хранения данных.Целью этого блога является объяснение того, как работать с базой данных через JDBC в Spring.
1. Решение проблемы при сборке проекта
Прежде чем объяснять JDBC, давайте сначала решим проблему, потому что следующая ошибка была зарегистрирована, когда программа, которая изначально была собрана нормально, была перестроена и упакована:
После поиска информации в Интернете было сказано, что версия зависимости конфликтует, поэтому я проверил зависимость Spring, добавленную ранее в pom.xml:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<!--spring aop支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>5.1.8.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.18.RELEASE</version>
<scope>test</scope>
</dependency>
Версия spring-aop — 5.1.8.RELEASE, а версия остальных трех пакетов — 4.3.18.RELEASE, а версия spring-aop также изменена на 4.3.18.RELEASE:
<!--spring aop支持-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
На этом этапе пересоберите пакет, ошибок больше не будет, и пакет выполнен успешно:
Однако приведенные выше зависимости также можно упростить следующим образом:
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.18.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>4.3.18.RELEASE</version>
<scope>test</scope>
</dependency>
Поскольку пакет spring-webmvc уже включает в себя spring-context и spring-aop, нет необходимости повторно добавлять эти две зависимости:
2. Настройте источник данных
Сначала выполните следующую инструкцию, чтобы создать базу данных MySql spring_action_db:
CREATE DATABASE spring_action_db DEFAULT CHARACTER SET utf8 COLLATE utf8_general_ci;
Затем выполните следующую инструкцию, чтобы создать книгу таблиц:
use spring_action_db;
create table Book
(
book_id bigint auto_increment comment '书籍id',
book_name varchar(50) not null comment '书名',
author varchar(50) not null comment '作者',
create_by varchar(20) not null comment '创建人',
create_time datetime not null comment '创建时间',
modify_by varchar(20) not null comment '修改人',
modify_time datetime not null comment '修改时间',
constraint Book_pk
primary key (book_id)
)
comment '书籍';
Когда будете готовы, создайте новый класс конфигурации для настройки источника данных:
package chapter10.config;
import org.apache.commons.dbcp2.BasicDataSource;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
@Configuration
@ComponentScan("chapter10")
public class DataSourceConfig {
@Bean
public BasicDataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName("com.mysql.jdbc.Driver");
dataSource.setUrl("jdbc:mysql://localhost:3306/spring_action_db");
dataSource.setUsername("root");
dataSource.setPassword("root");
return dataSource;
}
}
Поскольку мы используем базу данных MySql, для имени драйвера установлено значение: com.mysql.jdbc.Driver.
Если вы используете другой тип базы данных, вам необходимо изменить его на соответствующее имя.
Так как используется драйвер MySql, нам необходимо добавить в pom.xml следующие зависимости, иначе соединение не будет получено при обращении к базе данных:
<!-- MySql驱动 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
3. Используйте необработанный код JDBC
Сначала создайте новый класс объектов базы данных Book:
package chapter10.domain;
import java.util.Date;
public class Book {
private Long bookId;
private String bookName;
private String author;
private String createBy;
private Date createTime;
private String modifyBy;
private Date modifyTime;
public Book(String bookName, String author, String createBy) {
this.bookName = bookName;
this.author = author;
this.createBy = createBy;
this.createTime = new Date();
this.modifyBy=createBy;
this.modifyTime=new Date();
}
public Book(Long bookId, String bookName, String author, String modifyBy) {
this.bookId = bookId;
this.bookName = bookName;
this.author = author;
this.modifyBy = modifyBy;
}
public Book() {
}
// 省略get和set方法
}
Затем определите интерфейс доступа к данным BookRepository и пока добавьте только метод addBook:
package chapter10.db;
import chapter10.domain.Book;
public interface BookRepository {
void addBook(Book book);
}
3.1 Новые данные
Новый класс реализации доступа к данным JdbcBookRepository выглядит следующим образом:
package chapter10.db.jdbc;
import chapter10.db.BookRepository;
import chapter10.domain.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Repository;
import javax.sql.DataSource;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.Calendar;
import java.util.Date;
@Repository
public class JdbcBookRepository implements BookRepository {
private static final String SQL_INSERT_BOOK =
"INSERT INTO book(book_name, author, create_by, create_time, modify_by, modify_time) VALUES (?,?,?,?,?,?);";
@Autowired
private DataSource dataSource;
@Override
public void addBook(Book book) {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
connection = dataSource.getConnection();
preparedStatement = connection.prepareStatement(SQL_INSERT_BOOK);
preparedStatement.setString(1, book.getBookName());
preparedStatement.setString(2, book.getAuthor());
preparedStatement.setString(3, book.getCreateBy());
preparedStatement.setTimestamp(4, new Timestamp(calendar.getTimeInMillis()));
preparedStatement.setString(5, book.getModifyBy());
preparedStatement.setTimestamp(6, new Timestamp(calendar.getTimeInMillis()));
preparedStatement.execute();
} catch (SQLException e) {
// 异常处理相关代码
} finally {
try {
if (preparedStatement != null) {
preparedStatement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
// 异常处理相关代码
}
}
}
}
Примечание. Класс снабжен аннотацией @Repository, чтобы Spring мог сканировать его и зарегистрировать как bean-компонент.
Стоит отметить, что в этом коде мы дважды поймали SQLException, потому чтоconnection = dataSource.getConnection();
,preparedStatement.execute();
,preparedStatement.close();
,connection.close();
Оба будут генерировать проверенное исключение SQLException, поэтому метод должен быть перехвачен, иначе компиляция завершится ошибкой:
Connection getConnection() throws SQLException;
boolean execute() throws SQLException;
void close() throws SQLException;
void close() throws SQLException;
Наконец, новый класс модульного теста BookRepositoryTest выглядит следующим образом:
package chapter10;
import chapter10.config.DataSourceConfig;
import chapter10.db.BookRepository;
import chapter10.domain.Book;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = DataSourceConfig.class)
public class BookRepositoryTest {
@Autowired
private BookRepository bookRepository;
@Test
public void testAddBook() {
Book book = new Book("Spring实战(第4版)", "Craig Walls", "申城异乡人");
bookRepository.addBook(book);
book = new Book("Java EE开发的颠覆者:Spring Boot实战", "汪云飞", "申城异乡人");
bookRepository.addBook(book);
book = new Book("RabbitMQ实战指南", "朱忠华", "申城异乡人");
bookRepository.addBook(book);
}
}
Запускаем тестовый метод testAddBook(), данные успешно добавлены в базу:
3.2 Обновление данных
Сначала добавьте метод обновления в поставщик данных BookRepository:
void updateBook(Book book);
Затем реализуйте метод в классе реализации доступа к данным JdbcBookRepository:
private static final String SQL_UPDATE_BOOK =
"UPDATE Book SET book_name = ?,author = ?,modify_by = ?,modify_time=? WHERE book_id = ?;";
@Override
public void updateBook(Book book) {
Connection connection = null;
PreparedStatement preparedStatement = null;
try {
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
connection = dataSource.getConnection();
preparedStatement = connection.prepareStatement(SQL_UPDATE_BOOK);
preparedStatement.setString(1, book.getBookName());
preparedStatement.setString(2, book.getAuthor());
preparedStatement.setString(3, book.getModifyBy());
preparedStatement.setTimestamp(4, new Timestamp(calendar.getTimeInMillis()));
preparedStatement.setLong(5, book.getBookId());
preparedStatement.execute();
} catch (SQLException e) {
// 异常处理相关代码
} finally {
try {
if (preparedStatement != null) {
preparedStatement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
// 异常处理相关代码
}
}
}
Вы обнаружили, что его код почти такой же, как и предыдущий только что добавленный код, и он должен дважды перехватывать проверенное исключение SQLException?Люди с чистотой кода не могут не хотеть рефакторинга, ха-ха.
Наконец, добавьте тестовый метод testUpdateBook в тестовый класс BookRepositoryTest следующим образом:
@Test
public void testUpdateBook() {
Book book = new Book(1L, "Spring实战(第4版)", "Craig Walls", "zwwhnly");
bookRepository.updateBook(book);
book = new Book(2L, "Java EE开发的颠覆者:Spring Boot实战", "汪云飞", "zwwhnly");
bookRepository.updateBook(book);
book = new Book(3L, "RabbitMQ实战指南", "朱忠华", "zwwhnly");
bookRepository.updateBook(book);
}
Выполните тестовый метод, и обновление данных пройдет успешно:
3.3 Поиск данных
Сначала добавьте метод обновления в поставщик данных BookRepository:
Book findBook(long bookId);
Затем реализуйте метод в классе реализации доступа к данным JdbcBookRepository:
private static final String SQL_SELECT_BOOK =
"SELECT book_id,book_name,author,create_by,create_time,modify_by,modify_time FROM book WHERE book_id = ?;";
@Override
public Book findBook(long bookId) {
Connection connection = null;
PreparedStatement preparedStatement = null;
ResultSet resultSet = null;
Book book = null;
try {
connection = dataSource.getConnection();
preparedStatement = connection.prepareStatement(SQL_SELECT_BOOK);
preparedStatement.setLong(1, bookId);
resultSet = preparedStatement.executeQuery();
if (resultSet.next()) {
book = new Book();
book.setBookId(resultSet.getLong("book_id"));
book.setBookName(resultSet.getString("book_name"));
book.setAuthor(resultSet.getString("author"));
book.setCreateBy(resultSet.getString("create_by"));
book.setCreateTime(resultSet.getTimestamp("create_time"));
book.setModifyBy(resultSet.getString("modify_by"));
book.setModifyTime(resultSet.getTimestamp("modify_time"));
}
} catch (SQLException e) {
// 异常处理相关代码
} finally {
try {
if (resultSet != null) {
resultSet.close();
}
if (preparedStatement != null) {
preparedStatement.close();
}
if (connection != null) {
connection.close();
}
} catch (SQLException e) {
// 异常处理相关代码
}
}
return book;
}
Вы обнаружили, что его код в основном такой же, как и предыдущий новый и обновленный код, и он должен дважды перехватывать проверенное исключение SQLException, кто-нибудь с чистым кодом начал рефакторинг, ха-ха.
Наконец, добавьте тестовый метод testFindBook в тестовый класс BookRepositoryTest следующим образом:
@Test
public void testFindBook() {
Book book = bookRepository.findBook(1L);
Assert.assertNotNull(book);
Assert.assertEquals(book.getBookName(), "Spring实战(第4版)");
}
Выполните тестовый метод, и запрос данных будет успешным:
4. Использование шаблонов JDBC
После использования оригинального JDBC для работы с базой данных многие студенты с чистым кодом не могут не начать рефакторинг, потому что большая часть кода является шаблонным кодом, и только небольшая часть связана с бизнес-логикой.Хорошая новость заключается в том, что Spring помог нам провести рефакторинг.После построения Spring абстрагирует шаблонный код доступа к данным в класс шаблона, и мы можем использовать класс шаблона напрямую, тем самым упрощая код JDBC.
4.1 Новые данные
Сначала добавьте следующую конфигурацию в класс конфигурации DataSourceConfig:
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
Затем удалите аннотацию @Repository во вновь созданном классе JdbcBookRepository.
Затем создайте новый класс реализации доступа к данным JdbcTemplateBookRepository следующим образом:
package chapter10.db.jdbc;
import chapter10.db.BookRepository;
import chapter10.domain.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.JdbcOperations;
import org.springframework.stereotype.Repository;
import java.sql.Date;
@Repository
public class JdbcTemplateBookRepository implements BookRepository {
private static final String SQL_INSERT_BOOK =
"INSERT INTO book(book_name, author, create_by, create_time, modify_by, modify_time) VALUES (?,?,?,?,?,?);";
@Autowired
private JdbcOperations jdbcOperations;
@Override
public void addBook(Book book) {
jdbcOperations.update(SQL_INSERT_BOOK, book.getBookName(),
book.getAuthor(),
book.getCreateBy(),
new Date(System.currentTimeMillis()),
book.getModifyBy(),
new Date(System.currentTimeMillis()));
}
}
Примечание. Класс снабжен аннотацией @Repository, чтобы Spring мог сканировать его и зарегистрировать как bean-компонент.
Это очень лаконично?От предыдущей оптимизации кода до текущего кода студенты с чистотой кода, вероятно, счастливы умереть.
Поскольку в предыдущем тестовом классе BookRepositoryTest мы внедрили интерфейс, поэтому мы можем напрямую обращаться к методу реализации только что созданного класса JdbcTemplateBookRepository без изменения кода тестового класса:
@Autowired
private BookRepository bookRepository;
Запускаем предыдущий тестовый метод testAddBook(), данные успешно добавляются в базу:
4.2 Обновление данных
Добавьте следующий код в класс реализации доступа к данным JdbcTemplateBookRepository:
private static final String SQL_UPDATE_BOOK =
"UPDATE Book SET book_name = ?,author = ?,modify_by = ?,modify_time=? WHERE book_id = ?;";
@Override
public void updateBook(Book book) {
jdbcOperations.update(SQL_UPDATE_BOOK, book.getBookName(),
book.getAuthor(),
book.getModifyBy(),
new Timestamp(System.currentTimeMillis()),
book.getBookId());
}
Затем просто измените предыдущий тестовый метод testUpdateBook():
@Test
public void testUpdateBook() {
Book book = new Book(4L, "Spring实战(第4版)", "Craig Walls", "zwwhnly");
bookRepository.updateBook(book);
book = new Book(5L, "Java EE开发的颠覆者:Spring Boot实战", "汪云飞", "zwwhnly");
bookRepository.updateBook(book);
book = new Book(6L, "RabbitMQ实战指南", "朱忠华", "zwwhnly");
bookRepository.updateBook(book);
}
Запустите предыдущий тестовый метод testUpdateBook(), и обновление данных пройдет успешно:
4.3 Поиск данных
Добавьте следующий код в класс реализации доступа к данным JdbcTemplateBookRepository:
private static final String SQL_SELECT_BOOK =
"SELECT book_id,book_name,author,create_by,create_time,modify_by,modify_time FROM book WHERE book_id = ?;";
@Override
public Book findBook(long bookId) {
return jdbcOperations.queryForObject(SQL_SELECT_BOOK, new BookRowMapper(), bookId);
}
private static final class BookRowMapper implements RowMapper<Book> {
@Override
public Book mapRow(ResultSet resultSet, int i) throws SQLException {
Book book = new Book();
book.setBookId(resultSet.getLong("book_id"));
book.setBookName(resultSet.getString("book_name"));
book.setAuthor(resultSet.getString("author"));
book.setCreateBy(resultSet.getString("create_by"));
book.setCreateTime(resultSet.getTimestamp("create_time"));
book.setModifyBy(resultSet.getString("modify_by"));
book.setModifyTime(resultSet.getTimestamp("modify_time"));
return book;
}
}
Запустите предыдущий тестовый метод testFindBook(), и запрос данных будет выполнен успешно:
5. Исходный код и ссылка
Адрес источника:GitHub.com/Где находится Ухань/SPR…, добро пожаловать на скачивание.
Весна в действии (4-е издание) Крейга Уоллса