动力节点王鹤老师springboot3视频第四章
4 访问数据库
Spring Boot框架为SQL数据库提供了广泛的支持,既有用JdbcTemplate直接访问JDBC,同时支持“object relational mapping”技术(如Hibernate,MyBatis)。Spring Data独立的项目提供对多种关系型和非关系型数据库的访问支持。比如 MySQL, Oracle , MongoDB , Redis, R2DBC,Apache Solr,Elasticsearch...
Spring Boot也支持嵌入式数据库比如H2, HSQL, and Derby。这些数据库只需要提供jar包就能在内存中维护数据。我们这章访问关系型数据库。
4.1 DataSource
通常项目中使用MySQL,Oracle,PostgreSQL等大型关系数据库。Java中的jdbc技术支持了多种关系型数据库的访问。在代码中访问数据库,我们需要知道数据库程序所在的ip,端口,访问数据库的用户名和密码以及数据库的类型信息。以上信息用来初始化数据源,数据源也就是DataSource。数据源表示数据的来源,从某个ip上的数据库能够获取数据。javax.sql.DataSource接口表示数据源,提供了标准的方法获取与数据库绑定的连接对象(Connection)。
javax.sql.Connection是连接对象,在Connection上能够从程序代码发送查询命令,更新数据的语句给数据库;同时从Connection获取命令的执行结果。Connection很重要,像一个电话线把应用程序和数据库连接起来。
DataSource在application配置文件中以spring.datasource.*作为配置项。类似下面的代码:
spring.datasource.url=jdbc:mysql://localhost/mydb
spring.datasource.username=dbuser
spring.datasource.password=dbpass
DataSourceProperties.java是数据源的配置类,更多配置参考这个类的属性。
Spring Boot能够从spring.datasource.url推断所使用的数据驱动类,如果需要特殊指定请设置spring.datasource.driver-class-name为驱动类的全限定名称。
Spring Boot支持多种数据库连接池,优先使用 HikariCP,其次是Tomcat pooling,再次是 Commons DBCP2,如果以上都没有,最后会使用Oracle UCP连接池。当项目中starter依赖了spring-boot-starter-jdbc 或者spring-boot-starter-data-jpa默认添加HikariCP连接池依赖,也就是默认使用HikariCP连接池。
4.2 轻量的JdbcTemplate
使用JdbcTemplate我们提供自定义SQL, Spring执行这些SQL得到记录结果集。JdbcTemplate和NamedParameterJdbcTemplate类是自动配置的,您可以@Autowire注入到自己的Bean中。开箱即用。
JdbcTemplate执行完整的SQL语句,我们将SQL语句拼接好,交给JdbcTemplate执行,JdbcTemplate底层就是使用JDBC执行SQL语句。是JDBC的封装类而已。
NamedParameterJdbcTemplate可以在SQL语句部分使用“:命名参数”作为占位符, 对参数命名,可读性更好。NamedParameterJdbcTemplate包装了JdbcTemplate对象,“:命名参数”解析后,交给JdbcTemplate执行SQL语句。
JdbcTemplateAutoConfiguration自动配置了JdbcTemplate对象,交给JdbcTemplateConfiguration创建了JdbcTemplate对象。并对JdbcTemplate做了简单的初始设置(QueryTimeout,maxRows等)。
4.2.1 准备环境
访问数据库先准备数据库的script。SpringBoot能够自动执行DDL,DML脚本。两个脚本文件名称默认是schema.sql和data.sql。脚本文件在类路径中自动加载。
自动执行脚本还涉及到spring.sql.init.mode配置项:
[if !supportLists]· [endif]always:总是执行数据库初始化脚本
[if !supportLists]· [endif]never:禁用数据库初始化
更进一步
4.2.1.1 准备数据库和表脚本
首先创建数据库,安装MySQL8.5。有可用的MySQL数据库就可以,最好是5以上版本。
数据库名称Blog , 表目前使用一个 article(文章表),初始两条数据。
schema.sql
`id` int(11)NOT NULL AUTO_INCREMENT COMMENT '主键',
`user_id` int(11)NOT NULL COMMENT '作者ID',
`title` varchar(100)NOT NULL COMMENT '文章标题',
`summary` varchar(200)DEFAULT NULL COMMENT '文章概要',
`read_count` int(11)unsigned zerofill NOT NULL COMMENT '阅读读数',
`create_time`datetime NOT NULL COMMENT '创建时间',
`update_time`datetime NOT NULL COMMENT '最后修改时间',
PRIMARY KEY (`id`))ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
data.sql
INSERT INTO `article`VALUES ('1','2101','SpringBoot核心注解',
'核心注解的主要作用','00000008976','2023-01-16 12:11:12','2023-01-16 12:11:19');
INSERT INTO `article`VALUES ('2','356752','JVM调优',
'HotSpot虚拟机详解','00000000026','2023-01-16 12:15:27','2023-01-16 12:15:30');
4.2.1.2 创建Spring Boot工程
新建Spring Boot工程Lession09-JdbcTemplate
构建工具:Maven
包名:com.bjpowernode.jdbc
JDK:19
Starter依赖:Lombok,MySQL Driver, JDBC API
Maven依赖(pom.xml)
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
<artifactId>mysql-connector-j</artifactId>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
IDEA Maven Tool查看依赖列表
依赖包含了连接池com.zaxxer:HikariCP:5.0.1 , spring-jdbc 6.0.3 , mysql驱动mysql-connector-j 8.0.31。
4.2.2 JdbcTemplate访问MySQL
项目中依赖了spring-jdbc 6.0.3,JdbcTemplate对象会自动创建好。把JdbcTemplate对象注入给你的Bean,再调用JdbcTemplate的方法执行查询,更新,删除的SQL。
JdbcTemplate上手快,功能非常强大。提供了丰富、实用的方法,归纳起来主要有以下几种类型的方法:
(1)execute方法:可以用于执行任何SQL语句,常用来执行DDL语句。
(2)update、batchUpdate方法:用于执行新增、修改与删除等语句。
(3)query和queryForXXX方法:用于执行查询相关的语句。
(4)call方法:用于执行数据库存储过程和函数相关的语句。
我们在2.2.1.2已经创建了Spring Boot工程,在工程上继续添加代码,完成对Blog库,article表的CRUD。
step1:将schema.sql , data.sql拷贝到resources目录
step2:修改application.properties
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
step3: 创建实体类 ArticlePO
Lomok注解给类的属性生成set,get方法。 默认和所有参数构造方法
step4: 单元测试,注入JdbcTemplate对象
public class TestJdbcTemplate {
private JdbcTemplate jdbcTemplate;
测试聚合函数
String sql="select count(*) as ct from article";
Long count = jdbcTemplate.queryForObject(sql, Long.class);
System.out.println("文章总数 = " + count);
测试“?”占位符
String sql = "select * from article where id= ? ";
//BeanPropertyRowMapper 将查询结果集,列名与属性名称匹配, 名称完全匹配或驼峰
ArticlePO article = jdbcTemplate.queryForObject(sql,
new BeanPropertyRowMapper<>(ArticlePO.class), 1 );
System.out.println("查询到的文章 = " + article);
测试自定义RowMapper
测试List集合
String sql="select * from article order by id ";
List> listMap = jdbcTemplate.queryForList(sql);
System.out.println("字段名称:"+field+",列值:"+value);
System.out.println("===================================");
测试更新记录
String sql="update article set title = ? where id= ? ";
int updated = jdbcTemplate.update(sql, "Java核心技术思想", 2);
System.out.println("更新记录:"+updated);
4.2.3 NamedParameterJdbcTemplate
NamedParameterJdbcTemplate能够接受命名的参数,通过具名的参数提供代码的可读性,JdbcTemplate使用的是参数索引的方式。
在使用模板的位置注入NamedParameterJdbcTemplate对象,编写SQL语句,在SQL中WHERE部分“:命名参数”。调用NamedParameterJdbcTemplate的诸如query,queryForObject, execute,update等时,将参数封装到Map中。
step1:注入模板对象
private JdbcTemplate jdbcTemplate;
step2: 使用命名参数
String sql="select count(*) as ct from article where user_id=:uid and read_count > :num";
Long count = nameJdbcTemplate.queryForObject(sql, param, Long.class);
System.out.println("用户被阅读的文章数量 = " + count);
4.2.4 多表查询
多表查询关注是查询结果如何映射为Java Object。常用两种方案:一种是将查询结果转为Map。列名是key,列值是value,这种方式比较通用,适合查询任何表。第二种是根据查询结果中包含的列,创建相对的实体类。属性和查询结果的列对应。将查询结果自定义RowMapper、ResultSetExtractor映射为实体类对象。
现在创建新的表article_detail,存储文章内容,与article表是一对一关系。
article_detail表
CREATE TABLE `article_detail` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '注解',
`article_id` int(11) NOT NULL COMMENT '文章ID',
`content` text NOT NULL COMMENT '文章内容',
ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8mb4;
需求:查询某个文章的全部属性,包括文章内容
step1:创建新的实体类ArticleMainPO, 将ArticlePO作为成员变量
private LocalDateTime createTime;
private LocalDateTime updateTime;
private ArticleDetailPO articleDetail;
step2: 查询一对一文章
select m.*,d.id as detail_id, d.article_id,d.content
from article m join article_detail d
List list = nameJdbcTemplate.query(sql, param, (rs, rowNum) -> {
var userId = rs.getInt("user_id");
var title = rs.getString("title");
var summary = rs.getString("summary");
var readCount = rs.getInt("read_count");
var createTime = new Timestamp(rs.getTimestamp("create_time").getTime())
var updateTime = new Timestamp(rs.getTimestamp("update_time").getTime())
var detailId = rs.getInt("detail_id");
var content = rs.getString("content");
var articleId = rs.getInt("article_id");
ArticleDetailPO detail = new ArticleDetailPO();
detail.setArticleId(articleId);
return new ArticleMainPO(id, userId, title, summary, readCount,
createTime, updateTime, detail);
System.out.println("m.getId() = " + m.getId());
System.out.println("m.getArticleDetail() = " + m.getArticleDetail());
总结:
JdbcTemplate的优点简单,灵活,上手快,访问多种数据库。对数据的处理控制能力比较强,RowMapper, ResultSetExtractor能够提供按需要灵活定制记录集与实体类的关系。
缺点:对SQL要求高,适合对SQL比较了解,自定义查询结果比较多,调优需求的。
JdbcTemplate对象的调整参数,比较少。可设置spring.jdbc.template.开头的配置项目,比如设置超时为10秒,spring.jdbc.template.query-timeout=10。
4.3 MyBatis
数据库访问MyBatis,MyBatis-Plus国内很常用,掌握了MyBatis,MyBatis-Plus就会了大部分了。MyBatis-Plus附加的功能需要单独学习。我们以MyBatis来自介绍Spring Boot集成ORM框架。
MyBatis使用最多的是mapper xml文件编写SQL语句。本章使用MyBatis的注解,JDK新特性文本块,以及Record完成java对象和表数据的处理。
4.3.1 单表CRUD
首先向blog数据库的article表添加新的文章,以及修改,查询文章。在新工程Lession10-MyBatis集成MyBatis框架。项目包名com.bjpowernode.orm。依赖需要mysql驱动、mybatis依赖,Lombok。
step1: Maven依赖
step2:创建实体类
step3: 创建Mapper接口
@Results部分为结果映射(XML中的<ResultMap>), 或者用MyBatis的驼峰命名也能实现默认的映射关系。
application.properties
#驼峰,下划线命名mybatis.configuration.map-underscore-to-camel-case=true
step4: 启动类加入扫描注解
@MapperScan是扫描注解,参数是Mapper接口所在的包名。参数是数组,可以指定多个包位置。
step5: 配置数据源
application.properties或yml都可以
step6:单元测试
@SpringBootTestclass Lession10MyBatisApplicationTests {
@Autowired private ArticleMapper articleMapper;
4.3.2 ResultMap
查询操作得到包含多个列的集合,将列值转为对象属性使用结果映射的功能,注解@Results,@ResultMap能够帮助我们完成此功能。
@Results用于定义结果映射,每个列和Java对象属性的一一对应。
@ResultMap 指定使用哪个结果映射,两种方式可以使用@Results,另一种XML文件。
需求:执行多个select语句,使用结果映射转换数据库记录为Java Object。
step1:创建新的Mapper对象。
@Results的id定义当前结果映射的唯一名称, 后面内容是列和属性的一一映射说明。
其他的查询方法@ResultMap引用@Results的id。使用BaseMapper的映射规则处理查询结果。
step2: 单元测试
另一种方法在xml中定义标签,在@ResultMap注解引用。 这种方式首先创建xml。在resources目录下创建自定义的mapper目录。 新建ArticleMapper.xml 。
ArticleMapper.xml 代码清单: