動(dòng)力節(jié)點(diǎn)王鶴老師springboot3視頻第四章
4 訪問(wèn)數(shù)據(jù)庫(kù)
Spring Boot框架為SQL數(shù)據(jù)庫(kù)提供了廣泛的支持秫逝,既有用JdbcTemplate直接訪問(wèn)JDBC寸爆,同時(shí)支持“object relational mapping”技術(shù)(如Hibernate源哩,MyBatis)知态。Spring Data獨(dú)立的項(xiàng)目提供對(duì)多種關(guān)系型和非關(guān)系型數(shù)據(jù)庫(kù)的訪問(wèn)支持鸳君。比如 MySQL, Oracle , MongoDB , Redis, R2DBC倒得,Apache Solr泻红,Elasticsearch...
Spring Boot也支持嵌入式數(shù)據(jù)庫(kù)比如H2, HSQL, and Derby。這些數(shù)據(jù)庫(kù)只需要提供jar包就能在內(nèi)存中維護(hù)數(shù)據(jù)霞掺。我們這章訪問(wèn)關(guān)系型數(shù)據(jù)庫(kù)谊路。
4.1 DataSource
通常項(xiàng)目中使用MySQL,Oracle,PostgreSQL等大型關(guān)系數(shù)據(jù)庫(kù)。Java中的jdbc技術(shù)支持了多種關(guān)系型數(shù)據(jù)庫(kù)的訪問(wèn)根悼。在代碼中訪問(wèn)數(shù)據(jù)庫(kù)凶异,我們需要知道數(shù)據(jù)庫(kù)程序所在的ip,端口挤巡,訪問(wèn)數(shù)據(jù)庫(kù)的用戶名和密碼以及數(shù)據(jù)庫(kù)的類型信息。以上信息用來(lái)初始化數(shù)據(jù)源酷麦,數(shù)據(jù)源也就是DataSource矿卑。數(shù)據(jù)源表示數(shù)據(jù)的來(lái)源,從某個(gè)ip上的數(shù)據(jù)庫(kù)能夠獲取數(shù)據(jù)沃饶。javax.sql.DataSource接口表示數(shù)據(jù)源母廷,提供了標(biāo)準(zhǔn)的方法獲取與數(shù)據(jù)庫(kù)綁定的連接對(duì)象(Connection)。
javax.sql.Connection是連接對(duì)象糊肤,在Connection上能夠從程序代碼發(fā)送查詢命令琴昆,更新數(shù)據(jù)的語(yǔ)句給數(shù)據(jù)庫(kù);同時(shí)從Connection獲取命令的執(zhí)行結(jié)果馆揉。Connection很重要业舍,像一個(gè)電話線把應(yīng)用程序和數(shù)據(jù)庫(kù)連接起來(lái)。
DataSource在application配置文件中以spring.datasource.*作為配置項(xiàng)升酣。類似下面的代碼:
spring.datasource.url=jdbc:mysql://localhost/mydb
spring.datasource.username=dbuser
spring.datasource.password=dbpass
DataSourceProperties.java是數(shù)據(jù)源的配置類舷暮,更多配置參考這個(gè)類的屬性。
Spring Boot能夠從spring.datasource.url推斷所使用的數(shù)據(jù)驅(qū)動(dòng)類噩茄,如果需要特殊指定請(qǐng)?jiān)O(shè)置spring.datasource.driver-class-name為驅(qū)動(dòng)類的全限定名稱下面。
Spring Boot支持多種數(shù)據(jù)庫(kù)連接池,優(yōu)先使用 HikariCP绩聘,其次是Tomcat pooling沥割,再次是 Commons DBCP2耗啦,如果以上都沒(méi)有,最后會(huì)使用Oracle UCP連接池机杜。當(dāng)項(xiàng)目中starter依賴了spring-boot-starter-jdbc 或者spring-boot-starter-data-jpa默認(rèn)添加HikariCP連接池依賴芹彬,也就是默認(rèn)使用HikariCP連接池。
4.2 輕量的JdbcTemplate
使用JdbcTemplate我們提供自定義SQL, Spring執(zhí)行這些SQL得到記錄結(jié)果集叉庐。JdbcTemplate和NamedParameterJdbcTemplate類是自動(dòng)配置的舒帮,您可以@Autowire注入到自己的Bean中。開箱即用陡叠。
JdbcTemplate執(zhí)行完整的SQL語(yǔ)句玩郊,我們將SQL語(yǔ)句拼接好,交給JdbcTemplate執(zhí)行枉阵,JdbcTemplate底層就是使用JDBC執(zhí)行SQL語(yǔ)句译红。是JDBC的封裝類而已。
NamedParameterJdbcTemplate可以在SQL語(yǔ)句部分使用“:命名參數(shù)”作為占位符, 對(duì)參數(shù)命名兴溜,可讀性更好侦厚。NamedParameterJdbcTemplate包裝了JdbcTemplate對(duì)象,“:命名參數(shù)”解析后拙徽,交給JdbcTemplate執(zhí)行SQL語(yǔ)句刨沦。
JdbcTemplateAutoConfiguration自動(dòng)配置了JdbcTemplate對(duì)象,交給JdbcTemplateConfiguration創(chuàng)建了JdbcTemplate對(duì)象膘怕。并對(duì)JdbcTemplate做了簡(jiǎn)單的初始設(shè)置(QueryTimeout想诅,maxRows等)。
4.2.1 準(zhǔn)備環(huán)境
訪問(wèn)數(shù)據(jù)庫(kù)先準(zhǔn)備數(shù)據(jù)庫(kù)的script岛心。SpringBoot能夠自動(dòng)執(zhí)行DDL来破,DML腳本。兩個(gè)腳本文件名稱默認(rèn)是schema.sql和data.sql忘古。腳本文件在類路徑中自動(dòng)加載徘禁。
自動(dòng)執(zhí)行腳本還涉及到spring.sql.init.mode配置項(xiàng):
[if !supportLists]·?[endif]always:總是執(zhí)行數(shù)據(jù)庫(kù)初始化腳本
[if !supportLists]·?[endif]never:禁用數(shù)據(jù)庫(kù)初始化
更進(jìn)一步
4.2.1.1 準(zhǔn)備數(shù)據(jù)庫(kù)和表腳本
首先創(chuàng)建數(shù)據(jù)庫(kù),安裝MySQL8.5祭阀。有可用的MySQL數(shù)據(jù)庫(kù)就可以鹉戚,最好是5以上版本鲜戒。
數(shù)據(jù)庫(kù)名稱Blog , 表目前使用一個(gè) article(文章表)抹凳,初始兩條數(shù)據(jù)遏餐。
schema.sql
`id`?int(11)NOT?NULL?AUTO_INCREMENT?COMMENT?'主鍵',
`user_id`?int(11)NOT?NULL?COMMENT?'作者ID',
`title`?varchar(100)NOT?NULL?COMMENT?'文章標(biāo)題',
`summary`?varchar(200)DEFAULT?NULL?COMMENT?'文章概要',
`read_count`?int(11)unsigned?zerofill?NOT?NULL?COMMENT?'閱讀讀數(shù)',
`create_time`datetime?NOT?NULL?COMMENT?'創(chuàng)建時(shí)間',
`update_time`datetime?NOT?NULL?COMMENT?'最后修改時(shí)間',
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調(diào)優(yōu)',
'HotSpot虛擬機(jī)詳解','00000000026','2023-01-16?12:15:27','2023-01-16?12:15:30');
4.2.1.2 創(chuàng)建Spring Boot工程
新建Spring Boot工程Lession09-JdbcTemplate
構(gòu)建工具: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驅(qū)動(dòng)mysql-connector-j 8.0.31赢底。
4.2.2 JdbcTemplate訪問(wèn)MySQL
項(xiàng)目中依賴了spring-jdbc 6.0.3失都,JdbcTemplate對(duì)象會(huì)自動(dòng)創(chuàng)建好。把JdbcTemplate對(duì)象注入給你的Bean幸冻,再調(diào)用JdbcTemplate的方法執(zhí)行查詢粹庞,更新,刪除的SQL洽损。
JdbcTemplate上手快庞溜,功能非常強(qiáng)大。提供了豐富碑定、實(shí)用的方法流码,歸納起來(lái)主要有以下幾種類型的方法:
(1)execute方法:可以用于執(zhí)行任何SQL語(yǔ)句,常用來(lái)執(zhí)行DDL語(yǔ)句延刘。
(2)update漫试、batchUpdate方法:用于執(zhí)行新增、修改與刪除等語(yǔ)句访娶。
(3)query和queryForXXX方法:用于執(zhí)行查詢相關(guān)的語(yǔ)句商虐。
(4)call方法:用于執(zhí)行數(shù)據(jù)庫(kù)存儲(chǔ)過(guò)程和函數(shù)相關(guān)的語(yǔ)句。
我們?cè)?.2.1.2已經(jīng)創(chuàng)建了Spring Boot工程崖疤,在工程上繼續(xù)添加代碼,完成對(duì)Blog庫(kù)典勇,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
#總是執(zhí)行數(shù)據(jù)庫(kù)腳本,以后設(shè)置為never
step3: 創(chuàng)建實(shí)體類 ArticlePO
Lomok注解給類的屬性生成set割笙,get方法权烧。 默認(rèn)和所有參數(shù)構(gòu)造方法
step4: 單元測(cè)試,注入JdbcTemplate對(duì)象
public class TestJdbcTemplate {
?private JdbcTemplate jdbcTemplate;
測(cè)試聚合函數(shù)
?String sql="select count(*) as ct from article";
?Long count = jdbcTemplate.queryForObject(sql, Long.class);
System.out.println("文章總數(shù) = " + count);??
測(cè)試“?”占位符
?String sql = "select * from article where id= ? ";
?//BeanPropertyRowMapper 將查詢結(jié)果集般码,列名與屬性名稱匹配, 名稱完全匹配或駝峰
?ArticlePO article = jdbcTemplate.queryForObject(sql,
?new BeanPropertyRowMapper<>(ArticlePO.class), 1 );
System.out.println("查詢到的文章 = " + article);?
測(cè)試自定義RowMapper
測(cè)試List集合
?String sql="select * from article order by id ";
?List> listMap = jdbcTemplate.queryForList(sql);
?el.forEach( (field,value)->{????????
System.out.println("字段名稱:"+field+"乱顾,列值:"+value);??????
?System.out.println("===================================");????
測(cè)試更新記錄
?String sql="update article set title = ? where id= ? ";
?//參數(shù)是從左往右 第一個(gè)板祝,第二個(gè)...
int updated = jdbcTemplate.update(sql, "Java核心技術(shù)思想", 2);????
System.out.println("更新記錄:"+updated);??
4.2.3 NamedParameterJdbcTemplate
NamedParameterJdbcTemplate能夠接受命名的參數(shù),通過(guò)具名的參數(shù)提供代碼的可讀性走净,JdbcTemplate使用的是參數(shù)索引的方式券时。
在使用模板的位置注入NamedParameterJdbcTemplate對(duì)象孤里,編寫SQL語(yǔ)句,在SQL中WHERE部分“:命名參數(shù)”橘洞。調(diào)用NamedParameterJdbcTemplate的諸如query捌袜,queryForObject, execute,update等時(shí),將參數(shù)封裝到Map中炸枣。
step1:注入模板對(duì)象
private?JdbcTemplate jdbcTemplate;
step2: 使用命名參數(shù)
?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("用戶被閱讀的文章數(shù)量 = " + count);
4.2.4 多表查詢
多表查詢關(guān)注是查詢結(jié)果如何映射為Java Object虏等。常用兩種方案:一種是將查詢結(jié)果轉(zhuǎn)為Map。列名是key适肠,列值是value霍衫,這種方式比較通用,適合查詢?nèi)魏伪碛睾铩5诙N是根據(jù)查詢結(jié)果中包含的列慕淡,創(chuàng)建相對(duì)的實(shí)體類。屬性和查詢結(jié)果的列對(duì)應(yīng)沸毁。將查詢結(jié)果自定義RowMapper峰髓、ResultSetExtractor映射為實(shí)體類對(duì)象。
現(xiàn)在創(chuàng)建新的表article_detail息尺,存儲(chǔ)文章內(nèi)容携兵,與article表是一對(duì)一關(guān)系。
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?'文章內(nèi)容',
ENGINE=InnoDB?AUTO_INCREMENT=1?DEFAULT?CHARSET=utf8mb4;
需求:查詢某個(gè)文章的全部屬性搂誉,包括文章內(nèi)容
step1:創(chuàng)建新的實(shí)體類ArticleMainPO, 將ArticlePO作為成員變量
?private LocalDateTime createTime;
?private LocalDateTime updateTime;
?private ArticleDetailPO articleDetail;
step2: 查詢一對(duì)一文章
?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());
總結(jié):
JdbcTemplate的優(yōu)點(diǎn)簡(jiǎn)單徐紧,靈活,上手快炭懊,訪問(wèn)多種數(shù)據(jù)庫(kù)并级。對(duì)數(shù)據(jù)的處理控制能力比較強(qiáng),RowMapper, ResultSetExtractor能夠提供按需要靈活定制記錄集與實(shí)體類的關(guān)系侮腹。
缺點(diǎn):對(duì)SQL要求高嘲碧,適合對(duì)SQL比較了解,自定義查詢結(jié)果比較多父阻,調(diào)優(yōu)需求的愈涩。
JdbcTemplate對(duì)象的調(diào)整參數(shù),比較少加矛÷耐瘢可設(shè)置spring.jdbc.template.開頭的配置項(xiàng)目,比如設(shè)置超時(shí)為10秒斟览,spring.jdbc.template.query-timeout=10毁腿。
4.3 MyBatis
數(shù)據(jù)庫(kù)訪問(wèn)MyBatis,MyBatis-Plus國(guó)內(nèi)很常用,掌握了MyBatis狸棍,MyBatis-Plus就會(huì)了大部分了身害。MyBatis-Plus附加的功能需要單獨(dú)學(xué)習(xí)。我們以MyBatis來(lái)自介紹Spring Boot集成ORM框架草戈。
MyBatis使用最多的是mapper xml文件編寫SQL語(yǔ)句塌鸯。本章使用MyBatis的注解,JDK新特性文本塊唐片,以及Record完成java對(duì)象和表數(shù)據(jù)的處理丙猬。
4.3.1 單表CRUD
首先向blog數(shù)據(jù)庫(kù)的article表添加新的文章,以及修改费韭,查詢文章茧球。在新工程Lession10-MyBatis集成MyBatis框架。項(xiàng)目包名com.bjpowernode.orm星持。依賴需要mysql驅(qū)動(dòng)抢埋、mybatis依賴,Lombok督暂。
step1: Maven依賴
step2:創(chuàng)建實(shí)體類
step3: 創(chuàng)建Mapper接口
@Results部分為結(jié)果映射(XML中的<ResultMap>), 或者用MyBatis的駝峰命名也能實(shí)現(xiàn)默認(rèn)的映射關(guān)系揪垄。
application.properties
#駝峰,下劃線命名mybatis.configuration.map-underscore-to-camel-case=true
step4: 啟動(dòng)類加入掃描注解
@MapperScan是掃描注解逻翁,參數(shù)是Mapper接口所在的包名饥努。參數(shù)是數(shù)組,可以指定多個(gè)包位置八回。
step5: 配置數(shù)據(jù)源
application.properties或yml都可以
step6:單元測(cè)試
@SpringBootTestclass Lession10MyBatisApplicationTests {
?@Autowired private ArticleMapper articleMapper;
4.3.2 ResultMap
查詢操作得到包含多個(gè)列的集合,將列值轉(zhuǎn)為對(duì)象屬性使用結(jié)果映射的功能缠诅,注解@Results溶浴,@ResultMap能夠幫助我們完成此功能。
@Results用于定義結(jié)果映射管引,每個(gè)列和Java對(duì)象屬性的一一對(duì)應(yīng)戳葵。
@ResultMap 指定使用哪個(gè)結(jié)果映射,兩種方式可以使用@Results汉匙,另一種XML文件。
需求:執(zhí)行多個(gè)select語(yǔ)句生蚁,使用結(jié)果映射轉(zhuǎn)換數(shù)據(jù)庫(kù)記錄為Java Object噩翠。
step1:創(chuàng)建新的Mapper對(duì)象。
@Results的id定義當(dāng)前結(jié)果映射的唯一名稱邦投, 后面內(nèi)容是列和屬性的一一映射說(shuō)明伤锚。
其他的查詢方法@ResultMap引用@Results的id。使用BaseMapper的映射規(guī)則處理查詢結(jié)果志衣。
step2: 單元測(cè)試
另一種方法在xml中定義標(biāo)簽屯援,在@ResultMap注解引用猛们。 這種方式首先創(chuàng)建xml。在resources目錄下創(chuàng)建自定義的mapper目錄狞洋。 新建ArticleMapper.xml 弯淘。
ArticleMapper.xml 代碼清單: