七、 SpringBoot整合Spring Data JPA

JPA(java persistence api)

jpa它并不是一个框架,而是一组规范。我觉得对于任何一个开发人员来说,理解“规范”这个词应该不在话下。其中,Hibernate就实现了这个规范。

Spring Data

  众所周知,Spring 是一个大家庭,一个技术的生态圈,其中整合的内容包罗万象。而 Spring Data xxx 系列就是 Spring 这个生态圈对数据持久化层的整合。

  Spring Data是Spring用来解决数据访问的解决方案,它包含了大量关系型数据库以及NoSQL数据库的数据持久层访问解决方案。它包含Spring Data JPA、Spring Data MongoDB、Spring Data Neo4j、Spring Data GernFile、Spring Data Cassandra等。

  目前现有的 Spring-data-jpa,Spring-data-template,Spring-data-mongodb,Spring-data-redis 等。当然,也包括最开始用的 mybatis-spring ,MyBatis 与 Spring 的整合。

  于是,就出现了 Spring-data-jpa ,Spring 与 JPA 的整合。具体详情参看 Spring Data 官网介绍

Spring Data JPA

  正如上所说,Spring Data JPA 是 Spring 与 JPA 的整合。是基于 JPA 对数据库访问层的增强支持。旨在简化数据库访问层,减少工作量

  具体详情参看 Spring Data JPA 官网

核心概念

对于开发者来说,使用起来很简单。下面我们看看 Spring Data JPA 几个核心概念。

  • Repository:最顶层的接口,是一个空的接口,目的是为了统一所有Repository的类型,且能让组件扫描的时候自动识别;

  • CrudRepository :是Repository的子接口,提供CRUD的功能;

  • @NoRepositoryBean
    public interface CrudRepository<T, ID> extends Repository<T, ID> {
    	<S extends T> S save(S entity);
    	<S extends T> Iterable<S> saveAll(Iterable<S> entities);
    	Optional<T> findById(ID id);
    	boolean existsById(ID id);
    	Iterable<T> findAll();
    	Iterable<T> findAllById(Iterable<ID> ids);
    	long count();
    	void deleteById(ID id);
    	void delete(T entity);
    	void deleteAll(Iterable<? extends T> entities);
    	void deleteAll();
    }

    JpaRepository:是PagingAndSortingRepository的子接口,增加了一些实用的功能,比如:批量操作等;

  • @NoRepositoryBean
    public interface JpaRepository<T, ID> extends PagingAndSortingRepository<T, ID>, QueryByExampleExecutor<T> {
        List<T> findAll();
        List<T> findAll(Sort var1);
        List<T> findAllById(Iterable<ID> var1);
        <S extends T> List<S> saveAll(Iterable<S> var1);
        void flush();
        <S extends T> S saveAndFlush(S var1);
        void deleteInBatch(Iterable<T> var1);
        void deleteAllInBatch();
        T getOne(ID var1);
        <S extends T> List<S> findAll(Example<S> var1);
        <S extends T> List<S> findAll(Example<S> var1, Sort var2);
    }

    PagingAndSortingRepository:是 CrudRepository 的子接口,添加分页和排序的功能;

@NoRepositoryBean
public interface PagingAndSortingRepository<T, ID> extends CrudRepository<T, ID> {
	Iterable<T> findAll(Sort sort);
	Page<T> findAll(Pageable pageable);
}

Specification:是Spring Data JPA提供的一个查询规范,要做复杂的查询,只需围绕这个规范来设置查询条件即可;  

public interface Specification<T> extends Serializable {
    long serialVersionUID = 1L;
    static <T> Specification<T> not(Specification<T> spec) {
        return Specifications.negated(spec);
    }
    static <T> Specification<T> where(Specification<T> spec) {
        return Specifications.where(spec);
    }
    default Specification<T> and(Specification<T> other) {
        return Specifications.composed(this, other, CompositionType.AND);
    }
    default Specification<T> or(Specification<T> other) {
        return Specifications.composed(this, other, CompositionType.OR);
    }
    @Nullable
    Predicate toPredicate(Root<T> var1, CriteriaQuery<?> var2, CriteriaBuilder var3);
}

 JpaSpecificationExecutor:用来做负责查询的接口。

public interface JpaSpecificationExecutor<T> {
    Optional<T> findOne(@Nullable Specification<T> var1);
    List<T> findAll(@Nullable Specification<T> var1);
    Page<T> findAll(@Nullable Specification<T> var1, Pageable var2);
    List<T> findAll(@Nullable Specification<T> var1, Sort var2);
    long count(@Nullable Specification<T> var1);

项目实践:

创建一个springboot项目,然后添加以下一些依赖:

5.3.1 添加Spring Data JPA的起步依赖

<!-- springBoot JPA的起步依赖 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

5.3.2 添加数据库驱动依赖+lombok依赖

<!-- MySQL连接驱动 -->
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
</dependency>
 <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <version>1.14.4</version>
    </dependency>

5.3.3 在application.properties中配置数据库和jpa的相关属性

#DB Configuration:
spring.datasource.driverClassName=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/baijie?characterEncoding=utf8
spring.datasource.username=root
spring.datasource.password=123456

#JPA Configuration:
spring.jpa.database=MySQL
#日志显示SQL语句
spring.jpa.show-sql=true
#初始化数据库结构,
#spring.jpa.generate-dd和spring.jpa.hibernate.ddl-auto功能类似,选一即可!
#spring.jpa.generate-ddl=true
#自动更新
spring.jpa.hibernate.ddl-auto=update

其他配置明细:

spring.jpa.hibernate.ddl-auto的四个属性的含义见下表:

属性值作用
create每次加载hibernate时都会删除上一次的生成的表,然后根据你的model类再重新来生成新表,哪怕两次没有任何改变也要这样执行,这就是导致数据库表数据丢失的一个重要原因。
create-drop每次加载hibernate时根据model类生成表,但是sessionFactory一关闭,表就自动删除。
update最常用的属性,第一次加载hibernate时根据model类会自动建立起表的结构(前提是先建立好数据库),以后加载hibernate时根据 model类自动更新表结构,即使表结构改变了但表中的行仍然存在不会删除以前的行。要注意的是当部署到服务器后,表结构是不会被马上建立起来的,是要等应用第一次运行起来后才会。
validate每次加载hibernate时,验证创建数据库表结构,只会和数据库中的表进行比较,不会创建新表,但是会插入新值。

【细节】SpringBoot启动时初始化数据库及spring.jpa.generate-dll与spring.jpa.hibernate.ddl-auto之间的困惑

解答参考链接:https://www.cnblogs.com/StarkBrothers/p/11804554.html

5.3.4 创建实体配置实体

  • @Entity 表示这个类是一个实体类,接受JPA控制管理,对应数据库中的一个表

  • @Table 指定这个类对应数据库中的表名。如果这个类名和数据库表名符合驼峰及下划线规则,可以省略这个注解。

  • @Id 指定这个字段为表的主键

  • @GeneratedValue(strategy=GenerationType.IDENTITY) 指定主键的生成方式

  • @Column 注解针对一个字段,对应表中的一列。nullable = false表示数据库字段不能为空, unique = true表示数据库字段不能有重复值,length = 32表示数据库字段最大程度为32.

  • 如图展示:

JPA提供的四种策略生成器,标准用法为:TABLE,SEQUENCE,IDENTITY,AUTO.

  • TABLE:使用一个特定的数据库表格来保存主键。

  • SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。

  • IDENTITY:主键由数据库自动生成(主要是自动增长型)

  • AUTO:主键由程序控制。

@Entity
@Data
public class User {
    // 主键
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    // 用户名
    private String name;
    // 密码
    private String password;
}

5.3.5 编写UserRepository

public interface UserRepository extends JpaRepository<User,Long>{
   
}

5.3.6 编写测试类

@SpringBootTest
public class JpaTest {

    @Autowired
    private UserRepository userDaoInterface;
    @Test
    public void test1(){

        List<User> all = UserRepository.findAll();
        for (User user : all) {
            System.out.println(user.toString());
        }
    }
}

 【温馨提示】在上述示例中,我们使用的是 JPA 的内置方法 findAll() ,另外也有很多别的方法,如下:

5.3.7 控制台打印信息

 注意:如果是jdk9,执行报错如下:

