首页 > 其他分享 >SpringBoot整合Spring Data JPA

SpringBoot整合Spring Data JPA

时间:2022-11-16 21:13:53浏览次数:38  
标签:name JPA Spring List SQL import Data public

目录

1 Spring Data JPA

1.1 简介

1.1.1 JPA

JPAJava Persistence API)即java持久化API,它的出现主要是为了简化持久层开发以及整合ORM技术,结束Hibernate、TopLink、JDO等ORM框架各自为营的局面。JPA是在吸收现有ORM框架的基础上发展而来,易于使用,伸缩性强。

JPA诞生的缘由是为了整合第三方ORM框架,建立一种标准的方式,是JDK为了实现ORM的天下归一,目前也是在按照这个方向发展,但是还没能完全实现。在ORM框架中,Hibernate是一支很大的部队,使用很广泛,也很方便,能力也很强,同时Hibernate也是和JPA整合的比较良好,我们可以认为JPA是标准,事实上也是,JPA几乎都是接口,实现都是Hibernate在做,宏观上面看,在JPA的统一之下Hibernate很良好的运行

总的来说,JPA包括以下三方面的技术:

  • ORM映射元数据:支持XML和注解两种元数据的形式,元数据描述对象和表之间的映射关系
  • API:操作实体对象来执行CRUD操作
  • 查询语言:通过面向对象而非面向数据库的查询语言查询数据,避免程序的SQL语句紧密耦合

1.1.2 Spring Data JPA

Spring Data JPA的基本介绍:
Spring Data JPASpring Data家族的一部分,可以轻松实现基于JPA的存储库。此模块处理对基于JPA的数据访问层的增强支持。它使构建使用数据访问技术的Spring驱动应用程序变得更加容易。底层是使用hibernate实现

在相当长的一段时间内,实现应用程序的数据访问层一直很麻烦。必须编写太多样板代码来执行简单查询以及执行分页和审计。Spring Data JPA旨在减少实际需要的工作量来显著改善数据访问层的实现

1.2 配置文件

pom依赖
SpringBoot整合Spring Data JPA
导入依赖:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>

数据库配置:

spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost:3306/chapter05?characterEncoding=utf8&serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
#表示Jpa对应的数据库是mysql
spring.jpa.show-sql=true
#项目启动时根据实体类更新数据库中的表
spring.jpa.hibernate.ddl-auto=update

其中:spring.jpa.hibernate.ddl-auto,可选参数:

  • create:每次运行程序时,都会重新创建表,故而数据会丢失
  • create-drop:每次运行程序时会先创建表结构,然后程序结束时清空表
  • update:每次运行程序,没有表时会创建表,如果对象发生改变会更新表结构,原有数据不会清空,只会更新(推荐使用)
  • validate:运行程序会校验数据于数据库的字段类型是否相同,字段不同会报错
  • none:禁用DDL处理

1.3 操作使用JPA

1.3.1 实体类相关

创建实体类:

import lombok.Data;
import javax.persistence.*;
 
@Data
@Entity
@Table(name = "t_book")
public class Book {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Integer id;
    @Column(name = "book_name")
    private String name;
    @Column(name = "book_author")
    private String author;
    private Float price;
    @Transient
    private String description;
}

注解解释:

  • @Entity:注解表示该类是一个实体类,在项目启动时会根据该类自动生成一张表,表的名称即@Entity注解中name的值,如果不配置name,默认表名为类名
    指定实体名称(表名):
    • 没有指定name属性且没有使用@Table,命名为类名生成
    • 指定name属性且没有使用@Table,命名为name属性value
    • 指定name属性且使用了@Table指定name,命名以@Tablenamevalue
  • @Id:所有的实体类都要有的主键,@Id注解表示该属性是一个主键
  • @GneeratedValue:注解表示主键自动生成,strategy则表示主键的生成策略
    JPA自带的几种主键生成策略:
    • TABLE:使用一个特定的数据库表格来保存主键
    • SEQUENCE:根据底层数据库的序列来生成主键,条件是数据库支持序列。这个值要于generator一起使用,generator指定生成主键的生成器
    • IDENTITY:主键由数据库自动生成(主要支持自动增长的数据库,如mysql)
    • AUTO:主键由程序控制,也是GenerationType的默认值,mysql不支持,会报错:test.hibernate_sequence不存在
  • @Column:默认情况下,生成的表中字段的名称是实体类中属性的名称,通过@Column注解可以定制生成的字段的属性,name表示该属性对应的数据表中字段的名称,nullable表示该字段非空
  • @Transient:注解表示在生成数据库的表时,该属性被忽略,即不生成对应的字段

1.3.2 Dao层

1.3.2.1 基本示例

创建Dao接口,继承JpaRepository,代码如下:


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.Component;
 
import java.util.List;
 
@Component
public interface BookDao extends JpaRepository<User,Integer> {
    //查询以某个字符开始的所有书
    List<Book> getBooksByAuthorStartingWith(String author);
    //查询单价大于某个值的所有书
    List<Book> getBooksByPriceGreaterThan(Float price);
    @Query(value = "select * from t_book where id=(select max(id) from t_book)",nativeQuery = true)
    Book getMaxIdBook();
    @Query("select b from t_book b where b.id>:id and b.author=:author")
    List<Book> getBookByIdAndAuthor(@Param("author") String author,@Param("id") Integer id);
    @Query("select b from t_book b where b.id<?2 and b.name like %?1%")
    List<Book> getBookByIdAndName(String name,Integer id);
}

解释:

  • 自定义Dao继承自JpaRepositiory<T,ID>,需要两个参数T指当前需要映射得实体,ID指当前映射得实体中的主键类型。JpaRepositiory中提供了一些基本的数据操作方法,有基本的增删改查、分页查询、排序查询等
  • Spring Data JPA中,只要方法的定义符合既定规范,Spring Data就能分析出开发者意图,从而避免开发者定义SQL

1.3.2.2 @Query注解

@Query注解使用起来很简单,默认的属性是value,就是当前写的SQL语句,有时会用到nativeQuery属性,这个属性是用来标记当前的SQL是本地SQL,还是符合JPA语法规范的SQL
这里需要解释一下本地SQL和JPA语法规范的SQL区别。

  • 本地SQL:根据实际使用的数据库类型写的SQL,这种SQL中使用到的一些语法格式不能被JPA解析以及可能不兼容其他数据库,这种SQL称为本地SQL,此时需要将nativeQuery属性设置为true,否则会报错。
  • JPA语法规范的SQL:往往这种SQL本身是不适用于任何数据库的,需要JPA将这种SQL转换成真正当前数据库所需要的SQL语法格式。

注意JPA很好的一个特性就是用JPA语法规范写的SQL,会根据当前系统使用的数据库类型改变生成的SQL语法,兼容数据库类型的切换,如之前使用的是MySQL,现在换成Oracle,由于不同类型的数据库,SQL语法会有区别,如果使用的是mybatis,就需要手动去改SQL兼容Oracle,而JPA就不用,无缝对接。
说明:很大的时候使用JPA感觉都是为了兼容后期可能会有数据库切换的问题,所以在使用JPA的时候,不要去使用本地SQL,这就违背了使用JPA的初衷,让nativeQuery属性保持默认值即可

1.3.2.3 SQL传参

根据这个例子再引出一些常用的东西,代码如下:

//示例1
@Query("select t from Device t where t.deviceSn=:deviceSn and t.deleteFlag=1")
Device findExistDevice(@Param("deviceSn") String deviceSn);
//示例2
@Query("select t from Device t where t.deviceSn=:deviceSn and t.deviceType =:deviceType and t.deleteFlag=1")
Device findExistDevice(@Param("deviceSn") String deviceSn,@Param("deviceType")Integer deviceType);
//示例3
@Query("select t from Device t where t.deviceSn=?1 and t.deviceType = ?2 and t.deleteFlag=1")
Device findDevice(String deviceSn,Integer deviceType);

SQL上使用占位符的两种方式:

  • 使用:后加变量的名称,需要使用@Param注解来指定变量名
  • 使用?后加方法参数的位置。就需要注意参数的位置。

SQL语句中直接用实体类代表表名,因为在实体类中使用了@Table注解,将该实体类和表进行了关联

1.3.2.4 @Modifying注解

相信在正常的项目开发中都会涉及到修改数据信息的操作,如逻辑删除、封号、解封、修改用户名、头像等等。在使用JPA的时候,如果@Query涉及到update就必须同时加上@Modifying注解,注明当前方法是修改操作。

如下代码:

@Modifying
@Query("update Device t set t.userName =:userName where t.id =:userId")
User updateUserName(@Param("userId") Long userId,@Param("userName") String userName);

当在测试类中直接调用时,需要需要在测试类中添加注解@Transactional,但是@Transactional@Test一起连用会导致事务自动回滚,这时候需要指定事务不回滚@Rollback(false)

@Test
@Transactional
@Rollback(false)
public void updateById() {
    dao11.updateById("张三",1);
}

1.3.3 Service层

创建BookService,代码如下:

import com.example.demo.dao.BookDao;
import com.example.demo.domain.Book;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
 
import java.util.List;
 
@Service
public class BookService {
    @Autowired
    BookDao bookDao;
 
    //save方法由JpaRepository接口提供
    public void addBook(Book book){
        bookDao.save(book);
    }
 
    //分页查询
    public Page<Book> getBookByPage(Pageable pageable){
        return bookDao.findAll(pageable);
    }
 
    public List<Book> getBooksByAuthorStartingWith(String author){
        return bookDao.getBooksByAuthorStartingWith(author);
    }
 
    public List<Book> getBooksByPriceGreaterThan(Float price){
        return bookDao.getBooksByPriceGreaterThan(price);
    }
 
    public Book getMaxIdBook(){
        return bookDao.getMaxIdBook();
    }
 
    public List<Book> getBookByIdAndName(String name,Integer id){
        return bookDao.getBookByIdAndName(name,id);
    }
 
    public List<Book> getBookByIdAndAuthor(String author,Integer id){
        return bookDao.getBookByIdAndAuthor(author,id);
    }
}

1.3.4 Controller层

创建BookContrller,实现对数据的测试,代码如下:

import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
 
import java.util.List;
 
@RestController
public class BookController {
    @Autowired
    BookService bookService;
 
    @GetMapping(value = "/findAll")
    public void findAll(){
        PageRequest pageRequest = PageRequest.of(2,3);
        Page<Book> page = bookService.getBookByPage(pageRequest);
        System.out.println("总页数:"+page.getTotalPages());
        System.out.println("总记录数:"+page.getTotalElements());
        System.out.println("查询结果:"+page.getContent());
        //从0开始记,所以加上1
        System.out.println("当前页数:"+(page.getNumber()+1));
        System.out.println("当前记录数:"+page.getNumberOfElements());
        System.out.println("每页记录数:"+page.getSize());
    }
 
    @GetMapping(value = "search")
    public void search(){
        List<Book> bs1 = bookService.getBookByIdAndAuthor("鲁迅",7);
        List<Book> bs2 = bookService.getBooksByAuthorStartingWith("吴");
        List<Book> bs3 = bookService.getBookByIdAndName("西",8);
        List<Book> bs4 = bookService.getBooksByPriceGreaterThan(30F);
 
        Book b = bookService.getMaxIdBook();
        System.out.println("bs1:"+bs1);
        System.out.println("bs2:"+bs2);
        System.out.println("bs3:"+bs3);
        System.out.println("bs4:"+bs4);
        System.out.println("b:"+b);
    }
 
    @GetMapping(value = "/save")
    public void save(){
        Book book = new Book();
        book.setAuthor("鲁迅");
        book.setName("呐喊");
        book.setPrice(23F);
        bookService.addBook(book);
    }
}

代码解释:

在findAll接口中,首先通过调用PageRequest中的of方法构造PageRequest对象。of方法接收两个参数:第一个参数是页数,从0开始计数,第二个参数是每页显示的条数

1.4 Spring Data JPA提供的核心接口

1.4.1 Repository接口

Repository<T, ID>:其中T是要查询的表,ID是主键类型

1.4.1.1 方法名称命名方式

方法名称命名查询方式,hibername会根据方法名生成对应的sql语句
注意:方法名称必须要遵循驼峰命名规则

import cn.jzh.entity.User;
import org.springframework.data.repository.Repository;

import java.util.List;

/**
 * Repository接口方法名称命名查询
 */
public interface UserRepository extends Repository<User,Integer> {

    List<User> findByName (String name);

    List<User> findByNameOrAge(String name, Integer age);

    List<User> findByNameLike(String name);
}

1.4.1.2 注解方式

@Query注解查询方式,用这样的方式就不局限于方法名(hibernate根据方法名生成sql)

@Query("from User u where u.name = ?1")
List<User> queryByUseHQL (String name);

 @Modifying
@Query("update User set name =?1 where id =?2")
void updateById(String name, Integer id);

测试

 @Test
    public void queryByUse() {
        List<User> list = dao11.queryByUseHQL("哈哈");
        System.out.println(JSON.toJSON(list));
    }
    @Test
    @Transactional
    @Rollback(false)
    public void updateById() {
        dao11.updateById("张三",1);

    }

当在测试类中直接调用时,需要需要在测试类中添加注解@Transactional,但是@Transactional@Test一起连用会导致事务自动回滚,这时候需要指定事务不回滚@Rollback(false)

1.4.2 CrudRepository接口

CrudRepository<T, ID>:其中T是要查询的表,ID是主键类型
CrudRepository接口,主要是完成一些增删改查的操作,封装了好多接口可以直接使用,当然依旧可以使用注解方式
注意CrudRepository接口继承了Repository接口

public interface UserCrudRepository extends CrudRepository<User,Integer> {
}

测试

 @Test
    public void testCrudAdd(){
        User u = new User();
        u.setName("张无忌");
        u.setAge(20);
        u.setAddress("冰火岛");
        u.setTime(new Date());
        User save = userCrudRepository.save(u);
        System.out.println(save);
    }

1.4.3 PagingAndSortingRepository接口

PagingAndSortingRepository<T, ID>:其中T是要查询的表,ID是主键类型
PagingAndSortingRepository主要是提供了分页与排序的操作
注意PagingAndSortingRepository接口继承了CrudRepository接口

import org.springframework.data.repository.PagingAndSortingRepository;

public interface UserPagingAndSortingRepository  extends PagingAndSortingRepository<User,Integer> {
}

测试使用

//排序操作
@Test
public void pageAndSort(){
    Sort.Order order = new Sort.Order(Sort.Direction.DESC,"id");
    Sort sort = Sort.by(order);
    List<User> all = (List<User>)dao.findAll(sort);
    System.out.println(JSON.toJSON(all));
}
//分页操作
@Test
public void page(){
    PageRequest pageParam = PageRequest.of(0, 1);
    Page<User> page = dao.findAll(pageParam);

    System.out.println("总页数:"+page.getTotalPages());
    System.out.println("总记录数:"+page.getTotalElements());
    System.out.println("查询结果:"+page.getContent());
    //从0开始记,所以加上1
    System.out.println("当前页数:"+(page.getNumber()+1));
    System.out.println("当前记录数:"+page.getNumberOfElements());
    System.out.println("每页记录数:"+page.getSize());
}

1.4.4 JpaRepository接口

该接口继承了PagingAndSortingRepository接口,是开发中最常用的接口
对继承的父接口中的方法的返回值进行适配
具体使用方法参考上面例子

1.4.5 JpaSpecificationExecutor接口

JpaSpecificationExecutor<T>,其中T是要查询的表
JpaSpecificationExecutor接口主要是提供了多条件查询的支持,并且可以在查询中添加分页与排序。
注意JpaSpecificationExecutor是单独存在,完全独立的,一般是和上面接口联合使用

