Статьи

SpringBoot: работа с JdbcTemplate

Spring предоставляет отличную абстракцию поверх JDBC API с использованием JdbcTemplate, а также предоставляет отличные возможности управления транзакциями с использованием подхода на основе аннотаций.

Сначала давайте кратко рассмотрим, как мы обычно используем Spring JdbcTemplate ( без SpringBoot ), зарегистрировав bean- компоненты DataSource , TransactionManager и JdbcTemplate, и при желании мы можем зарегистрировать bean-компонент DataSourceInitializer для инициализации нашей базы данных.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
@Configuration
@ComponentScan
@EnableTransactionManagement
@PropertySource(value = { "classpath:application.properties" })
public class AppConfig
{
    @Autowired
    private Environment env;
 
    @Value("${init-db:false}")
    private String initDatabase;
     
    @Bean
    public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer()
    {
        return new PropertySourcesPlaceholderConfigurer();
    }   
 
    @Bean
    public JdbcTemplate jdbcTemplate(DataSource dataSource)
    {
        return new JdbcTemplate(dataSource);
    }
 
    @Bean
    public PlatformTransactionManager transactionManager(DataSource dataSource)
    {
        return new DataSourceTransactionManager(dataSource);
    }
 
    @Bean
    public DataSource dataSource()
    {
        BasicDataSource dataSource = new BasicDataSource();
        dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
        dataSource.setUrl(env.getProperty("jdbc.url"));
        dataSource.setUsername(env.getProperty("jdbc.username"));
        dataSource.setPassword(env.getProperty("jdbc.password"));
        return dataSource;
    }
 
    @Bean
    public DataSourceInitializer dataSourceInitializer(DataSource dataSource)
    {
        DataSourceInitializer dataSourceInitializer = new DataSourceInitializer();   
        dataSourceInitializer.setDataSource(dataSource);
        ResourceDatabasePopulator databasePopulator = new ResourceDatabasePopulator();
        databasePopulator.addScript(new ClassPathResource("data.sql"));
        dataSourceInitializer.setDatabasePopulator(databasePopulator);
        dataSourceInitializer.setEnabled(Boolean.parseBoolean(initDatabase));
        return dataSourceInitializer;
    }
}

С этой конфигурацией мы можем внедрить JdbcTemplate в компоненты доступа к данным для взаимодействия с базами данных.

1
2
3
4
5
6
7
8
public class User
{
    private Integer id;
    private String name;
    private String email;
 
    // setters & getters
}
01
02
03
04
05
06
07
08
09
10
11
@Repository
public class UserRepository
{
    @Autowired
    private JdbcTemplate jdbcTemplate;
 
    @Transactional(readOnly=true)
    public List<User> findAll() {
        return jdbcTemplate.query("select * from users", new UserRowMapper());
    }
}
01
02
03
04
05
06
07
08
09
10
11
12
13
class UserRowMapper implements RowMapper<User>
{
    @Override
    public User mapRow(ResultSet rs, int rowNum) throws SQLException
    {
        User user = new User();
        user.setId(rs.getInt("id"));
        user.setName(rs.getString("name"));
        user.setEmail(rs.getString("email"));
 
        return user;
    }
}

Возможно, вы заметили, что в большинстве случаев мы используем подобную конфигурацию в наших приложениях.

Теперь давайте посмотрим, как использовать JdbcTemplate без необходимости настраивать все эти bean-компоненты вручную с помощью SpringBoot .

Использование JdbcTemplate с SpringBoot

Используя SpringBoot, мы можем воспользоваться функцией автоматической настройки и избавиться от необходимости настраивать компоненты самостоятельно.

Создайте проект на основе SpringBoot maven и добавьте модуль spring-boot-starter-jdbc.

1
2
3
4
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>

Добавив модуль spring-boot-starter-jdbc , мы получим следующую автоматическую конфигурацию:

  • Модуль spring-boot-starter-jdbc транзитивно извлекает tomcat-jdbc- {version} .jar, который используется для настройки bean-компонента DataSource.
  • Если вы не определили какой-либо компонент DataSource явно и если у вас есть какой-либо встроенный драйвер базы данных в пути к классам, такой как H2, HSQL или Derby, то SpringBoot автоматически зарегистрирует компонент DataSource с использованием настроек базы данных в памяти.
  • Если вы не зарегистрировали ни один из следующих типов бинов, SpringBoot зарегистрирует их автоматически.
    • PlatformTransactionManager (DataSourceTransactionManager)
    • JdbcTemplate
    • NamedParameterJdbcTemplate
  • У нас могут быть файлы schema.sql и data.sql в корневом пути к классам, которые SpringBoot будет автоматически использовать для инициализации базы данных. В дополнение к schema.sql и data.sql Spring Boot будет загружать схему — $ {platform} .sql и data- $. Файлы {platform} .sql, если они доступны в корневом пути к классам. Здесь значением платформы является значение свойства spring.datasource.platform, которое может быть hsqldb, h2, oracle, mysql, postgresql и т. Д. Вы можете настроить имена скриптов по умолчанию, используя следующие свойства:
    • spring.datasource.schema = создать-db.sql
    • spring.datasource.data = семян data.sql

      SpringBoot использует значение свойства spring.datasource.initialize , которое по умолчанию имеет значение true , чтобы определить, инициализировать базу данных или нет. Если вы хотите отключить инициализацию базы данных, вы можете установить spring.datasource.initialize = false

      Если при выполнении скриптов возникают ошибки, приложение не запустится. Если вы хотите продолжить, вы можете установить spring.datasource.continueOnError = true .

Давайте добавим драйвер базы данных H2 в наш pom.xml .

