** Spring Data JPA系列**
1、SpringBoot集成JPA及基本使用
2、Spring Data JPA Criteria查询、部分字段查询
3、Spring Data JPA数据批量插入、批量更新真的用对了吗
4、Spring Data JPA的一对一、LazyInitializationException异常、一对多、多对多操作
5、Spring Data JPA自定义Id生成策略、复合主键配置、Auditing使用
6、【源码】Spring Data JPA原理解析之Repository的自动注入(一)
7、【源码】Spring Data JPA原理解析之Repository的自动注入(二)
8、【源码】Spring Data JPA原理解析之Repository执行过程及SimpleJpaRepository源码
9、【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(一)
10、【源码】Spring Data JPA原理解析之Repository自定义方法命名规则执行原理(二)
11、【源码】Spring Data JPA原理解析之Repository自定义方法添加@Query注解的执行原理
12、【源码】SpringBoot事务注册原理
13、【源码】Spring Data JPA原理解析之事务注册原理
14、【源码】Spring Data JPA原理解析之事务执行原理
15、【源码】SpringBoot编程式事务使用及执行原理
16、【源码】Spring事务之传播特性的详解
17、【源码】Spring事务之事务失效及原理
18、【源码】Spring Data JPA原理解析之Hibernate EventListener使用及原理
19、【源码】Spring Data JPA原理解析之Auditing执行原理
20、Spring Data JPA使用及实现原理总结
前言
在讲解SpringBoot集成JPA之前,先简单了解一下几个概念,JDBC、ORM、JPA以及Spring Data JPA。
1.1 JDBC
JDBC(Java DataBase Connectivity),是java连接数据库操作的原生接口API,为开发者访问数据库提供标准的接口。各数据库厂商依照JDBC规范,实现规范中的接口,实现数据库的连接。Java开发者使用同样的访问代码,配置不同的Driver、url以及账号,即可实现不同数据库厂家的数据库连接。
当数据库连接之后,通过拼接SQL语句,发送到数据库,达到对数据库中数据的操作。
缺点:
1)业务代码耦合SQL字符串拼接语句,维护比较麻烦;
2)不符合Java面向对象的编程思想;
1.2 ORM
对象-关系映射(Object-Relational Mapping,简称ORM),是一种描述对象与关系数据库之间映射的规范,采用面向对象编程的思想,操作数据库。
在Java中,ORM就是将Java类与DB中的Table表进行映射,代码中对相关Java类的操作,关联到数据库后,即体现为DB中关联的Table表的操作。
1.3 JPA
JPA是Java Persistence API的简称,中文名Java持久层API,是JDK5.x版本引入的。JPA的宗旨是为POJO提供持久化标准规范。
JPA采用ORM对象关系映射,以Java面向对象的编程思想,在javax.persistence包下提供对实体对象的CRUD操作,将开发者从繁琐的JDBC和SQL代码中解脱出来。
1.4 Spring Data JPA
Spring Data JPA是Spring提供的一套简化JPA开发的框架,按照约定好的方法名命规则写DAO层接口,可以在不写接口实现的情况下,实现对数据库中Table的操作,同时提供了除CRUD操作之外的许多功能,如分页、复杂查询等。
SpringBoot集成Spring Data JPA
2.1 引入依赖
在SpringBoot项目的pom.xml中引入相关依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 参数校验 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<!-- JPA是针对数据库的操作,需要引入对应的数据库 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
<version>2.2.5.RELEASE</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.28</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.22</version>
<scope>compile</scope>
</dependency>
2.2 参数配置
在application.yml中配置数据库连接信息。
spring:
datasource:
url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&serverTimezone=GMT%2B8&useSSL=false&allowMultiQueries=true
username: root
password: 123456
druid:
stat-view-servlet:
login-username: druid
login-password: 123456
url-pattern: /druid/*
enabled: true
filters: stat,wall
web-stat-filter:
url-pattern: /
enabled: true
exclusions: '*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*'
filter:
wall:
config: #支持单个事物多条sql语句执行
multi-statement-allow: true
none-base-statement-allow: true
enabled: true
jpa:
hibernate:
naming:
#Java代码实体字段命名与数据库表结构字段之间的名称映射策略
#当没有使用@Table和@Column注解时,implicit-strategy配置项才会被使用,当对象模型中已经指定时,
#implicit-strategy并不会起作用。
#implicit-strategy: org.hibernate.boot.model.naming.ImplicitNamingStrategyLegacyJpaImpl
#physical-strategy一定会被应用,与对象模型中是否显式地指定列名或者已经被隐式决定无关,
#SpringPhysicalNamingStrategy:表名,字段为小写,当有大写字母的时候会添加下划线分隔符号,默认值。
#PhysicalNamingStrategyStandardImpl:直接映射,不会做过多的处理,会禁止将驼峰转为下划线
#physical-strategy: org.hibernate.boot.model.naming.PhysicalNamingStrategyStandardImpl
# 用于指定 Session 是否在视图渲染完成后自动关闭,默认为false,意味着在视图渲染完成后,session会自动关闭
open-in-view: false
# 控制是否打印运行时的SQL语句与参数信息
show-sql: true
说明:spring.jpa.open-in-view通常设置为false,即当视图渲染完成后,Session自动关闭。Spring使用AOP(面向切面编程思想)管理事务,在方法调用前和调用后插入事务处理逻辑。如果open-in-view设置为true时,由于Session保持打开状态,可能导致事务的隔离性问题。另外,在多线程环境中,如果多个线程共享同一个Session,并且该Session的open-in-view设置为true,也可能导致事务的隔离性问题。
2.3 添加数据库表实体类Entity
package com.jingai.jpa.dao.entity;
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.*;
import java.util.Date;
@AllArgsConstructor
@NoArgsConstructor
@Data
@Entity
@JsonIgnoreProperties(value = { "hibernateLazyInitializer"})
@Table(name = "tb_product")
public class ProductEntity {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long pid;
private String name;
private String deliveryNo;
private String customer;
private String securityCode;
private Date createTime;
private Date validateTime;
private int validateNum;
}
说明:如果数据库访问时报com.fasterxml.jackson.databind.exc.InvalidDefinitionException异常,是因为在转化成json的时候,fasterxml.jackson将对象转换为json报错,发现有字段值为null。底层的hibernate会给被管理的Entity加入一个hibernateLazyInitializer属性,jsonplugin会把hibernateLazyInitializer也拿出来操作,并读取里面一个不能被反射操作的属性就产生了这个异常。
@GeneratedValue注解用于提供主键的生成策略规范。GenerationType有四种标准用法,分别为为:
1)TABLE:使用一个特定的数据库表格来保存主键。该策略通常和@TableGenerator注解一起使用,@TableGenerator注解指定生成主键的表(可以在实体类上指定,也可以在主键字段或属性上指定),JPA将会根据主键内容自动生成一张表作为系列表(或使用现有的系列表)。
2)SEQUENCE:使用底层数据库的序列来生成主键,前提是数据库支持序列。MySql数据库不支持序列。该策略通常与@SequenceGenerator主键一起使用,@SequenceGenerator注解指定了生成主键的系列。
3)IDENTITY:主键由数据库自动生成(主要是自动增长型)。比如MySql可以在创建表时声明auto_increment来指定主键自增长,在实体类中使用该策略。
4)AUTO:主键由程序控制,把主键生成策略较给持久化引擎,持久化引起会根据数据库在以上三种主键生成策略中选择其中一种。
2.4 添加Repository
继承JpaRepository接口,自动提供了基本的CRUD、分页、批量保存接口。
package com.jingai.jpa.dao;
import com.jingai.jpa.dao.entity.ProductEntity;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import java.util.List;
public interface ProductRepository extends JpaRepository<ProductEntity, Long> {
List<ProductEntity> findByPidBetween(long startPid, long endPid);
@Query("from ProductEntity where name like ?1")
List<ProductEntity> searchByName(String name);
}
在Repository接口中,除了JpaRepository自动提供的接口以外,可以自定义接口。
1)通过Spring Data JPA的命名规范,直接定义接口,无需写Sql语句;
2)使用自定义的SQL语句;
2.5 添加Service
package com.jingai.jpa.service;
import com.jingai.jpa.dao.entity.ProductEntity;
import java.util.List;
public interface ProductService {
ProductEntity save(ProductEntity entity);
ProductEntity getById(long id);
List<ProductEntity> findByPidBetween(long start, long end);
List<ProductEntity> searchByName(String name);
int batchSave(List<ProductEntity> list)
}
2.6 添加Service实现类
在Service实现类中,引入Repository对象,对数据库表进行操作。在此处,不仅可以使用ProductRepository中定义的searchByName和findByIdBetween(),而且还可以访问save、getById以及saveAll,这些是在JpaRespository中提供的实现。
package com.jingai.jpa.service.impl;
import com.jingai.jpa.dao.ProductRepository;
import com.jingai.jpa.dao.entity.ProductEntity;
import com.jingai.jpa.service.ProductService;
import org.apache.logging.log4j.util.Strings;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.interceptor.TransactionAspectSupport;
import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.Query;
import java.util.List;
@Service
public class ProductServiceImpl implements ProductService {
@Resource
private ProductRepository productRepository;
@Override
public ProductEntity save(ProductEntity entity) {
return productRepository.save(entity);
}
@Override
public ProductEntity getById(long id) {
return productRepository.getById(id);
}
@Override
public List<ProductEntity> findByPidBetween(long startPid, long endPid) {
return productRepository.findByPidBetween(startPid, endPid);
}
@Override
public List<ProductEntity> searchByName(String name) {
return productRepository.searchByName("%" + name + "%");
}
@Override
public int batchSave(List<ProductEntity> list) {
return productRepository.saveAll(list).size();
}
}
2.7 添加Controller
package com.jingai.jpa.controller;
import com.jingai.jpa.dao.entity.ProductEntity;
import com.jingai.jpa.service.ProductService;
import com.jingai.jpa.util.ResponseUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.StringUtils;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import javax.persistence.EntityNotFoundException;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import java.util.Map;
@Slf4j
@Validated
@RestController
@RequestMapping("product")
public class ProductController {
@Resource
private ProductService productService;
@GetMapping("get")
public Map<String, Object> get(@NotNull(message = "id不能为空") @Min(value = 1, message = "id无效") Long id) {
ProductEntity entity = productService.getById(id);
try {
if (entity == null || !StringUtils.hasText(entity.getSecurityCode())) {
log.info(String.format("id为%d的记录不存在", id));
}
} catch(EntityNotFoundException e) {
return ResponseUtil.fail(String.format("id为%d的记录不存在", id));
}
return ResponseUtil.success(entity);
}
}
在Controller类中引入Service,访问Service的提供的接口,实现对数据库的操作。其他接口的访问也是使用类似的方法,此处就不在贴代码了。
说明:@Validated、@NotNull、@Min为参数校验,详见:Spring validation参数校验基本使用_spring validate 参数-CSDN博客
Repository方法命名规则
规则:findBy(关键字)+ 属性名称(属性名称的首字母大写)+ 查询条件(首字母大写)
方法名词命名规范表
关键字 示例SQL表达式AndfindByCol1AndCol2(val1, val2)where col1 = ?1 and col2 = ?2OrfindByCol1OrCol2(val1, val2)where col1 = ?1 or col2 = ?2Is、EqualsfindByColumn(val)、
findByColumnIs(val)、
findByColumnEquals(val)where column = ?1BetweenfindByColBetween(val1, val2)where col between ?1 and ?2LessThan、BeforefindByColLessThan(val)、
findByColBefore(val)where col < ?1LessThanEqualfindByColLessThanEqual(val)whre col <= ?1GreaterThan、AfterfindByColGreaterThan(val)、
findByColAfter(val)where col > ?1GreaterThanEqualfindByColGreaterThanEqual(val)where col >= ?1IsNullfindByColIsNull()where col is nullIsNotNull、NotNullfindByColIsNotNull()、
findByColNotNull()where col is not nullLikefindByColLike(val)where col like ?1NotLikefindByColNotLike(val)where col not like ?1StartingWithfindByColStartingWith(val)where col like ?1
(参数增加前缀%)EndingWithfindByColEndingWith(val)where col like ?1
(参数增加后缀%)ContainingfindByColContaining(val)where col likt ?1
(参数被%包裹)OrderByfindByCol1OrderByCol2Asc(val)where col1 = ?1 order by col2 ascNotfindByColNotwhere col <> ?1InfindByColIn(Collection<?> val)where col in ?1NotInfindByColNotIn(Collection<?> val)where col not in ?1TruefindByColTrue()where col = trueFalsefindByColFalse()where col = falseIgnoreCasefindByColIgnoreCase(val)where upper(col) = upper(?1)
小结
限于篇幅,SpringBoot集成Spring Data JPA及基本使用就分享到这里。
关于本篇内容你有什么自己的想法或独到见解,欢迎在评论区一起交流探讨下吧。
版权归原作者 JingAi_jia917 所有, 如有侵权,请联系我们删除。