【SpringBoot1.x】SpringBoot1.x 資料訪問
SpringBoot1.x 資料訪問
簡介
對於資料訪問層,無論是 SQL 還是 NOSQL,Spring Boot 預設採用整合 Spring Data 的方式進行統一處理,新增大量自動配置,遮蔽了很多設定。引入各種 xxxTemplate,xxxRepository 來簡化我們對資料訪問層的操作,對我們來說只需要進行簡單的設定即可。
在本文中測試使用 SQL 相關內容,在其他文章中測試使用 NOSQL 相關內容。
JDBC API
通過包含以下設計決策,SpringData JDBC API 的目標是從概念上簡化得多:
- 如果載入實體,則將執行SQL語句。完成此操作後,您將擁有一個完全載入的實體。不會進行延遲載入或快取。
- 如果儲存實體,則將儲存它。如果您不這樣做,則不會。沒有骯髒的跟蹤,也沒有會話。
- 有一個簡單的模型可以將實體對映到表。它可能僅適用於相當簡單的情況。如果您不喜歡這樣做,則應編寫自己的策略。SpringData JDBC API 僅提供非常有限的支援,以通過註釋自定義策略。
所以它不提供 JPA 的快取,延遲載入,回寫或其他許多功能。這使 SpringData JDBC API 成為簡單,有限,易用的ORM。
新建 SpringBoot 專案,選擇 Web 模組,JDBC API 和 MySQL 模組,如下圖:
或者 新建 SpringBoot 專案後,新增如下的相關依賴:
<dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-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> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> <exclusions> <exclusion> <groupId>org.junit.vintage</groupId> <artifactId>junit-vintage-engine</artifactId> </exclusion> </exclusions> </dependency> </dependencies>
然後修改 application.yml 配置檔案:
# 配置資料來源
spring:
datasource:
username: root
password: 123456
url: jdbc:mysql://localhost:3306/dockerT?useSSL=false
driver-class-name: com.mysql.jdbc.Driver
預設是用 org.apache.tomcat.jdbc.pool.DataSource 作為資料來源,資料來源的相關配置都在 DataSourceProperties 裡面。
資料來源自動配置原理:
- 參考
org.springframework.boot.autoconfigure.jdbc.DataSourceConfiguration
spring.datasource.type
指定自定義的資料來源型別。 - SpringBoot預設可以支援資料來源為:
org.apache.tomcat.jdbc.pool.DataSource、HikariDataSource、BasicDataSource
- 通過
org.springframework.boot.autoconfigure.jdbc.DataSourceInitializer
的runSchemaScripts()
可以執行建表語句,runDataScripts()
可以執行插入資料的 sql 語句,預設只需要將檔案命名為schema‐all.sql data-sqll.sql
,也可以通過配置檔案的spring.datasource.schema
和spring.datasource.data
屬性指定。 - 因為自動配置了JdbcTemplate,所以可以直接用它操作資料庫
整合 Druid 資料來源
新增依賴:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid-spring-boot-starter</artifactId>
<version>1.1.24</version>
</dependency>
然後修改 application.yml 配置檔案:
# 配置資料來源
spring:
datasource:
# 資料來源基本配置
# ...
# 使用 DruidDataSource 作為資料來源
type: com.alibaba.druid.pool.DruidDataSource
# 資料來源其他配置
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
最後編寫 Druid 資料來源配置類:
/**
* @Author : parzulpan
* @Time : 2020-12
* @Desc : Druid 資料來源配置類
*/
@Configuration
public class DruidConfig {
@ConfigurationProperties(prefix = "spring.datasource")
@Bean
public DataSource druid() {
return new DruidDataSource();
}
// 配置Druid的監控
// 先配置一個管理後臺的 Servlet
@Bean
public ServletRegistrationBean statViewServlet() {
ServletRegistrationBean srb = new ServletRegistrationBean(new StatViewServlet(), "/druid/*");
Map<String, String> initParams = new HashMap<>();
initParams.put("loginUsername", "admin123");
initParams.put("loginPassword", "admin123");
initParams.put("allow", ""); // 預設允許所有訪問
initParams.put("deny", ""); // 拒絕的訪問
srb.setInitParameters(initParams);
return srb;
}
// 再配置一個 web 監控的 filter
@Bean
public FilterRegistrationBean webStatFilter() {
FilterRegistrationBean frb = new FilterRegistrationBean();
frb.setFilter(new WebStatFilter());
Map<String,String> initParams = new HashMap<>();
initParams.put("exclusions","*.js,*.css,/druid/*");
frb.setInitParameters(initParams);
frb.setUrlPatterns(Collections.singletonList("/*"));
return frb;
}
}
配置好後,可以通過訪問 /druid
通過管理後臺。
整合 MyBatis
新增依賴:
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>1.3.5</version>
</dependency>
註解方式
純註解方式,先建表,然後編寫實體類。
資料表:
use dockerT;
SET FOREIGN_KEY_CHECKS=0;
# Table structure for department
DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
`id` int(11) primary key NOT NULL AUTO_INCREMENT,
`departmentName` varchar(255) DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
# Table structure for employee
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
`id` int(11) primary key NOT NULL AUTO_INCREMENT,
`lastName` varchar(255) DEFAULT NULL,
`email` varchar(255) DEFAULT NULL,
`gender` int(2) DEFAULT NULL,
`birth` date DEFAULT NULL,
`d_id` int(11) DEFAULT NULL
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8;
實體類:
省略...
編寫操作 Department 表的 Mapper:
/**
* @Author : parzulpan
* @Time : 2020-12
* @Desc : 操作 Department 表的 Mapper
*/
@Repository
@Mapper
public interface DepartmentMapper {
@Select("select * from department where id=#{id}")
public Department getDeptById(Integer id);
@Delete("delete from department where id=#{id}")
public int deleteDeptById(Integer id);
// 自增
@Options(useGeneratedKeys = true, keyProperty = "id")
@Insert("insert into department(departmentName)values(#{departmentName})")
public int insertDept(Department department);
@Update("update department set departmentName=#{departmentName} where id=#{id}")
public int updateDept(Department department);
}
編寫控制類:
@RestController
public class DeptController {
@Autowired
DepartmentMapper departmentMapper;
// http://localhost:8080/dept?departmentName=Admin
@GetMapping("/dept")
public Department addDepartment(Department department) {
departmentMapper.insertDept(department);
return department;
}
// http://localhost:8080/dept/1001
@GetMapping("/dept/{id}")
public Department getDepartment(@PathVariable("id") Integer id) {
return departmentMapper.getDeptById(id);
}
}
也可以自定義 MyBatis 的配置規則,如果解決資料表名和屬性名不一致的情況,給容器中新增一個 ConfigurationCustomizer 即可。
@Configuration
public class MyBatisConfig {
public ConfigurationCustomizer configurationCustomizer() {
return new ConfigurationCustomizer() {
@Override
public void customize(org.apache.ibatis.session.Configuration configuration) {
configuration.setMapUnderscoreToCamelCase(true);
}
};
}
}
如果 Mapper 介面太多,可以需要寫很多個 @Mapper 註解,可以使用 @MapperScan 批量掃描所有的 Mapper 介面。
@MapperScan("cn.parzulpan.mapper")
@SpringBootApplication()
public class DataJdbcApiApplication {
public static void main(String[] args) {
SpringApplication.run(DataJdbcApiApplication.class, args);
}
}
配置方式
編寫全域性配置檔案:
編寫 SQL 對映檔案:
然後修改 application.yml 配置檔案,指定檔案位置:
mybatis:
config‐location: classpath:mybatis/mybatis‐config.xml
mapper‐locations: classpath:mybatis/mapper/*.xml
SpringData JPA
為了執行簡單查詢以及執行分頁和稽核,必須編寫太多樣板程式碼。SpringData JPA 旨在通過將工作量減少到實際需要的數量來顯著改善資料訪問層的實現。它相比與 SpringData JDBC API 功能更加強大,使用也更復雜。
使用步驟:
-
編寫一個實體類和資料表進行對映,並且配置好對映關係
/** * @Author : parzulpan * @Time : 2020-12 * @Desc : 使用 JPA 註解配置對映關係 */ @Entity // 告訴 JPA 這是一個實體類,即和資料庫對映的表 @Table // 指定和哪個資料表對應,如果沒有這個表在配置中可以指定自動建立,如果省略預設表名就是 user,即類名首字母小寫 public class User { @Id // 這是一個主鍵 @GeneratedValue(strategy = GenerationType.IDENTITY) // 策略是自增 private Integer id; @Column(name = "last_name", length = 50) // 指定和資料表對應的一個列,如果省略預設列名就是屬性名 private String lastName; @Column private String email; // getter setter }
-
編寫一個介面來操作實體類對應的資料表(Repository)
/** * @Author : parzulpan * @Time : 2020-12 * @Desc : 操作實體類對應的資料表的介面 * JpaRepository<T, ID extends Serializable> * T 是 實體類,ID 是實體類的主鍵 */ public interface UserRepository extends JpaRepository<User, Integer> { }
-
配置 JPA
spring: datasource: username: root password: 123456 url: jdbc:mysql://localhost:3306/dockerT?useSSL=false driver-class-name: com.mysql.jdbc.Driver jpa: hibernate: ddl-auto: update # 更新或者建立資料表結構 show-sql: true # 控制顯示相應 SQL
-
測試
package cn.parzulpan.controller; import cn.parzulpan.entity.User; import cn.parzulpan.repository.UserRepository; 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; @RestController public class UserController { @Autowired UserRepository userRepository; // http://localhost:8080/user/1 @GetMapping("/user/{id}") public User getUser(@PathVariable("id") Integer id){ User user = userRepository.findOne(id); return user; } // http://localhost:8080/user/?lastName=parzul&[email protected] @GetMapping("/user") public User insertUser(User user){ User save = userRepository.save(user); return save; } }