1
2
3
4
<dependency>
    <groupId>com.h2database</groupId>
    <artifactId>h2</artifactId>
</dependency>

Создайте schema.sql в src / main / resources следующим образом:

1
2
3
4
5
6
7
CREATE TABLE users
(
    id int(11) NOT NULL AUTO_INCREMENT,
    name varchar(100) NOT NULL,
    email varchar(100) DEFAULT NULL,
    PRIMARY KEY (id)
);

Создайте data.sql в src / main / resources следующим образом:

1
2
3
insert into users(id, name, email) values(1,'Siva','[email protected]');
insert into users(id, name, email) values(2,'Prasad','[email protected]');
insert into users(id, name, email) values(3,'Reddy','[email protected]');

Теперь вы можете добавить JdbcTemplate в UserRepository следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@Repository
public class UserRepository
{
    @Autowired
    private JdbcTemplate jdbcTemplate;
 
    @Transactional(readOnly=true)
    public List<User> findAll() {
        return jdbcTemplate.query("select * from users",
                new UserRowMapper());
    }
 
    @Transactional(readOnly=true)
    public User findUserById(int id) {
        return jdbcTemplate.queryForObject(
            "select * from users where id=?",
            new Object[]{id}, new UserRowMapper());
    }
 
    public User create(final User user)
    {
        final String sql = "insert into users(name,email) values(?,?)";
 
        KeyHolder holder = new GeneratedKeyHolder();
        jdbcTemplate.update(new PreparedStatementCreator() {
            @Override
            public PreparedStatement createPreparedStatement(Connection connection) throws SQLException {
                PreparedStatement ps = connection.prepareStatement(sql, Statement.RETURN_GENERATED_KEYS);
                ps.setString(1, user.getName());
                ps.setString(2, user.getEmail());
                return ps;
            }
        }, holder);
 
        int newUserId = holder.getKey().intValue();
        user.setId(newUserId);
        return user;
    }
}
 
class UserRowMapper implements RowMapper<User>
{
    @Override
    public User mapRow(ResultSet rs, int rowNum) throws SQLException {
        User user = new User();
        user.setId(rs.getInt("id"));
        user.setName(rs.getString("name"));
        user.setEmail(rs.getString("email"));
        return user;
    }
}

Создайте точку входа SpringbootJdbcDemoApplication.java .

1
2
3
4
5
6
7
8
@SpringBootApplication
public class SpringbootJdbcDemoApplication
{
    public static void main(String[] args)
    {
        SpringApplication.run(SpringbootJdbcDemoApplication.class, args);
    }
}

Давайте создадим класс JUnit Test для тестирования наших методов UserRepository.

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
@RunWith(SpringJUnit4ClassRunner.class)
@SpringApplicationConfiguration(SpringbootJdbcDemoApplication.class)
public class SpringbootJdbcDemoApplicationTests
{
    @Autowired
    private UserRepository userRepository;
 
    @Test
    public void findAllUsers() {
        List<User> users = userRepository.findAll();
        assertNotNull(users);
        assertTrue(!users.isEmpty());
    }
 
    @Test
    public void findUserById() {
        User user = userRepository.findUserById(1);
        assertNotNull(user);
    }
 
    @Test
    public void createUser() {
        User user = new User(0, "John", "[email protected]");
        User savedUser = userRepository.create(user);
        User newUser = userRepository.findUserById(savedUser.getId());
        assertNotNull(newUser);
        assertEquals("John", newUser.getName());
        assertEquals("[email protected]", newUser.getEmail());
    }
}

По умолчанию такие функции SpringBoot, как внешние свойства, ведение журналов и т. Д., Доступны в ApplicationContext, только если вы используете SpringApplication . Итак, SpringBoot предоставляет аннотацию @SpringApplicationConfiguration для настройки ApplicationContext для тестов, которые используют SpringApplication за кулисами.

Мы узнали, как быстро начать работу со встроенной базой данных. Что если мы хотим использовать не встроенные базы данных, такие как MySQL, Oracle, PostgreSQL и т. Д.? ,

Мы можем настроить свойства базы данных в файле application.properties, чтобы SpringBoot использовал эти параметры jdbc для настройки bean-компонента DataSource.

1
2
3
4
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=admin

По любой причине, если вы хотите больше контролировать и настраивать компонент DataSource самостоятельно, вы можете настроить компонент DataSource в классе Configuration. Если вы зарегистрируете компонент DataSource, SpringBoot не будет автоматически конфигурировать DataSource с помощью автоконфигурации. Что если вы хотите использовать другую библиотеку пулов соединений?

SpringBoot по умолчанию загружает tomcat-jdbc- {version} .jar и использует org.apache.tomcat.jdbc.pool.DataSource для настройки bean-компонента DataSource .

SpringBoot проверяет доступность следующих классов и использует первый, который доступен в classpath.

  • org.apache.tomcat.jdbc.pool.DataSource
  • com.zaxxer.hikari.HikariDataSource
  • org.apache.commons.dbcp.BasicDataSource
  • org.apache.commons.dbcp2.BasicDataSource

Например, если вы хотите использовать HikariDataSource, вы можете исключить tomcat-jdbc и добавить зависимость HikariCP следующим образом:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-jdbc</artifactId>
    <exclusions>
        <exclusion>
        <groupId>org.apache.tomcat</groupId>
        <artifactId>tomcat-jdbc</artifactId>
        </exclusion>
    </exclusions>
</dependency>
 
<dependency>
    <groupId>com.zaxxer</groupId>
    <artifactId>HikariCP</artifactId>
</dependency>

С этой конфигурацией зависимостей SpringBoot будет использовать HikariCP для настройки bean-компонента DataSource .