六:SpringBoot與數據訪問
1:JDBC的配置和使用
1.1:創建jdbc環境
pom.xml文件如下:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
1.2:jabc的配置文件配置--application.yml
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://10.40.129.44:3306/test?serverTimezone=UTC
#com.mysql.jdbc.Driver 是5版本的驅動
#com.mysql。cj.jdbc.Driver 是8版本的驅動
#?serverTimezone=UTC 解決時區的報錯
driver-class-name: com.mysql.jdbc.Driver
實驗:
最終我們使用的配置是
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://10.40.129.44:3306/test?serverTimezone=UTC
#com.mysql.jdbc.Driver 是5版本的驅動
#com.mysql。cj.jdbc.Driver 是8版本的驅動
#?serverTimezone=UTC 解決時區的報錯
driver-class-name: com.mysql.cj.jdbc.Driver
1.3:springboot鏈接數據庫的原理
數據源的相關配置都在DataSourceProperties裏面;ctrl+點擊配置文件中剛纔配置的屬性就可以進入;
1:參考DataSourceConfiguration ,根據配置創建數據源,springboot2.x默認使用hikari數據源;可以使用spring.datasource.type指定自定義的數據源類型;
2:springboot默認支持的數據源有
org.apache.commons.dbcp2.BasicDataSource,
com.zaxxer.hikari.HikariDataSource
org.apache.tomcat.jdbc.pool.DataSource
3:如果使用spring.datasource.type來自定義數據源
使用DataSourceBuilder創建數據源,利用反射創建響應type的數據源,並且綁定相關屬性
1.4:DataSourceInitializer:執行自定義建表sql語句
由他調用各種初始化方法
最終執行的是DataSourceInitializer的方法
1:所以要在系統開始的時候執行建表語句,必須在配置文件中配置
2:而建表語句的 文件名爲:默認爲:schema.sql,schema-all.sql;源代碼設置在下面:
3:也可以在配置文件中指定建表語句sql的位置,源代碼如下
設置如下:
1.5:DataSourceInitializer:執行初始化插入sql語句
和上邊建表的設置一樣
1:要不然設置插入sql語句的文件名默認爲:schema.sql,schema-all.sql;源代碼設置在下面:
2:要不然設置
1.6:springboot使用JdbcTemplate來操作數據庫
JdbcTemplate將我們使用的JDBC的流程封裝起來,包括了異常的捕捉、SQL的執行、查詢結果的轉換等等。spring大量使用Template Method模式來封裝固定流程的動作,XXXTemplate等類別都是基於這種方式的實現。
除了大量使用Template Method來封裝一些底層的操作細節,spring也大量使用callback方式類回調相關類別的方法以提供JDBC相關類別的功能,使傳統的JDBC的使用者也能清楚瞭解spring所提供的相關封裝類別方法的使用。
1:使用JdbcTemplate的execute()方法執行SQL語句
2:如果是UPDATE或INSERT,可以用update()方法。可以帶參數防止sql注入
3:使用JdbcTemplate進行查詢時,使用queryForXXX()等方法
例子:
//注入JdbcTemplate
@Autowired
JdbcTemplate jdbcTemplate;
@ResponseBody
@RequestMapping("/query")
public Map<String,Object> query(){
List<Map<String, Object>> maps = jdbcTemplate.queryForList("select * from department");
Map<String,Object> reslut = new HashMap<>();
reslut.put("date",maps);
return reslut;
}
結果是:
2、整合Druid數據源
Druid是一個關係型數據庫連接池,它是阿里巴巴的一個開源項目。Druid支持所有JDBC兼容數據庫,包括了Oracle、MySQL、PostgreSQL、SQL Server、H2等。Druid在監控、可擴展性、穩定性和性能方面具有明顯的優勢。通過Druid提供的監控功能,可以實時觀察數據庫連接池和SQL查詢的工作情況。使用Druid連接池在一定程度上可以提高數據訪問效率。
2.1:導入diuid依賴
<!-- 配置jdbc的依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jdbc</artifactId>
</dependency>
<!-- 配置mysql驅動的依賴 -->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
<!-- 導入druid數據源 -->
<!-- https://mvnrepository.com/artifact/com.alibaba/druid -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.22</version>
</dependency>
2.2:在配置文件中配置配置數據源
由於springboot默認不支持druid數據源,需要配置type
和其它連接池一樣DRUID的DataSource類爲:com.alibaba.druid.pool.DruidDataSource,基本配置參數如下:
配置 | 缺省值 | 說明 |
name | 配置這個屬性的意義在於,如果存在多個數據源,監控的時候可以通過名字來區分開來。 如果沒有配置,將會生成一個名字,格式是:"DataSource-" + System.identityHashCode(this) |
|
jdbcUrl | 連接數據庫的url,不同數據庫不一樣。例如: mysql : jdbc:mysql://10.20.153.104:3306/druid2 oracle : jdbc:oracle:thin:@10.20.149.85:1521:ocnauto |
|
username | 連接數據庫的用戶名 | |
password | 連接數據庫的密碼。如果你不希望密碼直接寫在配置文件中,可以使用ConfigFilter。詳細看這裏:https://github.com/alibaba/druid/wiki/%E4%BD%BF%E7%94%A8ConfigFilter | |
driverClassName | 根據url自動識別 | 這一項可配可不配,如果不配置druid會根據url自動識別dbType,然後選擇相應的driverClassName(建議配置下) |
initialSize | 0 | 初始化時建立物理連接的個數。初始化發生在顯示調用init方法,或者第一次getConnection時 |
maxActive | 8 | 最大連接池數量 |
maxIdle | 8 | 已經不再使用,配置了也沒效果 |
minIdle | 最小連接池數量 | |
maxWait | 獲取連接時最大等待時間,單位毫秒。配置了maxWait之後,缺省啓用公平鎖,併發效率會有所下降,如果需要可以通過配置useUnfairLock屬性爲true使用非公平鎖。 | |
poolPreparedStatements | false | 是否緩存preparedStatement,也就是PSCache。PSCache對支持遊標的數據庫性能提升巨大,比如說oracle。在mysql下建議關閉。 |
maxOpenPreparedStatements | -1 | 要啓用PSCache,必須配置大於0,當大於0時,poolPreparedStatements自動觸發修改爲true。在Druid中,不會存在Oracle下PSCache佔用內存過多的問題,可以把這個數值配置大一些,比如說100 |
validationQuery | 用來檢測連接是否有效的sql,要求是一個查詢語句。如果validationQuery爲null,testOnBorrow、testOnReturn、testWhileIdle都不會其作用。 | |
testOnBorrow | true | 申請連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能。 |
testOnReturn | false | 歸還連接時執行validationQuery檢測連接是否有效,做了這個配置會降低性能 |
testWhileIdle | false | 建議配置爲true,不影響性能,並且保證安全性。申請連接的時候檢測,如果空閒時間大於timeBetweenEvictionRunsMillis,執行validationQuery檢測連接是否有效。 |
timeBetweenEvictionRunsMillis | 有兩個含義: 1) Destroy線程會檢測連接的間隔時間2) testWhileIdle的判斷依據,詳細看testWhileIdle屬性的說明 |
|
numTestsPerEvictionRun | 不再使用,一個DruidDataSource只支持一個EvictionRun | |
minEvictableIdleTimeMillis | ||
connectionInitSqls | 物理連接初始化的時候執行的sql | |
exceptionSorter | 根據dbType自動識別 | 當數據庫拋出一些不可恢復的異常時,拋棄連接 |
filters | 屬性類型是字符串,通過別名的方式配置擴展插件,常用的插件有: 監控統計用的filter:stat日誌用的filter:log4j防禦sql注入的filter:wall |
|
proxyFilters |
類型是List<com.alibaba.druid.filter.Filter>,如果同時配置了filters和proxyFilters,是組合關係,並非替換關係 |
測試結果:
2.3:使用druid數據源-定製的數據源其他參數配置
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://127.0.0.1:3306/test?serverTimezone=Asia/Shanghai
#com.mysql.jdbc.Driver 是5版本的驅動
#com.mysql。cj.jdbc.Driver 是8版本的驅動
#?serverTimezone=UTC 解決時區的報錯
driver-class-name: com.mysql.cj.jdbc.Driver
# 設置此項,可以執行建表或者插入sql語句
# initialization-mode: always
# 指定建表sql的文件名,這是個集合,可以設置多個
# schema:
# - classpath:department.sql
# 插入數據sql文件位置,這也是一個集合
# data:
# - classpath:insert.sql
#配置druid數據源
type: com.alibaba.druid.pool.DruidDataSource
# 數據源其他配置
#初始化連接池大小
initialSize: 5
#連接池最小活躍數
minIdle: 5
#連接池最大活躍數
maxActive: 20
#最大連接等待時間
maxWait: 60000
#配置間隔多久才進行一次檢測,檢測需要關閉的空閒連接,單位是毫秒
timeBetweenEvictionRunsMillis: 60000
#配置一個連接在池中最小生存的時間,單位是毫秒
minEvictableIdleTimeMillis: 300000
#驗證連接有效與否的SQL,不同的數據配置不同
validationQuery: SELECT 1 FROM DUAL
testWhileIdle: true
#裏建議配置爲TRUE,防止取到的連接不可用
testOnBorrow: false
testOnReturn: false
#打開PSCache,並且指定每個連接上PSCache的大小
poolPreparedStatements: true
# 配置監控統計攔截的filters,去掉後監控界面sql無法統計,'wall'用於防火牆
filters: stat,wall,logback
#打開PSCache,並且指定每個連接上PSCache的大小
maxPoolPreparedStatementPerConnectionSize: 20
useGlobalDataSourceStat: true
connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
按照上述druid數據源的參數對數據源進行配置
在Spring Boot中驅動配置信息沒有問題,但是連接池的配置信息不再支持這裏的配置項,即無法通過配置項直接支持相應的連接池;這裏列出的這些配置項可以通過定製化DataSource來實現。
目前Spring Boot中默認支持的連接池有dbcp,dbcp2, tomcat, hikari三種連接池。
由於Druid暫時不在Spring Bootz中的直接支持,故需要進行配置信息的定製:
效果如圖:
2.4:使用Druid數據源-利用web.xml配置druid的web監控和攔截器
<servlet>
<servlet-name>DruidStatView</servlet-name>
<servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>DruidStatView</servlet-name>
<url-pattern>/druid/*</url-pattern>
</servlet-mapping>
<filter>
<filter-name>druidWebStatFilter</filter-name>
<filter-class>com.alibaba.druid.support.http.WebStatFilter</filter-class>
<init-param>
<param-name>exclusions</param-name>
<param-value>/public/*,*.js,*.css,/druid*,*.jsp,*.swf</param-value>
</init-param>
<init-param>
<param-name>principalSessionName</param-name>
<param-value>sessionInfo</param-value>
</init-param>
<init-param>
<param-name>profileEnable</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>druidWebStatFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
2.5:使用Druid數據源-利用ServletRegistrationBean配置druid的web監控和攔截器
在導入數據源的基礎上:
package com.wkl.springboot.config;
import com.alibaba.druid.pool.DruidDataSource;
import com.alibaba.druid.support.http.StatViewServlet;
import com.alibaba.druid.support.http.WebStatFilter;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import javax.sql.DataSource;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
/**
* Description:定製化配置druid數據源
* Date: 2020/6/3 - 下午 12:01
* author: wangkanglu
* version: V1.0
*/
@Configuration
public class DruidConfig {
// 這個註解表示從配置文件中讀取這些配置信息
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druid(){
return new DruidDataSource();
}
//配置Druid的監控
//1、配置一個管理後臺的Servlet
@Bean
public ServletRegistrationBean statViewServlet(){
//創建servlet註冊實體,StatViewServlet是druid的servlet
ServletRegistrationBean bean = new ServletRegistrationBean(new StatViewServlet(),"/druid/*");
Map<String,String> initParams = new HashMap<>();
//設置控制檯管理用戶
initParams.put("loginUsername","admin");
initParams.put("loginPassword","123456");
////設置ip白名單
initParams.put("allow","");//默認就是允許所有訪問
//設置ip黑名單,如果allow與deny共同存在時,deny優先於allow
initParams.put("deny","");
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;
}
}
效果:
訪問http://127.0.0.1:8088/druid即可登錄
同時也可以監控我們系統內接口等邏輯處理類,執行的sql可以監控各種信息
3:整合MyBatis
3.1:導入mybatis自己的依賴
<!-- 引入mybatis自己的依賴 -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.2</version>
</dependency>
圖解:
3.2:使用mybatis的步驟
1:配置數據源屬性(見上一章節)
2:數據庫創建表
3:創建javabean
3.3:使用註解版mybatis
指定這是操作數據庫的mapper
@Mapper
public interface DepartemetMapper {
@Select("select * from department where id = #{id}")
Department getDepartment(Integer id);
//Options標註告訴系統,改id字段爲自增的
@Options(useGeneratedKeys = true,keyProperty = "id")
@Insert("insert into department(departmentName) values(#{departmentName})")
int insertDept(Department department);
@Delete("delete from department where id = #{id}")
int deletDept(Integer id);
@Update("update department set departmentName = #{departmentName} where id = #{id}")
int updateDept(Department department);
}
創建接口訪問的Controller
@RestController
public class DepartmentController {
@Autowired
DepartemetMapper departemetMapper;
@GetMapping("/dept/{id}")
public Department getDept(@PathVariable("id") Integer id){
return departemetMapper.getDepartment(id);
}
@GetMapping("/dept")
public Department insertDept(Department department){
departemetMapper.insertDept(department);
return department;
}
}
開啓駝峯命名法
#開啓mybatis的駝峯命名法
mybatis:
configuration:
map-underscore-to-camel-case: true
使用MapperScan批量掃描所有的Mapper接口;這樣就可以不用每個mapper類的都寫@Mapper註解
@MapperScan(value = "com.atguigu.springboot.mapper")
@SpringBootApplication
public class SpringBoot06DataMybatisApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBoot06DataMybatisApplication.class, args);
}
}
3.4:使用配置版mybatis
指定數據庫訪問mapper
@Mapper
public interface EmpployeeMapper {
Empployee getEmp(int id);
void insertEmp(Empployee empployee);
}
編寫mapper的xml文件
<?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="com.wkl.springboot.mapper.EmpployeeMapper">
<!-- Empployee getEmp(int id);
void insertEmp(Empployee empployee);-->
<select id="getEmp" resultType="com.wkl.springboot.bean.Empployee">
select * from employee where id = #{id}
</select>
<insert id="insertEmp" >
insert into employee(lastName,email,gender,d_id) value (#{lastName},#{email},#{gender},#{dId})
</insert>
</mapper>
編寫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>
<!-- 解決關於mybatis的駝峯問題-->
<settings>
<setting name="mapUnderscoreToCamelCase" value="true"/>
</settings>
</configuration>
在配置文件中配置上述的配置文件位置
mybatis:
# configuration:
# map-underscore-to-camel-case: true
config-location: classpath:mybatis/mybatis_config.xml
mapper-locations: classpath:mybatis/mapper/*.xml
編寫Controller
@RestController
public class EmpController {
@Autowired
EmpployeeMapper empployeeMapper;
@GetMapping("/emp/{id}")
public Empployee getDept(@PathVariable("id") Integer id){
return empployeeMapper.getEmp(id);
}
}
4:整合SpringData jpa
4.1、SpringData簡介
4.2:springboot使用SpringData JPA的流程
JPA:ORM(Object Relational Mapping);
1:引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
2:編寫一個實體類(bean)和數據表進行映射,並且配置好映射關係;
package com.wkl.springboot;
import javax.persistence.*;
/**
* Description:
* Date: 2020/6/7 - 上午 1:34
* author: wangkanglu
* version: V1.0
*/
//使用JPA註解配置映射關係
@Entity //告訴JPA這是一個實體類(和數據表映射的類)
@Table(name="tab_user") //@Table來指定和哪個數據表對應;如果省略默認表名就是類名小寫user
public class entity {
@Id //這是一個主鍵
@GeneratedValue(strategy = GenerationType.IDENTITY) //自增主鍵
private Integer id;
@Column(name = "last_name",length = 50) //這是和數據表對應的一個列
private String lastName;
@Column //省略默認列名就是屬性名
private String email;
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getLastName() {
return lastName;
}
public void setLastName(String lastName) {
this.lastName = lastName;
}
public String getEmail() {
return email;
}
public void setEmail(String email) {
this.email = email;
}
}
3:編寫一個Dao接口來操作實體類對應的數據表(Repository)
package com.wkl.springboot.repository;
import com.wkl.springboot.entity.User;
import org.springframework.data.jpa.repository.JpaRepository;
/**
* Description:
* Date: 2020/6/7 - 上午 1:46
* author: wangkanglu
* version: V1.0
*/
//繼承JpaRepository來完成對數據庫的操作
// 表的類,主鍵類型
public interface UserReopsitory extends JpaRepository<User, Integer> {
}
4:配置文件中基本的配置JpaProperties
5:創建Controller
package com.wkl.springboot.controller;
import com.wkl.springboot.entity.User;
import com.wkl.springboot.repository.UserReopsitory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* Description:
* Date: 2020/6/7 - 上午 1:51
* author: wangkanglu
* version: V1.0
*/
@RestController
public class UserController {
@Autowired
UserReopsitory userReopsitory;
@GetMapping("/user/{id}")
public User getUser(@PathVariable("id")Integer id){
return userReopsitory.getOne(id);
}
@GetMapping("/user")
public User insertUser(User user){
User save = userReopsitory.save(user);
return save;
}
}
6:使用getUser方法報500錯誤
錯誤原因是:在轉化成json的時候,fasterxml.jackson將對象轉換爲json報錯,發現有字段爲null。
解決方案:實體類上添加
@JsonIgnoreProperties(value = { "hibernateLazyInitializer"})