 原因:jdk缺少相应的jar

解决方案:手动导入对应的maven坐标,如下:

<!--jdk9需要导入如下坐标-->
<dependency>
    <groupId>javax.xml.bind</groupId>
    <artifactId>jaxb-api</artifactId>
    <version>2.3.0</version>
</dependency>
5.3.7.1 增删改功能代码:
	/**
	 * 保存
	 */
	@Test
	public void saveUser(){
		User user=new User();
		user.setName("张飞");
		user.setPassword("666");
		dao.save(user);
	}

	/**
	 * 修改
	 */
	@Test
	public void updateUser(){
        
		Optional<User> opt = dao.findById(62);
		User user = opt.get();
		System.out.println(user.toString());
		user.setPassword("999999");
		dao.save(user);
	}

	/**
	 * 删除
	 */
	@Test
	public void deleteById(){
		dao.deleteById(70);
	}

    /**
	 * 删除方式2
	 */
	@Test
	public void deleteById(){
		User user=new User();
		user.setId(89l);
		dao.delete(user);
	}
5.3.7.2 分页+排序功能

【温馨提示】导入包时,小心别导入错了哦。比如Pageable接口的包如下:

import org.springframework.data.domain.Pageable;

Pageable接口是一个分页接口。PageRequest类是此接口中的一个实现类!  

5.3.8 dao层添加方法

单元测试:  

大家思考一个问题。为什么5.3.8中的方法findByName()明明是自己写的,为什么会成功执行?

答:因为方法名碰巧符合了spring Data Jpa方法命名规则!

5.3.9 方法命名规则查询

5.3.9.1 概述:

顾名思义,方法命名规则查询就是根据方法的名字,就能创建查询。只需要按照Spring Data JPA提供的方法命名规则定义方法的名称,就可以完成查询工作。Spring Data JPA在程序执行的时候会根据方法名称进行解析,并自动生成查询语句进行查询

按照Spring Data JPA 定义的规则,查询方法以findBy开头,涉及条件查询时,条件的属性用条件关键字连接,关键字后面连接的就是实体类的属性名。要注意的是:条件属性首字母需大写。框架在进行方法名解析时,会先把方法名多余的前缀截取掉,然后对剩下部分进行解析。

比如:

具体的关键字,使用方法和生产成SQL如下表所示:

KeywordSampleJPQL
AndfindByLastnameAndFirstname… where x.lastname = ?1 and x.firstname = ?2
OrfindByLastnameOrFirstname… where x.lastname = ?1 or x.firstname = ?2
Is,EqualsfindByFirstnameIs,findByFirstnameEquals… where x.firstname = ?1
BetweenfindByStartDateBetween… where x.startDate between ?1 and ?2
LessThanfindByAgeLessThan… where x.age < ?1
LessThanEqualfindByAgeLessThanEqual… where x.age ⇐ ?1
GreaterThanfindByAgeGreaterThan… where x.age > ?1
GreaterThanEqualfindByAgeGreaterThanEqual… where x.age >= ?1
AfterfindByStartDateAfter… where x.startDate > ?1
BeforefindByStartDateBefore… where x.startDate < ?1
IsNullfindByAgeIsNull… where x.age is null
IsNotNull,NotNullfindByAge(Is)NotNull… where x.age not null
LikefindByFirstnameLike… where x.firstname like ?1
NotLikefindByFirstnameNotLike… where x.firstname not like ?1
StartingWithfindByFirstnameStartingWith… where x.firstname like ?1 (parameter bound with appended %)
EndingWithfindByFirstnameEndingWith… where x.firstname like ?1 (parameter bound with prepended %)
ContainingfindByFirstnameContaining… where x.firstname like ?1 (parameter bound wrapped in %)
OrderByfindByAgeOrderByLastnameDesc… where x.age = ?1 order by x.lastname desc
NotfindByLastnameNot… where x.lastname <> ?1
InfindByAgeIn(Collection ages)… where x.age in ?1
NotInfindByAgeNotIn(Collection age)… where x.age not in ?1
TRUEfindByActiveTrue()… where x.active = true
FALSEfindByActiveFalse()… where x.active = false
IgnoreCasefindByFirstnameIgnoreCase… where UPPER(x.firstame) = UPPER(?1)
5.3.9.2 举例:实现模糊查询

多个参数模糊查询:

  • 26
    点赞
  • 21
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LiRS2001

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值