public interface UserJPASpecificationExecutor 
	extends JpaRepository<User, Integer>,JpaSpecificationExecutor<User> {}

测试使用

 @Autowired
    private UserJPASpecificationExecutor executor;
    @Test
    public void jpsExecutorPage1(){

        Specification<User> spec = new Specification<User>(){

            /**
             * 封装了查询条件
             * root:查询对象的属性封装
             * query:封装了要执行的查询中的各个部分信息:select from  order by 等,询哪些字段,排序是什么(主要是把多个查询的条件连系起来)
             * criteriaBuilder:查询条件的构造器,定义不同的查询条件
             * 字段之间是什么关系,如何生成一个查询条件,每一个查询条件都是什么方式
 *                      主要判断关系(和这个字段是相等,大于,小于like等)
             */
            @Override
            public Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {
                /**
                 * 参数一:查询属性
                 * 参数二:条件的值
                 */
                List<Predicate> list = new ArrayList<>();
                list.add(cb.equal(root.get("name"), "张三"));

                Predicate[] arr = new Predicate[list.size()];
                Predicate[] predicates = list.toArray(arr);
                return cb.and(predicates);

				或者这样操作
				return cb.and(cb.equal(root.get("name"), "张三"),cb.equal(root.get("name"), "张三"));
            }
        };

        List<User> list = executor.findAll(spec);
        System.out.println(JSON.toJSON(list));

    }

标签:name,JPA,Spring,List,SQL,import,Data,public
From: https://www.cnblogs.com/jingzh/p/16897508.html

相关文章

  • Datawahle — 2022年11月组队学习 — 李宏毅机器学习 — TASK02
    学习时间:2022年11月15-2022年11月16日学习内容:李宏毅机器学习视频P3和P4学习笔记 ......
  • 肖sir__Java中spring boot
    1.1springboot介绍什么是SpringBoot?SpringBoot是由Pivotal团队提供的全新框架,其设计目的是用来简化新Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来......
  • 第一章 进入SpringBoot世界
    第一章进入SpringBoot世界1.1什么是SpringBootSpringBoot的设计初衷是解决Spring各版本配置工作过于繁重的问题,简化初始搭建流程,降低开发难度,使开发人员只需要专注应用......
  • Spring注解开发
    1、使用注解需要导入的依赖1、1在application.xml文件中加入该约束xmlns:context=http://www.springframework.org/schema/contexthttp://www.springframew......
  • Spring--注解开发定义Bean
    注解开发先看一看之前的bean的做法:所谓注解开发,当然就要用到注解啊,就是在BookDao接口的实现类里面进行注解的定义如图所示:而在.xml文件里面,就需要进行这样一个操作:......
  • 蓝牙mesh组网实践(dataflash的占用与整理)
    目录蓝牙mesh协议中有不少数据需要存储dataflash,以记录网络中的数据。一些数据只需要配网时保存进dataflash,比如说本节点的网络地址、各类密钥等;另一些数据需要在运行中动......
  • datatables参数配置详解
    配置//@translatorcodepiano//@blogcodepiano//@[email protected]//尝试着翻译了一下,难免有错误的地方,欢迎发邮件告知,多谢。/*--------------......
  • 12.Seata:Spring Cloud Alibaba分布式事务组件(非常详细)
    随着业务的不断发展,单体架构已经无法满足我们的需求,分布式微服务架构逐渐成为大型互联网平台的首选,但所有使用分布式微服务架构的应用都必须面临一个十分棘手的问题,那就是......
  • Spring--数据库资源管理遗留问题
    遗留问题的解决在我们要再试一试其他属性的时候,就出现了一些小问题:定义的情况下,在.xml文件里面调用:却发现输出是这样的:这完全不对等啊!之后发现是系统的值,优先级要高于......
  • Error: Failed to download metadata for repo 'appstream': Cannot prepare internal
    报错原因:     Centos8于2021年年底停止了服务,大家再在使用yum源安装时候,出现下面错误“错误:Failedtodownloadmetadataforrepo‘AppStream’:Cannotprepa......