1.基本概念
Spring Data 项目是Spring用来解决数据访问问题的一揽子解决方案,Spring Data是一个伞形项目,包含了大量关系型数据库和非关系型数据的数据访问解决方案。Spring Data使我们可以快速且简单的使用普通的数据访问技术及新的数据访问技术。
Spring Data包含的子项目:
Spring Data JPA、Spring Data MongoDB、Spring Data Neo4J、Spring Data Redis、Spring Data Solr、Spring Data Hadoop、Spring Data GemFire、Spring Data REST、Spring Data JDBC Extensions、Spring Data CoachBase、Spring Data Elasticsearch、Spring Data Cassandra、Spring Data DynamoDB
SpringData为我们使用统一的API来对上述的数据存储技术进行数据访问操作提供了支持。这是Spring通过提供Spring Data Commons项目来实现的,它是上述各种Spring Data项目的依赖。Spring Data Commons让我们在使用关系型或菲关系型数据库访问技术时都是用基于Spring的统一标准,该标准包括CRUD、分页等操作。
Spring Data Commons的重要概念:Spring Data Repository抽象。使用Spring Data Repository可以极大的减少数据访问层的代码。既然数据访问层操作的统一标准,那肯定是定义了各种各样和数据访问相关的接口。
Spring Data Repository抽象的根接口:Repository
Repository子接口:CrudRepository(定义了和CRUD相关操作的内容)
CrudRepository子接口:PagingAndSortRepository(定义与分页和排序相关的内容)
2.引入Docker
(1)Docker的概念
Docker是一个轻量级容器技术,类似于虚拟机技术。是直接运行在当前操作系统(linux)之上,而不是运行在虚拟机中,但是也实现了虚拟机技术的资源隔离,性能远远高于虚拟机技术
Docker支持将软件编译成一个镜像,在镜像里做好对软件的各种配置,然后发布镜像,使用者可以运行这个镜像,运行中的镜像称为容器。容器的启动速度非常快,一般都是以秒为单位。
目前,各大流行云计算平台都支持Docker容器技术,包括阿里云、百度云等,Docker有一统云计算的趋势。
这里云计算平台一般指paas(平台即服务):平台提供了存储、数据库、网络、负载均衡、自动扩展等功能,你只需要将程序交给云计算平台就可以了。
Docker不是为了开发测试方便而提供的小工具,而是可以直接用于实际生产环境的一种极好的部署方式。
(2)Docker安装(windows)
(书上的,适合win7,且我自己尝试了失败了,你们可以自己先试一下)下载地址:
https://github.com/boot2docker/windows-installer/releases/latest
因为我自己是win10专业版,如果有和我一样的,使用下面的:
https://www.runoob.com/docker/windows-docker-install.html
(3)Docker常用命令及参数
3.1.通常情况下,Docker的镜像都是放置在Docker官网的Docker Hub上,地址是https://registry.hub.docker.com
3.2.Docker镜像命令(cmd,然后输入docker)
3.2.1.Docker镜像检索: docker search 镜像名
检索redis: docker search redis
下载镜像:docker pull redis
查看镜像:docker images
删除镜像:docker rmi <镜像id>
删除所有镜像:docker rmi $(docker images -q)
3.2 Docker容器命令
3.2.1.容器基本操作:运行一个redis容器
docker run --name <名称> -d <镜像>
--name:命名;-d:执行完这句话后控制台不会阻碍,可继续输入命令操作
3.2.2容器列表:docker ps(查看运行中的容器列表)
CONTAINER ID:启动容器时生成的ID
IMAGE:镜像
COMMAND:容器启动时调用的命令
CREATED:容器创建时间
STATUS:容器状态
PORTS:端口。redis默认6379
NAMES:我们自己起的名字在
查看运行和停止状态的容器:docker ps -a
3.2.3停止和启动容器
3.2.3.1 停止容器
docker stop <容器名>
3.2.3.2 启动容器
docker start <容器名>
3.2.3.3 端口映射:docker容器中软件运行的端口,本机和本机局域网不能访问。所以要将docker容器中的端口映射到当前主机端口上,这样本机和本机局域网就可以访问该软件了
docker run -d -p 6378:6379 --name portRedis redis
-p:用于端口映射,将6379映射成6378
因为docker其实是在虚拟机中运行的,所以这一次端口映射其实是映射到虚拟机,要想本机使用,还需要映射一次,后面写
3.2.4 容器日志
查看当前容器日志
docker logs <自己命名的容器名>
3.2.5 登录容器:运行的容器其实是一个功能完备的Linux操作系统,可以像常规的系统一样登录并访问容器
docker exec -it portRedis bash
使用exit退出
3.Spring Data Jpa
(1)基本概念
1.1Hibernate使用O/R映射技术实现数据访问,O/R映射即将领域模型类和数据库的表进行映射,通过程序操作对象而实现表数据操作的能力。
JPA是一套给予O/R映射的标准规范,只定义标准规则,不提供实现。(创建项目时勾选上Spring Data Jpa即可)
1.2定义数据访问层
只需要继承JpaRepository即可
public interface PersonRepository extends JpaRepository<Person, Long> {
}
1.3配置使用Spring Data Jpa
加上@EnableJpaRepositories注解即可
@Configuration
@EnableJpaRepositories("com.xupeng.repos")
public class JpaConfiguration {
}
1.4定义查询方法
1.4.1常规查询:主要是findBy,And,Like等关键词。限制结果数量查询:top和first
public interface PersonRepository extends JpaRepository<Person, Long>{
//通过名称相等查询
List<Person> findByName(String name);
//通过名称like查询
List<Person> findByNameLike(String name);
//通过名称和地址查询
List<Person> findByNameAndAddress(String name,String address);
//查询符合条件前10条
List<Person> findFirst10ByName(String name);
//查询符合条件前30条
List<Person> findTop30ByName(String name);
}
1.4.2 使用JPA的NamedQuery:一个名词映射一个查询语句
@Entity
@NamedQuery(name="Person.findByName", query = "select p from Person p where p.name=?1")
public class Person {
//这里使用的是NamedQuery里的查询语句,而不是根据方法名称查询
List<Person> findByName(String name);
1.4.3 使用@Query查询
1.4.3.1 使用参数索引
@Query("select p from Person p where p.address = ?1")
List<Person> findByAddress(String address);
1.4.3.2 使用命名参数
@Query("select p from Person p where p.address = :address")
List<Person> findByAddress(@Param("address") String address);
1.4.3.3 更新查询,使用@Modifying和@Query注解组合来做更新
@Transactional
@Modifying
@Query("update Person p set p.name = ?1")
int setName(String name);
1.4.4 Specification
1.4.4.1 JPA提供了基于准则查询的方式,即Criteria查询。而Spring Data JPA提供了一个Specification(规范)接口让我们可以更方便的构造准则查询。Specification接口定义了一个toPredicate方法来构造查询条件
前提(1):接口类必须实现JpaSpecificationExecutor接口
public interface PersonRepository extends JpaRepository<Person, Long>,JpaSpecificationExecutor<Person> {
}
前提(2):定义Criterial查询
public class CustomerSpecs {
public static Specification<Person> personFromShangHai(){
return new Specification<Person>(){
@Override
public Predicate toPredicate(Root<Person> root,
CriteriaQuery<?> query, CriteriaBuilder cb) {
return cb.equal(root.get("address"), "上海");
}
};
}
}
注:Predicate、Root、CriteriaQuery、CriteriaBuilder均来自JPA接口
其中,CriteriaBuilder包含多个条件构造
1.4.4.2 使用静态导入
import static repo.CustomerSpecs.*;
List<Person> people = personRepository.findAll(personFromShangHai());
1.4.5 排序与分页
1.4.5.1 使用:Sort类以及Page接口和Pageable接口
1.4.5.2 定义
List<Person> findByName(String name,Sort sort);
Page<Person> findByName(String name,Pageable pageable);
1.4.5.3 使用排序
Page<Person> prople2 = personRepository.findByName("xx", new PageRequest(0,10));
(2)Spring Boot的支持
2.1 Spring可以对JDBC、JPA、Spring Data Jpa进行自动配置,我们在SpringBoot 下使用Spring Data Jpa ,在项目的maven依赖里添加spring-boot-starter-data-jpa,然后只需要定义DataSource、实体类和数据访问层,并在需要使用数据访问的地方注入数据访问层的Bean即可,无需任何额外配置
2.2 实战
2.2.1下载oracle :Docker pull sath89/oracle-xe-11g
下载完之后:
docker ps
docker exec -it 4182d8cdde16 /bin/bash
netstat -nlpt
cd $ORACLE_HOME
su oracle
$ORACLE_HOME/bin/sqlplus / as sysdba
https://www.cnblogs.com/dance-walter/p/8686474.html
https://blog.csdn.net/yidu_fanchen/article/details/75568748
如果报错,看看这:https://blog.csdn.net/qq_41464283/article/details/89684302
好了之后可以通过system或者sys,密码oracle进入
2.2.2添加依赖
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.xupeng</groupId>
<artifactId>xupeng_20191123</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>xupeng_20191123</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.23.1-GA</version>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.1</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
2.2.3 目录结构:application.java一定要在最外层
2.2.4代码段
application.properties
spring.datasource.url=jdbc\:oracle\:thin\:@localhost\:1521
spring.datasource.username=system
spring.datasource.password=oracle
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.max-idle=10
spring.datasource.max-wait=10000
spring.datasource.min-idle=5
spring.datasource.initial-size=5
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=true
server.port=8080
spring.jackson.serialization.indent_output=true
Person.java:注意引入的类,一定要引入@javax.persistence.Id,import javax.persistence.Entity;不要引入带hibernate的,我被这个坑的好惨
package com.xupeng.xupeng_20191123.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import org.hibernate.annotations.NamedQuery;
@Entity
@NamedQuery(name="Person.withNameAndAddressNamedQuery", query = "select p from Person p where p.name=?1 and p.address =?2")
public class Person {
@javax.persistence.Id
@GeneratedValue()
private Integer id;
private String address;
private Integer age;
private String name;
public Person(){
super();
}
public Person(Integer id,String name,Integer age,String address){
super();
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
PersonRepository.java
package com.xupeng.xupeng_20191123.dao;
import java.util.List;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import com.xupeng.xupeng_20191123.domain.Person;
public interface PersonRepository extends JpaRepository<Person, Long> {
List<Person> findByAddress(String name);
Person findByNameAndAddress(String name,String address);
@Query("select p from Person p where p.name=:name and p.address=:address")
Person withNameAndAddressQuery(@Param("name")String name,@Param("address")String address);
Person withNameAndAddressNamedQuery(String name,String address);
}
DataController.java
package com.xupeng.xupeng_20191123.control;
import java.util.List;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.xupeng.xupeng_20191123.dao.PersonRepository;
import com.xupeng.xupeng_20191123.domain.Person;
@RestController
public class DataController {
// 因为继承了JpaRepository,所以Spring Data JPA已经自动为我们注册了Bean,会自动注入
@Autowired
PersonRepository personRepository;
@RequestMapping("/save")
public Person save( String name,String address,Integer age){
Person p = personRepository.save(new Person(null,name,age,address));
return p;
}
@RequestMapping("/t2")
public String q1(){
return "3333";
}
@RequestMapping("/q1")
@ResponseBody
public List<Person> q1(String address){
List<Person> people = personRepository.findByAddress(address);
return people;
}
@RequestMapping("/q2")
public Person q2(String name,String address){
Person people = personRepository.findByNameAndAddress(name, address);
return people;
}
@RequestMapping("/q3")
@ResponseBody
public Person q3(String name,String address){
Person p = personRepository.withNameAndAddressQuery(name, address);
return p;
}
@RequestMapping("/q4")
public Person q4(String name,String address){
Person p = personRepository.withNameAndAddressQuery(name, address);
return p;
}
@RequestMapping("/sort")
public List<Person> sort(){
Sort sort=Sort.by(Sort.Direction.ASC,"age");
List<Person> people = personRepository.findAll(sort);
return people;
}
@RequestMapping("/page")
public Page<Person> page(){
Sort sort=Sort.by(Sort.Direction.ASC,"age");
Pageable pageable = PageRequest.of(2, 2,sort);
Page<Person> pagePeople = personRepository.findAll(pageable);
return pagePeople;
}
}
测试
2.3 自定义Repository实现
上述实战包含了SpringBoot 和Spring Data Jpa组合的大多数功能。我们将结合Specification和自定义的Repository实现来定制一个自动模糊查询,即对于任意的实体对象进行查询,对象里有几个值我们就查几个值,当值为字符型时我们就自动like查询,其余类的使用自动等于查询,没有值就查询全部
pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath /> <!-- lookup parent from repository -->
</parent>
<groupId>com.xupeng</groupId>
<artifactId>xupeng_20191123</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>xupeng_20191123</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.23.1-GA</version>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.1</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</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>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
person.java
package com.xupeng.xupeng_20191123.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import org.hibernate.annotations.NamedQuery;
@Entity
@NamedQuery(name="Person.withNameAndAddressNamedQuery", query = "select p from Person p where p.name=?1 and p.address =?2")
public class Person {
@javax.persistence.Id
@GeneratedValue()
private Integer id;
private String address;
private Integer age;
private String name;
public Person(){
super();
}
public Person(Integer id,String name,Integer age,String address){
super();
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
CustomRepository.java:继承JpaRepository,JpaSpecificationExecutor
package com.xupeng.xupeng_20191123.service;
import java.io.Serializable;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
import org.springframework.data.repository.NoRepositoryBean;
@NoRepositoryBean
public interface CustomRepository<T,ID extends Serializable> extends JpaRepository<T, ID>,JpaSpecificationExecutor<T> {
Page<T> findByAuto(T example,Pageable pageable);
}
CustomRepositoryImpl.java:继承SimpleJpaRepository,写入byAuto方法
package com.xupeng.xupeng_20191123.service;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.criteria.CriteriaBuilder;
import javax.persistence.criteria.CriteriaQuery;
import javax.persistence.criteria.Predicate;
import javax.persistence.criteria.Root;
import javax.persistence.metamodel.Attribute;
import javax.persistence.metamodel.EntityType;
import javax.persistence.metamodel.SingularAttribute;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.domain.Specification;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.util.ReflectionUtils;
import org.springframework.util.StringUtils;
public class CustomRepositoryImpl <T,ID extends Serializable> extends SimpleJpaRepository<T, ID> implements CustomRepository<T, ID> {
private final EntityManager entityManager;
public CustomRepositoryImpl(Class<T> domainClass, EntityManager entityManager) {
super(domainClass, entityManager);
this.entityManager = entityManager;
}
@Override
public Page<T> findByAuto(T example, Pageable pageable) {
return findAll(byAuto(entityManager,example),pageable);
}
//定义返回值为Specification的方法byAuto,这里使用泛型T,所以这个Specification是可以用于任意的实体类的。
//他接受的参数是entityManager和当前值作为查询条件的实体类对象
public static <T> Specification<T> byAuto(final EntityManager entityManager,final T example){
final Class<T> type = (Class<T>)example.getClass();//获得实体对象类的类型
return new Specification<T>() {
@Override
public Predicate toPredicate(Root<T> root, CriteriaQuery<?> query,
CriteriaBuilder cb) {
List<Predicate> predicates = new ArrayList<Predicate>();//新建Predicate列表存储构造的查询条件
EntityType<T> entity = entityManager.getMetamodel().entity(type);//获得实体对象的EntityType,以获得实体类的属性
for(Attribute<T, ?> attr:entity.getDeclaredAttributes()){
Object attrValue = getValue(example,attr);//获得实体类对象某一属性的值
if(attrValue != null){
if(attrValue.getClass() == String.class){//当前属性值为字符类型的时候
if(!StringUtils.isEmpty(attrValue)){//若当前字符不为空的情况下
//构造当前属性like属性值查询条件,并添加到条件列表中
predicates.add(cb.like(root.get(attribute(entity,attr.getName(), String.class)),pattern((String)attrValue)));
}
}else{
//其余情况构建属性和属性值equal查询条件,并添加到条件列表中
predicates.add(cb.equal(root.get(attribute(entity,attr.getName(), attrValue.getClass())),attrValue));
}
}
}
//将条件列表转换成Pridicate
Predicate[] predicateArray = null;
if(!predicates.isEmpty()){
predicateArray = new Predicate[predicates.size()];
}
return predicates.isEmpty()?cb.conjunction():cb.and(predicates.toArray(predicateArray));
}
public <T> Object getValue(T example,Attribute<T, ?> attr){
return ReflectionUtils.getField((Field)attr.getJavaMember(), example);
}
public <E,T> SingularAttribute<T, E> attribute(EntityType<T> entity,String fieldName,Class<E> fieldClass){
return entity.getDeclaredSingularAttribute(fieldName, fieldClass);
}
};
}
static public String pattern(String str){
return "%"+str+"%";
}
}
CustomRepositoryFactoryBean.java
package com.xupeng.xupeng_20191123.domain;
import java.io.Serializable;
import javax.persistence.EntityManager;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
import org.springframework.data.jpa.repository.support.JpaRepositoryFactoryBean;
import org.springframework.data.jpa.repository.support.JpaRepositoryImplementation;
import org.springframework.data.jpa.repository.support.SimpleJpaRepository;
import org.springframework.data.repository.Repository;
import org.springframework.data.repository.core.RepositoryInformation;
import org.springframework.data.repository.core.RepositoryMetadata;
import org.springframework.data.repository.core.support.RepositoryFactorySupport;
import com.xupeng.xupeng_20191123.service.CustomRepositoryImpl;
public class CustomRepositoryFactoryBean<T extends JpaRepository<S, ID>, S, ID extends Serializable>
extends JpaRepositoryFactoryBean<T, S, ID> {
public CustomRepositoryFactoryBean(Class<? extends T> repositoryInterface) {
super(repositoryInterface);
// TODO Auto-generated constructor stub
}
@Override
protected RepositoryFactorySupport createRepositoryFactory(
EntityManager entityManager) {
return new CustomRepositoryFactory(entityManager);
}
private static class CustomRepositoryFactory extends JpaRepositoryFactory{
public CustomRepositoryFactory(EntityManager entityManager) {
super(entityManager);
// TODO Auto-generated constructor stub
}
@Override
protected SimpleJpaRepository<?, ?> getTargetRepository(
RepositoryInformation information, EntityManager entityManager) {// 获得当前自定义类的实现
return new CustomRepositoryImpl(information.getDomainType(), entityManager);
}
@Override
protected Class<?> getRepositoryBaseClass(RepositoryMetadata metadata) {// 获得当前自定义类的类型
return CustomRepositoryImpl.class;
}
}
}
PersonRepository.java
package com.xupeng.xupeng_20191123.dao;
import java.util.List;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;
import org.springframework.stereotype.Repository;
import org.springframework.stereotype.Service;
import com.xupeng.xupeng_20191123.domain.Person;
public interface PersonRepository extends JpaRepository<Person, Long> {
List<Person> findByAddress(String name);
Person findByNameAndAddress(String name,String address);
@Query("select p from Person p where p.name=:name and p.address=:address")
Person withNameAndAddressQuery(@Param("name")String name,@Param("address")String address);
Person withNameAndAddressNamedQuery(String name,String address);
Page<Person> findByAuto(Person person, Pageable pageable);
}
DataController.java
package com.xupeng.xupeng_20191123.control;
import java.util.List;
import javax.transaction.Transactional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;
import org.springframework.data.domain.Sort;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import com.xupeng.xupeng_20191123.dao.PersonRepository;
import com.xupeng.xupeng_20191123.domain.Person;
@RestController
public class DataController {
// 因为继承了JpaRepository,所以Spring Data JPA已经自动为我们注册了Bean,会自动注入
@Autowired
PersonRepository personRepository;
@RequestMapping("/auto")
public Page<Person> auto(Person person){
Sort sort=Sort.by(Sort.Direction.ASC,"age");
Pageable pageable = PageRequest.of(2, 2,sort);
Page<Person> pagePerson = personRepository.findByAuto(person,pageable);
return pagePerson;
}
@RequestMapping("/save")
public Person save( String name,String address,Integer age){
Person p = personRepository.save(new Person(null,name,age,address));
return p;
}
@RequestMapping("/q1")
@ResponseBody
public List<Person> q1(String address){
List<Person> people = personRepository.findByAddress(address);
return people;
}
@RequestMapping("/q2")
public Person q2(String name,String address){
Person people = personRepository.findByNameAndAddress(name, address);
return people;
}
@RequestMapping("/q3")
@ResponseBody
public Person q3(String name,String address){
Person p = personRepository.withNameAndAddressQuery(name, address);
return p;
}
@RequestMapping("/q4")
public Person q4(String name,String address){
Person p = personRepository.withNameAndAddressQuery(name, address);
return p;
}
@RequestMapping("/sort")
public List<Person> sort(){
Sort sort=Sort.by(Sort.Direction.ASC,"age");
List<Person> people = personRepository.findAll(sort);
return people;
}
@RequestMapping("/page")
public Page<Person> page(){
Sort sort=Sort.by(Sort.Direction.ASC,"age");
Pageable pageable = PageRequest.of(2, 2,sort);
Page<Person> pagePeople = personRepository.findAll(pageable);
return pagePeople;
}
}
Xupeng20191123Application.java
package com.xupeng.xupeng_20191123;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.EnableAutoConfiguration;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.xupeng.xupeng_20191123.domain.CustomRepositoryFactoryBean;
@SpringBootApplication
@EnableAutoConfiguration
@RestController
@EnableJpaRepositories(repositoryFactoryBeanClass=CustomRepositoryFactoryBean.class)
public class Xupeng20191123Application {
public static void main(String[] args) {
SpringApplication.run(Xupeng20191123Application.class, args);
}
}
2.4 声明式事务
2.4.1 所有的数据访问技术都有事务处理机制,而Spring的事务机制是用统一的机制来处理不同数据访问技术的事务处理。
Spring提供了一个PlatformTransactionManager接口,不同数据访问技术的事务使用不同的接口实现:
JDBC DataSourceTransactionManager
JPA JpaTransactionManager
Hibernate HibernateTransactionManager
JDO JdoTransactionManager
分布式事务 JtaTransactionManager
使用代码如下:
@Bean
public PlatformTransactionManager transactionManager(){
JpaTransactionManager transactionManager = new JpaTransactionManager();
transactionManager.setDataSource(dataSource());
return transactionManager();
}
2.4.2 声明式事务
所谓声明式事务,就是用注解来选择需要使用事务的方法。@Transaction。注意,此注解来自于org.springframework.transaction.annotation包,而不是javax.transaction
@Transaction不仅可以在方法上,还可以在类上
Spring提供一个@EnableTransactionManagement注解在配置类上来开启声明式事务的支持。使用后,Spring容器会自动扫描注解@Transaction的方法和类。
2.4.3 注解事务行为
@Transaction有如下属性来定制事务行为
propagation:定义了事务的生命周期
REQUIRED:A新建了一个事务,A中调用了B,B将使用相同的事务
REQUIRES_NEW:A、B方法在方法调用时都会开启一个新事务,则B出问题不会导致A回滚
NESTED:同REQUIRES_NEW,但只支持JDBC,不支持JPA和HIBERNATE
SUPPORTS:方法调用时有事务用事务,没有就不用
NOT_SUPPORTED:强制不用事务,事务最后会被挂起
NERVER:强制不用事务,若有报错
MANDATORY:强制需要事务
isolation:决定了事务的隔离级别
READ_UNCOMMOTTED:最低隔离级别,会导致脏读、重复读、幻读
READ_COMMITTED:不会导致脏读,但是会重复读的幻读
REPEATABLE_READ:不会导致脏读、重复读,会导致幻读
SERIALIZABLE:顺序执行,但是开销很大
DEFAULT:oracle,sqlerver默认:READ_COMMITTED,mysql:REPEATTED_COMMITTED
timeout:事务过期时间
readOnly:是否只读
rollbackFor:哪些事务可以回滚
noRollbackFor:哪些事务不可以回滚
2.4.4 Spring Data Jpa的事务支持
Spring Data Jpa对所有的默认方法都开启了事务支持,且查询类事务默认开启readOnly=true属性
2.4.5 Spring Boot的事务支持
2.4.5.1 自动配置的事务管理器
在使用JDBC作为数据访问技术的时候,SpringBoot为我们定义了PlatformTransactionManager的实现DataSourceTransactionManager的Bean
在使用JPA作为数据访问技术的时候,SpringBoot为我们定义了一个PlatformTransactionManager的实现JpaTransactionManager的Bean
2.4.5.2 自动开启注解事务的支持
在SpringBoot中,无需显示开启使用@EnableTransactionManagement
2.4.6 实战
使用Spring Boot的@Transaction使用异常导致数据回滚,和使用异常让数据不回滚
目录结构:
pom.xml:同上
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xupeng</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>xupeng_20191202</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.23.1-GA</version>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.1</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</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>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.8</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-asciidoctor</artifactId>
<version>${spring-restdocs.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
Person.java
package com.xupeng.xupeng20191202.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import org.hibernate.annotations.NamedQuery;
@Entity
@NamedQuery(name="Person.withNameAndAddressNamedQuery", query = "select p from Person p where p.name=?1 and p.address =?2")
public class Person {
@javax.persistence.Id
@GeneratedValue()
private Integer id;
private String address;
private Integer age;
private String name;
public Person(){
super();
}
public Person(Integer id,String name,Integer age,String address){
super();
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
PersonRepository.java
package com.xupeng.xupeng20191202.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import com.xupeng.xupeng20191202.domain.Person;
public interface PersonRepository extends JpaRepository<Person, Long> {
}
DemoService.java
package com.xupeng.xupeng20191202.service;
import com.xupeng.xupeng20191202.domain.Person;
public interface DemoService {
public Person savePersonWithRollBack(Person person);
public Person savePersonWithoutRollBack(Person person);
}
DemoServiceImpl.java:注意引得包对不对
package com.xupeng.xupeng20191202.service;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.xupeng.xupeng20191202.dao.PersonRepository;
import com.xupeng.xupeng20191202.domain.Person;
@Service
public class DemoServiceImpl implements DemoService {
@Autowired
PersonRepository personRepository;
@Transactional(rollbackFor={IllegalArgumentException.class})
public Person savePersonWithRollBack(Person person) {
Person p = personRepository.save(person);
if(person.getName().equals("aa")){
throw new IllegalArgumentException("aa已存在,数据回滚");
}
return p;
}
@Transactional(noRollbackFor={IllegalArgumentException.class})
public Person savePersonWithoutRollBack(Person person) {
Person p = personRepository.save(person);
if(person.getName().equals("aa")){
throw new IllegalArgumentException("aa已存在,但是不数据回滚");
}
return p;
}
}
Xupeng20191202Application.java
package com.xupeng.xupeng20191202;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.data.rest.RepositoryRestMvcAutoConfiguration;
import org.springframework.context.annotation.Import;
@SpringBootApplication
public class Xupeng20191202Application {
public static void main(String[] args) {
SpringApplication.run(Xupeng20191202Application.class, args);
}
}
测试结果
2.5 数据缓存
2.5.1 Spring缓存支持
2.5.1.1 Spring支持的CacheManager
Spring定义CacheManager和Cache接口用来统一不同的缓存技术。CacheManager:Spring提供的各种缓存技术抽象接口。Cache:缓存的各种操作(一般不和他打交道)
针对不同缓存技术,实现不同的CacheManager。在我们使用任意一个实现的CacheManager的时候,需要注册实现的CacheManager的Bean。
2.5.1.2 声明式缓存注解
Spring提供4个注解来声明缓存规则:
@Cacheable:方法执行前看缓存是否有数据,有的话用缓存的
@CachePut:无论怎么样,都把方法的返回值放到缓存中
@CacheEvict:将一条或多台数据从缓存中删除
@Cacheing:组合多个注解策略在一个方法上
前三个注解都有value属性,指定的是要使用的缓存名称;key属性指定的是数据在缓存中的存储的键
2.5.1.3 开启声明式缓存支持
加上@EnableCaching即可
2.5.2 Spring Boot的支持
在Spring Boot环境下,使用缓存技术只需在项目中导入相关缓存技术的依赖包,并在配置类使用@EnableCaching开启缓存支持即可
2.5.3 实战
以Spring Boot默认的ConcurrentMapCacheManager作为缓存技术,演示@Cacheable,@CachePut,@CacheEvict
目录结构
pom.xml
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.2.1.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.xupeng</groupId>
<artifactId>demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>xupeng_20191202</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<version>3.23.1-GA</version>
</dependency>
<dependency>
<groupId>org.antlr</groupId>
<artifactId>antlr4-runtime</artifactId>
<version>4.1</version>
</dependency>
<dependency>
<groupId>com.oracle</groupId>
<artifactId>ojdbc6</artifactId>
<version>11.2.0.3</version>
</dependency>
<dependency>
<groupId>com.google.guava</groupId>
<artifactId>guava</artifactId>
<version>18.0</version>
</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>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-mockmvc</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.asciidoctor</groupId>
<artifactId>asciidoctor-maven-plugin</artifactId>
<version>1.5.8</version>
<executions>
<execution>
<id>generate-docs</id>
<phase>prepare-package</phase>
<goals>
<goal>process-asciidoc</goal>
</goals>
<configuration>
<backend>html</backend>
<doctype>book</doctype>
</configuration>
</execution>
</executions>
<dependencies>
<dependency>
<groupId>org.springframework.restdocs</groupId>
<artifactId>spring-restdocs-asciidoctor</artifactId>
<version>${spring-restdocs.version}</version>
</dependency>
</dependencies>
</plugin>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
application.properties
spring.datasource.url=jdbc\:oracle\:thin\:@localhost\:1521
spring.datasource.username=system
spring.datasource.password=oracle
spring.datasource.driver-class-name=oracle.jdbc.driver.OracleDriver
spring.datasource.max-idle=10
spring.datasource.max-wait=10000
spring.datasource.min-idle=5
spring.datasource.initial-size=5
spring.jpa.hibernate.ddl-auto=none
spring.jpa.show-sql=true
server.port=8080
spring.jackson.serialization.indent_output=true
Person.java
package com.xupeng20191202.xupeng20191202.domain;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import org.hibernate.annotations.NamedQuery;
@Entity
@NamedQuery(name="Person.withNameAndAddressNamedQuery", query = "select p from Person p where p.name=?1 and p.address =?2")
public class Person {
@javax.persistence.Id
@GeneratedValue()
private Integer id;
private String address;
private Integer age;
private String name;
public Person(){
super();
}
public Person(Integer id,String name,Integer age,String address){
super();
this.id = id;
this.name = name;
this.age = age;
this.address = address;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
PersonRepository.java
package com.xupeng20191202.xupeng20191202.dao;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import com.xupeng20191202.xupeng20191202.domain.Person;
public interface PersonRepository extends JpaRepository<Person, Long> {
@Query("select p from Person p where p.id = ?1")
Person findById(Integer id);
}
DemoService.java
package com.xupeng20191202.xupeng20191202.service;
import com.xupeng20191202.xupeng20191202.domain.Person;
public interface DemoService {
public Person save(Person person);
public Person findOne(Integer id);
public void remove(Person person);
}
DemoServiceImpl.java
package com.xupeng20191202.xupeng20191202.service;
import java.util.ArrayList;
import java.util.Optional;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import com.xupeng20191202.xupeng20191202.dao.PersonRepository;
import com.xupeng20191202.xupeng20191202.domain.Person;
@Service
public class DemoServiceImpl implements DemoService {
@Autowired
PersonRepository personRepository;
@Override
@CachePut(value="people",key="#person.id")
public Person save(Person person) {
Person p = personRepository.save(person);
System.out.println("为ID,KEY为:"+p.getName()+"做了缓存");
return p;
}
@Override
@Cacheable(value="people",key="#id")
public Person findOne(Integer id) {
Person person2 = personRepository.findById(id);
System.out.println("为ID,KEY为:"+person2.getId()+"做了缓存");
return person2;
}
@Override
@CacheEvict(value="people",key="#person.id")
public void remove(Person person) {
System.out.println("删除了:"+person.getId()+"的数据缓存");
personRepository.delete(person);
}
}
CacheControl.java
package com.xupeng20191202.xupeng20191202.control;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.xupeng20191202.xupeng20191202.domain.Person;
import com.xupeng20191202.xupeng20191202.service.DemoService;
@RestController
public class CacheControl {
@Autowired
DemoService demoService;
@RequestMapping("/put")
public Person put(Person person){
return demoService.save(person);
}
@RequestMapping("/able")
public Person cacheable(Integer id){
return demoService.findOne(id);
}
@RequestMapping("/evit")
public String evit(Person person){
demoService.remove(person);
return "ok";
}
}
Xupeng20191202V2Application.java
package com.xupeng20191202.xupeng20191202;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
@SpringBootApplication
@EnableCaching
public class Xupeng20191202V2Application {
public static void main(String[] args) {
SpringApplication.run(Xupeng20191202V2Application.class, args);
}
}