SpringBoot整合Spring Data JPA
创始人
2024-01-25 19:08:31
0

文章目录

  • 1 Spring Data JPA
    • 1.1 简介
      • 1.1.1 JPA
      • 1.1.2 Spring Data JPA
    • 1.2 配置文件
    • 1.3 操作使用JPA
      • 1.3.1 实体类相关
      • 1.3.2 Dao层
        • 1.3.2.1 基本示例
        • 1.3.2.2 @Query注解
        • 1.3.2.3 SQL传参
        • 1.3.2.4 @Modifying注解
      • 1.3.3 Service层
      • 1.3.4 Controller层
    • 1.4 Spring Data JPA提供的核心接口
      • 1.4.1 Repository接口
        • 1.4.1.1 方法名称命名方式
        • 1.4.1.2 注解方式
      • 1.4.2 CrudRepository接口
      • 1.4.3 PagingAndSortingRepository接口
      • 1.4.4 JpaRepository接口
      • 1.4.5 JpaSpecificationExecutor接口

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
导入依赖:

org.springframework.bootspring-boot-starter-data-jpa

数据库配置:

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;@Transientprivate 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 {//查询以某个字符开始的所有书List getBooksByAuthorStartingWith(String author);//查询单价大于某个值的所有书List 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 getBookByIdAndAuthor(@Param("author") String author,@Param("id") Integer id);@Query("select b from t_book b where b.id getBookByIdAndName(String name,Integer id);
}

解释:

  • 自定义Dao继承自JpaRepositiory,需要两个参数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 {@AutowiredBookDao bookDao;//save方法由JpaRepository接口提供public void addBook(Book book){bookDao.save(book);}//分页查询public Page getBookByPage(Pageable pageable){return bookDao.findAll(pageable);}public List getBooksByAuthorStartingWith(String author){return bookDao.getBooksByAuthorStartingWith(author);}public List getBooksByPriceGreaterThan(Float price){return bookDao.getBooksByPriceGreaterThan(price);}public Book getMaxIdBook(){return bookDao.getMaxIdBook();}public List getBookByIdAndName(String name,Integer id){return bookDao.getBookByIdAndName(name,id);}public List 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 {@AutowiredBookService bookService;@GetMapping(value = "/findAll")public void findAll(){PageRequest pageRequest = PageRequest.of(2,3);Page page = bookService.getBookByPage(pageRequest);System.out.println("总页数:"+page.getTotalPages());System.out.println("总记录数:"+page.getTotalElements());System.out.println("查询结果:"+page.getContent());//从0开始记,所以加上1System.out.println("当前页数:"+(page.getNumber()+1));System.out.println("当前记录数:"+page.getNumberOfElements());System.out.println("每页记录数:"+page.getSize());}@GetMapping(value = "search")public void search(){List bs1 = bookService.getBookByIdAndAuthor("鲁迅",7);List bs2 = bookService.getBooksByAuthorStartingWith("吴");List bs3 = bookService.getBookByIdAndName("西",8);List 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是主键类型

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 {List findByName (String name);List findByNameOrAge(String name, Integer age);List findByNameLike(String name);
}

1.4.1.2 注解方式

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

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

测试

 @Testpublic void queryByUse() {List 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是主键类型
CrudRepository接口,主要是完成一些增删改查的操作,封装了好多接口可以直接使用,当然依旧可以使用注解方式
注意CrudRepository接口继承了Repository接口

public interface UserCrudRepository extends CrudRepository {
}

测试

 @Testpublic 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是主键类型
PagingAndSortingRepository主要是提供了分页与排序的操作
注意PagingAndSortingRepository接口继承了CrudRepository接口

import org.springframework.data.repository.PagingAndSortingRepository;public interface UserPagingAndSortingRepository  extends PagingAndSortingRepository {
}

测试使用

//排序操作
@Test
public void pageAndSort(){Sort.Order order = new Sort.Order(Sort.Direction.DESC,"id");Sort sort = Sort.by(order);List all = (List)dao.findAll(sort);System.out.println(JSON.toJSON(all));
}
//分页操作
@Test
public void page(){PageRequest pageParam = PageRequest.of(0, 1);Page page = dao.findAll(pageParam);System.out.println("总页数:"+page.getTotalPages());System.out.println("总记录数:"+page.getTotalElements());System.out.println("查询结果:"+page.getContent());//从0开始记,所以加上1System.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是要查询的表
JpaSpecificationExecutor接口主要是提供了多条件查询的支持,并且可以在查询中添加分页与排序。
注意JpaSpecificationExecutor是单独存在,完全独立的,一般是和上面接口联合使用

public interface UserJPASpecificationExecutor extends JpaRepository,JpaSpecificationExecutor {}

测试使用

 @Autowiredprivate UserJPASpecificationExecutor executor;@Testpublic void jpsExecutorPage1(){Specification spec = new Specification(){/*** 封装了查询条件* root:查询对象的属性封装* query:封装了要执行的查询中的各个部分信息:select from  order by 等,询哪些字段,排序是什么(主要是把多个查询的条件连系起来)* criteriaBuilder:查询条件的构造器,定义不同的查询条件* 字段之间是什么关系,如何生成一个查询条件,每一个查询条件都是什么方式*                      主要判断关系(和这个字段是相等,大于,小于like等)*/@Overridepublic Predicate toPredicate(Root root, CriteriaQuery query, CriteriaBuilder cb) {/*** 参数一:查询属性* 参数二:条件的值*/List 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 list = executor.findAll(spec);System.out.println(JSON.toJSON(list));}

相关内容

热门资讯

深圳战略性新兴产业和未来产业规... 只要几千元,一台无人机,就可以全新视角俯瞰世界;未来的小城市,也可能会用上“云轨”绿色交通,成为轨道...
中国有潜力的新兴行业 涓浗鏈... 确定不再关注此人吗确定取消文/新浪财经意见领袖专栏作家程实、钱智俊“世界赠我以创伤,而伤口将长出翅膀...
新兴产业基金是否仍有领涨潜力 ... 新兴产业基金是否仍有领涨潜力中国基金报中国基金报记者杨磊新兴产业基金领涨近一年半的时间,未来是否还会...
全国公布首批66个战略性新兴产... 原标题:全国公布首批66个战略性新兴产业集群名单,山东入选数量居首近日,国家发展改革委下发通知,在新...
西安大学生创业贷款最高50万元... 27岁的肖潼杰大学毕业后,和亲戚合伙开办了一家装修公司。日前,他来到西安市就业服务中心,将一整套资料...
妇联网谈:青岛女大学大学生创业... 妇联网谈:青岛女大学生可申请5万无息贷款来源:青岛新闻网作者:谢小真2015-07-字号:A-A+青...
新兴的服务行业有哪些 新兴的服... 2018年,江苏服务业保持总体平稳较快的发展态势,产业规模继续扩张,占GDP比重稳步攀升,内部结构不...
重庆加快发展战略性新兴服务业 ... 重庆商报讯昨日记者从重庆市政府网获悉,市政府办公厅发布《加快发展战略性新兴服务业的实施意见》,提出到...
服务业岗位受青睐 新兴产业技术... □记者原付川3月5日至13日,雄安新区在三县县城及部分乡镇,举办了9场春风行动专场招聘会。记者走访发...
本土创业第一读本孙陶然创业36... 拉卡拉集团董事长孙陶然百万册畅销书《创业36条军规》,近日将由中信出版社再版上市。本次再版,作者孙陶...