本章經過學習SpringData 和SpringBoot 相關知識將面向服務架構(SOA)的單點登陸系統(SSO)須要的代碼實現。這樣能夠從實戰中學習兩個框架的知識,又能夠爲單點登陸系統打下基礎。經過本章你將掌握 SpringBoot項目的搭建,Starter pom的使用,配置全局文件,核心註解SpringBootApplication 介紹以及單元測試 SpringBootTest註解的使用。SpringData 的入門使用,Repository接口的使用,查詢方法關鍵字的規則定義,@Query,@Modifying 註解的使用,最後是開發中的建議和遇到的問題。文章底部提供源碼。html
SpringBoot 是一個用於簡化Spring應用搭建開發的框架。開發過程當中,咱們常常經過配置xml文件來整合第三方技術。而這些重複整合的工做交給了SpringBoot完成。SpringBoot使用"習慣優於配置"的理念幫咱們快速搭建並運行項目。對主流的開發框架能無配置集成。筆者用的開發工具是sts(Spring Tool Suite),其操做和eclipse幾乎一致。若沒有這個工具,建立Maven項目是同樣的。前端
先看看Maven項目核心配置文件 pom.xmljava
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.itdragon</groupId> <artifactId>springbootStudy</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>war</packaging> <name>springbootStudy</name> <description>Demo project for Spring Boot</description> <!-- 添加 spring boot的父級依賴,它是SpringBoot項目的標誌 spring-boot-starter-parent 它是一個特殊的starter,提供了不少相關的Maven依賴,不用再爲version而頭疼了,大大簡化了開發 --> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.9.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency><!-- 添加web依賴 ,包含spring和springmvc等--> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency><!-- 添加對jpa的支持,包含spring-data和Hibernate --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency><!-- mysql鏈接的jar包 --> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-tomcat</artifactId> <scope>provided</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency><!-- 由於SpringBoot內嵌的tomcat不支持jsp頁面,同時SpringBoot也不推薦用jsp --> <groupId>org.apache.tomcat.embed</groupId> <artifactId>tomcat-embed-jasper</artifactId> <scope>provided</scope> </dependency> <dependency><!-- jsp標籤庫 --> <groupId>javax.servlet</groupId> <artifactId>jstl</artifactId> </dependency> </dependencies> <build> <plugins> <plugin><!-- SpringBoot 編譯插件 --> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
細心的同窗會發現該文件中出現大量的 spring-boot-starter-* 的語句。SpringBoot之因此能簡化開發的祕密就在這裏------ Starter pom
spring-boot-starter-parent :父級依賴,SpringBoot項目的標誌。裏面封裝了不少jar的版本
spring-boot-starter-web :對web項目的支持,其中包含了SpringMVC和tomcat
spring-boot-starter-data-jpa :對JPA的支持,其中包含了經常使用的SpringData和Hibernate,沒有Mybatis哦
spring-boot-starter-tomcat :使用tomcat做爲Servlet容器
spring-boot-starter-test :對經常使用測試框架的支持,如JUnit
還有不少......mysql
再看看SpringBoot項目全局配置文件 application.propertiesgit
# 配置tomcat端口號 server.port=8081 # 配置SpringMVC視圖解析器 spring.mvc.view.prefix=/WEB-INF/views/ spring.mvc.view.suffix=.jsp # 配置鏈接池,默認使用的是tomcat的鏈接池,但實際不多用tomcat的鏈接池 spring.datasource.url=jdbc:mysql://localhost:3306/jpa?useUnicode=true&characterEncoding=UTF8 spring.datasource.username=root spring.datasource.password=root spring.datasource.driver-class-name=com.mysql.jdbc.Driver # 配置方言 不然提示:Access to DialectResolutionInfo cannot be null when 'hibernate.dialect' not set spring.jpa.database-platform=org.hibernate.dialect.MySQL5Dialect # 自動更新數據庫表結構,也能夠是 validate | update | create | create-drop spring.jpa.properties.hibernate.hbm2ddl.auto=update # 顯示sql語句 spring.jpa.show-sql=true
全局配置文件能夠是application.properties 也能夠是 application.yml,建議放在resources目錄下。更多配置: https://github.com/ITDragonBlog/daydayup/blob/master/SpringBoot/SpringData/springbootStudy/src/main/resources/springboot.propertiesgithub
最後是SpringBoot HelloWorld項目的入口類,只須要下面一個java文件,執行main方法,便可實現頁面的跳轉和數據返回的功能。web
import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller @SpringBootApplication public class SpringbootStudyApplication { @RequestMapping("/") public String index() { return "index"; } @RequestMapping("hello") @ResponseBody public String helloWorld() { return "Hello SpringBoot !"; } public static void main(String[] args) { SpringApplication.run(SpringbootStudyApplication.class, args); } }
@SpringBootApplication:是 SpringBoot 的核心註解,通常用在入口類上。它是一個組合註解,其中主要內容有一下三個
@SpringBootConfiguration:是一個類級註釋,指示對象是一個bean定義的源,能夠理解爲xml中的beans,通常和 @Bean 註解一塊兒使用。
@EnableAutoConfiguration:啓用 Spring 應用程序上下文的自動配置,試圖猜想和配置您可能須要的bean。自動配置類一般採用基於你的 classpath 和已經定義的 beans 對象進行應用。
@ComponentScan:該註解會自動掃描指定包下的所有標有 @Component、@Service、@Repository、@Controller註解 的類,並註冊成beanspring
SpringData入口類sql
package com.itdragon; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class StartApplication { public static void main(String[] args) { SpringApplication.run(StartApplication.class, args); } }
SpringData 是一個用於簡化數據庫訪問,並支持雲服務的開源框架。支持非關係型數據庫(NoSQL) 和 關係型數據庫。其主要目的是使數據庫的訪問變得方便快捷。
SpringData JPA 是由Spring提供的簡化JPA開發的框架,致力於減小數據訪問層的開發量。數據庫
建立實體類User 表,對應數據庫表名是 itdragon_user,id做爲自增加的主鍵,plainPassword是不保存到數據庫的明文密碼。
import javax.persistence.Entity; import javax.persistence.GeneratedValue; import javax.persistence.GenerationType; import javax.persistence.Id; import javax.persistence.Table; import javax.persistence.Transient; /** * 用戶實體類 * @author itdragon * */ @Table(name="itdragon_user") @Entity public class User { @Id @GeneratedValue(strategy=GenerationType.AUTO) private Long id; // 自增加主鍵 private String account; // 登陸的帳號 private String userName; // 註冊的暱稱 @Transient private String plainPassword; // 登陸時的密碼,不持久化到數據庫 private String password; // 加密後的密碼 private String salt; // 用於加密的鹽 private String iphone; // 手機號 private String email; // 郵箱 private String platform; // 用戶來自的平臺 private String createdDate; // 用戶註冊時間 private String updatedDate; // 用戶最後一次登陸時間 // 省略get/set/toString 方法 }
建立UserRepository,這是SpringData 的核心知識點,咱們先看代碼
import java.util.List; import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Modifying; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.PagingAndSortingRepository; import org.springframework.data.repository.query.Param; import com.itdragon.pojo.User; /** * 核心知識:SpringData Repository 接口 * * CrudRepository 接口提供了最基本的對實體類的添刪改查操做 * - T save(T entity); //保存單個實體 * - T findOne(ID id); // 根據id查找實體 * - void delete(ID/T/Iterable); // 根據Id刪除實體,刪除實體,批量刪除 * PagingAndSortingRepository 提供了分頁與排序功能 * - <T, ID extends Serializable> // 第一個參數傳實體類,第二個參數傳註解數據類型 * - Iterable<T> findAll(Sort sort); // 排序 * - Page<T> findAll(Pageable pageable); // 分頁查詢(含排序功能) * JpaSpecificationExecutor 提供了Specification(封裝 JPA Criteria查詢條件)的查詢功能 * - List<T> findAll(Specification<T> spec); * - Page<T> findAll(Specification<T> spec, Pageable pageable); * - List<T> findAll(Specification<T> spec, Sort sort); * * 開發建議 * 1. 這裏值列出的是經常使用方法 * 2. CrudRepository 中的findAll() 方法要慎用。當數據庫中數據量大,多線程腳本調用findAll方法,系統可能會宕機。 * 3. CrudRepository 中的deletAll()方法要慎用。這是物理刪除,如今企業通常採用邏輯刪除。 * 4. PagingAndSortingRepository 和 JpaSpecificationExecutor 能知足大部分業務需求。 */ public interface UserRepository extends PagingAndSortingRepository<User, Long>, JpaSpecificationExecutor<User>{ /** * 重點知識:SpringData 查詢方法定義規範 * * 1. 查詢方法名通常以 find | read | get 開頭,建議用find * findByAccount : 經過account查詢User * account是User的屬性,拼接時首字母需大寫 * 2. 支持的關鍵詞有不少好比 Or,Between,isNull,Like,In等 * findByEmailEndingWithAndCreatedDateLessThan : 查詢在指定時間前註冊,並以xx郵箱結尾的用戶 * And : 而且 * EndingWith : 以某某結尾 * LessThan : 小於 * * 注意 * 如有User(用戶表) Platform(用戶平臺表) 存在一對一的關係,且User表中有platformId字段 * SpringData 爲了區分: * findByPlatFormId 表示經過platformId字段查詢 * findByPlatForm_Id 表示經過platform實體類中id字段查詢 * * 開發建議 * 表的設計,儘可能作單表查詢,以確保高併發場景減輕數據庫的壓力。 */ // 1 經過帳號查用戶信息 User findByAccount(String account); // 2 獲取指定時間內以xx郵箱結尾的用戶信息 List<User> findByEmailEndingWithAndCreatedDateLessThan(String email, String createdDate); /** * 重點知識:使用 @Query 註解 * * 上面的方法雖然簡單(不用寫sql語句),但它有最爲致命的問題-----不支持複雜查詢,其次是命名太長 * 1. 使用@Query 註解實現複雜查詢,設置 nativeQuery=true 使查詢支持原生sql * 2. 配合@Modifying 註解實現建立,修改,刪除操做 * 3. SpringData 默認查詢事件爲只讀事務,若要修改數據則需手動添加事務註解 * * 注意 * 若@Query 中有多個參數,SpringData 提供兩種方法: * 第一種 ?1 ... ?2 要求參數順序一致 * 第二種 :xxx ... :yyy xxx 和 yyy 必須是實體類對應的屬性值,不要求參數順序但參數前要加上@Param("xxx") * 模糊查詢可以使用 %xxx% * * 開發建議 * 1. 參數填寫的順序要保持一致,不要給本身添加麻煩 * 2. 建議使用@Query,可讀性較高 */ // 3 獲取某平臺活躍用戶數量 @Query(value="SELECT count(u.id) FROM User u WHERE u.platform = :platform AND u.updatedDate <= :updatedDate") long getActiveUserCount(@Param("platform")String platform, @Param("updatedDate")String updatedDate); // 4 經過郵箱或者手機號模糊查詢用戶信息 @Query(value="SELECT u FROM User u WHERE u.email LIKE %?1% OR u.iphone LIKE %?2%") List<User> findByEmailAndIhpneLike(String email, String iphone); // 5 修改用戶郵箱 @Modifying @Query("UPDATE User u SET u.email = :email WHERE u.id = :id") void updateUserEmail(@Param("id") Long id, @Param("email") String email); // 6 傳入的值是對象 @Modifying @Query("update Variable v set v.lastSavedValue =:#{#variable.lastSavedValue}, v.lastSavedValueTime =:#{#variable.lastSavedValueTime} where v.id=:#{#variable.id}") Variable updateVariableValue(Variable: variable); }
代碼中共有五個方法,每一個方法都包含了不少的知識點。
方法一和方法二主要介紹的是SpringData關鍵字的用法。
1 關鍵字的解析
這裏用findByPlatFormId() 方法來介紹SpringData 解析查詢方法的流程。
第一步:去除關鍵字findBy
第二步:剩下的PlatFormId 首字母小寫並在User對象中找是否有該屬性,如有則查詢並結束。若沒有則第三步
第三步:platFormId,從右到左截取到第一個大寫字母,再判斷剩下的platForm是否爲User對象,如此循環直到結束爲止。
2 級聯屬性區分
若查詢的屬性是實體類,爲了不誤會和衝突,用"_"表示屬性中的屬性
3 查詢分頁排序
若findByPlatFormId() 方法想要排序或者分頁,是能夠在後面加Pageable,Sort參數。
findByPlatFormId(String platFormId, Pageable pageable)
findByPlatFormId(String platFormId, Sort sort)
4 其餘的關鍵字
方法三到方法五主要介紹的是 @Query 註解的使用。
1 傳參方式
索引參數:?n ,n從1開始,表示第一個參數。方法傳入的參數的照順序和個數要和 n 保持一致。
命名參數::key ,傳參必須有 @Param("key") 註解修飾
2 原生的sql
@Query 註解支持本地查詢,即用原生的sql語句。如:@Query(value="xxxx", nativeQuery=true)
3 Modifying
若直接執行修改操做,SpringDataJPA 會提示錯誤信息 Executing an update/delete query 。是由於Spring Data 默認全部的查詢均聲明爲只讀事務。
因此咱們要在Service層添加 @Transactional 註解。
SpringDataJPA 核心知識Repository接口
1 Repository: 空接口,標識做用,代表任何繼承它的均爲Repository接口類
2 CrudRepository: 繼承 Repository,實現了一組 CRUD 相關的方法
3 PagingAndSortingRepository: 繼承 CrudRepository,實現了一組分頁排序相關的方法
4 JpaRepository: 繼承 PagingAndSortingRepository,實現一組 JPA 規範相關的方法
5 JpaSpecificationExecutor: 不屬於Repository體系,實現一組 JPA Criteria 查詢相關的方法
PagingAndSortingRepository 和 JpaSpecificationExecutor 基本知足企業中大部分的需求。也能夠自定義Repository,只需繼承 JpaRepository 便可具有了通用的數據訪問控制層的能力。
進入各自接口類中,使用快捷鍵 Ctrl + o 便可查看當前類的全部方法,因此這裏就不貼出來了。
建立UserService 並加上註解 @Transactional
import javax.transaction.Transactional; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.itdragon.common.ItdragonResult; import com.itdragon.pojo.User; import com.itdragon.repository.UserRepository; @Service @Transactional public class UserService { @Autowired private UserRepository userRepository; public ItdragonResult registerUser(User user) { // 檢查用戶名是否註冊,通常在前端驗證的時候處理,由於註冊不存在高併發的狀況,這裏再加一層查詢是不影響性能的 if (null != userRepository.findByAccount(user.getAccount())) { return ItdragonResult.build(400, ""); } userRepository.save(user); // 註冊成功後選擇發送郵件激活。如今通常都是短信驗證碼 return ItdragonResult.build(200, ""); } public ItdragonResult editUserEmail(String email) { // 經過Session 獲取用戶信息, 這裏僞裝從Session中獲取了用戶的id,後面講解SOA面向服務架構中的單點登陸系統時,修改此處代碼 FIXME long id = 3L; // 添加一些驗證,好比短信驗證 userRepository.updateUserEmail(id, email); return ItdragonResult.ok(); } }
SpringBoot 的單元測試,須要用到 @RunWith 和 @SpringBootTest註解,代碼註釋中有詳細介紹。
import java.util.List; import javax.persistence.criteria.CriteriaBuilder; import javax.persistence.criteria.CriteriaQuery; import javax.persistence.criteria.Predicate; import javax.persistence.criteria.Root; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Sort; import org.springframework.data.domain.Sort.Direction; import org.springframework.data.domain.Sort.Order; import org.springframework.data.jpa.domain.Specification; import org.springframework.test.context.junit4.SpringRunner; import com.itdragon.StartApplication; import com.itdragon.common.ItdragonUtils; import com.itdragon.pojo.User; import com.itdragon.repository.UserRepository; import com.itdragon.service.UserService; /** * @RunWith 它是一個運行器 * @RunWith(SpringRunner.class) 表示讓測試運行於Spring測試環境,不用啓動spring容器便可使用Spring環境 * @SpringBootTest(classes=StartApplication.class) 表示將StartApplication.class歸入到測試環境中,若不加這個則提示bean找不到。 * * @author itdragon * */ @RunWith(SpringRunner.class) @SpringBootTest(classes=StartApplication.class) public class SpringbootStudyApplicationTests { @Autowired private UserService userService; @Autowired private UserRepository userRepository; @Test public void contextLoads() { } @Test // 測試註冊,新增數據 public void registerUser() { User user = new User(); user.setAccount("gitLiu"); user.setUserName("ITDragonGit"); user.setEmail("itdragon@git.com"); user.setIphone("12349857999"); user.setPlainPassword("adminroot"); user.setPlatform("github"); user.setCreatedDate(ItdragonUtils.getCurrentDateTime()); user.setUpdatedDate(ItdragonUtils.getCurrentDateTime()); ItdragonUtils.entryptPassword(user); userService.registerUser(user); } @Test // 測試SpringData 關鍵字 public void findByEmailEndingWithAndCreatedDateLessThan() { List<User> users = userRepository.findByEmailEndingWithAndCreatedDateLessThan("qq.com", ItdragonUtils.getCurrentDateTime()); System.out.println(users.toString()); } @Test // 測試SpringData @Query 註解和傳多個參數 public void getActiveUserCount() { long activeUserCount = userRepository.getActiveUserCount("weixin", ItdragonUtils.getCurrentDateTime()); System.out.println(activeUserCount); } @Test // 測試SpringData @Query 註解,傳多個參數 和 like 查詢 public void findByEmailAndIhpneLike() { List<User> users = userRepository.findByEmailAndIhpneLike("163.com", "6666"); System.out.println(users.toString()); } @Test // 測試SpringData @Query 註解 和 @Modifying 註解 public void updateUserEmail() { /** * org.springframework.dao.InvalidDataAccessApiUsageException:Executing an update/delete query; nested exception is javax.persistence.TransactionRequiredException: Executing an update/delete query * userRepository.updateUserEmail(3L, "update@email.com"); */ userService.editUserEmail("update@email.com"); } @Test // 測試SpringData PagingAndSortingRepository 接口 public void testPagingAndSortingRepository() { int page = 1; // 從0開始,第二頁 int size = 3; // 每頁三天數據 PageRequest pageable = new PageRequest(page, size, new Sort(new Order(Direction.ASC, "id"))); Page<User> users = userRepository.findAll(pageable); System.out.println(users.getContent().toString()); // 當前數據庫中有5條數據,正常狀況能夠打印兩條數據,id分別爲4,5 (先排序,後分頁) } @Test // 測試SpringData JpaSpecificationExecutor 接口 public void testJpaSpecificationExecutor(){ int pageNo = 1; int pageSize = 3; PageRequest pageable = new PageRequest(pageNo, pageSize); Specification<User> specification = new Specification<User>() { @Override public Predicate toPredicate(Root<User> root, CriteriaQuery<?> query, CriteriaBuilder cb) { Predicate predicate = cb.gt(root.get("id"), 1); // 查詢id 大於 1的數據 return predicate; } }; Page<User> users = userRepository.findAll(specification, pageable); System.out.println(users.getContent().toString()); // 當前數據庫中有5條數據,正常狀況能夠打印一條數據,id爲5 } }
致使的緣由多是mysql服務器版本安裝不正確,解決方法有兩種。
第一種:換mysql-connector-java jar包版本爲 5.1.6 (不推薦); 當前jar版本爲 5.1.44。
第二種:重裝mysql版本,當前最新版本是5.7。教程都準備好了。
https://www.cnblogs.com/sshoub/p/4321640.html (mysql安裝)
http://blog.csdn.net/y694721975/article/details/52981377 (mysql卸載)
咱們只是在全局配置文件中設置了相關值,就完成了鏈接池的配置,想必你們都有所疑惑。其實當咱們在pom.xml文件中加入spring-boot-starter-data-jpa 依賴時,SpringBoot就會自動使用tomcat-jdbc鏈接池。
固然咱們也可使用其餘的鏈接池。
https://www.cnblogs.com/gslblog/p/7169481.html (springBoot數據庫鏈接池經常使用配置)
https://www.cnblogs.com/xiaosiyuan/p/6255292.html (SpringBoot使用c3p0)
解決方法:preference -> general -> keys ,找到 Organize Imports ,而後 在 When 裏面選擇 Editing Java Source
1 SpringDataJPA 是簡化JPA開發的框架,SpringBoot是簡化項目開發的框架。
2 spring-boot-starter-parent 是SpringBoot項目的標誌
3 SpringBootApplication 註解是SpringBoot項目的入口
4 SpringData 經過查詢關鍵字和 @Query註解實現對數據庫的訪問
5 SpringData 經過PagingAndSortingRepository 實現分頁,排序和經常使用的crud操做
源碼地址:https://github.com/ITDragonBlog/daydayup/tree/master/SpringBoot/SpringData
到這裏 SpringData 基於SpringBoot快速入門就結束了,若是有什麼問題請指教,若是以爲不錯能夠點一下推薦。