本章经过学习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/ITDragonBl...github
最后是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); }
代码中共有五个方法,每一个方法都包含了不少的知识点。
方法一和方法二主要介绍的是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接口
Repository: 空接口,标识做用,代表任何继承它的均为Repository接口类
CrudRepository: 继承 Repository,实现了一组 CRUD 相关的方法
PagingAndSortingRepository: 继承 CrudRepository,实现了一组分页排序相关的方法
JpaRepository: 继承 PagingAndSortingRepository,实现一组 JPA 规范相关的方法
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/sshou... (mysql安装)
http://blog.csdn.net/y6947219... (mysql卸载)
咱们只是在全局配置文件中设置了相关值,就完成了链接池的配置,想必你们都有所疑惑。其实当咱们在pom.xml文件中加入spring-boot-starter-data-jpa 依赖时,SpringBoot就会自动使用tomcat-jdbc链接池。
固然咱们也可使用其余的链接池。
https://www.cnblogs.com/gslbl... (springBoot数据库链接池经常使用配置)
https://www.cnblogs.com/xiaos... (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/ITDragonBl...
到这里 SpringData 基于SpringBoot快速入门就结束了,若是有什么问题请指教,若是以为不错能够点一下推荐。