文章目录
1. 概述
对于数据访问层,无论是SQL还是NOSQL,Spring Boot默认采用整合Spring Data的方式进行统一处理,添加大量自动配置,屏蔽了很多设置。引入各种xxxTemplate,xxxRepository来简化我们对数据访问层的操作。对我们来说只需要进行简单的设置即可。
2. JDBC
Spring 使用JDBC访问以及操作数据库需要进行如下步骤的操作:
-
导入JDBC依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency> -
配置数据库连接信息
spring: datasource: username: root password: 123456 url: jdbc:mysql://localhost:3306/sql_store?serverTimezone=GMT driver-class-name: com.mysql.cj.jdbc.Driver
和数据源相关的配置都可以在DataSourceProperties类中找到,所以通过spring.datasource.xxx可以配置相关的内容。
配置结束后,使用单元测试获取数据源以及连接:
@RunWith(SpringRunner.class)
@SpringBootTest
class DyliangApplicationTests {
@Autowired
DataSource dataSource;
@Test
void contextLoads() throws SQLException {
System.out.println(dataSource.getClass());
Connection connection = dataSource.getConnection();
System.out.println(connection);
connection.close();
}
}
此时控制台输出:
class com.zaxxer.hikari.HikariDataSource
HikariProxyConnection@2111669429 wrapping com.mysql.cj.jdbc.ConnectionImpl@73aeef7d
可见这里Spring Boot默认使用过的HikariDataSource,除此之外,Spring Boot还支持org.apache.tomcat.jdbc.pool.DataSource和BasicDataSource。
从DataSourceConfiguration中找到Spring Boot支持的数据源。
abstract class DataSourceConfiguration {
@SuppressWarnings("unchecked")
protected static <T> T createDataSource(DataSourceProperties properties, Class<? extends DataSource> type) {
return (T) properties.initializeDataSourceBuilder().type(type).build();
}
/**
* Tomcat Pool DataSource configuration.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(org.apache.tomcat.jdbc.pool.DataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.tomcat.jdbc.pool.DataSource",
matchIfMissing = true)
static class Tomcat {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.tomcat")
org.apache.tomcat.jdbc.pool.DataSource dataSource(DataSourceProperties properties) {
org.apache.tomcat.jdbc.pool.DataSource dataSource = createDataSource(properties,
org.apache.tomcat.jdbc.pool.DataSource.class);
DatabaseDriver databaseDriver = DatabaseDriver.fromJdbcUrl(properties.determineUrl());
String validationQuery = databaseDriver.getValidationQuery();
if (validationQuery != null) {
dataSource.setTestOnBorrow(true);
dataSource.setValidationQuery(validationQuery);
}
return dataSource;
}
}
/**
* Hikari DataSource configuration.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(HikariDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "com.zaxxer.hikari.HikariDataSource",
matchIfMissing = true)
static class Hikari {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.hikari")
HikariDataSource dataSource(DataSourceProperties properties) {
HikariDataSource dataSource = createDataSource(properties, HikariDataSource.class);
if (StringUtils.hasText(properties.getName())) {
dataSource.setPoolName(properties.getName());
}
return dataSource;
}
}
/**
* DBCP DataSource configuration.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass(org.apache.commons.dbcp2.BasicDataSource.class)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type", havingValue = "org.apache.commons.dbcp2.BasicDataSource",
matchIfMissing = true)
static class Dbcp2 {
@Bean
@ConfigurationProperties(prefix = "spring.datasource.dbcp2")
org.apache.commons.dbcp2.BasicDataSource dataSource(DataSourceProperties properties) {
return createDataSource(properties, org.apache.commons.dbcp2.BasicDataSource.class);
}
}
/**
* Generic DataSource configuration.
*/
@Configuration(proxyBeanMethods = false)
@ConditionalOnMissingBean(DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.type")
static class Generic {
@Bean
DataSource dataSource(DataSourceProperties properties) {
return properties.initializeDataSourceBuilder().build();
}
}
}
另外,在和JDBC自动配置相关的包下还可以看到DataSourceInitializer,其中:
runSchemaScripts():运行建表语句runDataScripts():运行插入数据的sql语句
默认只需要将文件命名为:
schema‐*.sql、data‐*.sql
默认规则使用schema.sql,schema‐all.sql。可以使用
schema:
‐ classpath:department.sql
可以使用
指定位置。
使用JDBC数据源还可以很方便的使用JdbcTempalte来进行数据库的相关操作,它和Spring中使用JdbcTempalte是一样的:
@Controller
public class HelloController {
@Autowired
JdbcTemplate jdbcTemplate;
@ResponseBody
@RequestMapping("/query")
public Map<String, Object> map(){
List<Map<String, Object>> maps = jdbcTemplate.queryForList("select * from account");
return maps.get(0);
}
}
3. 整合Druid数据源
虽然使用JDBC可以完成数据库的操作,但在实际的场景中,通常还需要考虑性能和并发性等其他的因素。因此,通常需要使用其他的数据源,例如阿里的Druid数据源。
使用Druid数据源首先需在pom.xml中引入相关的依赖:
<!--引入druid数据源-->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.8</version>
</dependency>
同时还需要引入log4j,不然运行项目时会报错。
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
同样的还可以配置数据源相关的内容,如下所示:
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/sql_store?serverTimezone=GMT
driver-class-name: com.mysql.cj.jdbc.Driver
# type用于指定数据源的类型
type: com.alibaba.druid.pool.DruidDataSource
# druid数据源常用的配置
initialSize: 5
minIdle: 5
maxActive: 20
maxWait: 60000
timeBetweenEvictionRunsMillis: 60000
minEvictableIdleTimeMillis: 300000
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
testOnBorrow: false
testOnReturn: false
poolPreparedStatements: true
# 配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
filters: stat,wall,log4j
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
但此时的配置并不会生效,因此,我们还需要写一个相关的配置类,使得数据源的获取通过自定义的配置类获取,而不是让系统通过反射得到。
@Configuration
public class DruidConfig {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druid() {
return new DruidDataSource();
}
}
此时运行单元测试可见数据源已发生改变:
class com.alibaba.druid.pool.DruidDataSource
com.mysql.cj.jdbc.ConnectionImpl@423c5404
通常使用Druid数据源环旭配置Druid的监控:
@Configuration
public class DruidConfig {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druid() {
return new DruidDataSource();
}
//配置Druid的监控
//1、配置一个管理后台的Servlet
@Bean
public ServletRegistrationBean statViewServlet() {
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
Map<String, String> initParams = new HashMap<>();
initParams.put("loginUsername", "admin");
initParams.put("loginPassword", "123456");
initParams.put("allow", "");//默认就是允许所有访问
bean.setInitParameters(initParams);
return bean;
}
//2、配置一个web监控的filter
@Bean
public FilterRegistrationBean webStatFilter() {
FilterRegistrationBean bean = new FilterRegistrationBean();
bean.setFilter(new WebStatFilter());
Map<String, String> initParams = new HashMap<>();
initParams.put("exclusions", "*.js,*.css,/druid/*");
bean.setInitParameters(initParams);
bean.setUrlPatterns(Arrays.asList("/*"));
return bean;
}
}
配置结束后,通过localhost:8080/druid就可以来到监控程序界面:
4. 整合Mybatis
Spring Boot整合Mybatis进行数据持久化操作需要执行以下步骤:
-
导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-jdbc</artifactId> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <scope>runtime</scope> </dependency>mybatis-spring-boot-starter在导入时会自动导入Mybatis相关的依赖:
<dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> </dependency> -
配置Druid数据源(可选,推荐):配置步骤如上面Druid数据源的配置
4.1 注解版
此时数据库中account表如下所示:
mysql> select * from account;
+----+----------+-------+
| id | name | money |
+----+----------+-------+
| 1 | Forlogen | 1000 |
| 2 | Kobe | 1000 |
| 3 | James | 1000 |
| 12 | dylaing | 23123 |
| 16 | Amy | 1000 |
+----+----------+-------+
5 rows in set (0.00 sec)
使用注解的方式通过Mybatis进行数据持久化操作只需要执行以下几步:
-
创建表对应的实体类
@NoArgsConstructor @AllArgsConstructor @Builder public class Account { @Getter @Setter private Integer id; @Getter @Setter private String name; @Getter @Setter private Float money; } -
编写持久层接口:@Mapper表示当前接口为一个mapper,使用@Select、@Insert、@Update执行SQL操作
@Mapper public interface IAccountDao { @Select("select * from account") public List<Account> findAll(); @Select("select * from account where id=#{id}") public Account findById(Integer id); @Insert("insert into account(name,money) values (#{name},#{money})") public int insertAccount(Account account); @Update("update account set name=#{name} where id=#{id}") public int updateAccount(Account account); } -
编写controller:类内配置IAccountDao对象,并使用@Autowired让Spring Boot自动注入
@RestController public class AccountController { @Autowired private IAccountDao accountDao; // http://localhost:8080/account @GetMapping("/account") public List<Account> testFindAll(){ return accountDao.findAll(); } // http://localhost:8080/account/1 public Account testFindById(@PathVariable("id") Integer id){ return accountDao.findById(id); } // http://localhost:8080/accountInsert?name=Amy&money=1000 @GetMapping("/accountInsert") public Account testFindById(Account account){ accountDao.insertAccount(account); return account; } } -
运行主程序,并在浏览器输入上面的URL便可得到表中的信息
@SpringBootApplication public class DyliangApplication { public static void main(String[] args) { SpringApplication.run(DyliangApplication.class, args); } }
如果想要添加自定义的配置,还可以使用@org.springframework.context.annotation.Configuration标识配置类,添加相应的配置:
@org.springframework.context.annotation.Configuration
public class MybatisConfig {
public ConfigurationCustomizer configurationCustomizer(){
return new ConfigurationCustomizer() {
@Override
public void customize(org.apache.ibatis.session.Configuration configuration) {
configuration.setMapUnderscoreToCamelCase(true);
}
};
}
}
4.2 XML版
使用XML配置文件的方式执行数据持久化操作,首先需要编写Mybatis全局的配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!--使用驼峰命名法,也可以配置其他的项-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
表对应的实体类和注解版的一致,但对应的接口不再使用注解的方式执行SQL操作:
@Mapper
public interface IAccountDao {
public List<Account> findAll();
public Account findById(Integer id);
public int insertAccount(Account account);
}
而是编写接口对应的配置文件:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="dyliang.dao.IAccountDao">
<select id="findAll" resultType="dyliang.domain.Account">
select * from account;
</select>
<select id="findById" resultType="dyliang.domain.Account">
select * from account where id=#{id};
</select>
<insert id="insertAccount">
insert into account(name,money) values (#{name},#{money}
</insert>
</mapper>
编写的配置文件需要让Spring Boot知道在哪,这样在程序加载时才能使用。因此,还需要在Spring Boot的配置文件application.yml或是application.properties中配置Mybatis配置文件的位置:
mybatis:
# 指定全局配置文件位置
config-location: classpath:mybatis/SqlMapConfig.xml
# 指定sql映射文件位置
mapper-locations: classpath:mybatis/dao/*.xml
同样需要编写一个Controller,内容和上面的相同:
@RestController
public class AccountController {
@Autowired
private IAccountDao accountDao;
// http://localhost:8080/account
@GetMapping("/account")
public List<Account> testFindAll(){
return accountDao.findAll();
}
// http://localhost:8080/account/1
@GetMapping("account/{id}")
public Account testFindById(@PathVariable("id") Integer id){
return accountDao.findById(id);
}
// http://localhost:8080/accountInsert?name=Amy&money=1000
@GetMapping("/accountInsert")
public Account testFindById(Account account){
accountDao.insertAccount(account);
return account;
}
}
执行主程序就可以在浏览器中得到相关的结果。
5. 整合Spring Data Jpa
5.1 Spring Data
Spring Data 项目的目的是为了简化构建基于Spring 框架应用的数据访问技术,包括非关系数据库、Map-Reduce 框架、云数据服务等等;另外也包含对关系数据库的访问支持。
Spring Data 包含多个子项目:
- Spring Data Commons
- Spring Data JPA
- Spring Data KeyValue
- Spring Data LDAP
- Spring Data MongoDB
- Spring Data Gemfire
- Spring Data REST
- Spring Data Redis
- Spring Data for Apache Cassandra
- Spring Data for Apache Solr
- Spring Data Couchbase (community module)
- Spring Data Elasticsearch (community module)
- Spring Data Neo4j (community module)
Spring Data提供了统一的API来对数据访问层进行操作,只要基于Spring Data Commons项目。Spring Data Commons让我们在使用关系型或者非关系型数据访问技术时都基于Spring提供的统一标准,标准包含了CRUD(创建、获取、更新、删除)、查询、排序和分页的相关操作。
Spring Data提供了统一的Repository接口,我们不需要自己编写相关的Dao接口,只需要继承提供的Repository接口,就可以使用其中有关SQL的操作来操作数据库。相关的接口有:
Repository<T, ID extends Serializable>:统一接口RevisionRepository<T, ID extends Serializable, N extends Number & Comparable<N>>:基于乐观锁机制CrudRepository<T, ID extends Serializable>:基本CRUD操作PagingAndSortingRepository<T, ID extends Serializable>:基本CRUD及分页
同时它还提供了数据访问的模板类xxxTemplate,如MongoTemplate、RedisTemplate等。
5.2 案例
同样使用account表来看一下如何使用Jpa进行数据持久化操作,首先,依然是依然相关的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
然后编写实体类。并配置好映射关系:
@NoArgsConstructor
@AllArgsConstructor
@Entity
@Table(name = "account")
public class Account {
@Getter
@Setter
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Integer id;
@Getter
@Setter
@Column
private String name;
@Getter
@Setter
@Column
private Float money;
}
其中:
- @Entity:必选,表示该类对应数据库中的一张表
- @Table(name = “AUTH_USER”) :可选,声明了数据库实体对应的表信息。包括表名称、索引信息等。这里声明这个实体类对应的表名是 account。如果没有指定,则表名和实体的名称保持一致
- @Id:声明主键
- @Column:声明实体属性的表字段的定义,默认的实体每个属性都对应了表的一个字段,字段的名称默认和属性名称保持一致(并不一定相等),字段的类型根据实体属性类型自动推断
编写Dao接口操作数据表,该接口要继承Repository接口中的一个:
public interface AccountRepository extends JpaRepository<Account, Integer> {
}
其中JpaRepository的泛型的第一个元素为对应的实体类,第二个为主键的数据类型。
JpaRepository源码如下,我们可以使用它提供的方法来执行SQL操作:
@NoRepositoryBean
public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
List<T> findAll();
List<T> findAll(Sort var1);
List<T> findAllById(Iterable<ID> var1);
<S extends T> List<S> saveAll(Iterable<S> var1);
void flush();
<S extends T> S saveAndFlush(S var1);
void deleteInBatch(Iterable<T> var1);
void deleteAllInBatch();
T getOne(ID var1);
<S extends T> List<S> findAll(Example<S> var1);
<S extends T> List<S> findAll(Example<S> var1, Sort var2);
}
在Spring Boot的配置文件中添加Jpa相关的配置:
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/sql_store?serverTimezone=GMT
driver-class-name: com.mysql.cj.jdbc.Driver
jpa:
hibernate:
# 更新或者创建数据表结构
ddl-auto: update
# 控制台显示SQL
show-sql: true
最后编写controller,直接使用JpaRepository接口中的方法来执行SQL操作:
@RestController
public class AccountController {
@Autowired
AccountRepository accountRepository;
@GetMapping("/account/{id}")
public Optional<Account> findOne(@PathVariable("id") Integer id){
Optional<Account> one = accountRepository.findById(id);
return one;
}
@GetMapping("/account")
public List<Account> findAll(){
List<Account> all = accountRepository.findAll();
return all;
}
@GetMapping("/accountInsert")
public Account insertAccount(Account account){
Account save = accountRepository.save(account);
return save;
}
}
执行主程序在浏览器中获取相关的结果。