《荀子·儒效》有云:“不闻不若闻之,闻之不若见之,见之不若知之,知之不若行之。学至于行之而止矣。行之,明也,明之为圣人。”
数据持久化是软件开发中重要的组成部分。目前,大多数软件都会使用关系数据库作为数据存储,虽然NoSQL数据存储(如MongoDB,Redis,Cassandra)越来越流行,但是大家更多的还是把NoSQL当成缓存在使用,而一些更新不是很频繁又很重要的数据,依然还是选择存放在关系型数据库里。
Java本身提供了JDBC API来与数据库进行交互,不过,它是一个低级API,需要大量的模板代码才能使用,于是Java EE平台贬提出了Java持久性API(JPA)规范,这是一个对象关系映射(ORM)框架。 像Hibernate和EclipseLink是最流行的JPA实现。还有其他流行的持久性框架,比如MyBatis和JOOQ,它们更注重SQL本身的实现。
Spring JdbcTemplate就是在JDBC API之上提供了一个很好的模板抽象,并且使用基于注解的方法提供了出色的事务管理功能。 本章会介绍如何在不使用Spring Boot的情况下使用JdbcTemplate,以及Spring Boot如何在不使用太多编码或配置的情况下轻松使用JdbcTemplate。
实战1:直接使用Spring JdbcTemplate
首先,让我们快速回顾一下过去最基础的用法,就是在Spring中,如何注册DataSource,TransactionManager和JdbcTemplate bean来使用JdbcTemplate(没有Spring Boot),并注册DataSourceInitializer bean来初始化数据库,代码如下:
@Configuration
@ComponentScan
@EnableTransactionManagement
@PropertySource(value = { "classpath:applicationNoBoot.properties" })
public class AppConfig {
@Autowired
private Environment env;
@Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer()
{
return new PropertySourcesPlaceholderConfigurer();
}
@Value("${init-db:false}")
private String initDatabase;
@Bean //JdbcTemplate模板
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("dataNoBoot.sql"));
dataSourceInitializer.setDatabasePopulator(databasePopulator);
dataSourceInitializer.setEnabled(Boolean.parseBoolean(initDatabase));
return dataSourceInitializer;
}
}
此时,还需要在src / main / resources / applicationNoBoot.properties中配置JDBC连接参数。 属性如下:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/test
jdbc.username=root
jdbc.password=hjf123456
然后,在src / main / resources中创建名为dataNoBoot.sql的数据库设置脚本,如下所示:
DROP TABLE IF EXISTS mans;
CREATE TABLE mans (
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
email varchar(100) DEFAULT NULL,
PRIMARY KEY (id)
);
insert into mans(id, name, email) values(1,'mickjoust','mickjoust@test.com');
insert into mans(id, name, email) values(2,'mia','mia@test.com');
insert into mans(id, name, email) values(3,'max','max@test.com');
如果希望将架构生成脚本和数据生成脚本分开,我们可以将它们分别放在单独的文件中并按如下方式添加它们:
databasePopulator.addScripts(new ClassPathResource("schema.sql"),
new ClassPathResource("seed-data.sql") );
通过这种配置,我们可以将JdbcTemplate注入到数据访问DAO组件与数据库进行交互了。
比如,我们创建一个示例Bean和服务类,如下代码:
public class Man {
private Integer id;
private String name;
private String email;
//省略构造函数、get、set
}
@Repository
public class ManRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional(readOnly=true)
public List<Man> findAll() {
return jdbcTemplate.query("select * from mans", new ManRowMapper());
}
}
public class ManRowMapper implements RowMapper<Man> {
@Override
public Man mapRow(ResultSet rs, int rowNum) throws SQLException {
Man man = new Man();
man.setId(rs.getInt("id"));
man.setName(rs.getString("name"));
man.setEmail(rs.getString("email"));
return man;
}
}
我们会发现,大多数情况下,很多连接数据库的应用程序中都有使用上面这种类似的配置。
实战2:在Spring Boot中使用JdbcTemplate
本节,我们将尝试在Spring Boot中使用JdbcTemplate,而使用Spring Boot的自动配置功能,则不必手动配置Bean。
第一步,创建一个基于Maven的Spring Boot项目并添加spring-boot-starter-jdbc模块,如下POM:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
通过添加spring-boot-starter-jdbc模块,我们便可以获得以下自动配置功能:
- 可以传递tomcat-jdbc- {version}.jar,用于配置DataSource bean;
- 如果尚未明确定义DataSource bean,并且在类路径中有任何嵌入式数据库驱动程序(例如H2,HSQL或Derby),则Spring Boot将使用内存数据库设置自动注册的DataSource bean。
- 如果尚未注册以下任何bean,则Spring Boot也将自动注册:
- PlatformTransactionManager(DataSourceTransactionManager)
- JdbcTemplate
- NamedParameterJdbcTemplate
- 可以在根类路径中配置schema.sql和data.sql文件,Spring Boot将自动使用它来初始化数据库。
第二步,初始化数据库。
在Spring Boot中,可以使用spring.datasource.initialize=
属性值(默认情况下为true)来确定是否初始化数据库。 如果spring.datasource.initialize属性设置为true,则Spring Boot将使用根类路径中的schema.sql和data.sql文件来自动初始化数据库。
除了schema.sql和data.sql之外,Spring Boot将在根类路径中可用时加载schema-$ {platform} .sql和data - $ {platform} .sql文件。 这里的值是spring.datasource.platform属性的值,可以是hsqldb,h2,oracle,mysql,postgresql等,比如:
spring.datasource.schema=create-db.sql
spring.datasource.data=seed-data.sql
如果我们想关闭数据库初始化操作,则可以设置spring.datasource.initialize = false
。 如果在执行脚本时出现错误,应用程序就将无法启动。 如果又想继续,可以设置spring.datasource.continueOnError = true
。
第三步,我们将H2数据库添加到pom.xml文件中,如下:
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<version>1.4.197<version>
</dependency>
并在src / main / resources中创建schema.sql,如下所示:
CREATE TABLE mans
(
id int(11) NOT NULL AUTO_INCREMENT,
name varchar(100) NOT NULL,
email varchar(100) DEFAULT NULL,
PRIMARY KEY (id)
);
创建data.sql,如下:
insert into mans(id, name, email) values(1,'mickjoust','mickjoust@test.com');
insert into mans(id, name, email) values(2,'mia','mia@test.com');
insert into mans(id, name, email) values(3,'max','max@test.com');
第四步,就可以将JdbcTemplate注入到UserRepository服务中,如下所示:
@Repository
public class ManRepository {
@Autowired
private JdbcTemplate jdbcTemplate;
@Transactional(readOnly=true)
public List<Man> findAll() {
return jdbcTemplate.query("select * from mans",
new ManRowMapper());
}
@Transactional(readOnly=true)
public Man findUserById(int id) {
return jdbcTemplate.queryForObject("select * mans users where id=?",
new Object[]{id}, new ManRowMapper());
}
public Man create(final Man man) {
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, man.getName());
ps.setString(2, man.getEmail());
return ps;
}
}, holder);
int newUserId = holder.getKey().intValue();
man.setId(newUserId);
return man;
}
}
这里,ManRowMapper使用前一小节里的类。
到这里,我们已经学会了如何快速开始使用嵌入式数据库。 但是如果我们是想使用一些正式的数据库,比如如MySQL,Oracle和PostgreSQL呢?
很简单!在application.properties文件中配置数据库属性即可,Spring Boot同样会将使用这些JDBC参数来配置给DataSource bean,比如添加mysql访问,如下:
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/test
spring.datasource.username=root
spring.datasource.password=hjf123456
如果我们还想拥有更多的控制权,并自己配置DataSource bean,则可以在配置类中进行配置。 因为只要手动注册了DataSource bean,Spring Boot将不会使用自动去配置它。
在默认情况下,Spring Boot会引入tomcat-jdbc- {version} .ja
r中的org.apache.tomcat.jdbc.pool.DataSource
来配置DataSource bean。除此之外,Spring Boot一下检查以下连接池的可用性,并使用第一个可用的类的类路径。
- org.apache.tomcat.jdbc.pool.DataSource
- com.zaxxer.hikari.HikariDataSource
- org.apache.commons.dbcp.BasicDataSource
- org.apache.commons.dbcp2.BasicDataSource
比如,使用HikariDataSource,首先,需要排除Tomcat的JDBC,并添加HikariCP依赖,如下:
<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>
然后,按如下方式设置HikariCP连接池设置:
spring.datasource.hikari.allow-pool-suspension=true
spring.datasource.hikari.connection-test-query=SELECT 1
spring.datasource.hikari.transaction-isolation=TRANSACTION_READ_COMMITTED
spring.datasource.hikari.connection-timeout=45000
Spring Boot中常见的配置方式前缀如下:
spring.datasource.tomcat.*= # Tomcat Datasource specific settings
spring.datasource.hikari.*= # Hikari DataSource specific settings
spring.datasource.dbcp2.*= # Commons DBCP2 specific setting
代码示例:boot-database/jdbc
小结
在本章中,您学习了如何在Spring Boot中轻松使用JdbcTemplate,以及如何连接到H2和MySQL等数据库。 本章还介绍了如何使用SQL脚本初始化数据库。 您还了解了如何使用各种连接池库。
参考资源
1、Spring jdbc
2、Spring Boot
持续践行,我们一路同行。